volute 0.13.2 → 0.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{channel-JZJJRRWT.js → channel-SLURLIRV.js} +28 -28
- package/dist/{chunk-KRJ6KCBI.js → chunk-2Y77MCFG.js} +3 -3
- package/dist/{chunk-AA5TDLXB.js → chunk-3FC42ZBM.js} +24 -24
- package/dist/{chunk-YYUSXARD.js → chunk-6BDNWYKG.js} +2 -2
- package/dist/{chunk-KN4WBLH2.js → chunk-BEFIBW5B.js} +2 -2
- package/dist/{chunk-FE5O5RSL.js → chunk-GSPWIM5E.js} +25 -25
- package/dist/{chunk-QRRXD2V7.js → chunk-J52CJCVI.js} +71 -69
- package/dist/{chunk-LGSW7T7K.js → chunk-M77QBTEH.js} +60 -57
- package/dist/{chunk-KXOFPDO6.js → chunk-MVSXRMJJ.js} +1 -1
- package/dist/chunk-NAOW2CLO.js +15 -0
- package/dist/{chunk-VQIJUR43.js → chunk-OJQ47SCA.js} +1 -1
- package/dist/{chunk-O4BN3ZIY.js → chunk-OYSZNX5I.js} +7 -7
- package/dist/{chunk-AOSGW3MX.js → chunk-PDLAZJGC.js} +28 -28
- package/dist/{chunk-XUA3JUFK.js → chunk-PO5Q2AYN.js} +2 -2
- package/dist/{chunk-6BQHEIDO.js → chunk-QJIIHU32.js} +2 -2
- package/dist/{chunk-NXT67PPK.js → chunk-ZCEYUUID.js} +19 -19
- package/dist/cli.js +42 -42
- package/dist/{connector-WFT5KK67.js → connector-JFAHYFQX.js} +21 -21
- package/dist/connectors/discord.js +7 -7
- package/dist/connectors/slack.js +7 -7
- package/dist/connectors/telegram.js +9 -9
- package/dist/{create-HT47ZH5T.js → create-ZWHCRT5F.js} +7 -7
- package/dist/{daemon-client-DEF7IFEJ.js → daemon-client-ODKDUYDE.js} +2 -2
- package/dist/{daemon-restart-P3FEE3QJ.js → daemon-restart-IZGEF4NA.js} +6 -6
- package/dist/daemon.js +2313 -1997
- package/dist/{delete-YG3RVURA.js → delete-6G6WEX4F.js} +8 -8
- package/dist/down-A56B5JLK.js +14 -0
- package/dist/{env-BQYYF4YL.js → env-6LXDUZDA.js} +25 -25
- package/dist/{history-I4KIKIUX.js → history-LKCJJMUV.js} +7 -7
- package/dist/{import-UHCK6PRC.js → import-EDGRLIGO.js} +3 -3
- package/dist/{logs-2DWFES6A.js → logs-GYOR3L2L.js} +8 -8
- package/dist/mind-OJN6RBZW.js +79 -0
- package/dist/mind-manager-PN5SUDJ4.js +15 -0
- package/dist/{package-MMTPOMUN.js → package-I7Z6G44Y.js} +4 -4
- package/dist/{restart-6PE3GWYZ.js → restart-YFAWFS5T.js} +9 -9
- package/dist/{schedule-5AYTQM3N.js → schedule-AGYLDMNS.js} +17 -17
- package/dist/{seed-3QQVFMBU.js → seed-AP4Q7RZ7.js} +9 -9
- package/dist/{send-FPFW7J5Q.js → send-SV4K2TDE.js} +32 -24
- package/dist/{service-5X5EKPVM.js → service-U7MZ2H7F.js} +4 -4
- package/dist/{setup-5NXV25ZS.js → setup-DJKIZKGW.js} +21 -16
- package/dist/{sprout-VOUJ4Y3I.js → sprout-TJ3BHVOG.js} +25 -18
- package/dist/{start-ICPSQ2ZK.js → start-3YYRXBKP.js} +7 -7
- package/dist/{status-JBT7ENQN.js → status-VSFZYX7S.js} +14 -14
- package/dist/{stop-IXJGAG4T.js → stop-AA5K5LYG.js} +9 -9
- package/dist/{up-ROC7LJ7G.js → up-C4MV6EXV.js} +5 -5
- package/dist/{update-GU6JYDSN.js → update-YAGN5ODG.js} +5 -5
- package/dist/{update-check-MUPZYTW4.js → update-check-APLTH4IN.js} +2 -2
- package/dist/{upgrade-275LKIEG.js → upgrade-KXZCQSZN.js} +8 -10
- package/dist/{variant-RE45F2IY.js → variant-X5QFG6KK.js} +30 -30
- package/dist/web-assets/assets/index-CeFLp8DZ.js +307 -0
- package/dist/web-assets/index.html +1 -1
- package/drizzle/0005_rename_agents_to_minds.sql +11 -0
- package/drizzle/meta/0005_snapshot.json +410 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +4 -4
- package/templates/_base/.init/.config/scripts/session-reader.ts +1 -1
- package/templates/_base/.init/SOUL.md +1 -1
- package/templates/_base/_skills/memory/SKILL.md +1 -1
- package/templates/_base/_skills/orientation/SKILL.md +6 -6
- package/templates/_base/_skills/sessions/SKILL.md +1 -1
- package/templates/_base/_skills/{volute-agent → volute-mind}/SKILL.md +21 -21
- package/templates/_base/home/VOLUTE.md +7 -7
- package/templates/_base/src/lib/auto-commit.ts +1 -1
- package/templates/_base/src/lib/auto-reply.ts +1 -1
- package/templates/_base/src/lib/daemon-client.ts +8 -8
- package/templates/_base/src/lib/router.ts +6 -6
- package/templates/_base/src/lib/routing.ts +9 -6
- package/templates/_base/src/lib/startup.ts +1 -1
- package/templates/_base/src/lib/volute-server.ts +1 -1
- package/templates/{agent-sdk → claude}/.init/CLAUDE.md +3 -3
- package/templates/{agent-sdk → claude}/src/agent.ts +10 -10
- package/templates/{agent-sdk → claude}/src/lib/hooks/pre-compact.ts +2 -2
- package/templates/{agent-sdk → claude}/src/lib/session-store.ts +2 -2
- package/templates/{agent-sdk → claude}/src/lib/stream-consumer.ts +1 -1
- package/templates/{agent-sdk → claude}/src/server.ts +4 -4
- package/templates/pi/.init/{AGENTS.md → MINDS.md} +3 -3
- package/templates/pi/home/.config/config.json.tmpl +1 -1
- package/templates/pi/src/agent.ts +12 -12
- package/templates/pi/src/lib/event-handler.ts +39 -4
- package/templates/pi/src/server.ts +3 -3
- package/dist/agent-IUSETOXJ.js +0 -79
- package/dist/agent-manager-4O4AC2S6.js +0 -15
- package/dist/chunk-AZEL2IEK.js +0 -15
- package/dist/down-36YCOZ7V.js +0 -14
- package/dist/web-assets/assets/index-TqXd1QOX.js +0 -307
- /package/templates/{agent-sdk → claude}/.init/.claude/settings.json +0 -0
- /package/templates/{agent-sdk → claude}/.init/.config/routes.json +0 -0
- /package/templates/{agent-sdk → claude}/package.json.tmpl +0 -0
- /package/templates/{agent-sdk → claude}/src/lib/content.ts +0 -0
- /package/templates/{agent-sdk → claude}/src/lib/hooks/auto-commit.ts +0 -0
- /package/templates/{agent-sdk → claude}/src/lib/hooks/identity-reload.ts +0 -0
- /package/templates/{agent-sdk → claude}/src/lib/hooks/session-context.ts +0 -0
- /package/templates/{agent-sdk → claude}/src/lib/message-channel.ts +0 -0
- /package/templates/{agent-sdk → claude}/volute-template.json +0 -0
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
loadMergedEnv
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-OYSZNX5I.js";
|
|
5
5
|
import {
|
|
6
|
-
|
|
6
|
+
chownMindDir,
|
|
7
7
|
isIsolationEnabled,
|
|
8
8
|
wrapForIsolation
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-ZCEYUUID.js";
|
|
10
10
|
import {
|
|
11
|
-
|
|
12
|
-
findAgent,
|
|
11
|
+
findMind,
|
|
13
12
|
findVariant,
|
|
14
|
-
|
|
13
|
+
mindDir,
|
|
14
|
+
setMindRunning,
|
|
15
15
|
setVariantRunning,
|
|
16
16
|
stateDir,
|
|
17
17
|
voluteHome
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-M77QBTEH.js";
|
|
19
19
|
|
|
20
|
-
// src/lib/
|
|
20
|
+
// src/lib/mind-manager.ts
|
|
21
21
|
import { execFile, spawn } from "child_process";
|
|
22
22
|
import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
23
23
|
import { resolve } from "path";
|
|
@@ -113,41 +113,41 @@ var RotatingLog = class extends Writable {
|
|
|
113
113
|
}
|
|
114
114
|
};
|
|
115
115
|
|
|
116
|
-
// src/lib/
|
|
116
|
+
// src/lib/mind-manager.ts
|
|
117
117
|
var execFileAsync = promisify(execFile);
|
|
118
|
-
function
|
|
119
|
-
return resolve(stateDir(name), "
|
|
118
|
+
function mindPidPath(name) {
|
|
119
|
+
return resolve(stateDir(name), "mind.pid");
|
|
120
120
|
}
|
|
121
121
|
var MAX_RESTART_ATTEMPTS = 5;
|
|
122
122
|
var BASE_RESTART_DELAY = 3e3;
|
|
123
123
|
var MAX_RESTART_DELAY = 6e4;
|
|
124
|
-
var
|
|
125
|
-
|
|
124
|
+
var MindManager = class {
|
|
125
|
+
minds = /* @__PURE__ */ new Map();
|
|
126
126
|
stopping = /* @__PURE__ */ new Set();
|
|
127
127
|
shuttingDown = false;
|
|
128
128
|
restartAttempts = /* @__PURE__ */ new Map();
|
|
129
129
|
pendingContext = /* @__PURE__ */ new Map();
|
|
130
130
|
resolveTarget(name) {
|
|
131
131
|
const [baseName, variantName] = name.split("@", 2);
|
|
132
|
-
const entry =
|
|
133
|
-
if (!entry) throw new Error(`Unknown
|
|
132
|
+
const entry = findMind(baseName);
|
|
133
|
+
if (!entry) throw new Error(`Unknown mind: ${baseName}`);
|
|
134
134
|
if (variantName) {
|
|
135
135
|
const variant = findVariant(baseName, variantName);
|
|
136
|
-
if (!variant) throw new Error(`Unknown variant: ${variantName} (
|
|
136
|
+
if (!variant) throw new Error(`Unknown variant: ${variantName} (mind: ${baseName})`);
|
|
137
137
|
return { dir: variant.path, port: variant.port, isVariant: true, baseName, variantName };
|
|
138
138
|
}
|
|
139
|
-
const dir =
|
|
140
|
-
if (!existsSync3(dir)) throw new Error(`
|
|
139
|
+
const dir = mindDir(baseName);
|
|
140
|
+
if (!existsSync3(dir)) throw new Error(`Mind directory missing: ${dir}`);
|
|
141
141
|
return { dir, port: entry.port, isVariant: false, baseName };
|
|
142
142
|
}
|
|
143
|
-
async
|
|
144
|
-
if (this.
|
|
145
|
-
throw new Error(`
|
|
143
|
+
async startMind(name) {
|
|
144
|
+
if (this.minds.has(name)) {
|
|
145
|
+
throw new Error(`Mind ${name} is already running`);
|
|
146
146
|
}
|
|
147
147
|
const target = this.resolveTarget(name);
|
|
148
148
|
const { dir, isVariant, baseName, variantName } = target;
|
|
149
149
|
const port = target.port;
|
|
150
|
-
const pidFile =
|
|
150
|
+
const pidFile = mindPidPath(name);
|
|
151
151
|
try {
|
|
152
152
|
if (existsSync3(pidFile)) {
|
|
153
153
|
const stalePid = parseInt(readFileSync2(pidFile, "utf-8").trim(), 10);
|
|
@@ -156,12 +156,12 @@ var AgentManager = class {
|
|
|
156
156
|
process.kill(stalePid, 0);
|
|
157
157
|
const { stdout } = await execFileAsync("ps", ["-p", String(stalePid), "-o", "args="]);
|
|
158
158
|
if (stdout.includes("server.ts")) {
|
|
159
|
-
console.error(`[daemon] killing stale
|
|
159
|
+
console.error(`[daemon] killing stale mind process ${stalePid} for ${name}`);
|
|
160
160
|
process.kill(-stalePid, "SIGTERM");
|
|
161
161
|
await new Promise((r) => setTimeout(r, 500));
|
|
162
162
|
} else {
|
|
163
163
|
console.error(
|
|
164
|
-
`[daemon] stale PID ${stalePid} for ${name} is not
|
|
164
|
+
`[daemon] stale PID ${stalePid} for ${name} is not a mind process, skipping`
|
|
165
165
|
);
|
|
166
166
|
}
|
|
167
167
|
} catch (err) {
|
|
@@ -184,27 +184,29 @@ var AgentManager = class {
|
|
|
184
184
|
}
|
|
185
185
|
} catch {
|
|
186
186
|
}
|
|
187
|
-
const
|
|
188
|
-
const logsDir = resolve(
|
|
187
|
+
const mindStateDir = stateDir(name);
|
|
188
|
+
const logsDir = resolve(mindStateDir, "logs");
|
|
189
189
|
mkdirSync(logsDir, { recursive: true });
|
|
190
190
|
if (isIsolationEnabled()) {
|
|
191
191
|
try {
|
|
192
|
-
|
|
192
|
+
chownMindDir(mindStateDir, baseName);
|
|
193
193
|
} catch (err) {
|
|
194
194
|
throw new Error(
|
|
195
|
-
`Cannot start
|
|
195
|
+
`Cannot start mind ${name}: failed to set ownership on state directory ${mindStateDir}: ${err instanceof Error ? err.message : err}`
|
|
196
196
|
);
|
|
197
197
|
}
|
|
198
198
|
}
|
|
199
|
-
const logStream = new RotatingLog(resolve(logsDir, "
|
|
200
|
-
const
|
|
199
|
+
const logStream = new RotatingLog(resolve(logsDir, "mind.log"));
|
|
200
|
+
const mindEnv = loadMergedEnv(name);
|
|
201
201
|
const env = {
|
|
202
202
|
...process.env,
|
|
203
|
-
...
|
|
204
|
-
|
|
203
|
+
...mindEnv,
|
|
204
|
+
VOLUTE_MIND: name,
|
|
205
205
|
VOLUTE_STATE_DIR: stateDir(name),
|
|
206
|
-
|
|
207
|
-
|
|
206
|
+
VOLUTE_MIND_DIR: dir,
|
|
207
|
+
VOLUTE_MIND_PORT: String(port),
|
|
208
|
+
// Strip CLAUDECODE so the Agent SDK can spawn Claude Code subprocesses
|
|
209
|
+
CLAUDECODE: void 0
|
|
208
210
|
};
|
|
209
211
|
if (isIsolationEnabled()) {
|
|
210
212
|
env.HOME = resolve(dir, "home");
|
|
@@ -219,13 +221,13 @@ var AgentManager = class {
|
|
|
219
221
|
env
|
|
220
222
|
};
|
|
221
223
|
const child = spawn(spawnCmd, spawnArgs, spawnOpts);
|
|
222
|
-
this.
|
|
224
|
+
this.minds.set(name, { child, port });
|
|
223
225
|
child.stdout?.pipe(logStream);
|
|
224
226
|
child.stderr?.pipe(logStream);
|
|
225
227
|
try {
|
|
226
228
|
await new Promise((resolve2, reject) => {
|
|
227
229
|
const timeout = setTimeout(() => {
|
|
228
|
-
reject(new Error(`
|
|
230
|
+
reject(new Error(`Mind ${name} did not start within 30s`));
|
|
229
231
|
}, 3e4);
|
|
230
232
|
function checkOutput(data) {
|
|
231
233
|
if (data.toString().match(/listening on :\d+/)) {
|
|
@@ -241,11 +243,11 @@ var AgentManager = class {
|
|
|
241
243
|
});
|
|
242
244
|
child.on("exit", (code) => {
|
|
243
245
|
clearTimeout(timeout);
|
|
244
|
-
reject(new Error(`
|
|
246
|
+
reject(new Error(`Mind ${name} exited with code ${code} during startup`));
|
|
245
247
|
});
|
|
246
248
|
});
|
|
247
249
|
} catch (err) {
|
|
248
|
-
this.
|
|
250
|
+
this.minds.delete(name);
|
|
249
251
|
try {
|
|
250
252
|
child.kill();
|
|
251
253
|
} catch {
|
|
@@ -264,9 +266,9 @@ var AgentManager = class {
|
|
|
264
266
|
if (isVariant) {
|
|
265
267
|
setVariantRunning(baseName, variantName, true);
|
|
266
268
|
} else {
|
|
267
|
-
|
|
269
|
+
setMindRunning(name, true);
|
|
268
270
|
}
|
|
269
|
-
console.error(`[daemon] started
|
|
271
|
+
console.error(`[daemon] started mind ${name} on port ${port}`);
|
|
270
272
|
await this.deliverPendingContext(name);
|
|
271
273
|
}
|
|
272
274
|
setPendingContext(name, context) {
|
|
@@ -275,7 +277,7 @@ var AgentManager = class {
|
|
|
275
277
|
async deliverPendingContext(name) {
|
|
276
278
|
const context = this.pendingContext.get(name);
|
|
277
279
|
if (!context) return;
|
|
278
|
-
const tracked = this.
|
|
280
|
+
const tracked = this.minds.get(name);
|
|
279
281
|
if (!tracked) return;
|
|
280
282
|
this.pendingContext.delete(name);
|
|
281
283
|
const parts = [];
|
|
@@ -283,7 +285,7 @@ var AgentManager = class {
|
|
|
283
285
|
parts.push(`[system] Variant "${context.name}" has been merged and you have been restarted.`);
|
|
284
286
|
} else if (context.type === "sprouted") {
|
|
285
287
|
parts.push(
|
|
286
|
-
"[system] You've sprouted. You now have full
|
|
288
|
+
"[system] You've sprouted. You now have full capabilities \u2014 connectors, schedules, variants, and the complete volute CLI. Check your new skills for details."
|
|
287
289
|
);
|
|
288
290
|
} else {
|
|
289
291
|
parts.push("[system] You have been restarted.");
|
|
@@ -306,9 +308,9 @@ var AgentManager = class {
|
|
|
306
308
|
}
|
|
307
309
|
setupCrashRecovery(name, child) {
|
|
308
310
|
child.on("exit", async (code) => {
|
|
309
|
-
this.
|
|
311
|
+
this.minds.delete(name);
|
|
310
312
|
if (this.shuttingDown || this.stopping.has(name)) return;
|
|
311
|
-
console.error(`[daemon]
|
|
313
|
+
console.error(`[daemon] mind ${name} exited with code ${code}`);
|
|
312
314
|
const attempts = this.restartAttempts.get(name) ?? 0;
|
|
313
315
|
if (attempts >= MAX_RESTART_ATTEMPTS) {
|
|
314
316
|
console.error(`[daemon] ${name} crashed ${attempts} times \u2014 giving up on restart`);
|
|
@@ -316,7 +318,7 @@ var AgentManager = class {
|
|
|
316
318
|
if (variant) {
|
|
317
319
|
setVariantRunning(base, variant, false);
|
|
318
320
|
} else {
|
|
319
|
-
|
|
321
|
+
setMindRunning(name, false);
|
|
320
322
|
}
|
|
321
323
|
return;
|
|
322
324
|
}
|
|
@@ -328,18 +330,18 @@ var AgentManager = class {
|
|
|
328
330
|
);
|
|
329
331
|
setTimeout(() => {
|
|
330
332
|
if (this.shuttingDown) return;
|
|
331
|
-
this.
|
|
333
|
+
this.startMind(name).catch((err) => {
|
|
332
334
|
console.error(`[daemon] failed to restart ${name}:`, err);
|
|
333
335
|
});
|
|
334
336
|
}, delay);
|
|
335
337
|
});
|
|
336
338
|
}
|
|
337
|
-
async
|
|
338
|
-
const tracked = this.
|
|
339
|
+
async stopMind(name) {
|
|
340
|
+
const tracked = this.minds.get(name);
|
|
339
341
|
if (!tracked) return;
|
|
340
342
|
this.stopping.add(name);
|
|
341
343
|
const { child } = tracked;
|
|
342
|
-
this.
|
|
344
|
+
this.minds.delete(name);
|
|
343
345
|
await new Promise((resolve2) => {
|
|
344
346
|
child.on("exit", () => resolve2());
|
|
345
347
|
try {
|
|
@@ -357,31 +359,31 @@ var AgentManager = class {
|
|
|
357
359
|
});
|
|
358
360
|
this.stopping.delete(name);
|
|
359
361
|
if (this.restartAttempts.delete(name)) this.saveCrashAttempts();
|
|
360
|
-
rmSync2(
|
|
362
|
+
rmSync2(mindPidPath(name), { force: true });
|
|
361
363
|
if (!this.shuttingDown) {
|
|
362
364
|
const [baseName, variantName] = name.split("@", 2);
|
|
363
365
|
if (variantName) {
|
|
364
366
|
setVariantRunning(baseName, variantName, false);
|
|
365
367
|
} else {
|
|
366
|
-
|
|
368
|
+
setMindRunning(name, false);
|
|
367
369
|
}
|
|
368
370
|
}
|
|
369
|
-
console.error(`[daemon] stopped
|
|
371
|
+
console.error(`[daemon] stopped mind ${name}`);
|
|
370
372
|
}
|
|
371
|
-
async
|
|
372
|
-
await this.
|
|
373
|
-
await this.
|
|
373
|
+
async restartMind(name) {
|
|
374
|
+
await this.stopMind(name);
|
|
375
|
+
await this.startMind(name);
|
|
374
376
|
}
|
|
375
377
|
async stopAll() {
|
|
376
378
|
this.shuttingDown = true;
|
|
377
|
-
const names = [...this.
|
|
378
|
-
await Promise.all(names.map((name) => this.
|
|
379
|
+
const names = [...this.minds.keys()];
|
|
380
|
+
await Promise.all(names.map((name) => this.stopMind(name)));
|
|
379
381
|
}
|
|
380
382
|
isRunning(name) {
|
|
381
|
-
return this.
|
|
383
|
+
return this.minds.has(name);
|
|
382
384
|
}
|
|
383
|
-
|
|
384
|
-
return [...this.
|
|
385
|
+
getRunningMinds() {
|
|
386
|
+
return [...this.minds.keys()];
|
|
385
387
|
}
|
|
386
388
|
get crashAttemptsPath() {
|
|
387
389
|
return resolve(voluteHome(), "crash-attempts.json");
|
|
@@ -424,22 +426,22 @@ async function killProcessOnPort(port) {
|
|
|
424
426
|
}
|
|
425
427
|
}
|
|
426
428
|
var instance = null;
|
|
427
|
-
function
|
|
428
|
-
if (instance) throw new Error("
|
|
429
|
-
instance = new
|
|
429
|
+
function initMindManager() {
|
|
430
|
+
if (instance) throw new Error("MindManager already initialized");
|
|
431
|
+
instance = new MindManager();
|
|
430
432
|
return instance;
|
|
431
433
|
}
|
|
432
|
-
function
|
|
433
|
-
if (!instance) instance = new
|
|
434
|
+
function getMindManager() {
|
|
435
|
+
if (!instance) instance = new MindManager();
|
|
434
436
|
return instance;
|
|
435
437
|
}
|
|
436
438
|
|
|
437
439
|
export {
|
|
440
|
+
RotatingLog,
|
|
438
441
|
loadJsonMap,
|
|
439
442
|
saveJsonMap,
|
|
440
443
|
clearJsonMap,
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
getAgentManager
|
|
444
|
+
MindManager,
|
|
445
|
+
initMindManager,
|
|
446
|
+
getMindManager
|
|
445
447
|
};
|
|
@@ -20,51 +20,54 @@ function voluteHome() {
|
|
|
20
20
|
return resolve(homedir(), ".volute");
|
|
21
21
|
}
|
|
22
22
|
function ensureVoluteHome() {
|
|
23
|
-
const
|
|
24
|
-
mkdirSync(
|
|
23
|
+
const mindsBase = process.env.VOLUTE_MINDS_DIR ?? resolve(voluteHome(), "minds");
|
|
24
|
+
mkdirSync(mindsBase, { recursive: true });
|
|
25
25
|
}
|
|
26
26
|
function readRegistry() {
|
|
27
|
-
const registryPath = resolve(voluteHome(), "
|
|
27
|
+
const registryPath = resolve(voluteHome(), "minds.json");
|
|
28
28
|
if (!existsSync(registryPath)) return [];
|
|
29
29
|
try {
|
|
30
30
|
const entries = JSON.parse(readFileSync(registryPath, "utf-8"));
|
|
31
|
-
return entries.map((e) => ({
|
|
31
|
+
return entries.map((e) => ({
|
|
32
|
+
...e,
|
|
33
|
+
running: e.running ?? false,
|
|
34
|
+
stage: e.stage ?? "sprouted"
|
|
35
|
+
}));
|
|
32
36
|
} catch {
|
|
33
37
|
return [];
|
|
34
38
|
}
|
|
35
39
|
}
|
|
36
40
|
function writeRegistry(entries) {
|
|
37
41
|
ensureVoluteHome();
|
|
38
|
-
const registryPath = resolve(voluteHome(), "
|
|
42
|
+
const registryPath = resolve(voluteHome(), "minds.json");
|
|
39
43
|
const tmpPath = `${registryPath}.tmp`;
|
|
40
44
|
writeFileSync(tmpPath, `${JSON.stringify(entries, null, 2)}
|
|
41
45
|
`);
|
|
42
46
|
renameSync(tmpPath, registryPath);
|
|
43
47
|
}
|
|
44
|
-
var
|
|
45
|
-
var
|
|
46
|
-
function
|
|
47
|
-
if (!name) return "
|
|
48
|
-
if (name.length >
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
return "Agent name must start with alphanumeric and contain only alphanumeric, dots, dashes, or underscores";
|
|
48
|
+
var MIND_NAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
|
|
49
|
+
var MIND_NAME_MAX = 64;
|
|
50
|
+
function validateMindName(name) {
|
|
51
|
+
if (!name) return "Mind name is required";
|
|
52
|
+
if (name.length > MIND_NAME_MAX) return `Mind name must be at most ${MIND_NAME_MAX} characters`;
|
|
53
|
+
if (!MIND_NAME_RE.test(name)) {
|
|
54
|
+
return "Mind name must start with alphanumeric and contain only alphanumeric, dots, dashes, or underscores";
|
|
52
55
|
}
|
|
53
56
|
return null;
|
|
54
57
|
}
|
|
55
|
-
function
|
|
56
|
-
const err =
|
|
58
|
+
function addMind(name, port, stage) {
|
|
59
|
+
const err = validateMindName(name);
|
|
57
60
|
if (err) throw new Error(err);
|
|
58
61
|
const entries = readRegistry();
|
|
59
62
|
const filtered = entries.filter((e) => e.name !== name);
|
|
60
63
|
filtered.push({ name, port, created: (/* @__PURE__ */ new Date()).toISOString(), running: false, stage });
|
|
61
64
|
writeRegistry(filtered);
|
|
62
65
|
}
|
|
63
|
-
function
|
|
66
|
+
function removeMind(name) {
|
|
64
67
|
const entries = readRegistry();
|
|
65
68
|
writeRegistry(entries.filter((e) => e.name !== name));
|
|
66
69
|
}
|
|
67
|
-
function
|
|
70
|
+
function setMindRunning(name, running) {
|
|
68
71
|
const entries = readRegistry();
|
|
69
72
|
const entry = entries.find((e) => e.name === name);
|
|
70
73
|
if (entry) {
|
|
@@ -72,7 +75,7 @@ function setAgentRunning(name, running) {
|
|
|
72
75
|
writeRegistry(entries);
|
|
73
76
|
}
|
|
74
77
|
}
|
|
75
|
-
function
|
|
78
|
+
function setMindStage(name, stage) {
|
|
76
79
|
const entries = readRegistry();
|
|
77
80
|
const entry = entries.find((e) => e.name === name);
|
|
78
81
|
if (entry) {
|
|
@@ -80,14 +83,14 @@ function setAgentStage(name, stage) {
|
|
|
80
83
|
writeRegistry(entries);
|
|
81
84
|
}
|
|
82
85
|
}
|
|
83
|
-
function
|
|
86
|
+
function findMind(name) {
|
|
84
87
|
return readRegistry().find((e) => e.name === name);
|
|
85
88
|
}
|
|
86
|
-
function
|
|
87
|
-
if (process.env.
|
|
88
|
-
return resolve(process.env.
|
|
89
|
+
function mindDir(name) {
|
|
90
|
+
if (process.env.VOLUTE_MINDS_DIR) {
|
|
91
|
+
return resolve(process.env.VOLUTE_MINDS_DIR, name);
|
|
89
92
|
}
|
|
90
|
-
return resolve(voluteHome(), "
|
|
93
|
+
return resolve(voluteHome(), "minds", name);
|
|
91
94
|
}
|
|
92
95
|
function stateDir(name) {
|
|
93
96
|
return resolve(voluteHome(), "state", name);
|
|
@@ -112,20 +115,20 @@ function daemonLoopback() {
|
|
|
112
115
|
if (host === "::") return "[::1]";
|
|
113
116
|
return host;
|
|
114
117
|
}
|
|
115
|
-
function
|
|
118
|
+
function resolveMind(name) {
|
|
116
119
|
const [baseName, variantName] = name.split("@", 2);
|
|
117
|
-
const entry =
|
|
120
|
+
const entry = findMind(baseName);
|
|
118
121
|
if (!entry) {
|
|
119
|
-
throw new Error(`Unknown
|
|
122
|
+
throw new Error(`Unknown mind: ${baseName}`);
|
|
120
123
|
}
|
|
121
|
-
const dir =
|
|
124
|
+
const dir = mindDir(baseName);
|
|
122
125
|
if (!existsSync(dir)) {
|
|
123
|
-
throw new Error(`
|
|
126
|
+
throw new Error(`Mind directory missing: ${dir}`);
|
|
124
127
|
}
|
|
125
128
|
if (variantName) {
|
|
126
129
|
const variant = findVariant(baseName, variantName);
|
|
127
130
|
if (!variant) {
|
|
128
|
-
throw new Error(`Unknown variant: ${variantName} (
|
|
131
|
+
throw new Error(`Unknown variant: ${variantName} (mind: ${baseName})`);
|
|
129
132
|
}
|
|
130
133
|
return { entry: { ...entry, port: variant.port }, dir: variant.path };
|
|
131
134
|
}
|
|
@@ -150,59 +153,59 @@ function writeAllVariants(all) {
|
|
|
150
153
|
writeFileSync2(variantsPath(), `${JSON.stringify(all, null, 2)}
|
|
151
154
|
`);
|
|
152
155
|
}
|
|
153
|
-
function readVariants(
|
|
154
|
-
return readAllVariants()[
|
|
156
|
+
function readVariants(mindName) {
|
|
157
|
+
return readAllVariants()[mindName] ?? [];
|
|
155
158
|
}
|
|
156
|
-
function writeVariants(
|
|
159
|
+
function writeVariants(mindName, variants) {
|
|
157
160
|
const all = readAllVariants();
|
|
158
161
|
if (variants.length === 0) {
|
|
159
|
-
delete all[
|
|
162
|
+
delete all[mindName];
|
|
160
163
|
} else {
|
|
161
|
-
all[
|
|
164
|
+
all[mindName] = variants;
|
|
162
165
|
}
|
|
163
166
|
writeAllVariants(all);
|
|
164
167
|
}
|
|
165
|
-
function addVariant(
|
|
166
|
-
const variants = readVariants(
|
|
168
|
+
function addVariant(mindName, variant) {
|
|
169
|
+
const variants = readVariants(mindName);
|
|
167
170
|
const filtered = variants.filter((v) => v.name !== variant.name);
|
|
168
171
|
filtered.push(variant);
|
|
169
|
-
writeVariants(
|
|
172
|
+
writeVariants(mindName, filtered);
|
|
170
173
|
}
|
|
171
|
-
function removeVariant(
|
|
172
|
-
const variants = readVariants(
|
|
174
|
+
function removeVariant(mindName, name) {
|
|
175
|
+
const variants = readVariants(mindName);
|
|
173
176
|
writeVariants(
|
|
174
|
-
|
|
177
|
+
mindName,
|
|
175
178
|
variants.filter((v) => v.name !== name)
|
|
176
179
|
);
|
|
177
180
|
}
|
|
178
|
-
function findVariant(
|
|
179
|
-
return readVariants(
|
|
181
|
+
function findVariant(mindName, name) {
|
|
182
|
+
return readVariants(mindName).find((v) => v.name === name);
|
|
180
183
|
}
|
|
181
|
-
function setVariantRunning(
|
|
184
|
+
function setVariantRunning(mindName, variantName, running) {
|
|
182
185
|
const all = readAllVariants();
|
|
183
|
-
const variants = all[
|
|
186
|
+
const variants = all[mindName] ?? [];
|
|
184
187
|
const variant = variants.find((v) => v.name === variantName);
|
|
185
188
|
if (variant) {
|
|
186
189
|
variant.running = running;
|
|
187
|
-
all[
|
|
190
|
+
all[mindName] = variants;
|
|
188
191
|
writeAllVariants(all);
|
|
189
192
|
}
|
|
190
193
|
}
|
|
191
194
|
function getAllRunningVariants() {
|
|
192
195
|
const all = readAllVariants();
|
|
193
196
|
const result = [];
|
|
194
|
-
for (const [
|
|
197
|
+
for (const [mindName, variants] of Object.entries(all)) {
|
|
195
198
|
for (const variant of variants) {
|
|
196
199
|
if (variant.running) {
|
|
197
|
-
result.push({
|
|
200
|
+
result.push({ mindName, variant });
|
|
198
201
|
}
|
|
199
202
|
}
|
|
200
203
|
}
|
|
201
204
|
return result;
|
|
202
205
|
}
|
|
203
|
-
function removeAllVariants(
|
|
206
|
+
function removeAllVariants(mindName) {
|
|
204
207
|
const all = readAllVariants();
|
|
205
|
-
delete all[
|
|
208
|
+
delete all[mindName];
|
|
206
209
|
writeAllVariants(all);
|
|
207
210
|
}
|
|
208
211
|
async function checkHealth(port) {
|
|
@@ -242,15 +245,15 @@ export {
|
|
|
242
245
|
voluteHome,
|
|
243
246
|
ensureVoluteHome,
|
|
244
247
|
readRegistry,
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
248
|
+
validateMindName,
|
|
249
|
+
addMind,
|
|
250
|
+
removeMind,
|
|
251
|
+
setMindRunning,
|
|
252
|
+
setMindStage,
|
|
253
|
+
findMind,
|
|
254
|
+
mindDir,
|
|
252
255
|
stateDir,
|
|
253
256
|
nextPort,
|
|
254
257
|
daemonLoopback,
|
|
255
|
-
|
|
258
|
+
resolveMind
|
|
256
259
|
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/lib/resolve-mind-name.ts
|
|
4
|
+
function resolveMindName(flags) {
|
|
5
|
+
const name = flags.mind || process.env.VOLUTE_MIND;
|
|
6
|
+
if (!name) {
|
|
7
|
+
console.error("No mind specified. Provide a name or set VOLUTE_MIND.");
|
|
8
|
+
process.exit(1);
|
|
9
|
+
}
|
|
10
|
+
return name;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export {
|
|
14
|
+
resolveMindName
|
|
15
|
+
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
stateDir,
|
|
4
4
|
voluteHome
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-M77QBTEH.js";
|
|
6
6
|
|
|
7
7
|
// src/lib/env.ts
|
|
8
8
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
@@ -10,8 +10,8 @@ import { dirname, resolve } from "path";
|
|
|
10
10
|
function sharedEnvPath() {
|
|
11
11
|
return resolve(voluteHome(), "env.json");
|
|
12
12
|
}
|
|
13
|
-
function
|
|
14
|
-
return resolve(stateDir(
|
|
13
|
+
function mindEnvPath(mindName) {
|
|
14
|
+
return resolve(stateDir(mindName), "env.json");
|
|
15
15
|
}
|
|
16
16
|
function readEnv(path) {
|
|
17
17
|
if (!existsSync(path)) return {};
|
|
@@ -26,15 +26,15 @@ function writeEnv(path, env) {
|
|
|
26
26
|
writeFileSync(path, `${JSON.stringify(env, null, 2)}
|
|
27
27
|
`, { mode: 384 });
|
|
28
28
|
}
|
|
29
|
-
function loadMergedEnv(
|
|
29
|
+
function loadMergedEnv(mindName) {
|
|
30
30
|
const shared = readEnv(sharedEnvPath());
|
|
31
|
-
const
|
|
32
|
-
return { ...shared, ...
|
|
31
|
+
const mind = readEnv(mindEnvPath(mindName));
|
|
32
|
+
return { ...shared, ...mind };
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
export {
|
|
36
36
|
sharedEnvPath,
|
|
37
|
-
|
|
37
|
+
mindEnvPath,
|
|
38
38
|
readEnv,
|
|
39
39
|
writeEnv,
|
|
40
40
|
loadMergedEnv
|