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.
Files changed (114) hide show
  1. package/README.md +15 -20
  2. package/dist/{activity-events-4O37J7PD.js → activity-events-ZMBAKLUF.js} +2 -2
  3. package/dist/api.d.ts +590 -10
  4. package/dist/{auth-HM2RSPY7.js → auth-4TV573WE.js} +2 -2
  5. package/dist/{channel-HZOSHGNF.js → channel-ZVZV42UD.js} +3 -3
  6. package/dist/{chunk-NOBRGACV.js → chunk-2VO7453N.js} +56 -19
  7. package/dist/{chunk-OOW675I3.js → chunk-3CFRE2VC.js} +931 -775
  8. package/dist/{chunk-PHHKNGA3.js → chunk-3TV4GLFO.js} +2 -2
  9. package/dist/{chunk-4TJ72QQ3.js → chunk-5Y3PBKW6.js} +3 -3
  10. package/dist/{chunk-BFK6SOEJ.js → chunk-J2CO4WEV.js} +1 -1
  11. package/dist/{chunk-TQDITGES.js → chunk-LX22GRG7.js} +10 -13
  12. package/dist/{chunk-E7GOKNOT.js → chunk-NWI2425I.js} +1 -1
  13. package/dist/{chunk-2767L2RZ.js → chunk-OZFKBXD6.js} +1 -1
  14. package/dist/{chunk-XLC342FO.js → chunk-SIAG3QMM.js} +14 -1
  15. package/dist/{chunk-RVKR2R7F.js → chunk-SSI47XP2.js} +10 -2
  16. package/dist/chunk-TZKJLDQN.js +78 -0
  17. package/dist/{chunk-P3W36ZGD.js → chunk-USNBKHYG.js} +33 -5
  18. package/dist/chunk-UTL75LP6.js +113 -0
  19. package/dist/{chunk-3AIBT4TW.js → chunk-V63B7DX3.js} +24 -1
  20. package/dist/{chunk-33XAVCS4.js → chunk-WBHMQ5OZ.js} +49 -0
  21. package/dist/{chunk-TRQEV3CD.js → chunk-WGOGUMPO.js} +22 -3
  22. package/dist/chunk-XOXLRRR2.js +176 -0
  23. package/dist/{chunk-JTDFJWI2.js → chunk-YJA7P64S.js} +1 -1
  24. package/dist/chunk-ZYGKG6VC.js +22 -0
  25. package/dist/cli.js +44 -20
  26. package/dist/{cloud-sync-DIU3OCPV.js → cloud-sync-NI2K3C7G.js} +11 -9
  27. package/dist/{connector-M6XFI6GM.js → connector-G722WXAU.js} +4 -4
  28. package/dist/{create-VDQJER52.js → create-4YBRTTJS.js} +1 -1
  29. package/dist/{daemon-client-JOVQZ52X.js → daemon-client-Z7FAJ6JW.js} +1 -1
  30. package/dist/{daemon-restart-YMPEATQH.js → daemon-restart-BJZ3O4U4.js} +6 -5
  31. package/dist/daemon.js +982 -340
  32. package/dist/{delete-2MRR4JX5.js → delete-27OYNK25.js} +1 -1
  33. package/dist/{down-674SX2IZ.js → down-7UKFMJJZ.js} +4 -4
  34. package/dist/{env-2FPOZK37.js → env-M336ONDP.js} +4 -4
  35. package/dist/{export-IKFAPRAO.js → export-HP4G5DQC.js} +1 -1
  36. package/dist/{file-KT3UIQM3.js → file-HUDKTRAS.js} +3 -3
  37. package/dist/{history-46WZN5CN.js → history-B64GTFTD.js} +3 -3
  38. package/dist/{import-FRDPQPJ2.js → import-XIB7UV4S.js} +2 -2
  39. package/dist/{log-6SGSSR3D.js → log-PBFNILJ4.js} +3 -3
  40. package/dist/{login-UO6AOVEA.js → login-6U7U6BNG.js} +1 -1
  41. package/dist/login-B5E7N7MY.js +46 -0
  42. package/dist/logout-XSJRYS3U.js +39 -0
  43. package/dist/{logs-HRBONI5I.js → logs-3CART7O7.js} +3 -3
  44. package/dist/{merge-KSFJKX6T.js → merge-VK2HSKMA.js} +3 -3
  45. package/dist/{message-delivery-S7BCNV6Y.js → message-delivery-MS5JYPZX.js} +11 -9
  46. package/dist/{mind-KPLCRKQA.js → mind-HZ3QSDDJ.js} +17 -17
  47. package/dist/{mind-activity-tracker-NMDDEV3K.js → mind-activity-tracker-4G6FURY2.js} +3 -3
  48. package/dist/{mind-manager-ZNRIYEK3.js → mind-manager-VVK67AY3.js} +6 -4
  49. package/dist/{mind-sleep-GHPTSAYN.js → mind-sleep-DTV7L44D.js} +3 -3
  50. package/dist/{mind-wake-BJDJFMDF.js → mind-wake-PFN4FN3T.js} +3 -3
  51. package/dist/notes-37FW2UR2.js +230 -0
  52. package/dist/{package-S5YF25XV.js → package-VZWLXPHV.js} +3 -1
  53. package/dist/{pages-TWR6U7DS.js → pages-DIIT5HMQ.js} +1 -1
  54. package/dist/{publish-BZNHKUUK.js → publish-HQV7YREB.js} +4 -4
  55. package/dist/{pull-D32SPFVU.js → pull-2MB4SK3C.js} +3 -3
  56. package/dist/{register-U2UO6TC4.js → register-EFND67FQ.js} +1 -1
  57. package/dist/{restart-5BMNV7KU.js → restart-CCK7D6TV.js} +3 -3
  58. package/dist/sandbox-EHGFF52K.js +19 -0
  59. package/dist/{schedule-YEFDLVMJ.js → schedule-6F7ELB2M.js} +3 -3
  60. package/dist/{seed-6FEKB3YC.js → seed-E5OQGWX3.js} +1 -1
  61. package/dist/{send-IISDYFCL.js → send-IH6XZKPC.js} +6 -20
  62. package/dist/service-LLBV3R7M.js +122 -0
  63. package/dist/setup-F6TWFYGQ.js +371 -0
  64. package/dist/setup-YGAAIKKZ.js +17 -0
  65. package/dist/{shared-LWMNTTZN.js → shared-UMO4S7CC.js} +4 -4
  66. package/dist/{skill-BQOFACEI.js → skill-42LGFBQC.js} +13 -5
  67. package/dist/skills/dreaming/SKILL.md +68 -0
  68. package/dist/skills/dreaming/references/INSTALL.md +56 -0
  69. package/dist/skills/dreaming/scripts/dream.ts +289 -0
  70. package/dist/skills/dreaming/scripts/wake-context-dreams.sh +30 -0
  71. package/dist/skills/imagegen/SKILL.md +37 -0
  72. package/dist/skills/imagegen/references/INSTALL.md +13 -0
  73. package/dist/skills/imagegen/scripts/imagegen.ts +136 -0
  74. package/dist/skills/notes/SKILL.md +34 -0
  75. package/dist/skills/resonance/SKILL.md +73 -0
  76. package/dist/skills/resonance/assets/default-config.json +21 -0
  77. package/dist/skills/resonance/references/INSTALL.md +23 -0
  78. package/dist/skills/resonance/scripts/resonance.ts +1250 -0
  79. package/dist/skills/volute-mind/SKILL.md +23 -3
  80. package/dist/{sleep-manager-XXSWQQLE.js → sleep-manager-EE4NRN2Q.js} +11 -9
  81. package/dist/{sprout-CGSW4CF5.js → sprout-QL74KR2X.js} +5 -5
  82. package/dist/{start-C7XITZ5O.js → start-O5JQASRC.js} +3 -3
  83. package/dist/{status-SIRPLEZC.js → status-FZBEBM7Q.js} +3 -3
  84. package/dist/{status-LYS4NUOZ.js → status-WXD4HXRL.js} +3 -3
  85. package/dist/{stop-CVKBSLXY.js → stop-2SOG5NYF.js} +3 -3
  86. package/dist/up-SDMCSVI3.js +17 -0
  87. package/dist/{update-7XCZMYBT.js → update-5VUDAI3D.js} +6 -6
  88. package/dist/{upgrade-7RUIXGOO.js → upgrade-QCCO33BK.js} +1 -1
  89. package/dist/{variant-UGREB4G5.js → variant-WWLDY6D5.js} +4 -4
  90. package/dist/{version-notify-SZ75QRGO.js → version-notify-USFZBWMG.js} +11 -9
  91. package/dist/web-assets/assets/index-CUQ31ieL.js +69 -0
  92. package/dist/web-assets/assets/index-CW8NSl1o.css +1 -0
  93. package/dist/web-assets/favicon.png +0 -0
  94. package/dist/web-assets/index.html +5 -4
  95. package/dist/web-assets/logo.png +0 -0
  96. package/drizzle/0015_notes.sql +23 -0
  97. package/drizzle/0016_note_reactions_and_replies.sql +15 -0
  98. package/drizzle/meta/_journal.json +14 -0
  99. package/package.json +3 -1
  100. package/templates/_base/.init/.config/hooks/wake-context.sh +7 -0
  101. package/templates/_base/home/public/.gitkeep +0 -0
  102. package/templates/_base/src/lib/startup.ts +8 -0
  103. package/templates/claude/src/agent.ts +51 -1
  104. package/templates/claude/src/server.ts +1 -0
  105. package/templates/pi/package.json.tmpl +1 -0
  106. package/templates/pi/src/agent.ts +48 -1
  107. package/templates/pi/src/lib/subagents.ts +150 -0
  108. package/templates/pi/src/server.ts +1 -0
  109. package/dist/chunk-NWPT4ASZ.js +0 -89
  110. package/dist/service-FASYWLTC.js +0 -247
  111. package/dist/setup-BMLM2UTK.js +0 -230
  112. package/dist/up-OMHACRJL.js +0 -15
  113. package/dist/web-assets/assets/index-Bx9WDoaQ.js +0 -69
  114. 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-JTDFJWI2.js";
7
+ } from "./chunk-YJA7P64S.js";
8
8
  import {
9
9
  isIsolationEnabled,
10
10
  mindUserName
11
- } from "./chunk-NWPT4ASZ.js";
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-XLC342FO.js";
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-JOVQZ52X.js");
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-JOVQZ52X.js");
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()), {
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  activity,
4
4
  getDb
5
- } from "./chunk-33XAVCS4.js";
5
+ } from "./chunk-WBHMQ5OZ.js";
6
6
  import {
7
7
  logger_default
8
8
  } from "./chunk-YUIHSKR6.js";
@@ -4,10 +4,13 @@ import {
4
4
  modeLabel,
5
5
  pollHealth,
6
6
  startService
7
- } from "./chunk-3AIBT4TW.js";
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
  };
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  publish
4
- } from "./chunk-BFK6SOEJ.js";
4
+ } from "./chunk-J2CO4WEV.js";
5
5
  import {
6
6
  logger_default
7
7
  } from "./chunk-YUIHSKR6.js";
@@ -5,7 +5,7 @@ import {
5
5
  pollHealthDown,
6
6
  readDaemonConfig,
7
7
  stopService
8
- } from "./chunk-3AIBT4TW.js";
8
+ } from "./chunk-V63B7DX3.js";
9
9
  import {
10
10
  voluteHome
11
11
  } from "./chunk-B2CPS4QU.js";
@@ -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
- return readJson(path);
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 promptLine(prompt) {
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-33XAVCS4.js";
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-JTDFJWI2.js";
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-JTDFJWI2.js";
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
- if (config.token) {
51
- headers.set("Authorization", `Bearer ${config.token}`);
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
- return await fetch(`${url}${path}`, { ...options, headers });
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");