volute 0.44.0 → 0.45.2

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 (130) hide show
  1. package/dist/{activity-events-OMCSM7XW.js → activity-events-BUR2RYTJ.js} +2 -2
  2. package/dist/{ai-service-7PHSRDMO.js → ai-service-UODCCE4O.js} +3 -3
  3. package/dist/{archive-6R4TZMYI.js → archive-WS2L2VIQ.js} +2 -2
  4. package/dist/{auth-FRZJVQCS.js → auth-FLAEXXZK.js} +3 -3
  5. package/dist/{avatar-image-BNWEXSA2.js → avatar-image-6KY6UU4D.js} +3 -3
  6. package/dist/backup-TST5C2YN.js +381 -0
  7. package/dist/{chat-I4EDDIQC.js → chat-76MRWWQX.js} +2 -2
  8. package/dist/{chunk-5AHYQHNT.js → chunk-4JOQZPCE.js} +3 -2
  9. package/dist/{chunk-2BONPXAF.js → chunk-6EMWJZBK.js} +1 -1
  10. package/dist/{chunk-ZMIMSIIT.js → chunk-7C44R625.js} +1 -1
  11. package/dist/{chunk-I2Y2YHIO.js → chunk-7ZUB63PS.js} +4 -4
  12. package/dist/{chunk-LAXUUY7O.js → chunk-A7WQZKOU.js} +2 -2
  13. package/dist/{chunk-H7IYFRAL.js → chunk-ALKYQULU.js} +10 -5
  14. package/dist/{chunk-KP3KWNG6.js → chunk-CDPBTTZO.js} +2 -2
  15. package/dist/{chunk-SQL7M4HJ.js → chunk-DRJ24GXK.js} +2 -2
  16. package/dist/{chunk-HVU45COX.js → chunk-EJVPTVTI.js} +1 -1
  17. package/dist/{chunk-PNV24SEP.js → chunk-F7H6ATM4.js} +14 -2
  18. package/dist/{chunk-7FT24JBQ.js → chunk-FR6S2KNN.js} +10 -10
  19. package/dist/chunk-FXORA5JG.js +327 -0
  20. package/dist/{chunk-3ERIL673.js → chunk-H2X54EHC.js} +1 -1
  21. package/dist/{chunk-J47TIYHA.js → chunk-IHKGWTGT.js} +1 -1
  22. package/dist/{chunk-3GYVW2X3.js → chunk-JZ62SYWR.js} +1 -1
  23. package/dist/{chunk-EQZD4YU3.js → chunk-NYC5U6K7.js} +9 -9
  24. package/dist/{chunk-FZMEVCWC.js → chunk-OP4JG2BS.js} +9 -6
  25. package/dist/{chunk-CU6OFXMM.js → chunk-PFZBNKUH.js} +9 -0
  26. package/dist/{chunk-QMJJWTNL.js → chunk-QCN24XEN.js} +1 -1
  27. package/dist/{chunk-7KWANSLX.js → chunk-QNQMSWZE.js} +3 -3
  28. package/dist/{chunk-UMI4UIJG.js → chunk-R77O2WMK.js} +1 -1
  29. package/dist/{chunk-IEWZ7VD7.js → chunk-RRNXJOKB.js} +2 -2
  30. package/dist/{chunk-FHWUQZXE.js → chunk-RXEEYDIB.js} +1 -1
  31. package/dist/{chunk-CYI534DJ.js → chunk-T6HYVHPG.js} +2 -2
  32. package/dist/{chunk-Q2XBKQ2D.js → chunk-TQJ6ZYVY.js} +2 -2
  33. package/dist/{chunk-NF75HPNR.js → chunk-UYLGEDVD.js} +40 -15
  34. package/dist/{chunk-OMETFX5H.js → chunk-YT7PEESJ.js} +261 -102
  35. package/dist/{chunk-5GT7URXJ.js → chunk-ZDZISKCL.js} +1 -1
  36. package/dist/cli.js +21 -15
  37. package/dist/{cloud-sync-6WJ7WFYD.js → cloud-sync-3OJOPZRT.js} +16 -16
  38. package/dist/{conversations-WAJYZKOC.js → conversations-JZTD6KLR.js} +3 -3
  39. package/dist/{create-YZIV5OJC.js → create-IOV4GCRX.js} +1 -1
  40. package/dist/{daemon-restart-OUJVINXV.js → daemon-restart-TIHKVVVA.js} +6 -6
  41. package/dist/daemon.js +562 -321
  42. package/dist/{db-JFLCJCUG.js → db-MF2JNRUW.js} +1 -1
  43. package/dist/delivery-manager-VDVKVA46.js +32 -0
  44. package/dist/{delivery-router-GODSIS62.js → delivery-router-N4A7ZC5T.js} +2 -2
  45. package/dist/{down-XCAZXWLB.js → down-U2CZ23M4.js} +4 -4
  46. package/dist/echo-text-6JJOPUD4.js +32 -0
  47. package/dist/{exec-4GWHAHS3.js → exec-G3ZRST4Z.js} +2 -2
  48. package/dist/{export-ID7YUSLZ.js → export-GGIVO76O.js} +2 -2
  49. package/dist/{extensions-3QNDGW5E.js → extensions-JONTW2HF.js} +16 -16
  50. package/dist/{files-BQ2WB5KC.js → files-4LN3EN7Q.js} +2 -2
  51. package/dist/{import-SKID37QX.js → import-42YNQBF4.js} +5 -5
  52. package/dist/{isolation-5MB3J73F.js → isolation-YMGZZ4E5.js} +2 -2
  53. package/dist/{join-RXPXGV4L.js → join-ZJV7W6MO.js} +4 -0
  54. package/dist/{login-LM4NF4ZB.js → login-M6OJUPQ2.js} +1 -1
  55. package/dist/{logout-CTJVIZGA.js → logout-WQBWO77G.js} +1 -1
  56. package/dist/message-delivery-3C65ZI6L.js +34 -0
  57. package/dist/{mind-MI7Z6KM2.js → mind-DWB6XYCH.js} +7 -7
  58. package/dist/{mind-activity-tracker-BGHJE7XA.js → mind-activity-tracker-POBLU3LC.js} +3 -3
  59. package/dist/mind-service-3F4I46CI.js +36 -0
  60. package/dist/{package-5B4XDTJH.js → package-A7KXT67Q.js} +1 -1
  61. package/dist/{registry-NUJAGLQJ.js → registry-OUZBZ3Q4.js} +5 -1
  62. package/dist/{sandbox-IOPWWILM.js → sandbox-ZJ7QA6Z6.js} +3 -3
  63. package/dist/scheduler-P6DXLLXI.js +30 -0
  64. package/dist/{seed-UUPVQWFB.js → seed-HCP5NYKH.js} +1 -1
  65. package/dist/{seed-cmd-7XPKDQUC.js → seed-cmd-ILOGNIFB.js} +2 -2
  66. package/dist/{seed-create-IZ4M5ESO.js → seed-create-OURS2LS4.js} +3 -3
  67. package/dist/{seed-sprout-POJXHJWV.js → seed-sprout-ELDFDAN6.js} +7 -7
  68. package/dist/{send-O6W53VBM.js → send-AAIOPJAV.js} +2 -2
  69. package/dist/{service-AQVPUANO.js → service-UOCLXDWR.js} +3 -3
  70. package/dist/{service-install-4QNUDKLM.js → service-install-4HHFDDWY.js} +4 -4
  71. package/dist/{setup-5GCO3CUB.js → setup-DQM5VPY4.js} +6 -6
  72. package/dist/{setup-MLRKNP7Y.js → setup-Y2EJI3GG.js} +2 -2
  73. package/dist/skills/imagegen/scripts/imagegen.ts +100 -40
  74. package/dist/skills/memory/SKILL.md +2 -1
  75. package/dist/skills/orientation/SKILL.md +1 -1
  76. package/dist/skills/plan-coordinator/SKILL.md +2 -2
  77. package/dist/skills/volute-admin/SKILL.md +9 -6
  78. package/dist/skills/volute-mind/SKILL.md +20 -17
  79. package/dist/skills/volute-mind/references/extensions.md +5 -3
  80. package/dist/skills/volute-mind/references/integrations.md +2 -2
  81. package/dist/skills/volute-mind/references/routing.md +19 -23
  82. package/dist/skills/volute-mind/references/variants.md +10 -8
  83. package/dist/{skills-775C7QFF.js → skills-BKINFL7W.js} +4 -4
  84. package/dist/sleep-manager-HCUH4PM4.js +34 -0
  85. package/dist/{spirit-IK5OYWMV.js → spirit-DBTIW7P4.js} +7 -7
  86. package/dist/{sprout-ZY5QGOEL.js → sprout-W3KC7UGK.js} +1 -1
  87. package/dist/{src-XFY3XDD6.js → src-OJHARHTF.js} +28 -17
  88. package/dist/{status-E5DQNCAO.js → status-SWRVGXBI.js} +4 -4
  89. package/dist/system-chat-XOALE2RD.js +36 -0
  90. package/dist/{tailscale-QAWVKQKU.js → tailscale-3XOKOW7T.js} +1 -1
  91. package/dist/{turn-tracker-RWZMY7M6.js → turn-tracker-2W6Q2NSR.js} +6 -4
  92. package/dist/{up-FLCDR2UB.js → up-FTK7CTO3.js} +5 -5
  93. package/dist/{update-7TYVH5FM.js → update-SWAYZRWJ.js} +4 -4
  94. package/dist/{update-check-VSCSJ7KX.js → update-check-PQ2MZ47K.js} +2 -2
  95. package/dist/{upgrade-37DAGF6H.js → upgrade-NWAZ7MLD.js} +1 -1
  96. package/dist/{version-notify-B67N35A3.js → version-notify-3XMAIQTE.js} +17 -17
  97. package/dist/{volute-config-TS62GS6A.js → volute-config-A52FHWKM.js} +5 -1
  98. package/dist/web-assets/assets/{index-dJdNzAPn.css → index-B1JBQv2m.css} +1 -1
  99. package/dist/web-assets/assets/index-Bc2m8gRC.js +86 -0
  100. package/dist/web-assets/index.html +2 -2
  101. package/package.json +1 -1
  102. package/packages/extensions/plan/skills/plan/SKILL.md +1 -1
  103. package/templates/_base/.init/.config/prompts.json +1 -1
  104. package/templates/_base/.init/.local/hooks/startup-context.ts +0 -8
  105. package/templates/_base/.init/MEMORY.md +2 -1
  106. package/templates/_base/home/VOLUTE.md +6 -9
  107. package/templates/_base/src/lib/file-handler.ts +3 -3
  108. package/templates/_base/src/lib/router.ts +21 -25
  109. package/templates/_base/src/lib/startup.ts +3 -1
  110. package/templates/_base/src/lib/types.ts +2 -2
  111. package/templates/claude/.init/CLAUDE.md +1 -1
  112. package/templates/claude/src/agent.ts +97 -12
  113. package/templates/claude/src/lib/message-channel.ts +23 -0
  114. package/templates/claude/src/lib/session-reaper.ts +37 -0
  115. package/templates/claude/src/lib/stream-consumer.ts +7 -4
  116. package/templates/claude/src/server.ts +1 -0
  117. package/templates/codex/.init/AGENTS.md +4 -2
  118. package/templates/codex/src/agent.ts +31 -13
  119. package/templates/pi/.init/MINDS.md +4 -2
  120. package/templates/pi/src/agent.ts +18 -7
  121. package/templates/pi/src/lib/event-handler.ts +6 -2
  122. package/dist/delivery-manager-TEVOHX5W.js +0 -32
  123. package/dist/echo-text-T7ZA5EYD.js +0 -32
  124. package/dist/message-delivery-2EA5LLMD.js +0 -34
  125. package/dist/mind-service-S3SY7HAM.js +0 -36
  126. package/dist/scheduler-CXWQ6Z4E.js +0 -30
  127. package/dist/sleep-manager-HINI6Y46.js +0 -34
  128. package/dist/system-chat-4DQZOEZI.js +0 -34
  129. package/dist/web-assets/assets/index-Domoz5Cg.js +0 -79
  130. package/templates/_base/.init/.config/hooks/pre-prompt/session-activity.ts +0 -40
@@ -3,9 +3,9 @@ import {
3
3
  broadcast,
4
4
  publish,
5
5
  subscribe
6
- } from "./chunk-ZMIMSIIT.js";
6
+ } from "./chunk-7C44R625.js";
7
7
  import "./chunk-T2TP6ZC6.js";
8
- import "./chunk-H7IYFRAL.js";
8
+ import "./chunk-ALKYQULU.js";
9
9
  import "./chunk-ZV7MFISX.js";
10
10
  import "./chunk-K3NQKI34.js";
11
11
  export {
@@ -25,10 +25,10 @@ import {
25
25
  setProviderRefreshHook,
26
26
  setUtilityModel,
27
27
  unqualifyModelId
28
- } from "./chunk-J47TIYHA.js";
29
- import "./chunk-PNV24SEP.js";
28
+ } from "./chunk-IHKGWTGT.js";
29
+ import "./chunk-F7H6ATM4.js";
30
30
  import "./chunk-T2TP6ZC6.js";
31
- import "./chunk-H7IYFRAL.js";
31
+ import "./chunk-ALKYQULU.js";
32
32
  import "./chunk-ZV7MFISX.js";
33
33
  import "./chunk-K3NQKI34.js";
34
34
  export {
@@ -5,8 +5,8 @@ import {
5
5
  extractArchive,
6
6
  isHomeOnlyArchive,
7
7
  readManifest
8
- } from "./chunk-QMJJWTNL.js";
9
- import "./chunk-H7IYFRAL.js";
8
+ } from "./chunk-QCN24XEN.js";
9
+ import "./chunk-ALKYQULU.js";
10
10
  import "./chunk-ZV7MFISX.js";
11
11
  import "./chunk-K3NQKI34.js";
12
12
  export {
@@ -17,10 +17,10 @@ import {
17
17
  syncMindProfile,
18
18
  updateUserProfile,
19
19
  verifyUser
20
- } from "./chunk-CYI534DJ.js";
21
- import "./chunk-ZMIMSIIT.js";
20
+ } from "./chunk-T6HYVHPG.js";
21
+ import "./chunk-7C44R625.js";
22
22
  import "./chunk-T2TP6ZC6.js";
23
- import "./chunk-H7IYFRAL.js";
23
+ import "./chunk-ALKYQULU.js";
24
24
  import "./chunk-ZV7MFISX.js";
25
25
  import "./chunk-K3NQKI34.js";
26
26
  export {
@@ -3,10 +3,10 @@ import {
3
3
  AVATAR_DIM,
4
4
  migrateAvatarSizes,
5
5
  normalizeAvatar
6
- } from "./chunk-SQL7M4HJ.js";
7
- import "./chunk-CU6OFXMM.js";
6
+ } from "./chunk-DRJ24GXK.js";
7
+ import "./chunk-PFZBNKUH.js";
8
8
  import "./chunk-T2TP6ZC6.js";
9
- import "./chunk-H7IYFRAL.js";
9
+ import "./chunk-ALKYQULU.js";
10
10
  import "./chunk-ZV7MFISX.js";
11
11
  import "./chunk-K3NQKI34.js";
12
12
  export {
@@ -0,0 +1,381 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ stopDaemon
4
+ } from "./chunk-RRNXJOKB.js";
5
+ import {
6
+ promptLine,
7
+ promptPassword
8
+ } from "./chunk-VHWGEJ4V.js";
9
+ import {
10
+ daemonFetch
11
+ } from "./chunk-RBNMDAKB.js";
12
+ import {
13
+ getClient,
14
+ urlOf
15
+ } from "./chunk-LOPXTW6H.js";
16
+ import {
17
+ command,
18
+ subcommands
19
+ } from "./chunk-TXSA4Q3V.js";
20
+ import "./chunk-O7IGP7ZW.js";
21
+ import {
22
+ RESTIC_INSTALL_HINT,
23
+ resticVersion,
24
+ restoreStagedDb
25
+ } from "./chunk-FXORA5JG.js";
26
+ import {
27
+ getServiceMode,
28
+ modeLabel,
29
+ stopService
30
+ } from "./chunk-TQJ6ZYVY.js";
31
+ import {
32
+ chownMindDir,
33
+ exec,
34
+ execInherit,
35
+ wrapForIsolation
36
+ } from "./chunk-H2X54EHC.js";
37
+ import {
38
+ _resetConfigCache,
39
+ readGlobalConfig
40
+ } from "./chunk-F7H6ATM4.js";
41
+ import "./chunk-T2TP6ZC6.js";
42
+ import {
43
+ voluteHome,
44
+ voluteSystemDir
45
+ } from "./chunk-ALKYQULU.js";
46
+ import "./chunk-ZV7MFISX.js";
47
+ import "./chunk-K3NQKI34.js";
48
+
49
+ // src/commands/backup.ts
50
+ import { existsSync, readdirSync, rmSync } from "fs";
51
+ import { resolve } from "path";
52
+ function printPassphrase(password) {
53
+ console.log("\n\u250C\u2500 BACKUP PASSPHRASE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
54
+ console.log(` ${password}`);
55
+ console.log("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
56
+ console.log("Store this somewhere OFF this machine (password manager, paper).");
57
+ console.log("Without it your backups are unreadable \u2014 this is the exact");
58
+ console.log("scenario (lost machine) that backups exist for.\n");
59
+ console.log(`It is also stored in ${resolve(voluteSystemDir(), "secrets.json")}`);
60
+ }
61
+ async function apiError(res, fallback) {
62
+ const data = await res.json().catch(() => ({}));
63
+ console.error(data.error ?? `${fallback}: HTTP ${res.status}`);
64
+ process.exit(1);
65
+ }
66
+ var initCmd = command({
67
+ name: "volute backup init",
68
+ description: "Configure the backup repository and initialize it",
69
+ flags: {
70
+ repo: { type: "string", description: "Restic repository (path, s3:..., b2:..., sftp:...)" },
71
+ password: { type: "string", description: "Repository passphrase (generated if omitted)" }
72
+ },
73
+ examples: [
74
+ "volute backup init --repo /mnt/backups/volute",
75
+ "volute backup init --repo s3:s3.amazonaws.com/my-bucket/volute"
76
+ ],
77
+ run: async ({ flags }) => {
78
+ let repo = flags.repo;
79
+ if (!repo) {
80
+ repo = (await promptLine("Repository (path or s3:/b2:/sftp: URL): ")).trim();
81
+ if (!repo) {
82
+ console.error("A repository is required.");
83
+ process.exit(1);
84
+ }
85
+ }
86
+ const client = getClient();
87
+ const putRes = await daemonFetch(urlOf(client.api.backup.config.$url()), {
88
+ method: "PUT",
89
+ headers: { "Content-Type": "application/json" },
90
+ body: JSON.stringify({ repository: repo, password: flags.password ?? "" })
91
+ });
92
+ if (!putRes.ok) await apiError(putRes, "Failed to save backup config");
93
+ const initRes = await daemonFetch(urlOf(client.api.backup.init.$url()), { method: "POST" });
94
+ if (!initRes.ok) await apiError(initRes, "Failed to initialize repository");
95
+ const { password } = await initRes.json();
96
+ console.log(`Backup repository initialized: ${repo}`);
97
+ if (password) printPassphrase(password);
98
+ console.log("\nEnable scheduled backups with: volute backup schedule --enable");
99
+ console.log("Or run one now with: volute backup create");
100
+ }
101
+ });
102
+ var createCmd = command({
103
+ name: "volute backup create",
104
+ description: "Run a backup now",
105
+ flags: {},
106
+ run: async () => {
107
+ const client = getClient();
108
+ console.log("Running backup (this may take a while on first run)...");
109
+ const res = await daemonFetch(urlOf(client.api.backup.run.$url()), { method: "POST" });
110
+ if (!res.ok) await apiError(res, "Backup failed");
111
+ const summary = await res.json();
112
+ const addedMB = (summary.dataAdded / 1024 / 1024).toFixed(1);
113
+ console.log(`Snapshot ${summary.snapshotId.slice(0, 8)} created.`);
114
+ console.log(
115
+ ` Files: ${summary.totalFilesProcessed} processed, ${summary.filesNew} new, ${summary.filesChanged} changed`
116
+ );
117
+ console.log(` Data added: ${addedMB} MB in ${(summary.durationMs / 1e3).toFixed(1)}s`);
118
+ }
119
+ });
120
+ var listCmd = command({
121
+ name: "volute backup list",
122
+ description: "List backup snapshots",
123
+ flags: {},
124
+ run: async () => {
125
+ const client = getClient();
126
+ const res = await daemonFetch(urlOf(client.api.backup.snapshots.$url()));
127
+ if (!res.ok) await apiError(res, "Failed to list snapshots");
128
+ const snapshots = await res.json();
129
+ if (snapshots.length === 0) {
130
+ console.log("No snapshots yet. Run: volute backup create");
131
+ return;
132
+ }
133
+ console.log("ID TIME HOST");
134
+ for (const s of snapshots) {
135
+ console.log(`${s.short_id} ${new Date(s.time).toLocaleString().padEnd(24)} ${s.hostname}`);
136
+ }
137
+ }
138
+ });
139
+ var scheduleCmd = command({
140
+ name: "volute backup schedule",
141
+ description: "Enable, disable, or set the backup schedule",
142
+ flags: {
143
+ enable: { type: "boolean", description: "Enable scheduled backups" },
144
+ disable: { type: "boolean", description: "Disable scheduled backups" },
145
+ cron: { type: "string", description: 'Cron expression (default "0 3 * * *")' }
146
+ },
147
+ examples: ["volute backup schedule --enable", 'volute backup schedule --cron "30 4 * * *"'],
148
+ run: async ({ flags }) => {
149
+ const body = {};
150
+ if (flags.enable) body.enabled = true;
151
+ if (flags.disable) body.enabled = false;
152
+ if (flags.cron) body.schedule = flags.cron;
153
+ if (Object.keys(body).length === 0) {
154
+ console.error("Nothing to change. Use --enable, --disable, or --cron.");
155
+ process.exit(1);
156
+ }
157
+ const client = getClient();
158
+ const res = await daemonFetch(urlOf(client.api.backup.config.$url()), {
159
+ method: "PUT",
160
+ headers: { "Content-Type": "application/json" },
161
+ body: JSON.stringify(body)
162
+ });
163
+ if (!res.ok) await apiError(res, "Failed to update schedule");
164
+ const config = await res.json();
165
+ console.log(
166
+ `Scheduled backups ${config.enabled ? "enabled" : "disabled"} (cron: ${config.schedule})`
167
+ );
168
+ if (config.enabled && (!config.repository || !config.hasPassword)) {
169
+ console.log("Note: backups will NOT run until a repository is set up: volute backup init");
170
+ }
171
+ }
172
+ });
173
+ var statusCmd = command({
174
+ name: "volute backup status",
175
+ description: "Show backup configuration and last run",
176
+ flags: {},
177
+ run: async () => {
178
+ const client = getClient();
179
+ const res = await daemonFetch(urlOf(client.api.backup.status.$url()));
180
+ if (!res.ok) await apiError(res, "Failed to get backup status");
181
+ const status = await res.json();
182
+ console.log(`Restic: ${status.resticVersion ?? "not installed"}`);
183
+ if (status.installHint) console.log(` ${status.installHint}`);
184
+ console.log(`Repository: ${status.config.repository ?? "not configured"}`);
185
+ console.log(`Passphrase: ${status.config.hasPassword ? "set" : "not set"}`);
186
+ console.log(
187
+ `Schedule: ${status.config.enabled ? `enabled (${status.config.schedule})` : "disabled"}`
188
+ );
189
+ if (status.state.lastRun) {
190
+ console.log(`Last successful backup: ${new Date(status.state.lastRun).toLocaleString()}`);
191
+ if (status.state.lastSnapshotId) {
192
+ console.log(` Snapshot: ${status.state.lastSnapshotId.slice(0, 8)}`);
193
+ }
194
+ if (status.state.pruneError) {
195
+ console.log(` Retention prune failed: ${status.state.pruneError}`);
196
+ }
197
+ } else {
198
+ console.log("Last successful backup: never");
199
+ }
200
+ if (status.state.lastError) {
201
+ const at = status.state.lastAttempt ? ` at ${new Date(status.state.lastAttempt).toLocaleString()}` : "";
202
+ console.log(`Last attempt failed${at}: ${status.state.lastError}`);
203
+ }
204
+ }
205
+ });
206
+ var restoreCmd = command({
207
+ name: "volute backup restore",
208
+ description: "Restore the whole system from a backup (stops the daemon)",
209
+ flags: {
210
+ repo: { type: "string", description: "Restic repository (defaults to configured one)" },
211
+ snapshot: { type: "string", description: "Snapshot ID (default: latest)" },
212
+ target: { type: "string", description: "Restore to this directory instead of in place" },
213
+ yes: { type: "boolean", description: "Skip confirmation" }
214
+ },
215
+ examples: [
216
+ "volute backup restore",
217
+ "volute backup restore --repo s3:s3.amazonaws.com/my-bucket/volute",
218
+ "volute backup restore --snapshot 1a2b3c4d --target /tmp/inspect"
219
+ ],
220
+ run: async ({ flags }) => {
221
+ if (!await resticVersion()) {
222
+ console.error(`restic is not installed. ${RESTIC_INSTALL_HINT}`);
223
+ process.exit(1);
224
+ }
225
+ const backupConfig = readGlobalConfig().backup ?? {};
226
+ const repo = flags.repo ?? backupConfig.repository;
227
+ if (!repo) {
228
+ console.error("No repository configured. Pass --repo <repository>.");
229
+ process.exit(1);
230
+ }
231
+ let password = backupConfig.password ?? process.env.RESTIC_PASSWORD;
232
+ if (!password) {
233
+ password = await promptPassword("Repository passphrase: ");
234
+ if (!password) {
235
+ console.error("A passphrase is required.");
236
+ process.exit(1);
237
+ }
238
+ }
239
+ const env = {
240
+ ...process.env,
241
+ ...backupConfig.env,
242
+ RESTIC_REPOSITORY: repo,
243
+ RESTIC_PASSWORD: password
244
+ };
245
+ const snapshot = flags.snapshot ?? "latest";
246
+ if (flags.target) {
247
+ console.log(`Restoring snapshot ${snapshot} to ${flags.target}...`);
248
+ await execInherit("restic", ["restore", snapshot, "--target", flags.target], { env });
249
+ console.log("Done. (Inspection restore: no databases moved, no daemon touched.)");
250
+ return;
251
+ }
252
+ const home = voluteHome();
253
+ try {
254
+ const out = await exec("restic", ["snapshots", snapshot, "--json"], { env });
255
+ const snaps = JSON.parse(out || "[]");
256
+ const paths = snaps[0]?.paths ?? [];
257
+ if (paths.length > 0 && !paths.includes(home)) {
258
+ console.error(`This snapshot was taken with a different volute home:`);
259
+ for (const p of paths) console.error(` ${p}`);
260
+ console.error(`This machine's volute home is ${home}, so an in-place restore would`);
261
+ console.error("write to the old paths and restore nothing here. Either set VOLUTE_HOME");
262
+ console.error("to match, or inspect with --target and move files manually.");
263
+ process.exit(1);
264
+ }
265
+ } catch (err) {
266
+ console.error(
267
+ `Could not read snapshot ${snapshot}: ${err instanceof Error ? err.message : err}`
268
+ );
269
+ process.exit(1);
270
+ }
271
+ if (!flags.yes) {
272
+ console.log(`This restores snapshot ${snapshot} from ${repo} IN PLACE:`);
273
+ console.log(" - the daemon will be stopped");
274
+ console.log(" - current system state and mind files will be overwritten");
275
+ console.log(
276
+ " - minds wake with fresh sessions (transcripts are excluded unless includeSessions was on)"
277
+ );
278
+ const answer = (await promptLine("Continue? [y/N] ")).trim().toLowerCase();
279
+ if (answer !== "y" && answer !== "yes") {
280
+ console.log("Aborted.");
281
+ return;
282
+ }
283
+ }
284
+ const mode = getServiceMode();
285
+ if (mode !== "manual") {
286
+ console.log(`Stopping volute (${modeLabel(mode)})...`);
287
+ try {
288
+ await stopService(mode);
289
+ } catch (err) {
290
+ console.error(`Failed to stop service: ${err instanceof Error ? err.message : err}`);
291
+ process.exit(1);
292
+ }
293
+ } else {
294
+ const result = await stopDaemon();
295
+ if (!result.stopped && result.reason !== "not-running") {
296
+ console.error("Could not stop the daemon \u2014 aborting so live databases aren't replaced.");
297
+ process.exit(1);
298
+ }
299
+ }
300
+ try {
301
+ console.log(`Restoring snapshot ${snapshot} from ${repo}...`);
302
+ await execInherit("restic", ["restore", snapshot, "--target", "/"], { env });
303
+ const systemDir = voluteSystemDir();
304
+ const staging = resolve(systemDir, "backup-staging");
305
+ const mainLive = resolve(systemDir, "volute.db");
306
+ if (!restoreStagedDb(resolve(staging, "volute.db"), mainLive)) {
307
+ console.error(`
308
+ FATAL: staged database not found at ${staging}/volute.db.`);
309
+ console.error("The snapshot does not contain a system database; whatever volute.db");
310
+ console.error("was on disk before this restore is still in place, and it does NOT");
311
+ console.error("match the restored files. Do not trust this system until you restore");
312
+ console.error("a snapshot that includes backup-staging/volute.db.");
313
+ process.exit(1);
314
+ }
315
+ console.log(`Restored database: ${mainLive}`);
316
+ const stagedExt = resolve(staging, "extension-data");
317
+ if (existsSync(stagedExt)) {
318
+ for (const id of readdirSync(stagedExt)) {
319
+ const live = resolve(systemDir, "extension-data", id, "data.db");
320
+ if (restoreStagedDb(resolve(stagedExt, id, "data.db"), live)) {
321
+ console.log(`Restored database: ${live}`);
322
+ } else {
323
+ console.error(` Warning: no staged copy for extension DB ${id} \u2014 skipped.`);
324
+ }
325
+ }
326
+ }
327
+ rmSync(staging, { recursive: true, force: true });
328
+ } catch (err) {
329
+ console.error(`
330
+ Restore failed: ${err instanceof Error ? err.message : err}`);
331
+ console.error("The system is in a PARTIALLY RESTORED state (the daemon is stopped and");
332
+ console.error("some files may already be overwritten). Do not start the daemon.");
333
+ console.error("Fix the underlying problem and re-run: volute backup restore");
334
+ process.exit(1);
335
+ }
336
+ _resetConfigCache();
337
+ if (readGlobalConfig().setup?.isolation === "user") {
338
+ process.env.VOLUTE_ISOLATION = "user";
339
+ }
340
+ const { readAllMinds, mindDir } = await import("./registry-OUZBZ3Q4.js");
341
+ const seen = /* @__PURE__ */ new Set();
342
+ for (const mind of await readAllMinds()) {
343
+ const dir = mind.dir ?? mindDir(mind.name);
344
+ if (seen.has(dir) || !existsSync(resolve(dir, "package.json"))) continue;
345
+ seen.add(dir);
346
+ console.log(`Installing dependencies for ${mind.name}...`);
347
+ try {
348
+ await chownMindDir(dir, mind.name);
349
+ const [cmd2, args] = await wrapForIsolation(
350
+ "npm",
351
+ ["install", "--no-audit", "--no-fund"],
352
+ mind.name
353
+ );
354
+ await exec(cmd2, args, { cwd: dir });
355
+ } catch (err) {
356
+ console.error(
357
+ ` npm install failed for ${mind.name}: ${err instanceof Error ? err.message : err}`
358
+ );
359
+ console.error(` Run it manually: cd ${dir} && npm install`);
360
+ }
361
+ }
362
+ console.log("\nRestore complete. Start the daemon with: volute up");
363
+ console.log("Minds' memory and history are restored from the snapshot.");
364
+ }
365
+ });
366
+ var cmd = subcommands({
367
+ name: "volute backup",
368
+ description: "Back up and restore the whole volute system (restic-based)",
369
+ commands: {
370
+ init: { description: "Configure and initialize the backup repository", run: initCmd.execute },
371
+ create: { description: "Run a backup now", run: createCmd.execute },
372
+ list: { description: "List snapshots", run: listCmd.execute },
373
+ schedule: { description: "Enable/disable/set the backup schedule", run: scheduleCmd.execute },
374
+ status: { description: "Show backup configuration and last run", run: statusCmd.execute },
375
+ restore: { description: "Restore the system from a backup", run: restoreCmd.execute }
376
+ }
377
+ });
378
+ var run = cmd.execute;
379
+ export {
380
+ run
381
+ };
@@ -12,7 +12,7 @@ var cmd = subcommands({
12
12
  commands: {
13
13
  send: {
14
14
  description: "Send a message",
15
- run: (args) => import("./send-O6W53VBM.js").then((m) => m.run(args))
15
+ run: (args) => import("./send-AAIOPJAV.js").then((m) => m.run(args))
16
16
  },
17
17
  list: {
18
18
  description: "List conversations",
@@ -32,7 +32,7 @@ var cmd = subcommands({
32
32
  },
33
33
  files: {
34
34
  description: "List pending incoming files",
35
- run: (args) => import("./files-BQ2WB5KC.js").then((m) => m.run(args))
35
+ run: (args) => import("./files-4LN3EN7Q.js").then((m) => m.run(args))
36
36
  },
37
37
  accept: {
38
38
  description: "Accept a pending file",
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-T2TP6ZC6.js";
5
5
  import {
6
6
  mindDir
7
- } from "./chunk-H7IYFRAL.js";
7
+ } from "./chunk-ALKYQULU.js";
8
8
 
9
9
  // packages/daemon/src/lib/delivery/delivery-router.ts
10
10
  import { readFileSync, statSync } from "fs";
@@ -186,7 +186,8 @@ function resolveDeliveryMode(config, sessionName, rule) {
186
186
  delivery = {
187
187
  mode: "batch",
188
188
  debounce: sessionConfig.delivery.debounce ?? DEFAULT_BATCH_DEBOUNCE,
189
- maxWait: sessionConfig.delivery.maxWait ?? DEFAULT_BATCH_MAX_WAIT
189
+ maxWait: sessionConfig.delivery.maxWait ?? DEFAULT_BATCH_MAX_WAIT,
190
+ triggers: sessionConfig.delivery.triggers
190
191
  };
191
192
  }
192
193
  return {
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  voluteSystemDir
4
- } from "./chunk-H7IYFRAL.js";
4
+ } from "./chunk-ALKYQULU.js";
5
5
 
6
6
  // packages/daemon/src/lib/update-check.ts
7
7
  import { execFile as execFileCb, execFileSync } from "child_process";
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-T2TP6ZC6.js";
5
5
  import {
6
6
  getDb
7
- } from "./chunk-H7IYFRAL.js";
7
+ } from "./chunk-ALKYQULU.js";
8
8
  import {
9
9
  activity
10
10
  } from "./chunk-ZV7MFISX.js";
@@ -2,11 +2,11 @@
2
2
  import {
3
3
  exec,
4
4
  gitExec
5
- } from "./chunk-3ERIL673.js";
5
+ } from "./chunk-H2X54EHC.js";
6
6
  import {
7
7
  readGlobalConfig,
8
8
  writeGlobalConfig
9
- } from "./chunk-PNV24SEP.js";
9
+ } from "./chunk-F7H6ATM4.js";
10
10
  import {
11
11
  logger_default
12
12
  } from "./chunk-T2TP6ZC6.js";
@@ -15,7 +15,7 @@ import {
15
15
  mindDir,
16
16
  readRegistry,
17
17
  voluteHome
18
- } from "./chunk-H7IYFRAL.js";
18
+ } from "./chunk-ALKYQULU.js";
19
19
  import {
20
20
  sharedSkills
21
21
  } from "./chunk-ZV7MFISX.js";
@@ -46,7 +46,7 @@ async function initDefaultSkills() {
46
46
  const config = readGlobalConfig();
47
47
  let extensionSkills = [];
48
48
  try {
49
- const { getExtensionStandardSkills } = await import("./extensions-3QNDGW5E.js");
49
+ const { getExtensionStandardSkills } = await import("./extensions-JONTW2HF.js");
50
50
  extensionSkills = getExtensionStandardSkills();
51
51
  } catch (err) {
52
52
  logger_default.warn("failed to load extension standard skills during init", logger_default.errorData(err));
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  setBridgeConfig
4
- } from "./chunk-FHWUQZXE.js";
4
+ } from "./chunk-RXEEYDIB.js";
5
5
  import {
6
6
  readEnv,
7
7
  sharedEnvPath,
8
8
  writeEnv
9
- } from "./chunk-5GT7URXJ.js";
9
+ } from "./chunk-ZDZISKCL.js";
10
10
 
11
11
  // packages/daemon/src/lib/template/import-utils.ts
12
12
  import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from "fs";
@@ -6,7 +6,7 @@ import {
6
6
 
7
7
  // packages/daemon/src/lib/db.ts
8
8
  import { chmodSync, existsSync } from "fs";
9
- import { dirname as dirname2, resolve as resolve2 } from "path";
9
+ import { dirname, resolve as resolve2 } from "path";
10
10
  import { fileURLToPath as fileURLToPath2 } from "url";
11
11
  import { sql } from "drizzle-orm";
12
12
  import { drizzle } from "drizzle-orm/libsql";
@@ -15,13 +15,16 @@ import { migrate } from "drizzle-orm/libsql/migrator";
15
15
  // packages/daemon/src/lib/mind/registry.ts
16
16
  import { mkdirSync } from "fs";
17
17
  import { homedir } from "os";
18
- import { dirname, resolve } from "path";
18
+ import { resolve } from "path";
19
19
  import { fileURLToPath } from "url";
20
20
  import { and, eq, inArray, isNull } from "drizzle-orm";
21
+ var SPIRIT_NAME = "volute";
22
+ function isSpiritName(name) {
23
+ return name === SPIRIT_NAME;
24
+ }
21
25
  function voluteHome() {
22
26
  if (process.env.VOLUTE_HOME) return process.env.VOLUTE_HOME;
23
- const dir = dirname(fileURLToPath(import.meta.url));
24
- if (dir.endsWith("/src/lib")) {
27
+ if (fileURLToPath(import.meta.url).endsWith(".ts")) {
25
28
  throw new Error(
26
29
  'VOLUTE_HOME must be set when running from source. For tests, run via "npm test" or add "--import ./test/setup.ts".'
27
30
  );
@@ -200,7 +203,7 @@ function daemonLoopback() {
200
203
  }
201
204
 
202
205
  // packages/daemon/src/lib/db.ts
203
- var __dirname = dirname2(fileURLToPath2(import.meta.url));
206
+ var __dirname = dirname(fileURLToPath2(import.meta.url));
204
207
  var migrationsFolder = existsSync(resolve2(__dirname, "../drizzle")) ? resolve2(__dirname, "../drizzle") : existsSync(resolve2(__dirname, "../../drizzle")) ? resolve2(__dirname, "../../drizzle") : resolve2(__dirname, "../../../../drizzle");
205
208
  var db = null;
206
209
  var dbPromise = null;
@@ -246,6 +249,8 @@ async function getDb() {
246
249
 
247
250
  export {
248
251
  getDb,
252
+ SPIRIT_NAME,
253
+ isSpiritName,
249
254
  voluteHome,
250
255
  voluteUserHome,
251
256
  voluteSystemDir,
@@ -3,10 +3,10 @@ import {
3
3
  LAUNCHD_PLIST_LABEL,
4
4
  LAUNCHD_PLIST_PATH,
5
5
  USER_SYSTEMD_UNIT
6
- } from "./chunk-Q2XBKQ2D.js";
6
+ } from "./chunk-TQJ6ZYVY.js";
7
7
  import {
8
8
  resolveVoluteBin
9
- } from "./chunk-3ERIL673.js";
9
+ } from "./chunk-H2X54EHC.js";
10
10
 
11
11
  // packages/daemon/src/lib/config/service-install.ts
12
12
  import { execFile } from "child_process";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  readVoluteConfig
4
- } from "./chunk-CU6OFXMM.js";
4
+ } from "./chunk-PFZBNKUH.js";
5
5
  import {
6
6
  logger_default
7
7
  } from "./chunk-T2TP6ZC6.js";
@@ -9,7 +9,7 @@ import {
9
9
  mindDir,
10
10
  readAllMinds,
11
11
  voluteHome
12
- } from "./chunk-H7IYFRAL.js";
12
+ } from "./chunk-ALKYQULU.js";
13
13
 
14
14
  // packages/daemon/src/lib/util/avatar-image.ts
15
15
  import { readdir, readFile, writeFile } from "fs/promises";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  publish
4
- } from "./chunk-ZMIMSIIT.js";
4
+ } from "./chunk-7C44R625.js";
5
5
  import {
6
6
  logger_default
7
7
  } from "./chunk-T2TP6ZC6.js";