volute 0.24.0 → 0.26.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 +15 -20
- package/dist/{activity-events-4O37J7PD.js → activity-events-ZMBAKLUF.js} +2 -2
- package/dist/api.d.ts +590 -10
- package/dist/{auth-HM2RSPY7.js → auth-4TV573WE.js} +2 -2
- package/dist/{channel-HZOSHGNF.js → channel-ZVZV42UD.js} +3 -3
- package/dist/{chunk-NOBRGACV.js → chunk-2VO7453N.js} +56 -19
- package/dist/{chunk-OOW675I3.js → chunk-3CFRE2VC.js} +931 -775
- package/dist/{chunk-PHHKNGA3.js → chunk-3TV4GLFO.js} +2 -2
- package/dist/{chunk-4TJ72QQ3.js → chunk-5Y3PBKW6.js} +3 -3
- package/dist/{chunk-BFK6SOEJ.js → chunk-J2CO4WEV.js} +1 -1
- package/dist/{chunk-TQDITGES.js → chunk-LX22GRG7.js} +10 -13
- package/dist/{chunk-E7GOKNOT.js → chunk-NWI2425I.js} +1 -1
- package/dist/{chunk-2767L2RZ.js → chunk-OZFKBXD6.js} +1 -1
- package/dist/{chunk-XLC342FO.js → chunk-SIAG3QMM.js} +14 -1
- package/dist/{chunk-RVKR2R7F.js → chunk-SSI47XP2.js} +10 -2
- package/dist/chunk-TZKJLDQN.js +78 -0
- package/dist/{chunk-P3W36ZGD.js → chunk-USNBKHYG.js} +33 -5
- package/dist/chunk-UTL75LP6.js +113 -0
- package/dist/{chunk-3AIBT4TW.js → chunk-V63B7DX3.js} +24 -1
- package/dist/{chunk-33XAVCS4.js → chunk-WBHMQ5OZ.js} +49 -0
- package/dist/{chunk-TRQEV3CD.js → chunk-WGOGUMPO.js} +22 -3
- package/dist/chunk-XOXLRRR2.js +176 -0
- package/dist/{chunk-JTDFJWI2.js → chunk-YJA7P64S.js} +1 -1
- package/dist/chunk-ZYGKG6VC.js +22 -0
- package/dist/cli.js +44 -20
- package/dist/{cloud-sync-DIU3OCPV.js → cloud-sync-NI2K3C7G.js} +11 -9
- package/dist/{connector-M6XFI6GM.js → connector-G722WXAU.js} +4 -4
- package/dist/{create-VDQJER52.js → create-4YBRTTJS.js} +1 -1
- package/dist/{daemon-client-JOVQZ52X.js → daemon-client-Z7FAJ6JW.js} +1 -1
- package/dist/{daemon-restart-YMPEATQH.js → daemon-restart-BJZ3O4U4.js} +6 -5
- package/dist/daemon.js +982 -340
- package/dist/{delete-2MRR4JX5.js → delete-27OYNK25.js} +1 -1
- package/dist/{down-674SX2IZ.js → down-7UKFMJJZ.js} +4 -4
- package/dist/{env-2FPOZK37.js → env-M336ONDP.js} +4 -4
- package/dist/{export-IKFAPRAO.js → export-HP4G5DQC.js} +1 -1
- package/dist/{file-KT3UIQM3.js → file-HUDKTRAS.js} +3 -3
- package/dist/{history-46WZN5CN.js → history-B64GTFTD.js} +3 -3
- package/dist/{import-FRDPQPJ2.js → import-XIB7UV4S.js} +2 -2
- package/dist/{log-6SGSSR3D.js → log-PBFNILJ4.js} +3 -3
- package/dist/{login-UO6AOVEA.js → login-6U7U6BNG.js} +1 -1
- package/dist/login-B5E7N7MY.js +46 -0
- package/dist/logout-XSJRYS3U.js +39 -0
- package/dist/{logs-HRBONI5I.js → logs-3CART7O7.js} +3 -3
- package/dist/{merge-KSFJKX6T.js → merge-VK2HSKMA.js} +3 -3
- package/dist/{message-delivery-S7BCNV6Y.js → message-delivery-MS5JYPZX.js} +11 -9
- package/dist/{mind-KPLCRKQA.js → mind-HZ3QSDDJ.js} +17 -17
- package/dist/{mind-activity-tracker-NMDDEV3K.js → mind-activity-tracker-4G6FURY2.js} +3 -3
- package/dist/{mind-manager-ZNRIYEK3.js → mind-manager-VVK67AY3.js} +6 -4
- package/dist/{mind-sleep-GHPTSAYN.js → mind-sleep-DTV7L44D.js} +3 -3
- package/dist/{mind-wake-BJDJFMDF.js → mind-wake-PFN4FN3T.js} +3 -3
- package/dist/notes-37FW2UR2.js +230 -0
- package/dist/{package-S5YF25XV.js → package-VZWLXPHV.js} +3 -1
- package/dist/{pages-TWR6U7DS.js → pages-DIIT5HMQ.js} +1 -1
- package/dist/{publish-BZNHKUUK.js → publish-HQV7YREB.js} +4 -4
- package/dist/{pull-D32SPFVU.js → pull-2MB4SK3C.js} +3 -3
- package/dist/{register-U2UO6TC4.js → register-EFND67FQ.js} +1 -1
- package/dist/{restart-5BMNV7KU.js → restart-CCK7D6TV.js} +3 -3
- package/dist/sandbox-EHGFF52K.js +19 -0
- package/dist/{schedule-YEFDLVMJ.js → schedule-6F7ELB2M.js} +3 -3
- package/dist/{seed-6FEKB3YC.js → seed-E5OQGWX3.js} +1 -1
- package/dist/{send-IISDYFCL.js → send-IH6XZKPC.js} +6 -20
- package/dist/service-LLBV3R7M.js +122 -0
- package/dist/setup-F6TWFYGQ.js +371 -0
- package/dist/setup-YGAAIKKZ.js +17 -0
- package/dist/{shared-LWMNTTZN.js → shared-UMO4S7CC.js} +4 -4
- package/dist/{skill-BQOFACEI.js → skill-42LGFBQC.js} +13 -5
- 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/imagegen/SKILL.md +37 -0
- package/dist/skills/imagegen/references/INSTALL.md +13 -0
- package/dist/skills/imagegen/scripts/imagegen.ts +136 -0
- package/dist/skills/notes/SKILL.md +34 -0
- package/dist/skills/resonance/SKILL.md +73 -0
- package/dist/skills/resonance/assets/default-config.json +21 -0
- package/dist/skills/resonance/references/INSTALL.md +23 -0
- package/dist/skills/resonance/scripts/resonance.ts +1250 -0
- package/dist/skills/volute-mind/SKILL.md +23 -3
- package/dist/{sleep-manager-XXSWQQLE.js → sleep-manager-EE4NRN2Q.js} +11 -9
- package/dist/{sprout-CGSW4CF5.js → sprout-QL74KR2X.js} +5 -5
- package/dist/{start-C7XITZ5O.js → start-O5JQASRC.js} +3 -3
- package/dist/{status-SIRPLEZC.js → status-FZBEBM7Q.js} +3 -3
- package/dist/{status-LYS4NUOZ.js → status-WXD4HXRL.js} +3 -3
- package/dist/{stop-CVKBSLXY.js → stop-2SOG5NYF.js} +3 -3
- package/dist/up-SDMCSVI3.js +17 -0
- package/dist/{update-7XCZMYBT.js → update-5VUDAI3D.js} +6 -6
- package/dist/{upgrade-7RUIXGOO.js → upgrade-QCCO33BK.js} +1 -1
- package/dist/{variant-UGREB4G5.js → variant-WWLDY6D5.js} +4 -4
- package/dist/{version-notify-SZ75QRGO.js → version-notify-USFZBWMG.js} +11 -9
- package/dist/web-assets/assets/index-CUQ31ieL.js +69 -0
- package/dist/web-assets/assets/index-CW8NSl1o.css +1 -0
- package/dist/web-assets/favicon.png +0 -0
- package/dist/web-assets/index.html +5 -4
- package/dist/web-assets/logo.png +0 -0
- package/drizzle/0015_notes.sql +23 -0
- package/drizzle/0016_note_reactions_and_replies.sql +15 -0
- package/drizzle/meta/_journal.json +14 -0
- package/package.json +3 -1
- package/templates/_base/.init/.config/hooks/wake-context.sh +7 -0
- package/templates/_base/home/public/.gitkeep +0 -0
- package/templates/_base/src/lib/startup.ts +8 -0
- 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/chunk-NWPT4ASZ.js +0 -89
- package/dist/service-FASYWLTC.js +0 -247
- package/dist/setup-BMLM2UTK.js +0 -230
- package/dist/up-OMHACRJL.js +0 -15
- package/dist/web-assets/assets/index-Bx9WDoaQ.js +0 -69
- package/dist/web-assets/assets/index-Clz8OhmJ.css +0 -1
|
@@ -4,11 +4,11 @@ import {
|
|
|
4
4
|
} from "./chunk-YUIHSKR6.js";
|
|
5
5
|
import {
|
|
6
6
|
gitExec
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-YJA7P64S.js";
|
|
8
8
|
import {
|
|
9
9
|
isIsolationEnabled,
|
|
10
10
|
mindUserName
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-XOXLRRR2.js";
|
|
12
12
|
import {
|
|
13
13
|
voluteHome
|
|
14
14
|
} from "./chunk-B2CPS4QU.js";
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
readVoluteConfig,
|
|
4
4
|
writeVoluteConfig
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-SIAG3QMM.js";
|
|
6
6
|
import {
|
|
7
7
|
mindEnvPath,
|
|
8
8
|
readEnv,
|
|
@@ -39,7 +39,7 @@ async function run(args) {
|
|
|
39
39
|
return;
|
|
40
40
|
}
|
|
41
41
|
const wsDir = resolveWorkspace(inputPath);
|
|
42
|
-
const { daemonFetch } = await import("./daemon-client-
|
|
42
|
+
const { daemonFetch } = await import("./daemon-client-Z7FAJ6JW.js");
|
|
43
43
|
const { getClient, urlOf } = await import("./api-client-YPKOZP2O.js");
|
|
44
44
|
const client = getClient();
|
|
45
45
|
const res = await daemonFetch(urlOf(client.api.minds.import.$url()), {
|
|
@@ -91,7 +91,7 @@ async function importArchive(archivePath, nameOverride) {
|
|
|
91
91
|
process.exit(1);
|
|
92
92
|
}
|
|
93
93
|
try {
|
|
94
|
-
const { daemonFetch } = await import("./daemon-client-
|
|
94
|
+
const { daemonFetch } = await import("./daemon-client-Z7FAJ6JW.js");
|
|
95
95
|
const { getClient, urlOf } = await import("./api-client-YPKOZP2O.js");
|
|
96
96
|
const client = getClient();
|
|
97
97
|
const res = await daemonFetch(urlOf(client.api.minds.import.$url()), {
|
|
@@ -4,10 +4,13 @@ import {
|
|
|
4
4
|
modeLabel,
|
|
5
5
|
pollHealth,
|
|
6
6
|
startService
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-V63B7DX3.js";
|
|
8
8
|
import {
|
|
9
9
|
parseArgs
|
|
10
10
|
} from "./chunk-D424ZQGI.js";
|
|
11
|
+
import {
|
|
12
|
+
readGlobalConfig
|
|
13
|
+
} from "./chunk-TZKJLDQN.js";
|
|
11
14
|
import {
|
|
12
15
|
voluteHome
|
|
13
16
|
} from "./chunk-B2CPS4QU.js";
|
|
@@ -16,22 +19,13 @@ import {
|
|
|
16
19
|
import { spawn } from "child_process";
|
|
17
20
|
import { existsSync, mkdirSync, openSync, readFileSync } from "fs";
|
|
18
21
|
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
22
|
async function run(args) {
|
|
30
23
|
const { flags } = parseArgs(args, {
|
|
31
24
|
port: { type: "number" },
|
|
32
25
|
host: { type: "string" },
|
|
33
26
|
foreground: { type: "boolean" },
|
|
34
|
-
tailscale: { type: "boolean" }
|
|
27
|
+
tailscale: { type: "boolean" },
|
|
28
|
+
"no-sandbox": { type: "boolean" }
|
|
35
29
|
});
|
|
36
30
|
const mode = getServiceMode();
|
|
37
31
|
if (!flags.foreground && mode !== "manual") {
|
|
@@ -95,6 +89,9 @@ async function run(args) {
|
|
|
95
89
|
}
|
|
96
90
|
} catch {
|
|
97
91
|
}
|
|
92
|
+
if (flags["no-sandbox"]) {
|
|
93
|
+
process.env.VOLUTE_SANDBOX = "0";
|
|
94
|
+
}
|
|
98
95
|
if (flags.foreground) {
|
|
99
96
|
const { startDaemon } = await import("./daemon.js");
|
|
100
97
|
await startDaemon({ port, hostname, foreground: true, tailscale: flags.tailscale });
|
|
@@ -110,6 +107,7 @@ async function run(args) {
|
|
|
110
107
|
const logFd = openSync(logFile, "a");
|
|
111
108
|
const daemonArgs = [daemonModule, "--port", String(port), "--host", hostname];
|
|
112
109
|
if (flags.tailscale) daemonArgs.push("--tailscale");
|
|
110
|
+
if (flags["no-sandbox"]) daemonArgs.push("--no-sandbox");
|
|
113
111
|
const child = spawn(process.execPath, daemonArgs, {
|
|
114
112
|
stdio: ["ignore", "ignore", logFd],
|
|
115
113
|
detached: true
|
|
@@ -151,6 +149,5 @@ async function run(args) {
|
|
|
151
149
|
}
|
|
152
150
|
|
|
153
151
|
export {
|
|
154
|
-
readGlobalConfig,
|
|
155
152
|
run
|
|
156
153
|
};
|
|
@@ -14,7 +14,20 @@ function readJson(path) {
|
|
|
14
14
|
}
|
|
15
15
|
function readVoluteConfig(mindDir) {
|
|
16
16
|
const path = resolve(mindDir, "home/.config/volute.json");
|
|
17
|
-
|
|
17
|
+
const config = readJson(path);
|
|
18
|
+
if (!config) return null;
|
|
19
|
+
const legacy = config;
|
|
20
|
+
if (!config.profile && ("displayName" in config || "description" in config || "avatar" in config)) {
|
|
21
|
+
config.profile = {
|
|
22
|
+
displayName: legacy.displayName,
|
|
23
|
+
description: legacy.description,
|
|
24
|
+
avatar: legacy.avatar
|
|
25
|
+
};
|
|
26
|
+
delete legacy.displayName;
|
|
27
|
+
delete legacy.description;
|
|
28
|
+
delete legacy.avatar;
|
|
29
|
+
}
|
|
30
|
+
return config;
|
|
18
31
|
}
|
|
19
32
|
function writeVoluteConfig(mindDir, config) {
|
|
20
33
|
const path = resolve(mindDir, "home/.config/volute.json");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/lib/prompt.ts
|
|
4
|
-
function
|
|
4
|
+
function rawPrompt(prompt, echo) {
|
|
5
5
|
process.stderr.write(prompt);
|
|
6
6
|
return new Promise((resolve) => {
|
|
7
7
|
let value = "";
|
|
@@ -23,6 +23,7 @@ function promptLine(prompt) {
|
|
|
23
23
|
value = value.slice(0, -1);
|
|
24
24
|
} else {
|
|
25
25
|
value += String.fromCharCode(byte);
|
|
26
|
+
if (echo) process.stderr.write(String.fromCharCode(byte));
|
|
26
27
|
}
|
|
27
28
|
}
|
|
28
29
|
};
|
|
@@ -31,7 +32,14 @@ function promptLine(prompt) {
|
|
|
31
32
|
process.stdin.on("data", onData);
|
|
32
33
|
});
|
|
33
34
|
}
|
|
35
|
+
function promptLine(prompt) {
|
|
36
|
+
return rawPrompt(prompt, true);
|
|
37
|
+
}
|
|
38
|
+
function promptPassword(prompt) {
|
|
39
|
+
return rawPrompt(prompt, false);
|
|
40
|
+
}
|
|
34
41
|
|
|
35
42
|
export {
|
|
36
|
-
promptLine
|
|
43
|
+
promptLine,
|
|
44
|
+
promptPassword
|
|
37
45
|
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
voluteHome
|
|
4
|
+
} from "./chunk-B2CPS4QU.js";
|
|
5
|
+
|
|
6
|
+
// src/lib/setup.ts
|
|
7
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
8
|
+
import { resolve } from "path";
|
|
9
|
+
function configPath() {
|
|
10
|
+
return resolve(voluteHome(), "config.json");
|
|
11
|
+
}
|
|
12
|
+
function readGlobalConfig() {
|
|
13
|
+
const path = configPath();
|
|
14
|
+
if (!existsSync(path)) return {};
|
|
15
|
+
try {
|
|
16
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
17
|
+
} catch (err) {
|
|
18
|
+
console.error(`Failed to parse ${path}: ${err instanceof Error ? err.message : err}`);
|
|
19
|
+
return {};
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function writeGlobalConfig(config) {
|
|
23
|
+
const path = configPath();
|
|
24
|
+
mkdirSync(voluteHome(), { recursive: true });
|
|
25
|
+
writeFileSync(path, JSON.stringify(config, null, 2) + "\n");
|
|
26
|
+
}
|
|
27
|
+
function isSetupComplete() {
|
|
28
|
+
const config = readGlobalConfig();
|
|
29
|
+
return config.setup != null;
|
|
30
|
+
}
|
|
31
|
+
function migrateSetupConfig() {
|
|
32
|
+
const config = readGlobalConfig();
|
|
33
|
+
if (config.setup) return;
|
|
34
|
+
const home = voluteHome();
|
|
35
|
+
const registryPath = resolve(home, "minds.json");
|
|
36
|
+
if (!existsSync(registryPath)) return;
|
|
37
|
+
const isSystem = process.env.VOLUTE_ISOLATION === "user";
|
|
38
|
+
const mindsDir = process.env.VOLUTE_MINDS_DIR || resolve(home, "minds");
|
|
39
|
+
let hasService = false;
|
|
40
|
+
try {
|
|
41
|
+
if (process.platform === "darwin") {
|
|
42
|
+
const plistPath = resolve(
|
|
43
|
+
process.env.HOME || "",
|
|
44
|
+
"Library",
|
|
45
|
+
"LaunchAgents",
|
|
46
|
+
"com.volute.daemon.plist"
|
|
47
|
+
);
|
|
48
|
+
if (existsSync(plistPath)) hasService = true;
|
|
49
|
+
}
|
|
50
|
+
if (process.platform === "linux") {
|
|
51
|
+
if (existsSync("/etc/systemd/system/volute.service")) hasService = true;
|
|
52
|
+
const userUnit = resolve(
|
|
53
|
+
process.env.HOME || "",
|
|
54
|
+
".config",
|
|
55
|
+
"systemd",
|
|
56
|
+
"user",
|
|
57
|
+
"volute.service"
|
|
58
|
+
);
|
|
59
|
+
if (existsSync(userUnit)) hasService = true;
|
|
60
|
+
}
|
|
61
|
+
} catch {
|
|
62
|
+
}
|
|
63
|
+
const setup = {
|
|
64
|
+
type: isSystem ? "system" : "local",
|
|
65
|
+
isolation: isSystem ? "user" : "none",
|
|
66
|
+
mindsDir,
|
|
67
|
+
service: hasService
|
|
68
|
+
};
|
|
69
|
+
writeGlobalConfig({ ...config, setup });
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export {
|
|
73
|
+
configPath,
|
|
74
|
+
readGlobalConfig,
|
|
75
|
+
writeGlobalConfig,
|
|
76
|
+
isSetupComplete,
|
|
77
|
+
migrateSetupConfig
|
|
78
|
+
};
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
import {
|
|
3
3
|
getDb,
|
|
4
4
|
sharedSkills
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-WBHMQ5OZ.js";
|
|
6
6
|
import {
|
|
7
7
|
logger_default
|
|
8
8
|
} from "./chunk-YUIHSKR6.js";
|
|
9
9
|
import {
|
|
10
10
|
exec,
|
|
11
11
|
gitExec
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-YJA7P64S.js";
|
|
13
13
|
import {
|
|
14
14
|
voluteHome
|
|
15
15
|
} from "./chunk-B2CPS4QU.js";
|
|
@@ -30,7 +30,7 @@ import { basename, dirname, join, resolve } from "path";
|
|
|
30
30
|
import { eq, sql } from "drizzle-orm";
|
|
31
31
|
var VALID_SKILL_ID = /^[a-zA-Z0-9_-]+$/;
|
|
32
32
|
var SEED_SKILLS = ["orientation", "memory"];
|
|
33
|
-
var STANDARD_SKILLS = ["volute-mind", "memory", "sessions"];
|
|
33
|
+
var STANDARD_SKILLS = ["volute-mind", "memory", "sessions", "notes", "dreaming"];
|
|
34
34
|
function validateSkillId(id) {
|
|
35
35
|
if (!id || !VALID_SKILL_ID.test(id)) {
|
|
36
36
|
throw new Error(`Invalid skill ID: ${id}`);
|
|
@@ -41,13 +41,15 @@ function sharedSkillsDir() {
|
|
|
41
41
|
}
|
|
42
42
|
function parseSkillMd(content) {
|
|
43
43
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
44
|
-
if (!match) return { name: "", description: "" };
|
|
44
|
+
if (!match) return { name: "", description: "", npmDependencies: [] };
|
|
45
45
|
const frontmatter = match[1];
|
|
46
46
|
const nameMatch = frontmatter.match(/^name:\s*(.+)$/m);
|
|
47
47
|
const descMatch = frontmatter.match(/^description:\s*(.+)$/m);
|
|
48
|
+
const depsMatch = frontmatter.match(/^\s*npm-dependencies:\s*(.+)$/m);
|
|
48
49
|
return {
|
|
49
50
|
name: nameMatch?.[1].trim() ?? "",
|
|
50
|
-
description: descMatch?.[1].trim() ?? ""
|
|
51
|
+
description: descMatch?.[1].trim() ?? "",
|
|
52
|
+
npmDependencies: depsMatch ? depsMatch[1].trim().split(/[\s,]+/).filter(Boolean) : []
|
|
51
53
|
};
|
|
52
54
|
}
|
|
53
55
|
async function listSharedSkills() {
|
|
@@ -127,7 +129,32 @@ async function installSkill(_mindName, dir, skillId) {
|
|
|
127
129
|
if (existsSync(destDir)) throw new Error(`Skill already installed: ${skillId}`);
|
|
128
130
|
mkdirSync(destDir, { recursive: true });
|
|
129
131
|
cpSync(sourceDir, destDir, { recursive: true });
|
|
132
|
+
const npmInstalled = [];
|
|
133
|
+
const skillMdPath = join(sourceDir, "SKILL.md");
|
|
134
|
+
if (existsSync(skillMdPath)) {
|
|
135
|
+
const { npmDependencies } = parseSkillMd(readFileSync(skillMdPath, "utf-8"));
|
|
136
|
+
if (npmDependencies.length > 0) {
|
|
137
|
+
try {
|
|
138
|
+
await exec("npm", ["install", ...npmDependencies], { cwd: dir });
|
|
139
|
+
npmInstalled.push(...npmDependencies);
|
|
140
|
+
} catch (e) {
|
|
141
|
+
rmSync(destDir, { recursive: true });
|
|
142
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
143
|
+
throw new Error(
|
|
144
|
+
`Failed to install npm dependencies (${npmDependencies.join(", ")}): ${msg}`
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
let installNotes = null;
|
|
150
|
+
const installMdPath = join(destDir, "references", "INSTALL.md");
|
|
151
|
+
if (existsSync(installMdPath)) {
|
|
152
|
+
installNotes = readFileSync(installMdPath, "utf-8");
|
|
153
|
+
}
|
|
130
154
|
await gitExec(["add", join("home", ".claude", "skills", skillId)], { cwd: dir });
|
|
155
|
+
if (npmInstalled.length > 0) {
|
|
156
|
+
await gitExec(["add", "package.json", "package-lock.json"], { cwd: dir });
|
|
157
|
+
}
|
|
131
158
|
await gitExec(["commit", "-m", `Install shared skill: ${skillId}`], { cwd: dir });
|
|
132
159
|
const commitHash = (await gitExec(["rev-parse", "HEAD"], { cwd: dir })).trim();
|
|
133
160
|
const upstream = {
|
|
@@ -141,6 +168,7 @@ async function installSkill(_mindName, dir, skillId) {
|
|
|
141
168
|
cwd: dir
|
|
142
169
|
});
|
|
143
170
|
await gitExec(["commit", "--amend", "--no-edit"], { cwd: dir });
|
|
171
|
+
return { installNotes, npmInstalled };
|
|
144
172
|
}
|
|
145
173
|
async function uninstallSkill(_mindName, dir, skillId) {
|
|
146
174
|
validateSkillId(skillId);
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
logger_default
|
|
4
|
+
} from "./chunk-YUIHSKR6.js";
|
|
5
|
+
import {
|
|
6
|
+
readGlobalConfig
|
|
7
|
+
} from "./chunk-TZKJLDQN.js";
|
|
8
|
+
import {
|
|
9
|
+
voluteHome
|
|
10
|
+
} from "./chunk-B2CPS4QU.js";
|
|
11
|
+
|
|
12
|
+
// src/lib/sandbox.ts
|
|
13
|
+
import { existsSync, readFileSync } from "fs";
|
|
14
|
+
import { resolve } from "path";
|
|
15
|
+
var slog = logger_default.child("sandbox");
|
|
16
|
+
var sandboxManager = null;
|
|
17
|
+
function isSandboxEnabled() {
|
|
18
|
+
if (process.env.VOLUTE_SANDBOX === "0") return false;
|
|
19
|
+
return readGlobalConfig().setup?.isolation === "sandbox";
|
|
20
|
+
}
|
|
21
|
+
async function initSandbox() {
|
|
22
|
+
if (!isSandboxEnabled()) return;
|
|
23
|
+
try {
|
|
24
|
+
const { SandboxManager } = await import("@anthropic-ai/sandbox-runtime");
|
|
25
|
+
const config = {
|
|
26
|
+
network: {
|
|
27
|
+
allowedDomains: ["*"],
|
|
28
|
+
deniedDomains: [],
|
|
29
|
+
allowLocalBinding: true
|
|
30
|
+
},
|
|
31
|
+
filesystem: {
|
|
32
|
+
denyRead: [],
|
|
33
|
+
allowWrite: [],
|
|
34
|
+
denyWrite: []
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
await SandboxManager.initialize(config);
|
|
38
|
+
sandboxManager = SandboxManager;
|
|
39
|
+
} catch (err) {
|
|
40
|
+
slog.error(
|
|
41
|
+
"sandbox runtime not available \u2014 minds will run without sandbox isolation",
|
|
42
|
+
logger_default.errorData(err)
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function buildDenyRead(mindName, mindDir) {
|
|
47
|
+
const home = voluteHome();
|
|
48
|
+
const userHome = process.env.HOME || "";
|
|
49
|
+
const mindsDir = process.env.VOLUTE_MINDS_DIR || resolve(home, "minds");
|
|
50
|
+
const deny = [];
|
|
51
|
+
deny.push(resolve(home, "state"));
|
|
52
|
+
deny.push(resolve(home, "volute.db"));
|
|
53
|
+
deny.push(resolve(home, "env.json"));
|
|
54
|
+
deny.push(resolve(home, "config.json"));
|
|
55
|
+
deny.push(resolve(home, "daemon.json"));
|
|
56
|
+
deny.push(resolve(home, "minds.json"));
|
|
57
|
+
deny.push(resolve(home, "systems.json"));
|
|
58
|
+
try {
|
|
59
|
+
const registryPath = resolve(home, "minds.json");
|
|
60
|
+
if (existsSync(registryPath)) {
|
|
61
|
+
const registry = JSON.parse(readFileSync(registryPath, "utf-8"));
|
|
62
|
+
for (const entry of registry) {
|
|
63
|
+
if (entry.name === mindName.split("@")[0]) continue;
|
|
64
|
+
const otherDir = resolve(mindsDir, entry.name);
|
|
65
|
+
if (otherDir !== mindDir) {
|
|
66
|
+
deny.push(otherDir);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
} catch (err) {
|
|
71
|
+
slog.warn("failed to read minds registry for deny-read list", logger_default.errorData(err));
|
|
72
|
+
}
|
|
73
|
+
if (userHome) {
|
|
74
|
+
deny.push(resolve(userHome, ".ssh"));
|
|
75
|
+
deny.push(resolve(userHome, ".aws"));
|
|
76
|
+
deny.push(resolve(userHome, ".gnupg"));
|
|
77
|
+
deny.push(resolve(userHome, ".config"));
|
|
78
|
+
}
|
|
79
|
+
return deny;
|
|
80
|
+
}
|
|
81
|
+
function shellEscape(s) {
|
|
82
|
+
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
83
|
+
}
|
|
84
|
+
async function wrapForSandbox(cmd, args, mindDir, mindName, allowWrite) {
|
|
85
|
+
if (!sandboxManager) return [cmd, args];
|
|
86
|
+
const denyRead = buildDenyRead(mindName, mindDir);
|
|
87
|
+
const customConfig = {
|
|
88
|
+
filesystem: {
|
|
89
|
+
denyRead,
|
|
90
|
+
allowWrite: allowWrite ?? [mindDir],
|
|
91
|
+
denyWrite: []
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
try {
|
|
95
|
+
const shellCmd = [cmd, ...args].map(shellEscape).join(" ");
|
|
96
|
+
const wrapped = await sandboxManager.wrapWithSandbox(shellCmd, void 0, customConfig);
|
|
97
|
+
return ["bash", ["-c", wrapped]];
|
|
98
|
+
} catch (err) {
|
|
99
|
+
slog.error(
|
|
100
|
+
`failed to sandbox mind ${mindName} \u2014 running without isolation`,
|
|
101
|
+
logger_default.errorData(err)
|
|
102
|
+
);
|
|
103
|
+
return [cmd, args];
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export {
|
|
108
|
+
isSandboxEnabled,
|
|
109
|
+
initSandbox,
|
|
110
|
+
buildDenyRead,
|
|
111
|
+
shellEscape,
|
|
112
|
+
wrapForSandbox
|
|
113
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
execInherit
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-YJA7P64S.js";
|
|
5
5
|
import {
|
|
6
6
|
voluteHome
|
|
7
7
|
} from "./chunk-B2CPS4QU.js";
|
|
@@ -20,6 +20,7 @@ var LAUNCHD_PLIST_PATH = resolve(
|
|
|
20
20
|
"LaunchAgents",
|
|
21
21
|
`${LAUNCHD_PLIST_LABEL}.plist`
|
|
22
22
|
);
|
|
23
|
+
var SYSTEM_LAUNCHD_PLIST_PATH = "/Library/LaunchDaemons/com.volute.daemon.plist";
|
|
23
24
|
var HEALTH_POLL_TIMEOUT = 3e4;
|
|
24
25
|
var STOP_GRACE_TIMEOUT = 1e4;
|
|
25
26
|
var POLL_INTERVAL = 500;
|
|
@@ -38,6 +39,9 @@ function getServiceMode() {
|
|
|
38
39
|
} catch {
|
|
39
40
|
}
|
|
40
41
|
}
|
|
42
|
+
if (process.platform === "darwin" && existsSync(SYSTEM_LAUNCHD_PLIST_PATH)) {
|
|
43
|
+
return "system-launchd";
|
|
44
|
+
}
|
|
41
45
|
if (process.platform === "darwin" && existsSync(LAUNCHD_PLIST_PATH)) {
|
|
42
46
|
return "user-launchd";
|
|
43
47
|
}
|
|
@@ -92,6 +96,9 @@ async function startService(mode) {
|
|
|
92
96
|
case "user-systemd":
|
|
93
97
|
await execInherit("systemctl", ["--user", "start", "volute"]);
|
|
94
98
|
break;
|
|
99
|
+
case "system-launchd":
|
|
100
|
+
await execInherit("sudo", ["launchctl", "load", SYSTEM_LAUNCHD_PLIST_PATH]);
|
|
101
|
+
break;
|
|
95
102
|
case "user-launchd":
|
|
96
103
|
await execInherit("launchctl", ["load", LAUNCHD_PLIST_PATH]);
|
|
97
104
|
break;
|
|
@@ -105,6 +112,9 @@ async function stopService(mode) {
|
|
|
105
112
|
case "user-systemd":
|
|
106
113
|
await execInherit("systemctl", ["--user", "stop", "volute"]);
|
|
107
114
|
break;
|
|
115
|
+
case "system-launchd":
|
|
116
|
+
await execInherit("sudo", ["launchctl", "unload", SYSTEM_LAUNCHD_PLIST_PATH]);
|
|
117
|
+
break;
|
|
108
118
|
case "user-launchd":
|
|
109
119
|
await execInherit("launchctl", ["unload", LAUNCHD_PLIST_PATH]);
|
|
110
120
|
break;
|
|
@@ -118,6 +128,16 @@ async function restartService(mode) {
|
|
|
118
128
|
case "user-systemd":
|
|
119
129
|
await execInherit("systemctl", ["--user", "restart", "volute"]);
|
|
120
130
|
break;
|
|
131
|
+
case "system-launchd":
|
|
132
|
+
try {
|
|
133
|
+
await execInherit("sudo", ["launchctl", "unload", SYSTEM_LAUNCHD_PLIST_PATH]);
|
|
134
|
+
} catch (err) {
|
|
135
|
+
console.warn(
|
|
136
|
+
`Warning: launchctl unload failed: ${err instanceof Error ? err.message : err}`
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
await execInherit("sudo", ["launchctl", "load", SYSTEM_LAUNCHD_PLIST_PATH]);
|
|
140
|
+
break;
|
|
121
141
|
case "user-launchd":
|
|
122
142
|
try {
|
|
123
143
|
await execInherit("launchctl", ["unload", LAUNCHD_PLIST_PATH]);
|
|
@@ -152,6 +172,8 @@ function modeLabel(mode) {
|
|
|
152
172
|
return "system service (systemd)";
|
|
153
173
|
case "user-systemd":
|
|
154
174
|
return "user service (systemd)";
|
|
175
|
+
case "system-launchd":
|
|
176
|
+
return "system service (launchd)";
|
|
155
177
|
case "user-launchd":
|
|
156
178
|
return "user service (launchd)";
|
|
157
179
|
case "manual":
|
|
@@ -164,6 +186,7 @@ export {
|
|
|
164
186
|
USER_SYSTEMD_UNIT,
|
|
165
187
|
LAUNCHD_PLIST_LABEL,
|
|
166
188
|
LAUNCHD_PLIST_PATH,
|
|
189
|
+
SYSTEM_LAUNCHD_PLIST_PATH,
|
|
167
190
|
getServiceMode,
|
|
168
191
|
getDaemonUrl,
|
|
169
192
|
pollHealth,
|
|
@@ -16,6 +16,9 @@ __export(schema_exports, {
|
|
|
16
16
|
deliveryQueue: () => deliveryQueue,
|
|
17
17
|
messages: () => messages,
|
|
18
18
|
mindHistory: () => mindHistory,
|
|
19
|
+
noteComments: () => noteComments,
|
|
20
|
+
noteReactions: () => noteReactions,
|
|
21
|
+
notes: () => notes,
|
|
19
22
|
sessions: () => sessions,
|
|
20
23
|
sharedSkills: () => sharedSkills,
|
|
21
24
|
systemPrompts: () => systemPrompts,
|
|
@@ -149,6 +152,49 @@ var conversationReads = sqliteTable(
|
|
|
149
152
|
uniqueIndex("idx_conversation_reads_unique").on(table.user_id, table.conversation_id)
|
|
150
153
|
]
|
|
151
154
|
);
|
|
155
|
+
var notes = sqliteTable(
|
|
156
|
+
"notes",
|
|
157
|
+
{
|
|
158
|
+
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
159
|
+
author_id: integer("author_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
|
160
|
+
title: text("title").notNull(),
|
|
161
|
+
slug: text("slug").notNull(),
|
|
162
|
+
content: text("content").notNull(),
|
|
163
|
+
reply_to_id: integer("reply_to_id"),
|
|
164
|
+
created_at: text("created_at").notNull().default(sql`(datetime('now'))`),
|
|
165
|
+
updated_at: text("updated_at").notNull().default(sql`(datetime('now'))`)
|
|
166
|
+
},
|
|
167
|
+
(table) => [
|
|
168
|
+
uniqueIndex("idx_notes_author_slug").on(table.author_id, table.slug),
|
|
169
|
+
index("idx_notes_created_at").on(table.created_at),
|
|
170
|
+
index("idx_notes_reply_to").on(table.reply_to_id)
|
|
171
|
+
]
|
|
172
|
+
);
|
|
173
|
+
var noteComments = sqliteTable(
|
|
174
|
+
"note_comments",
|
|
175
|
+
{
|
|
176
|
+
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
177
|
+
note_id: integer("note_id").notNull().references(() => notes.id, { onDelete: "cascade" }),
|
|
178
|
+
author_id: integer("author_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
|
179
|
+
content: text("content").notNull(),
|
|
180
|
+
created_at: text("created_at").notNull().default(sql`(datetime('now'))`)
|
|
181
|
+
},
|
|
182
|
+
(table) => [index("idx_note_comments_note_id").on(table.note_id)]
|
|
183
|
+
);
|
|
184
|
+
var noteReactions = sqliteTable(
|
|
185
|
+
"note_reactions",
|
|
186
|
+
{
|
|
187
|
+
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
188
|
+
note_id: integer("note_id").notNull().references(() => notes.id, { onDelete: "cascade" }),
|
|
189
|
+
user_id: integer("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
|
190
|
+
emoji: text("emoji").notNull(),
|
|
191
|
+
created_at: text("created_at").notNull().default(sql`(datetime('now'))`)
|
|
192
|
+
},
|
|
193
|
+
(table) => [
|
|
194
|
+
uniqueIndex("idx_note_reactions_unique").on(table.note_id, table.user_id, table.emoji),
|
|
195
|
+
index("idx_note_reactions_note_id").on(table.note_id)
|
|
196
|
+
]
|
|
197
|
+
);
|
|
152
198
|
var messages = sqliteTable(
|
|
153
199
|
"messages",
|
|
154
200
|
{
|
|
@@ -198,6 +244,9 @@ export {
|
|
|
198
244
|
deliveryQueue,
|
|
199
245
|
activity,
|
|
200
246
|
conversationReads,
|
|
247
|
+
notes,
|
|
248
|
+
noteComments,
|
|
249
|
+
noteReactions,
|
|
201
250
|
messages,
|
|
202
251
|
getDb
|
|
203
252
|
};
|
|
@@ -6,6 +6,15 @@ import {
|
|
|
6
6
|
// src/lib/daemon-client.ts
|
|
7
7
|
import { existsSync, readFileSync } from "fs";
|
|
8
8
|
import { resolve } from "path";
|
|
9
|
+
function readCliSession() {
|
|
10
|
+
const sessionPath = resolve(voluteHome(), "cli-session.json");
|
|
11
|
+
if (!existsSync(sessionPath)) return null;
|
|
12
|
+
try {
|
|
13
|
+
return JSON.parse(readFileSync(sessionPath, "utf-8"));
|
|
14
|
+
} catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
9
18
|
function readDaemonConfig() {
|
|
10
19
|
const configPath = resolve(voluteHome(), "daemon.json");
|
|
11
20
|
if (!existsSync(configPath)) {
|
|
@@ -47,12 +56,22 @@ async function daemonFetch(path, options) {
|
|
|
47
56
|
const config = readDaemonConfig();
|
|
48
57
|
const url = buildUrl(config);
|
|
49
58
|
const headers = new Headers(options?.headers);
|
|
50
|
-
|
|
51
|
-
|
|
59
|
+
const cliSession = readCliSession();
|
|
60
|
+
if (cliSession?.sessionId) {
|
|
61
|
+
headers.set("Authorization", `Bearer ${cliSession.sessionId}`);
|
|
52
62
|
}
|
|
53
63
|
headers.set("Origin", url);
|
|
54
64
|
try {
|
|
55
|
-
|
|
65
|
+
const res = await fetch(`${url}${path}`, { ...options, headers });
|
|
66
|
+
if (res.status === 401 && !path.startsWith("/api/auth/")) {
|
|
67
|
+
if (cliSession) {
|
|
68
|
+
console.error("Session expired. Run `volute login` again.");
|
|
69
|
+
} else {
|
|
70
|
+
console.error("Not logged in. Run `volute login` first.");
|
|
71
|
+
}
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
return res;
|
|
56
75
|
} catch (err) {
|
|
57
76
|
if (err instanceof TypeError && err.cause?.code === "ECONNREFUSED") {
|
|
58
77
|
console.error("Volute is not running. Start with: volute up");
|