volute 0.19.0 → 0.20.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 +66 -66
- package/dist/activity-events-OMXKXD5N.js +16 -0
- package/dist/{chunk-Z524RFCJ.js → chunk-5XNT2472.js} +1 -1
- package/dist/{chunk-FGV2H4TX.js → chunk-FGSYHIS3.js} +112 -24
- package/dist/chunk-GZ7DW4YL.js +97 -0
- package/dist/{chunk-OTWLI7F4.js → chunk-IKMY5X76.js} +2 -2
- package/dist/{chunk-VQWDC6UK.js → chunk-NSE7VJQA.js} +17 -0
- package/dist/{chunk-EMQSAY3B.js → chunk-O6ASDHFO.js} +2 -1
- package/dist/{chunk-2TJGRJ4O.js → chunk-PUVXOZ6T.js} +8 -2
- package/dist/{chunk-4KPUF5JD.js → chunk-TIWH32HP.js} +15 -2
- package/dist/chunk-UU7A7KLB.js +58 -0
- package/dist/cli.js +19 -9
- package/dist/{daemon-restart-JMZM3QY4.js → daemon-restart-KPSWNYTH.js} +3 -3
- package/dist/daemon.js +1802 -1082
- package/dist/{db-5ZVC6MQF.js → db-C2CJ46ZU.js} +2 -2
- package/dist/{delivery-manager-ISTJMZDW.js → delivery-manager-CSG7LXA4.js} +3 -3
- package/dist/{export-GCDNQCF3.js → export-6QBUOQGC.js} +2 -2
- package/dist/file-C57SK5DK.js +204 -0
- package/dist/{import-M63VIUJ5.js → import-XEC34Y4Z.js} +1 -1
- package/dist/{mind-PQ5NCPSU.js → mind-Z7CKD6DG.js} +2 -2
- package/dist/mind-activity-tracker-624QLQLC.js +19 -0
- package/dist/{mind-manager-RVCFROAY.js → mind-manager-3DMYKZPB.js} +3 -3
- package/dist/{package-MYE2ZJLV.js → package-4NHAVUUI.js} +1 -1
- package/dist/{pages-AXCOSY3P.js → pages-4DGQT7ZA.js} +2 -2
- package/dist/{publish-YB377JB7.js → publish-TAJUET4I.js} +7 -4
- package/dist/{schedule-LMX7GAQZ.js → schedule-FFZG23IW.js} +25 -5
- package/dist/{schema-5BW7DFZI.js → schema-GFH6RV3W.js} +3 -1
- package/dist/{setup-OH3PJUJO.js → setup-52YRV7VP.js} +16 -0
- package/dist/skills/volute-mind/SKILL.md +33 -3
- package/dist/{sprout-VBEX63LX.js → sprout-QN7Y4VVO.js} +3 -3
- package/dist/{status-JCJAOXTW.js → status-FU2PFVVF.js} +3 -2
- package/dist/{up-WG65SWJU.js → up-FS7CKM6V.js} +1 -1
- package/dist/web-assets/assets/index-CUZTZzaW.js +64 -0
- package/dist/web-assets/assets/index-adVuCkqy.css +1 -0
- package/dist/web-assets/index.html +2 -2
- package/drizzle/0012_activity.sql +11 -0
- package/drizzle/meta/0012_snapshot.json +7 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +1 -1
- package/templates/_base/home/.config/routes.json +2 -2
- package/templates/_base/home/VOLUTE.md +1 -1
- package/templates/_base/src/lib/daemon-client.ts +22 -0
- package/templates/_base/src/lib/transparency.ts +1 -1
- package/templates/claude/.init/.config/routes.json +7 -1
- package/templates/pi/.init/.config/routes.json +7 -1
- package/templates/pi/src/agent.ts +11 -5
- package/templates/pi/src/lib/session-context-extension.ts +6 -4
- package/templates/pi/src/server.ts +2 -0
- package/dist/web-assets/assets/index-BAbuRsVF.css +0 -1
- package/dist/web-assets/assets/index-CiQhSKi_.js +0 -63
- /package/dist/{chunk-VE4D3GOP.js → chunk-7UFKREVW.js} +0 -0
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
DeliveryManager,
|
|
4
4
|
getDeliveryManager,
|
|
5
5
|
initDeliveryManager
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-FGSYHIS3.js";
|
|
7
7
|
import "./chunk-YUIHSKR6.js";
|
|
8
|
-
import "./chunk-
|
|
9
|
-
import "./chunk-
|
|
8
|
+
import "./chunk-5XNT2472.js";
|
|
9
|
+
import "./chunk-NSE7VJQA.js";
|
|
10
10
|
import "./chunk-EBGCNDMM.js";
|
|
11
11
|
import "./chunk-K3NQKI34.js";
|
|
12
12
|
export {
|
|
@@ -59,9 +59,9 @@ async function run(args) {
|
|
|
59
59
|
});
|
|
60
60
|
if (includeHistory) {
|
|
61
61
|
try {
|
|
62
|
-
const { getDb } = await import("./db-
|
|
62
|
+
const { getDb } = await import("./db-C2CJ46ZU.js");
|
|
63
63
|
const { eq } = await import("drizzle-orm");
|
|
64
|
-
const { mindHistory } = await import("./schema-
|
|
64
|
+
const { mindHistory } = await import("./schema-GFH6RV3W.js");
|
|
65
65
|
const db = await getDb();
|
|
66
66
|
const rows = await db.select().from(mindHistory).where(eq(mindHistory.mind, name));
|
|
67
67
|
addHistoryToArchive(zip, rows);
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
resolveMindName
|
|
4
|
+
} from "./chunk-NAOW2CLO.js";
|
|
5
|
+
import {
|
|
6
|
+
parseArgs
|
|
7
|
+
} from "./chunk-D424ZQGI.js";
|
|
8
|
+
import {
|
|
9
|
+
daemonFetch
|
|
10
|
+
} from "./chunk-WC6ZHVRL.js";
|
|
11
|
+
import "./chunk-EBGCNDMM.js";
|
|
12
|
+
import "./chunk-K3NQKI34.js";
|
|
13
|
+
|
|
14
|
+
// src/commands/file.ts
|
|
15
|
+
async function run(args) {
|
|
16
|
+
const subcommand = args[0];
|
|
17
|
+
switch (subcommand) {
|
|
18
|
+
case "send":
|
|
19
|
+
await sendFile(args.slice(1));
|
|
20
|
+
break;
|
|
21
|
+
case "list":
|
|
22
|
+
await listPending(args.slice(1));
|
|
23
|
+
break;
|
|
24
|
+
case "accept":
|
|
25
|
+
await acceptFile(args.slice(1));
|
|
26
|
+
break;
|
|
27
|
+
case "reject":
|
|
28
|
+
await rejectFile(args.slice(1));
|
|
29
|
+
break;
|
|
30
|
+
case "trust":
|
|
31
|
+
await trustSender(args.slice(1));
|
|
32
|
+
break;
|
|
33
|
+
case "untrust":
|
|
34
|
+
await untrustSender(args.slice(1));
|
|
35
|
+
break;
|
|
36
|
+
case "--help":
|
|
37
|
+
case "-h":
|
|
38
|
+
case void 0:
|
|
39
|
+
printUsage();
|
|
40
|
+
break;
|
|
41
|
+
default:
|
|
42
|
+
printUsage();
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function printUsage() {
|
|
47
|
+
console.log(`Usage:
|
|
48
|
+
volute file send <path> <target-mind> [--mind <name>] Send a file to another mind
|
|
49
|
+
volute file list [--mind <name>] List pending incoming files
|
|
50
|
+
volute file accept <id> [--mind <name>] Accept a pending file
|
|
51
|
+
volute file reject <id> [--mind <name>] Reject a pending file
|
|
52
|
+
volute file trust <sender> [--mind <name>] Trust a sender (auto-deliver)
|
|
53
|
+
volute file untrust <sender> [--mind <name>] Remove sender trust`);
|
|
54
|
+
}
|
|
55
|
+
async function sendFile(args) {
|
|
56
|
+
const { positional, flags } = parseArgs(args, {
|
|
57
|
+
mind: { type: "string" }
|
|
58
|
+
});
|
|
59
|
+
const mind = resolveMindName(flags);
|
|
60
|
+
const filePath = positional[0];
|
|
61
|
+
const targetMind = positional[1];
|
|
62
|
+
if (!filePath || !targetMind) {
|
|
63
|
+
console.error("Usage: volute file send <path> <target-mind> [--mind <name>]");
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
const res = await daemonFetch(`/api/minds/${encodeURIComponent(mind)}/files/send`, {
|
|
67
|
+
method: "POST",
|
|
68
|
+
headers: { "Content-Type": "application/json" },
|
|
69
|
+
body: JSON.stringify({ targetMind, filePath })
|
|
70
|
+
});
|
|
71
|
+
if (!res.ok) {
|
|
72
|
+
const data2 = await res.json();
|
|
73
|
+
console.error(data2.error ?? `Failed to send file: ${res.status}`);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
const data = await res.json();
|
|
77
|
+
if (data.status === "delivered") {
|
|
78
|
+
console.log(`File delivered to ${targetMind}: ${data.destPath}`);
|
|
79
|
+
} else {
|
|
80
|
+
console.log(`File pending approval from ${targetMind} (id: ${data.id})`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async function listPending(args) {
|
|
84
|
+
const { flags } = parseArgs(args, {
|
|
85
|
+
mind: { type: "string" }
|
|
86
|
+
});
|
|
87
|
+
const mind = resolveMindName(flags);
|
|
88
|
+
const res = await daemonFetch(`/api/minds/${encodeURIComponent(mind)}/files/pending`);
|
|
89
|
+
if (!res.ok) {
|
|
90
|
+
const data = await res.json();
|
|
91
|
+
console.error(data.error ?? `Failed to list pending files: ${res.status}`);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
const pending = await res.json();
|
|
95
|
+
if (pending.length === 0) {
|
|
96
|
+
console.log("No pending files.");
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const idW = Math.max(2, ...pending.map((p) => p.id.length));
|
|
100
|
+
const senderW = Math.max(6, ...pending.map((p) => p.sender.length));
|
|
101
|
+
const fileW = Math.max(4, ...pending.map((p) => p.filename.length));
|
|
102
|
+
console.log(`${"ID".padEnd(idW)} ${"SENDER".padEnd(senderW)} ${"FILE".padEnd(fileW)} SIZE`);
|
|
103
|
+
for (const p of pending) {
|
|
104
|
+
console.log(
|
|
105
|
+
`${p.id.padEnd(idW)} ${p.sender.padEnd(senderW)} ${p.filename.padEnd(fileW)} ${formatSize(p.size)}`
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async function acceptFile(args) {
|
|
110
|
+
const { positional, flags } = parseArgs(args, {
|
|
111
|
+
mind: { type: "string" }
|
|
112
|
+
});
|
|
113
|
+
const mind = resolveMindName(flags);
|
|
114
|
+
const id = positional[0];
|
|
115
|
+
if (!id) {
|
|
116
|
+
console.error("Usage: volute file accept <id> [--mind <name>]");
|
|
117
|
+
process.exit(1);
|
|
118
|
+
}
|
|
119
|
+
const res = await daemonFetch(`/api/minds/${encodeURIComponent(mind)}/files/accept`, {
|
|
120
|
+
method: "POST",
|
|
121
|
+
headers: { "Content-Type": "application/json" },
|
|
122
|
+
body: JSON.stringify({ id })
|
|
123
|
+
});
|
|
124
|
+
if (!res.ok) {
|
|
125
|
+
const data2 = await res.json();
|
|
126
|
+
console.error(data2.error ?? `Failed to accept file: ${res.status}`);
|
|
127
|
+
process.exit(1);
|
|
128
|
+
}
|
|
129
|
+
const data = await res.json();
|
|
130
|
+
console.log(`File accepted: ${data.destPath}`);
|
|
131
|
+
}
|
|
132
|
+
async function rejectFile(args) {
|
|
133
|
+
const { positional, flags } = parseArgs(args, {
|
|
134
|
+
mind: { type: "string" }
|
|
135
|
+
});
|
|
136
|
+
const mind = resolveMindName(flags);
|
|
137
|
+
const id = positional[0];
|
|
138
|
+
if (!id) {
|
|
139
|
+
console.error("Usage: volute file reject <id> [--mind <name>]");
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
const res = await daemonFetch(`/api/minds/${encodeURIComponent(mind)}/files/reject`, {
|
|
143
|
+
method: "POST",
|
|
144
|
+
headers: { "Content-Type": "application/json" },
|
|
145
|
+
body: JSON.stringify({ id })
|
|
146
|
+
});
|
|
147
|
+
if (!res.ok) {
|
|
148
|
+
const data = await res.json();
|
|
149
|
+
console.error(data.error ?? `Failed to reject file: ${res.status}`);
|
|
150
|
+
process.exit(1);
|
|
151
|
+
}
|
|
152
|
+
console.log(`File rejected: ${id}`);
|
|
153
|
+
}
|
|
154
|
+
async function trustSender(args) {
|
|
155
|
+
const { positional, flags } = parseArgs(args, {
|
|
156
|
+
mind: { type: "string" }
|
|
157
|
+
});
|
|
158
|
+
const mind = resolveMindName(flags);
|
|
159
|
+
const sender = positional[0];
|
|
160
|
+
if (!sender) {
|
|
161
|
+
console.error("Usage: volute file trust <sender> [--mind <name>]");
|
|
162
|
+
process.exit(1);
|
|
163
|
+
}
|
|
164
|
+
const res = await daemonFetch(`/api/minds/${encodeURIComponent(mind)}/files/trust`, {
|
|
165
|
+
method: "POST",
|
|
166
|
+
headers: { "Content-Type": "application/json" },
|
|
167
|
+
body: JSON.stringify({ sender })
|
|
168
|
+
});
|
|
169
|
+
if (!res.ok) {
|
|
170
|
+
const data = await res.json();
|
|
171
|
+
console.error(data.error ?? `Failed to trust sender: ${res.status}`);
|
|
172
|
+
process.exit(1);
|
|
173
|
+
}
|
|
174
|
+
console.log(`Trusted sender: ${sender}`);
|
|
175
|
+
}
|
|
176
|
+
async function untrustSender(args) {
|
|
177
|
+
const { positional, flags } = parseArgs(args, {
|
|
178
|
+
mind: { type: "string" }
|
|
179
|
+
});
|
|
180
|
+
const mind = resolveMindName(flags);
|
|
181
|
+
const sender = positional[0];
|
|
182
|
+
if (!sender) {
|
|
183
|
+
console.error("Usage: volute file untrust <sender> [--mind <name>]");
|
|
184
|
+
process.exit(1);
|
|
185
|
+
}
|
|
186
|
+
const res = await daemonFetch(
|
|
187
|
+
`/api/minds/${encodeURIComponent(mind)}/files/trust/${encodeURIComponent(sender)}`,
|
|
188
|
+
{ method: "DELETE" }
|
|
189
|
+
);
|
|
190
|
+
if (!res.ok) {
|
|
191
|
+
const data = await res.json();
|
|
192
|
+
console.error(data.error ?? `Failed to untrust sender: ${res.status}`);
|
|
193
|
+
process.exit(1);
|
|
194
|
+
}
|
|
195
|
+
console.log(`Untrusted sender: ${sender}`);
|
|
196
|
+
}
|
|
197
|
+
function formatSize(bytes) {
|
|
198
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
199
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
200
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
201
|
+
}
|
|
202
|
+
export {
|
|
203
|
+
run
|
|
204
|
+
};
|
|
@@ -41,10 +41,10 @@ async function run(args) {
|
|
|
41
41
|
await import("./upgrade-AIT24B5I.js").then((m) => m.run(args.slice(1)));
|
|
42
42
|
break;
|
|
43
43
|
case "import":
|
|
44
|
-
await import("./import-
|
|
44
|
+
await import("./import-XEC34Y4Z.js").then((m) => m.run(args.slice(1)));
|
|
45
45
|
break;
|
|
46
46
|
case "export":
|
|
47
|
-
await import("./export-
|
|
47
|
+
await import("./export-6QBUOQGC.js").then((m) => m.run(args.slice(1)));
|
|
48
48
|
break;
|
|
49
49
|
case "--help":
|
|
50
50
|
case "-h":
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
getActiveMinds,
|
|
4
|
+
markIdle,
|
|
5
|
+
onMindEvent,
|
|
6
|
+
stopAll
|
|
7
|
+
} from "./chunk-GZ7DW4YL.js";
|
|
8
|
+
import "./chunk-UU7A7KLB.js";
|
|
9
|
+
import "./chunk-YUIHSKR6.js";
|
|
10
|
+
import "./chunk-5XNT2472.js";
|
|
11
|
+
import "./chunk-NSE7VJQA.js";
|
|
12
|
+
import "./chunk-EBGCNDMM.js";
|
|
13
|
+
import "./chunk-K3NQKI34.js";
|
|
14
|
+
export {
|
|
15
|
+
getActiveMinds,
|
|
16
|
+
markIdle,
|
|
17
|
+
onMindEvent,
|
|
18
|
+
stopAll
|
|
19
|
+
};
|
|
@@ -3,11 +3,11 @@ import {
|
|
|
3
3
|
MindManager,
|
|
4
4
|
getMindManager,
|
|
5
5
|
initMindManager
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-PUVXOZ6T.js";
|
|
7
7
|
import "./chunk-YUIHSKR6.js";
|
|
8
8
|
import "./chunk-VDWCHYTS.js";
|
|
9
|
-
import "./chunk-
|
|
10
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-5XNT2472.js";
|
|
10
|
+
import "./chunk-NSE7VJQA.js";
|
|
11
11
|
import "./chunk-OGXOMR65.js";
|
|
12
12
|
import "./chunk-EBGCNDMM.js";
|
|
13
13
|
import "./chunk-K3NQKI34.js";
|
|
@@ -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.20.0",
|
|
8
8
|
description: "CLI for creating and managing self-modifying AI minds powered by the Claude Agent SDK",
|
|
9
9
|
type: "module",
|
|
10
10
|
license: "MIT",
|
|
@@ -6,10 +6,10 @@ async function run(args) {
|
|
|
6
6
|
const subcommand = args[0];
|
|
7
7
|
switch (subcommand) {
|
|
8
8
|
case "publish":
|
|
9
|
-
await import("./publish-
|
|
9
|
+
await import("./publish-TAJUET4I.js").then((m) => m.run(args.slice(1)));
|
|
10
10
|
break;
|
|
11
11
|
case "status":
|
|
12
|
-
await import("./status-
|
|
12
|
+
await import("./status-FU2PFVVF.js").then((m) => m.run(args.slice(1)));
|
|
13
13
|
break;
|
|
14
14
|
case "--help":
|
|
15
15
|
case "-h":
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from "./chunk-NAOW2CLO.js";
|
|
8
8
|
import {
|
|
9
9
|
sharedDir
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-TIWH32HP.js";
|
|
11
11
|
import {
|
|
12
12
|
readSystemsConfig
|
|
13
13
|
} from "./chunk-FCDU5BFX.js";
|
|
@@ -27,17 +27,20 @@ import { existsSync, lstatSync, readdirSync, readFileSync } from "fs";
|
|
|
27
27
|
import { relative, resolve } from "path";
|
|
28
28
|
async function run(args) {
|
|
29
29
|
const { flags } = parseArgs(args, {
|
|
30
|
-
mind: { type: "string" }
|
|
30
|
+
mind: { type: "string" },
|
|
31
|
+
system: { type: "boolean" }
|
|
31
32
|
});
|
|
32
33
|
const config = readSystemsConfig();
|
|
33
34
|
if (!config) {
|
|
34
35
|
console.error('Not logged in. Run "volute pages register" or "volute pages login" first.');
|
|
35
36
|
process.exit(1);
|
|
36
37
|
}
|
|
37
|
-
const hasMind = flags.mind || process.env.VOLUTE_MIND;
|
|
38
38
|
let mindName;
|
|
39
39
|
let pagesDir;
|
|
40
|
-
if (
|
|
40
|
+
if (flags.system) {
|
|
41
|
+
mindName = "system";
|
|
42
|
+
pagesDir = resolve(sharedDir(), "pages");
|
|
43
|
+
} else if (flags.mind || process.env.VOLUTE_MIND) {
|
|
41
44
|
mindName = resolveMindName(flags);
|
|
42
45
|
pagesDir = resolve(mindDir(mindName), "home", "pages");
|
|
43
46
|
} else {
|
|
@@ -16,6 +16,7 @@ import "./chunk-EBGCNDMM.js";
|
|
|
16
16
|
import "./chunk-K3NQKI34.js";
|
|
17
17
|
|
|
18
18
|
// src/commands/schedule.ts
|
|
19
|
+
import { CronExpressionParser } from "cron-parser";
|
|
19
20
|
async function run(args) {
|
|
20
21
|
const subcommand = args[0];
|
|
21
22
|
switch (subcommand) {
|
|
@@ -42,6 +43,7 @@ function printUsage() {
|
|
|
42
43
|
console.log(`Usage:
|
|
43
44
|
volute schedule list [--mind <name>]
|
|
44
45
|
volute schedule add [--mind <name>] --cron "..." --message "..." [--id name]
|
|
46
|
+
volute schedule add [--mind <name>] --cron "..." --script "..." [--id name]
|
|
45
47
|
volute schedule remove [--mind <name>] --id <id>`);
|
|
46
48
|
}
|
|
47
49
|
async function listSchedules(args) {
|
|
@@ -65,10 +67,11 @@ async function listSchedules(args) {
|
|
|
65
67
|
}
|
|
66
68
|
const idW = Math.max(2, ...schedules.map((s) => s.id.length));
|
|
67
69
|
const cronW = Math.max(4, ...schedules.map((s) => s.cron.length));
|
|
68
|
-
|
|
70
|
+
const actionLabel = (s) => s.script ? `[script] ${s.script}` : s.message ?? "";
|
|
71
|
+
console.log(`${"ID".padEnd(idW)} ${"CRON".padEnd(cronW)} ENABLED ACTION`);
|
|
69
72
|
for (const s of schedules) {
|
|
70
73
|
console.log(
|
|
71
|
-
`${s.id.padEnd(idW)} ${s.cron.padEnd(cronW)} ${String(s.enabled).padEnd(7)} ${s
|
|
74
|
+
`${s.id.padEnd(idW)} ${s.cron.padEnd(cronW)} ${String(s.enabled).padEnd(7)} ${actionLabel(s)}`
|
|
72
75
|
);
|
|
73
76
|
}
|
|
74
77
|
}
|
|
@@ -77,14 +80,31 @@ async function addSchedule(args) {
|
|
|
77
80
|
mind: { type: "string" },
|
|
78
81
|
cron: { type: "string" },
|
|
79
82
|
message: { type: "string" },
|
|
83
|
+
script: { type: "string" },
|
|
80
84
|
id: { type: "string" }
|
|
81
85
|
});
|
|
82
86
|
const mind = resolveMindName(flags);
|
|
83
|
-
if (!flags.cron
|
|
84
|
-
console.error("--cron
|
|
87
|
+
if (!flags.cron) {
|
|
88
|
+
console.error("--cron is required");
|
|
85
89
|
process.exit(1);
|
|
86
90
|
}
|
|
87
|
-
|
|
91
|
+
if (!flags.message && !flags.script) {
|
|
92
|
+
console.error("--message or --script is required");
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
if (flags.message && flags.script) {
|
|
96
|
+
console.error("--message and --script are mutually exclusive");
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
try {
|
|
100
|
+
CronExpressionParser.parse(flags.cron);
|
|
101
|
+
} catch {
|
|
102
|
+
console.error(`Invalid cron expression: ${flags.cron}`);
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
const body = { cron: flags.cron };
|
|
106
|
+
if (flags.message) body.message = flags.message;
|
|
107
|
+
if (flags.script) body.script = flags.script;
|
|
88
108
|
if (flags.id) body.id = flags.id;
|
|
89
109
|
const client = getClient();
|
|
90
110
|
const res = await daemonFetch(
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
activity,
|
|
3
4
|
conversationParticipants,
|
|
4
5
|
conversations,
|
|
5
6
|
deliveryQueue,
|
|
@@ -9,9 +10,10 @@ import {
|
|
|
9
10
|
sharedSkills,
|
|
10
11
|
systemPrompts,
|
|
11
12
|
users
|
|
12
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-NSE7VJQA.js";
|
|
13
14
|
import "./chunk-K3NQKI34.js";
|
|
14
15
|
export {
|
|
16
|
+
activity,
|
|
15
17
|
conversationParticipants,
|
|
16
18
|
conversations,
|
|
17
19
|
deliveryQueue,
|
|
@@ -95,6 +95,22 @@ function install(port, host) {
|
|
|
95
95
|
console.log(`Created ${MINDS_DIR}`);
|
|
96
96
|
ensureVoluteGroup({ force: true });
|
|
97
97
|
console.log("Ensured volute group exists");
|
|
98
|
+
try {
|
|
99
|
+
execFileSync("git", ["config", "--system", "user.name"]);
|
|
100
|
+
console.log("System git identity already configured, skipping");
|
|
101
|
+
} catch {
|
|
102
|
+
try {
|
|
103
|
+
execFileSync("git", ["config", "--system", "user.name", "Volute"]);
|
|
104
|
+
execFileSync("git", ["config", "--system", "user.email", "volute@localhost"]);
|
|
105
|
+
console.log("Configured system git identity");
|
|
106
|
+
} catch (err) {
|
|
107
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
108
|
+
console.warn(`Warning: failed to set system git config: ${msg}`);
|
|
109
|
+
console.warn("Git commits by the daemon may fail. You can set this manually with:");
|
|
110
|
+
console.warn(' git config --system user.name "Volute"');
|
|
111
|
+
console.warn(' git config --system user.email "volute@localhost"');
|
|
112
|
+
}
|
|
113
|
+
}
|
|
98
114
|
execFileSync("chmod", ["755", DATA_DIR]);
|
|
99
115
|
execFileSync("chmod", ["755", MINDS_DIR]);
|
|
100
116
|
console.log("Set permissions on directories");
|
|
@@ -29,7 +29,7 @@ You manage yourself through the `volute` CLI. Your mind name is auto-detected vi
|
|
|
29
29
|
| `volute channel list [<platform>]` | List conversations on a platform (or all platforms) |
|
|
30
30
|
| `volute channel users <platform>` | List users/contacts on a platform |
|
|
31
31
|
| `volute channel create <platform> --participants u1,u2 [--name "..."]` | Create a conversation on a platform |
|
|
32
|
-
| `volute schedule add --cron "..." --message "..."` | Schedule a recurring
|
|
32
|
+
| `volute schedule add --cron "..." --message/--script "..."` | Schedule a recurring task |
|
|
33
33
|
| `volute schedule list` | List your schedules |
|
|
34
34
|
| `volute schedule remove --id <id>` | Remove a schedule |
|
|
35
35
|
| `volute shared status` | See your pending changes vs main |
|
|
@@ -46,6 +46,12 @@ volute schedule add --cron "0 9 * * *" --message "morning — review what's on y
|
|
|
46
46
|
volute schedule add --cron "0 0 * * 0" --message "weekly — consolidate your memory and reflect on the past week"
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
+
You can also schedule scripts that run and deliver their output as a message (empty output is silent — no wake-up):
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
volute schedule add --cron "*/30 * * * *" --script "cat status.txt" --id check-status
|
|
53
|
+
```
|
|
54
|
+
|
|
49
55
|
## Piping Messages via Stdin
|
|
50
56
|
|
|
51
57
|
All send commands accept the message from stdin instead of as an argument. This avoids shell escaping issues with quotes, special characters, and multiline content:
|
|
@@ -71,6 +77,23 @@ For group conversations, use `volute channel create volute --participants mind-b
|
|
|
71
77
|
|
|
72
78
|
Your `.config/volute.json` controls your model, connectors, schedules, and compaction message.
|
|
73
79
|
|
|
80
|
+
### Transparency
|
|
81
|
+
|
|
82
|
+
The `transparency` setting in `.config/volute.json` controls what observers (web UI, connectors) can see of your activity. Presets:
|
|
83
|
+
|
|
84
|
+
| Preset | Thinking | Text | Tool use | Tool results | Logs/usage |
|
|
85
|
+
|--------|----------|------|----------|--------------|------------|
|
|
86
|
+
| `transparent` | yes | yes | yes (with args) | yes | yes |
|
|
87
|
+
| `standard` | no | yes | name only | no | yes |
|
|
88
|
+
| `private` | no | no | no | no | no |
|
|
89
|
+
| `silent` | no | no | no | no | no |
|
|
90
|
+
|
|
91
|
+
Default is `transparent`. Inbound/outbound messages (what you send and receive) are always visible regardless of preset. To change:
|
|
92
|
+
|
|
93
|
+
```json
|
|
94
|
+
{ "transparency": "standard" }
|
|
95
|
+
```
|
|
96
|
+
|
|
74
97
|
## Startup Context
|
|
75
98
|
|
|
76
99
|
Edit `.config/hooks/startup-context.sh` to customize what you see when a new session starts. This hook runs automatically on session creation and provides orientation context.
|
|
@@ -132,7 +155,7 @@ Your `shared/` directory is a collaborative space backed by git. Each mind works
|
|
|
132
155
|
|
|
133
156
|
**Conflicts:** If your merge fails due to conflicts, pull the latest (`volute shared pull`), reconcile the conflicting files, and merge again. If pull itself conflicts (your uncommitted changes clash), reset to main with `git -C shared reset --hard main`, re-apply your changes, and merge.
|
|
134
157
|
|
|
135
|
-
**Shared pages:** The `shared/pages/` directory is the system-level website. Any mind can contribute. Publish with `volute pages publish
|
|
158
|
+
**Shared pages:** The `shared/pages/` directory is the system-level website. Any mind can contribute. Publish with `volute pages publish --system` to deploy the shared site.
|
|
136
159
|
|
|
137
160
|
## MCP Configuration
|
|
138
161
|
|
|
@@ -185,9 +208,10 @@ The `sessions` section configures behavior per session. Keys are glob patterns m
|
|
|
185
208
|
|
|
186
209
|
| Field | Description |
|
|
187
210
|
|-------|-------------|
|
|
188
|
-
| `
|
|
211
|
+
| `delivery` | Delivery mode: `"immediate"` (default), `"batch"`, or `{ "mode": "batch", "debounce": N, "maxWait": N }` |
|
|
189
212
|
| `interrupt` | Whether to interrupt an in-progress turn (default: `true`) |
|
|
190
213
|
| `instructions` | Instructions prepended to messages for this session (e.g. `"Brief responses only."`) |
|
|
214
|
+
| `batch` | Legacy alias for batch config (use `delivery` instead) |
|
|
191
215
|
|
|
192
216
|
### Batch config
|
|
193
217
|
|
|
@@ -209,6 +233,10 @@ Examples:
|
|
|
209
233
|
|
|
210
234
|
Batched messages arrive as a single message with a `[Batch: N messages — ...]` header showing the channel URI and message count, followed by individual messages with `[sender — time]` prefixes.
|
|
211
235
|
|
|
236
|
+
### New-speaker interrupts
|
|
237
|
+
|
|
238
|
+
In batch mode, if you're mid-turn and a **new speaker** sends a message in the **same channel**, the pending batch is force-flushed with `interrupt: true` so you can incorporate the new voice. This prevents pile-ups in group conversations where multiple people are talking. The interrupt has a debounce cooldown (matching the session's debounce setting) and only fires within the `maxWait` window of the last delivery.
|
|
239
|
+
|
|
212
240
|
## Channel Gating
|
|
213
241
|
|
|
214
242
|
When `gateUnmatched` is `true` (the default), messages from channels without a matching rule are held:
|
|
@@ -249,7 +277,9 @@ Publish your `home/pages/` directory to the web. Your system must be registered
|
|
|
249
277
|
|
|
250
278
|
```sh
|
|
251
279
|
volute pages publish # publish your pages/ directory
|
|
280
|
+
volute pages publish --system # publish the shared/pages/ system site
|
|
252
281
|
volute pages status # check your published URL and status
|
|
282
|
+
volute pages status --system # check the system site status
|
|
253
283
|
```
|
|
254
284
|
|
|
255
285
|
Your pages are served at `https://{system}.volute.systems/~{your-name}/`. Create an `index.html` in `home/pages/` to get started.
|
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
getSharedSkill,
|
|
5
5
|
installSkill,
|
|
6
6
|
uninstallSkill
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-IKMY5X76.js";
|
|
8
8
|
import "./chunk-YUIHSKR6.js";
|
|
9
|
-
import "./chunk-
|
|
10
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-5XNT2472.js";
|
|
10
|
+
import "./chunk-NSE7VJQA.js";
|
|
11
11
|
import "./chunk-DYZGP3EW.js";
|
|
12
12
|
import "./chunk-OGXOMR65.js";
|
|
13
13
|
import {
|
|
@@ -17,14 +17,15 @@ import "./chunk-K3NQKI34.js";
|
|
|
17
17
|
// src/commands/pages/status.ts
|
|
18
18
|
async function run(args) {
|
|
19
19
|
const { flags } = parseArgs(args, {
|
|
20
|
-
mind: { type: "string" }
|
|
20
|
+
mind: { type: "string" },
|
|
21
|
+
system: { type: "boolean" }
|
|
21
22
|
});
|
|
22
23
|
const config = readSystemsConfig();
|
|
23
24
|
if (!config) {
|
|
24
25
|
console.error('Not logged in. Run "volute pages register" or "volute pages login" first.');
|
|
25
26
|
process.exit(1);
|
|
26
27
|
}
|
|
27
|
-
const mindName = resolveMindName(flags);
|
|
28
|
+
const mindName = flags.mind || process.env.VOLUTE_MIND ? resolveMindName(flags) : "system";
|
|
28
29
|
const res = await systemsFetch(`${config.apiUrl}/api/pages/status/${mindName}`, {
|
|
29
30
|
headers: { Authorization: `Bearer ${config.apiKey}` }
|
|
30
31
|
});
|