volute 0.25.0 → 0.27.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 +28 -33
- package/dist/{activity-events-4O37J7PD.js → activity-events-BBIEA2F4.js} +2 -3
- package/dist/api.d.ts +886 -220
- package/dist/{archive-4ZQYK5MN.js → archive-UA4BDFXQ.js} +2 -2
- package/dist/{auth-HM2RSPY7.js → auth-D3OT2ARB.js} +3 -3
- package/dist/bridge-FQHZL3MC.js +206 -0
- package/dist/chat-MHJ3L6JQ.js +58 -0
- package/dist/{chunk-PHU4DEAJ.js → chunk-2WPW7OT6.js} +3 -3
- package/dist/{chunk-BOTQ25QT.js → chunk-2YP2TVDT.js} +138 -56
- package/dist/{chunk-DG7TO7EE.js → chunk-4WXYUOAK.js} +5 -7
- package/dist/{chunk-JTDFJWI2.js → chunk-AW7PFDVN.js} +5 -5
- package/dist/{chunk-2767L2RZ.js → chunk-EHYDTZTF.js} +6 -6
- package/dist/{chunk-ZSH4G2P5.js → chunk-GIE6CSN5.js} +17 -17
- package/dist/chunk-H7OZRFJB.js +432 -0
- package/dist/{chunk-ON3FF5JA.js → chunk-HDN7MNGD.js} +3 -3
- package/dist/chunk-IAYBDWVG.js +477 -0
- package/dist/chunk-IKRVFPWU.js +83 -0
- package/dist/{chunk-TRQEV3CD.js → chunk-JGFVMROS.js} +32 -6
- package/dist/{chunk-PHHKNGA3.js → chunk-JKOWNZ4P.js} +3 -3
- package/dist/{chunk-E7GOKNOT.js → chunk-K5NAC55T.js} +1 -1
- package/dist/{chunk-HFCBO2GL.js → chunk-KDGS53OS.js} +4 -4
- package/dist/chunk-KTLFDYPT.js +61 -0
- package/dist/{chunk-3AIBT4TW.js → chunk-LAC664WU.js} +30 -4
- package/dist/{chunk-PMX4EIJK.js → chunk-OQZH4PBB.js} +467 -1054
- package/dist/{chunk-SHSWYG2J.js → chunk-PHSAT7YL.js} +71 -58
- package/dist/chunk-RKQEHRBB.js +177 -0
- package/dist/{chunk-RVKR2R7F.js → chunk-SSI47XP2.js} +10 -2
- package/dist/chunk-T6HKBWXZ.js +23 -0
- package/dist/chunk-USUXRNVD.js +113 -0
- package/dist/{chunk-BFK6SOEJ.js → chunk-VIVMW2H2.js} +4 -4
- package/dist/{chunk-KTJGZ7M7.js → chunk-XBLSAVJF.js} +1 -1
- package/dist/chunk-ZYGKG6VC.js +22 -0
- package/dist/cli.js +51 -32
- package/dist/{cloud-sync-PPBBJDY6.js → cloud-sync-T7M3ESC3.js} +15 -12
- package/dist/connectors/discord-bridge.js +158 -0
- package/dist/connectors/slack-bridge.js +119 -0
- package/dist/connectors/telegram-bridge.js +133 -0
- package/dist/conversations-M2K4253F.js +55 -0
- package/dist/create-D7J73A6H.js +45 -0
- package/dist/{create-VDQJER52.js → create-QWV73WXD.js} +1 -1
- package/dist/{daemon-client-JOVQZ52X.js → daemon-client-I42FK2BF.js} +2 -2
- package/dist/{daemon-restart-FDNOZEAD.js → daemon-restart-M2QTYMEG.js} +7 -6
- package/dist/daemon.js +2247 -1085
- package/dist/db-IC4J52XQ.js +8 -0
- package/dist/{delete-2MRR4JX5.js → delete-4JYGD4VN.js} +1 -1
- package/dist/down-LVBXEULC.js +14 -0
- package/dist/{env-2FPOZK37.js → env-YJMUMFIY.js} +5 -5
- package/dist/{export-IKFAPRAO.js → export-BOJQWBMA.js} +4 -4
- package/dist/{file-KT3UIQM3.js → file-CR36YUPD.js} +4 -4
- package/dist/{history-46WZN5CN.js → history-XKRTAFS2.js} +7 -7
- package/dist/{import-TH26J76F.js → import-SRTQXBGH.js} +4 -4
- package/dist/join-J4QU42DL.js +66 -0
- package/dist/list-R73GENNL.js +40 -0
- package/dist/{log-6SGSSR3D.js → log-ABYNVYJ3.js} +4 -4
- package/dist/login-3QZNR2DF.js +46 -0
- package/dist/{login-UO6AOVEA.js → login-XX37I52P.js} +3 -3
- package/dist/logout-T53VKCPU.js +39 -0
- package/dist/{logout-UKD5LA37.js → logout-W4KOOBIT.js} +2 -2
- package/dist/{logs-HRBONI5I.js → logs-U35JR2KE.js} +7 -7
- package/dist/{merge-KSFJKX6T.js → merge-LNSMSAOF.js} +4 -4
- package/dist/message-delivery-LDXLGERA.js +25 -0
- package/dist/migrate-registry-to-db-XC7T5B7P.js +110 -0
- package/dist/{mind-YVWAHL2A.js → mind-DI33C74K.js} +25 -25
- package/dist/{mind-activity-tracker-NMDDEV3K.js → mind-activity-tracker-EN6XNXPF.js} +3 -4
- package/dist/{mind-manager-4NDNAYAB.js → mind-manager-M6EMUW5I.js} +6 -5
- package/dist/{mind-sleep-GHPTSAYN.js → mind-sleep-BTSWQNAC.js} +4 -4
- package/dist/{mind-wake-BJDJFMDF.js → mind-wake-SBAKIDVP.js} +4 -4
- package/dist/notes-XCER3I7M.js +220 -0
- package/dist/{package-3HF5MXU2.js → package-7WY6VKU3.js} +2 -1
- package/dist/{pages-Y6DRWUOJ.js → pages-6EBS6CBR.js} +2 -2
- package/dist/{publish-EEKTZBHW.js → publish-66UB2ZFY.js} +5 -5
- package/dist/{pull-D32SPFVU.js → pull-XCHJTM5M.js} +4 -4
- package/dist/read-36UFXN3G.js +46 -0
- package/dist/{register-U2UO6TC4.js → register-6B2CXTYM.js} +3 -3
- package/dist/{registry-D2BSQ2X5.js → registry-NDNOOYG4.js} +15 -9
- package/dist/{restart-5BMNV7KU.js → restart-6ESL3NBO.js} +6 -6
- package/dist/sandbox-TGBX22DS.js +19 -0
- package/dist/{schedule-YEFDLVMJ.js → schedule-QTJMFATP.js} +7 -7
- package/dist/{seed-6FEKB3YC.js → seed-SSUCYYDF.js} +2 -2
- package/dist/{send-IISDYFCL.js → send-ZNCJDSRP.js} +28 -36
- package/dist/service-6LIN3F3K.js +122 -0
- package/dist/setup-JG4QAEBV.js +371 -0
- package/dist/setup-JHL5ZEST.js +17 -0
- package/dist/{shared-LWMNTTZN.js → shared-ML5I4Q2A.js} +4 -4
- package/dist/{skill-T3EMR6IR.js → skill-AUAQTSP5.js} +7 -7
- package/dist/skills/dreaming/SKILL.md +68 -0
- package/dist/skills/dreaming/references/INSTALL.md +56 -0
- package/dist/skills/dreaming/scripts/dream.ts +289 -0
- package/dist/skills/dreaming/scripts/wake-context-dreams.sh +30 -0
- package/dist/skills/notes/SKILL.md +34 -0
- package/dist/skills/orientation/SKILL.md +3 -3
- package/dist/skills/volute-mind/SKILL.md +32 -30
- package/dist/sleep-manager-MWYHM5HV.js +29 -0
- package/dist/split-TKJ5OT3P.js +63 -0
- package/dist/{sprout-QJVGJDSH.js → sprout-IJVVKSJ2.js} +6 -7
- package/dist/{start-C7XITZ5O.js → start-EUJSS5R4.js} +4 -4
- package/dist/{status-SIRPLEZC.js → status-77YEPHMW.js} +5 -5
- package/dist/{status-LYS4NUOZ.js → status-7GA4SM4Y.js} +4 -4
- package/dist/{status-LV34BG6G.js → status-THLOBLWG.js} +2 -2
- package/dist/{stop-CVKBSLXY.js → stop-3XAITBBF.js} +6 -6
- package/dist/{tailscale-AJ4VL5XK.js → tailscale-NY5MUMY3.js} +1 -1
- package/dist/up-NKSMXBWR.js +17 -0
- package/dist/{update-7XCZMYBT.js → update-PTSH22AZ.js} +11 -11
- package/dist/{update-check-F5Z3ALXX.js → update-check-64FWC4Y2.js} +2 -2
- package/dist/{upgrade-7RUIXGOO.js → upgrade-HA47CS4C.js} +12 -5
- package/dist/variant-7TGZHOU3.js +41 -0
- package/dist/{version-notify-AZQMC32A.js → version-notify-5Z4MNR6M.js} +26 -28
- package/dist/web-assets/assets/index-CI5wgghI.css +1 -0
- package/dist/web-assets/assets/index-is5CvJWH.js +75 -0
- package/dist/web-assets/favicon.png +0 -0
- package/dist/web-assets/index.html +2 -2
- package/drizzle/0015_notes.sql +23 -0
- package/drizzle/0016_note_reactions_and_replies.sql +15 -0
- package/drizzle/0017_minds.sql +16 -0
- package/drizzle/meta/_journal.json +21 -0
- package/package.json +2 -1
- package/templates/_base/.init/.config/hooks/wake-context.sh +7 -0
- package/templates/_base/.init/.config/prompts.json +2 -2
- package/templates/_base/home/VOLUTE.md +5 -5
- package/templates/_base/src/lib/startup.ts +10 -2
- package/templates/claude/src/agent.ts +51 -1
- package/templates/claude/src/server.ts +1 -0
- package/templates/pi/package.json.tmpl +1 -0
- package/templates/pi/src/agent.ts +48 -1
- package/templates/pi/src/lib/subagents.ts +150 -0
- package/templates/pi/src/server.ts +1 -0
- package/dist/channel-HZOSHGNF.js +0 -260
- package/dist/chunk-33XAVCS4.js +0 -203
- package/dist/chunk-B2CPS4QU.js +0 -283
- package/dist/chunk-NWPT4ASZ.js +0 -89
- package/dist/chunk-SIAG3QMM.js +0 -42
- package/dist/chunk-WSLPZF72.js +0 -173
- package/dist/connector-M6XFI6GM.js +0 -147
- package/dist/connectors/discord.js +0 -177
- package/dist/connectors/slack.js +0 -181
- package/dist/connectors/telegram.js +0 -187
- package/dist/down-674SX2IZ.js +0 -14
- package/dist/message-delivery-XMGV3FUM.js +0 -23
- package/dist/service-FASYWLTC.js +0 -247
- package/dist/setup-BMLM2UTK.js +0 -230
- package/dist/sleep-manager-RKTFZPD3.js +0 -27
- package/dist/up-CJ26KQLN.js +0 -15
- package/dist/variant-UGREB4G5.js +0 -207
- package/dist/web-assets/assets/index-CGPSVu19.js +0 -69
- package/dist/web-assets/assets/index-V_rNDsM8.css +0 -1
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
wrapForIsolation
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-RKQEHRBB.js";
|
|
5
5
|
|
|
6
6
|
// src/lib/exec.ts
|
|
7
7
|
import { execFile as execFileCb, execFileSync, spawn } from "child_process";
|
|
8
|
-
function exec(cmd, args, options) {
|
|
9
|
-
const [wrappedCmd, wrappedArgs] = options?.mindName ? wrapForIsolation(cmd, args, options.mindName) : [cmd, args];
|
|
8
|
+
async function exec(cmd, args, options) {
|
|
9
|
+
const [wrappedCmd, wrappedArgs] = options?.mindName ? await wrapForIsolation(cmd, args, options.mindName) : [cmd, args];
|
|
10
10
|
return new Promise((resolve, reject) => {
|
|
11
11
|
execFileCb(
|
|
12
12
|
wrappedCmd,
|
|
@@ -35,8 +35,8 @@ function resolveVoluteBin() {
|
|
|
35
35
|
throw new Error("Could not find volute binary on PATH");
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
|
-
function execInherit(cmd, args, options) {
|
|
39
|
-
const [wrappedCmd, wrappedArgs] = options?.mindName ? wrapForIsolation(cmd, args, options.mindName) : [cmd, args];
|
|
38
|
+
async function execInherit(cmd, args, options) {
|
|
39
|
+
const [wrappedCmd, wrappedArgs] = options?.mindName ? await wrapForIsolation(cmd, args, options.mindName) : [cmd, args];
|
|
40
40
|
return new Promise((resolve, reject) => {
|
|
41
41
|
const child = spawn(wrappedCmd, wrappedArgs, {
|
|
42
42
|
cwd: options?.cwd,
|
|
@@ -5,19 +5,19 @@ import {
|
|
|
5
5
|
pollHealthDown,
|
|
6
6
|
readDaemonConfig,
|
|
7
7
|
stopService
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-LAC664WU.js";
|
|
9
9
|
import {
|
|
10
|
-
|
|
11
|
-
} from "./chunk-
|
|
10
|
+
voluteSystemDir
|
|
11
|
+
} from "./chunk-H7OZRFJB.js";
|
|
12
12
|
|
|
13
13
|
// src/commands/down.ts
|
|
14
14
|
import { existsSync, readFileSync, unlinkSync } from "fs";
|
|
15
15
|
import { resolve } from "path";
|
|
16
16
|
async function stopDaemon() {
|
|
17
|
-
const
|
|
18
|
-
const pidPath = resolve(
|
|
17
|
+
const systemDir = voluteSystemDir();
|
|
18
|
+
const pidPath = resolve(systemDir, "daemon.pid");
|
|
19
19
|
if (!existsSync(pidPath)) {
|
|
20
|
-
const configPath = resolve(
|
|
20
|
+
const configPath = resolve(systemDir, "daemon.json");
|
|
21
21
|
let port = 1618;
|
|
22
22
|
let hostname = "localhost";
|
|
23
23
|
if (existsSync(configPath)) {
|
|
@@ -4,34 +4,29 @@ import {
|
|
|
4
4
|
modeLabel,
|
|
5
5
|
pollHealth,
|
|
6
6
|
startService
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-LAC664WU.js";
|
|
8
|
+
import {
|
|
9
|
+
readGlobalConfig
|
|
10
|
+
} from "./chunk-IKRVFPWU.js";
|
|
8
11
|
import {
|
|
9
12
|
parseArgs
|
|
10
13
|
} from "./chunk-D424ZQGI.js";
|
|
11
14
|
import {
|
|
12
|
-
voluteHome
|
|
13
|
-
|
|
15
|
+
voluteHome,
|
|
16
|
+
voluteSystemDir
|
|
17
|
+
} from "./chunk-H7OZRFJB.js";
|
|
14
18
|
|
|
15
19
|
// src/commands/up.ts
|
|
16
20
|
import { spawn } from "child_process";
|
|
17
21
|
import { existsSync, mkdirSync, openSync, readFileSync } from "fs";
|
|
18
22
|
import { dirname, resolve } from "path";
|
|
19
|
-
function readGlobalConfig() {
|
|
20
|
-
const configPath = resolve(voluteHome(), "config.json");
|
|
21
|
-
if (!existsSync(configPath)) return {};
|
|
22
|
-
try {
|
|
23
|
-
return JSON.parse(readFileSync(configPath, "utf-8"));
|
|
24
|
-
} catch (err) {
|
|
25
|
-
console.error(`Invalid config file ${configPath}: ${err instanceof Error ? err.message : err}`);
|
|
26
|
-
process.exit(1);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
23
|
async function run(args) {
|
|
30
24
|
const { flags } = parseArgs(args, {
|
|
31
25
|
port: { type: "number" },
|
|
32
26
|
host: { type: "string" },
|
|
33
27
|
foreground: { type: "boolean" },
|
|
34
|
-
tailscale: { type: "boolean" }
|
|
28
|
+
tailscale: { type: "boolean" },
|
|
29
|
+
"no-sandbox": { type: "boolean" }
|
|
35
30
|
});
|
|
36
31
|
const mode = getServiceMode();
|
|
37
32
|
if (!flags.foreground && mode !== "manual") {
|
|
@@ -57,7 +52,8 @@ async function run(args) {
|
|
|
57
52
|
const port = flags.port ?? config.port ?? 1618;
|
|
58
53
|
const hostname = flags.host ?? config.hostname ?? "127.0.0.1";
|
|
59
54
|
const home = voluteHome();
|
|
60
|
-
const
|
|
55
|
+
const systemDir = voluteSystemDir();
|
|
56
|
+
const pidPath = resolve(systemDir, "daemon.pid");
|
|
61
57
|
if (existsSync(pidPath)) {
|
|
62
58
|
try {
|
|
63
59
|
const pid = parseInt(readFileSync(pidPath, "utf-8").trim(), 10);
|
|
@@ -95,6 +91,9 @@ async function run(args) {
|
|
|
95
91
|
}
|
|
96
92
|
} catch {
|
|
97
93
|
}
|
|
94
|
+
if (flags["no-sandbox"]) {
|
|
95
|
+
process.env.VOLUTE_SANDBOX = "0";
|
|
96
|
+
}
|
|
98
97
|
if (flags.foreground) {
|
|
99
98
|
const { startDaemon } = await import("./daemon.js");
|
|
100
99
|
await startDaemon({ port, hostname, foreground: true, tailscale: flags.tailscale });
|
|
@@ -106,10 +105,12 @@ async function run(args) {
|
|
|
106
105
|
process.exit(1);
|
|
107
106
|
}
|
|
108
107
|
mkdirSync(home, { recursive: true });
|
|
109
|
-
|
|
108
|
+
mkdirSync(systemDir, { recursive: true });
|
|
109
|
+
const logFile = resolve(systemDir, "daemon.log");
|
|
110
110
|
const logFd = openSync(logFile, "a");
|
|
111
111
|
const daemonArgs = [daemonModule, "--port", String(port), "--host", hostname];
|
|
112
112
|
if (flags.tailscale) daemonArgs.push("--tailscale");
|
|
113
|
+
if (flags["no-sandbox"]) daemonArgs.push("--no-sandbox");
|
|
113
114
|
const child = spawn(process.execPath, daemonArgs, {
|
|
114
115
|
stdio: ["ignore", "ignore", logFd],
|
|
115
116
|
detached: true
|
|
@@ -151,6 +152,5 @@ async function run(args) {
|
|
|
151
152
|
}
|
|
152
153
|
|
|
153
154
|
export {
|
|
154
|
-
readGlobalConfig,
|
|
155
155
|
run
|
|
156
156
|
};
|
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
__export
|
|
4
|
+
} from "./chunk-K3NQKI34.js";
|
|
5
|
+
|
|
6
|
+
// src/lib/db.ts
|
|
7
|
+
import { chmodSync, existsSync } from "fs";
|
|
8
|
+
import { dirname as dirname2, resolve as resolve2 } from "path";
|
|
9
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
10
|
+
import { drizzle } from "drizzle-orm/libsql";
|
|
11
|
+
import { migrate } from "drizzle-orm/libsql/migrator";
|
|
12
|
+
|
|
13
|
+
// src/lib/registry.ts
|
|
14
|
+
import { mkdirSync } from "fs";
|
|
15
|
+
import { homedir } from "os";
|
|
16
|
+
import { dirname, resolve } from "path";
|
|
17
|
+
import { fileURLToPath } from "url";
|
|
18
|
+
import { eq, isNull } from "drizzle-orm";
|
|
19
|
+
|
|
20
|
+
// src/lib/schema.ts
|
|
21
|
+
var schema_exports = {};
|
|
22
|
+
__export(schema_exports, {
|
|
23
|
+
activity: () => activity,
|
|
24
|
+
conversationParticipants: () => conversationParticipants,
|
|
25
|
+
conversationReads: () => conversationReads,
|
|
26
|
+
conversations: () => conversations,
|
|
27
|
+
deliveryQueue: () => deliveryQueue,
|
|
28
|
+
messages: () => messages,
|
|
29
|
+
mindHistory: () => mindHistory,
|
|
30
|
+
minds: () => minds,
|
|
31
|
+
noteComments: () => noteComments,
|
|
32
|
+
noteReactions: () => noteReactions,
|
|
33
|
+
notes: () => notes,
|
|
34
|
+
sessions: () => sessions,
|
|
35
|
+
sharedSkills: () => sharedSkills,
|
|
36
|
+
systemPrompts: () => systemPrompts,
|
|
37
|
+
users: () => users
|
|
38
|
+
});
|
|
39
|
+
import { sql } from "drizzle-orm";
|
|
40
|
+
import { index, integer, sqliteTable, text, uniqueIndex } from "drizzle-orm/sqlite-core";
|
|
41
|
+
var minds = sqliteTable(
|
|
42
|
+
"minds",
|
|
43
|
+
{
|
|
44
|
+
name: text("name").primaryKey(),
|
|
45
|
+
port: integer("port").notNull().unique(),
|
|
46
|
+
parent: text("parent").references(() => minds.name, { onDelete: "cascade" }),
|
|
47
|
+
dir: text("dir"),
|
|
48
|
+
branch: text("branch"),
|
|
49
|
+
stage: text("stage"),
|
|
50
|
+
template: text("template"),
|
|
51
|
+
template_hash: text("template_hash"),
|
|
52
|
+
running: integer("running").notNull().default(0),
|
|
53
|
+
created_at: text("created_at").notNull().default(sql`(datetime('now'))`)
|
|
54
|
+
},
|
|
55
|
+
(table) => [index("idx_minds_parent").on(table.parent)]
|
|
56
|
+
);
|
|
57
|
+
var users = sqliteTable("users", {
|
|
58
|
+
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
59
|
+
username: text("username").unique().notNull(),
|
|
60
|
+
password_hash: text("password_hash").notNull(),
|
|
61
|
+
role: text("role").notNull().default("pending"),
|
|
62
|
+
user_type: text("user_type").notNull().default("brain"),
|
|
63
|
+
display_name: text("display_name"),
|
|
64
|
+
description: text("description"),
|
|
65
|
+
avatar: text("avatar"),
|
|
66
|
+
created_at: text("created_at").notNull().default(sql`(datetime('now'))`)
|
|
67
|
+
});
|
|
68
|
+
var conversations = sqliteTable(
|
|
69
|
+
"conversations",
|
|
70
|
+
{
|
|
71
|
+
id: text("id").primaryKey(),
|
|
72
|
+
mind_name: text("mind_name"),
|
|
73
|
+
channel: text("channel").notNull(),
|
|
74
|
+
type: text("type").notNull().default("dm"),
|
|
75
|
+
name: text("name"),
|
|
76
|
+
user_id: integer("user_id").references(() => users.id),
|
|
77
|
+
title: text("title"),
|
|
78
|
+
created_at: text("created_at").notNull().default(sql`(datetime('now'))`),
|
|
79
|
+
updated_at: text("updated_at").notNull().default(sql`(datetime('now'))`)
|
|
80
|
+
},
|
|
81
|
+
(table) => [
|
|
82
|
+
index("idx_conversations_mind_name").on(table.mind_name),
|
|
83
|
+
index("idx_conversations_user_id").on(table.user_id),
|
|
84
|
+
index("idx_conversations_updated_at").on(table.updated_at),
|
|
85
|
+
uniqueIndex("idx_conversations_name").on(table.name)
|
|
86
|
+
]
|
|
87
|
+
);
|
|
88
|
+
var mindHistory = sqliteTable(
|
|
89
|
+
"mind_history",
|
|
90
|
+
{
|
|
91
|
+
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
92
|
+
mind: text("mind").notNull(),
|
|
93
|
+
channel: text("channel"),
|
|
94
|
+
session: text("session"),
|
|
95
|
+
sender: text("sender"),
|
|
96
|
+
message_id: text("message_id"),
|
|
97
|
+
type: text("type").notNull(),
|
|
98
|
+
content: text("content"),
|
|
99
|
+
metadata: text("metadata"),
|
|
100
|
+
created_at: text("created_at").notNull().default(sql`(datetime('now'))`)
|
|
101
|
+
},
|
|
102
|
+
(table) => [
|
|
103
|
+
index("idx_mind_history_mind").on(table.mind),
|
|
104
|
+
index("idx_mind_history_mind_channel").on(table.mind, table.channel),
|
|
105
|
+
index("idx_mind_history_mind_type").on(table.mind, table.type)
|
|
106
|
+
]
|
|
107
|
+
);
|
|
108
|
+
var conversationParticipants = sqliteTable(
|
|
109
|
+
"conversation_participants",
|
|
110
|
+
{
|
|
111
|
+
conversation_id: text("conversation_id").notNull().references(() => conversations.id, { onDelete: "cascade" }),
|
|
112
|
+
user_id: integer("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
|
113
|
+
role: text("role").notNull().default("member"),
|
|
114
|
+
joined_at: text("joined_at").notNull().default(sql`(datetime('now'))`)
|
|
115
|
+
},
|
|
116
|
+
(table) => [
|
|
117
|
+
uniqueIndex("idx_cp_unique").on(table.conversation_id, table.user_id),
|
|
118
|
+
index("idx_cp_user_id").on(table.user_id)
|
|
119
|
+
]
|
|
120
|
+
);
|
|
121
|
+
var sessions = sqliteTable("sessions", {
|
|
122
|
+
id: text("id").primaryKey(),
|
|
123
|
+
userId: integer("user_id").references(() => users.id, { onDelete: "cascade" }).notNull(),
|
|
124
|
+
createdAt: integer("created_at").notNull()
|
|
125
|
+
});
|
|
126
|
+
var systemPrompts = sqliteTable("system_prompts", {
|
|
127
|
+
key: text("key").primaryKey(),
|
|
128
|
+
content: text("content").notNull(),
|
|
129
|
+
updated_at: text("updated_at").notNull().default(sql`(datetime('now'))`)
|
|
130
|
+
});
|
|
131
|
+
var sharedSkills = sqliteTable("shared_skills", {
|
|
132
|
+
id: text("id").primaryKey(),
|
|
133
|
+
name: text("name").notNull(),
|
|
134
|
+
description: text("description").notNull().default(""),
|
|
135
|
+
author: text("author").notNull(),
|
|
136
|
+
version: integer("version").notNull().default(1),
|
|
137
|
+
created_at: text("created_at").notNull().default(sql`(datetime('now'))`),
|
|
138
|
+
updated_at: text("updated_at").notNull().default(sql`(datetime('now'))`)
|
|
139
|
+
});
|
|
140
|
+
var deliveryQueue = sqliteTable(
|
|
141
|
+
"delivery_queue",
|
|
142
|
+
{
|
|
143
|
+
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
144
|
+
mind: text("mind").notNull(),
|
|
145
|
+
session: text("session").notNull(),
|
|
146
|
+
channel: text("channel"),
|
|
147
|
+
sender: text("sender"),
|
|
148
|
+
status: text("status").notNull().default("pending"),
|
|
149
|
+
payload: text("payload").notNull(),
|
|
150
|
+
created_at: text("created_at").notNull().default(sql`(datetime('now'))`)
|
|
151
|
+
},
|
|
152
|
+
(table) => [
|
|
153
|
+
index("idx_delivery_queue_mind_session").on(table.mind, table.session),
|
|
154
|
+
index("idx_delivery_queue_mind_status").on(table.mind, table.status)
|
|
155
|
+
]
|
|
156
|
+
);
|
|
157
|
+
var activity = sqliteTable(
|
|
158
|
+
"activity",
|
|
159
|
+
{
|
|
160
|
+
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
161
|
+
type: text("type").notNull(),
|
|
162
|
+
mind: text("mind").notNull(),
|
|
163
|
+
summary: text("summary").notNull(),
|
|
164
|
+
metadata: text("metadata"),
|
|
165
|
+
created_at: text("created_at").notNull().default(sql`(datetime('now'))`)
|
|
166
|
+
},
|
|
167
|
+
(table) => [
|
|
168
|
+
index("idx_activity_created_at").on(table.created_at),
|
|
169
|
+
index("idx_activity_mind").on(table.mind)
|
|
170
|
+
]
|
|
171
|
+
);
|
|
172
|
+
var conversationReads = sqliteTable(
|
|
173
|
+
"conversation_reads",
|
|
174
|
+
{
|
|
175
|
+
user_id: integer("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
|
176
|
+
conversation_id: text("conversation_id").notNull().references(() => conversations.id, { onDelete: "cascade" }),
|
|
177
|
+
last_read_message_id: integer("last_read_message_id").notNull().default(0)
|
|
178
|
+
},
|
|
179
|
+
(table) => [
|
|
180
|
+
uniqueIndex("idx_conversation_reads_unique").on(table.user_id, table.conversation_id)
|
|
181
|
+
]
|
|
182
|
+
);
|
|
183
|
+
var notes = sqliteTable(
|
|
184
|
+
"notes",
|
|
185
|
+
{
|
|
186
|
+
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
187
|
+
author_id: integer("author_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
|
188
|
+
title: text("title").notNull(),
|
|
189
|
+
slug: text("slug").notNull(),
|
|
190
|
+
content: text("content").notNull(),
|
|
191
|
+
reply_to_id: integer("reply_to_id"),
|
|
192
|
+
created_at: text("created_at").notNull().default(sql`(datetime('now'))`),
|
|
193
|
+
updated_at: text("updated_at").notNull().default(sql`(datetime('now'))`)
|
|
194
|
+
},
|
|
195
|
+
(table) => [
|
|
196
|
+
uniqueIndex("idx_notes_author_slug").on(table.author_id, table.slug),
|
|
197
|
+
index("idx_notes_created_at").on(table.created_at),
|
|
198
|
+
index("idx_notes_reply_to").on(table.reply_to_id)
|
|
199
|
+
]
|
|
200
|
+
);
|
|
201
|
+
var noteComments = sqliteTable(
|
|
202
|
+
"note_comments",
|
|
203
|
+
{
|
|
204
|
+
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
205
|
+
note_id: integer("note_id").notNull().references(() => notes.id, { onDelete: "cascade" }),
|
|
206
|
+
author_id: integer("author_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
|
207
|
+
content: text("content").notNull(),
|
|
208
|
+
created_at: text("created_at").notNull().default(sql`(datetime('now'))`)
|
|
209
|
+
},
|
|
210
|
+
(table) => [index("idx_note_comments_note_id").on(table.note_id)]
|
|
211
|
+
);
|
|
212
|
+
var noteReactions = sqliteTable(
|
|
213
|
+
"note_reactions",
|
|
214
|
+
{
|
|
215
|
+
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
216
|
+
note_id: integer("note_id").notNull().references(() => notes.id, { onDelete: "cascade" }),
|
|
217
|
+
user_id: integer("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
|
218
|
+
emoji: text("emoji").notNull(),
|
|
219
|
+
created_at: text("created_at").notNull().default(sql`(datetime('now'))`)
|
|
220
|
+
},
|
|
221
|
+
(table) => [
|
|
222
|
+
uniqueIndex("idx_note_reactions_unique").on(table.note_id, table.user_id, table.emoji),
|
|
223
|
+
index("idx_note_reactions_note_id").on(table.note_id)
|
|
224
|
+
]
|
|
225
|
+
);
|
|
226
|
+
var messages = sqliteTable(
|
|
227
|
+
"messages",
|
|
228
|
+
{
|
|
229
|
+
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
230
|
+
conversation_id: text("conversation_id").notNull().references(() => conversations.id, { onDelete: "cascade" }),
|
|
231
|
+
role: text("role").notNull(),
|
|
232
|
+
sender_name: text("sender_name"),
|
|
233
|
+
content: text("content").notNull(),
|
|
234
|
+
created_at: text("created_at").notNull().default(sql`(datetime('now'))`)
|
|
235
|
+
},
|
|
236
|
+
(table) => [index("idx_messages_conversation_id").on(table.conversation_id)]
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
// src/lib/registry.ts
|
|
240
|
+
function voluteHome() {
|
|
241
|
+
if (process.env.VOLUTE_HOME) return process.env.VOLUTE_HOME;
|
|
242
|
+
const dir = dirname(fileURLToPath(import.meta.url));
|
|
243
|
+
if (dir.endsWith("/src/lib")) {
|
|
244
|
+
throw new Error(
|
|
245
|
+
'VOLUTE_HOME must be set when running from source. For tests, run via "npm test" or add "--import ./test/setup.ts".'
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
return resolve(homedir(), ".volute");
|
|
249
|
+
}
|
|
250
|
+
function voluteUserHome() {
|
|
251
|
+
if (process.env.VOLUTE_USER_HOME) return process.env.VOLUTE_USER_HOME;
|
|
252
|
+
return resolve(homedir(), ".volute");
|
|
253
|
+
}
|
|
254
|
+
function voluteSystemDir() {
|
|
255
|
+
return resolve(voluteHome(), "system");
|
|
256
|
+
}
|
|
257
|
+
function ensureSystemDir() {
|
|
258
|
+
mkdirSync(voluteSystemDir(), { recursive: true });
|
|
259
|
+
}
|
|
260
|
+
function ensureVoluteHome() {
|
|
261
|
+
const mindsBase = process.env.VOLUTE_MINDS_DIR ?? resolve(voluteHome(), "minds");
|
|
262
|
+
mkdirSync(mindsBase, { recursive: true });
|
|
263
|
+
ensureSystemDir();
|
|
264
|
+
}
|
|
265
|
+
function rowToEntry(row) {
|
|
266
|
+
return {
|
|
267
|
+
name: row.name,
|
|
268
|
+
port: row.port,
|
|
269
|
+
created: row.created_at,
|
|
270
|
+
running: row.running === 1,
|
|
271
|
+
stage: row.stage ?? (row.parent ? void 0 : "sprouted"),
|
|
272
|
+
template: row.template ?? void 0,
|
|
273
|
+
templateHash: row.template_hash ?? void 0,
|
|
274
|
+
parent: row.parent ?? void 0,
|
|
275
|
+
dir: row.dir ?? void 0,
|
|
276
|
+
branch: row.branch ?? void 0
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
async function readRegistry() {
|
|
280
|
+
const db2 = await getDb();
|
|
281
|
+
const rows = await db2.select().from(minds).where(isNull(minds.parent));
|
|
282
|
+
return rows.map(rowToEntry);
|
|
283
|
+
}
|
|
284
|
+
async function readAllMinds() {
|
|
285
|
+
const db2 = await getDb();
|
|
286
|
+
const rows = await db2.select().from(minds);
|
|
287
|
+
return rows.map(rowToEntry);
|
|
288
|
+
}
|
|
289
|
+
var MIND_NAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
|
|
290
|
+
var MIND_NAME_MAX = 64;
|
|
291
|
+
function validateMindName(name) {
|
|
292
|
+
if (!name) return "Mind name is required";
|
|
293
|
+
if (name.length > MIND_NAME_MAX) return `Mind name must be at most ${MIND_NAME_MAX} characters`;
|
|
294
|
+
if (!MIND_NAME_RE.test(name)) {
|
|
295
|
+
return "Mind name must start with alphanumeric and contain only alphanumeric, dots, dashes, or underscores";
|
|
296
|
+
}
|
|
297
|
+
return null;
|
|
298
|
+
}
|
|
299
|
+
async function addMind(name, port, stage, template) {
|
|
300
|
+
const err = validateMindName(name);
|
|
301
|
+
if (err) throw new Error(err);
|
|
302
|
+
const db2 = await getDb();
|
|
303
|
+
await db2.insert(minds).values({ name, port, stage: stage ?? null, template: template ?? null }).onConflictDoUpdate({
|
|
304
|
+
target: minds.name,
|
|
305
|
+
set: { port, stage: stage ?? null, template: template ?? null }
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
async function addVariant(name, parent, port, dir, branch) {
|
|
309
|
+
const err = validateMindName(name);
|
|
310
|
+
if (err) throw new Error(err);
|
|
311
|
+
const db2 = await getDb();
|
|
312
|
+
await db2.insert(minds).values({ name, port, parent, dir, branch }).onConflictDoUpdate({
|
|
313
|
+
target: minds.name,
|
|
314
|
+
set: { port, parent, dir, branch }
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
async function removeMind(name) {
|
|
318
|
+
const db2 = await getDb();
|
|
319
|
+
await db2.delete(minds).where(eq(minds.name, name));
|
|
320
|
+
}
|
|
321
|
+
async function setMindRunning(name, running) {
|
|
322
|
+
const db2 = await getDb();
|
|
323
|
+
await db2.update(minds).set({ running: running ? 1 : 0 }).where(eq(minds.name, name));
|
|
324
|
+
}
|
|
325
|
+
async function setMindStage(name, stage) {
|
|
326
|
+
const db2 = await getDb();
|
|
327
|
+
await db2.update(minds).set({ stage }).where(eq(minds.name, name));
|
|
328
|
+
}
|
|
329
|
+
async function setMindTemplateHash(name, hash) {
|
|
330
|
+
const db2 = await getDb();
|
|
331
|
+
await db2.update(minds).set({ template_hash: hash }).where(eq(minds.name, name));
|
|
332
|
+
}
|
|
333
|
+
async function findMind(name) {
|
|
334
|
+
const db2 = await getDb();
|
|
335
|
+
const rows = await db2.select().from(minds).where(eq(minds.name, name));
|
|
336
|
+
if (rows.length === 0) return void 0;
|
|
337
|
+
return rowToEntry(rows[0]);
|
|
338
|
+
}
|
|
339
|
+
async function findVariants(parent) {
|
|
340
|
+
const db2 = await getDb();
|
|
341
|
+
const rows = await db2.select().from(minds).where(eq(minds.parent, parent));
|
|
342
|
+
return rows.map(rowToEntry);
|
|
343
|
+
}
|
|
344
|
+
async function getBaseName(name) {
|
|
345
|
+
const entry = await findMind(name);
|
|
346
|
+
return entry?.parent ?? name;
|
|
347
|
+
}
|
|
348
|
+
function mindDir(name) {
|
|
349
|
+
if (process.env.VOLUTE_MINDS_DIR) {
|
|
350
|
+
return resolve(process.env.VOLUTE_MINDS_DIR, name);
|
|
351
|
+
}
|
|
352
|
+
return resolve(voluteHome(), "minds", name);
|
|
353
|
+
}
|
|
354
|
+
function stateDir(name) {
|
|
355
|
+
return resolve(voluteSystemDir(), "state", name);
|
|
356
|
+
}
|
|
357
|
+
async function nextPort() {
|
|
358
|
+
const db2 = await getDb();
|
|
359
|
+
const rows = await db2.select({ port: minds.port }).from(minds);
|
|
360
|
+
const usedPorts = new Set(rows.map((r) => r.port));
|
|
361
|
+
const basePort = parseInt(process.env.VOLUTE_BASE_PORT || "4100", 10);
|
|
362
|
+
let port = basePort;
|
|
363
|
+
while (usedPorts.has(port)) port++;
|
|
364
|
+
if (port > 65535) throw new Error("No available ports \u2014 all ports 4100-65535 are allocated");
|
|
365
|
+
return port;
|
|
366
|
+
}
|
|
367
|
+
function daemonLoopback() {
|
|
368
|
+
const host = process.env.VOLUTE_DAEMON_HOSTNAME || "127.0.0.1";
|
|
369
|
+
if (host === "0.0.0.0") return "127.0.0.1";
|
|
370
|
+
if (host === "::") return "[::1]";
|
|
371
|
+
return host;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// src/lib/db.ts
|
|
375
|
+
var __dirname = dirname2(fileURLToPath2(import.meta.url));
|
|
376
|
+
var migrationsFolder = existsSync(resolve2(__dirname, "../drizzle")) ? resolve2(__dirname, "../drizzle") : resolve2(__dirname, "../../drizzle");
|
|
377
|
+
var db = null;
|
|
378
|
+
async function getDb() {
|
|
379
|
+
if (db) return db;
|
|
380
|
+
const dbPath = process.env.VOLUTE_DB_PATH || resolve2(voluteSystemDir(), "volute.db");
|
|
381
|
+
db = drizzle({ connection: { url: `file:${dbPath}` }, schema: schema_exports });
|
|
382
|
+
await migrate(db, { migrationsFolder });
|
|
383
|
+
try {
|
|
384
|
+
chmodSync(dbPath, 384);
|
|
385
|
+
} catch (err) {
|
|
386
|
+
console.error(
|
|
387
|
+
`[volute] WARNING: Failed to restrict database file permissions on ${dbPath}:`,
|
|
388
|
+
err
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
return db;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
export {
|
|
395
|
+
minds,
|
|
396
|
+
users,
|
|
397
|
+
conversations,
|
|
398
|
+
mindHistory,
|
|
399
|
+
conversationParticipants,
|
|
400
|
+
sessions,
|
|
401
|
+
systemPrompts,
|
|
402
|
+
sharedSkills,
|
|
403
|
+
deliveryQueue,
|
|
404
|
+
activity,
|
|
405
|
+
conversationReads,
|
|
406
|
+
notes,
|
|
407
|
+
noteComments,
|
|
408
|
+
noteReactions,
|
|
409
|
+
messages,
|
|
410
|
+
getDb,
|
|
411
|
+
voluteHome,
|
|
412
|
+
voluteUserHome,
|
|
413
|
+
voluteSystemDir,
|
|
414
|
+
ensureSystemDir,
|
|
415
|
+
ensureVoluteHome,
|
|
416
|
+
readRegistry,
|
|
417
|
+
readAllMinds,
|
|
418
|
+
validateMindName,
|
|
419
|
+
addMind,
|
|
420
|
+
addVariant,
|
|
421
|
+
removeMind,
|
|
422
|
+
setMindRunning,
|
|
423
|
+
setMindStage,
|
|
424
|
+
setMindTemplateHash,
|
|
425
|
+
findMind,
|
|
426
|
+
findVariants,
|
|
427
|
+
getBaseName,
|
|
428
|
+
mindDir,
|
|
429
|
+
stateDir,
|
|
430
|
+
nextPort,
|
|
431
|
+
daemonLoopback
|
|
432
|
+
};
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
} from "./chunk-
|
|
3
|
+
voluteSystemDir
|
|
4
|
+
} from "./chunk-H7OZRFJB.js";
|
|
5
5
|
|
|
6
6
|
// src/lib/update-check.ts
|
|
7
7
|
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
8
8
|
import { resolve } from "path";
|
|
9
9
|
var CACHE_TTL = 60 * 60 * 1e3;
|
|
10
10
|
function cachePath() {
|
|
11
|
-
return resolve(
|
|
11
|
+
return resolve(voluteSystemDir(), "update-check.json");
|
|
12
12
|
}
|
|
13
13
|
function readCache() {
|
|
14
14
|
try {
|