volute 0.49.0 → 0.50.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 +2 -2
- package/dist/{accept-KYNC5X3U.js → accept-5NK5XJB5.js} +2 -1
- package/dist/{activity-events-GJJAI2WT.js → activity-events-H7YI3BTF.js} +3 -3
- package/dist/{ai-service-A7FS7EC3.js → ai-service-V4CTNGI3.js} +5 -4
- package/dist/{archive-7E3YE6VF.js → archive-ORM3ENHI.js} +3 -3
- package/dist/{auth-ABYLOEZ7.js → auth-DT7GY7EZ.js} +10 -6
- package/dist/{avatar-image-6ATV2IOZ.js → avatar-image-QEPAP3A5.js} +3 -3
- package/dist/{backup-XYAMT4CU.js → backup-IE6PMXJO.js} +11 -12
- package/dist/{bridge-3FNSWLNQ.js → bridge-YK7NTRU5.js} +2 -1
- package/dist/{channels-SGQZHNQJ.js → channels-FSAHOQWI.js} +2 -1
- package/dist/{chat-OGXAEXMV.js → chat-W5K6HCBE.js} +9 -9
- package/dist/{chunk-3B427GUK.js → chunk-3BAFMGCJ.js} +2 -2
- package/dist/{chunk-4B62E4CG.js → chunk-4CN7OCJE.js} +2 -2
- package/dist/{chunk-VNRSXEBF.js → chunk-574M6I7F.js} +38 -5
- package/dist/{chunk-HCYQRME2.js → chunk-5FVANDON.js} +2 -2
- package/dist/{chunk-P4LHVRNE.js → chunk-5IIVOWSP.js} +27 -19
- package/dist/{chunk-TXJL36ZQ.js → chunk-5PKFJHCR.js} +2 -2
- package/dist/{chunk-JPGHSXSV.js → chunk-5TPC4KCV.js} +23 -9
- package/dist/{chunk-YIJGKHIS.js → chunk-632FJ23O.js} +24 -20
- package/dist/{chunk-IH5TKXX6.js → chunk-636GFQNE.js} +11 -11
- package/dist/{chunk-EFT2OALG.js → chunk-6BM2I736.js} +20 -5
- package/dist/{chunk-JCBPU57A.js → chunk-74T3CEXK.js} +6 -6
- package/dist/{chunk-EG5LSKHU.js → chunk-77IZUVBB.js} +2 -2
- package/dist/{chunk-K3JOLGWF.js → chunk-7DXLIYPY.js} +76 -63
- package/dist/chunk-7FDWPQCU.js +289 -0
- package/dist/chunk-7OZWKHA3.js +548 -0
- package/dist/{chunk-PY24ZL62.js → chunk-7UWXAJMK.js} +1 -1
- package/dist/{chunk-MG3Z34YE.js → chunk-7XDOTN4R.js} +1 -1
- package/dist/{chunk-SX72DOO3.js → chunk-AFSLNUTT.js} +1 -1
- package/dist/{chunk-DVN6DNMI.js → chunk-AJWFDIEY.js} +17 -2
- package/dist/chunk-ALIVXDM4.js +1014 -0
- package/dist/{chunk-RSBL3FAD.js → chunk-CAIYV33P.js} +234 -24
- package/dist/{chunk-ZX46PGCC.js → chunk-E5S3ILVA.js} +1 -1
- package/dist/{chunk-SHKM5RGD.js → chunk-EEOMA4DX.js} +1 -1
- package/dist/chunk-EQKSRVDE.js +2857 -0
- package/dist/chunk-FNJCBRD3.js +31 -0
- package/dist/{chunk-EADX54RG.js → chunk-HW6Q7AMB.js} +4 -4
- package/dist/chunk-JI67D7O2.js +1222 -0
- package/dist/{chunk-KHCKZ36O.js → chunk-KAEZOVP6.js} +1 -1
- package/dist/chunk-LNNI3MS4.js +54 -0
- package/dist/{chunk-BCG45AQB.js → chunk-M74I3HHF.js} +15 -5
- package/dist/chunk-MLZBBLMT.js +109 -0
- package/dist/{chunk-5T5YMX6S.js → chunk-PW65BXY5.js} +5 -0
- package/dist/{chunk-NOIAHMJB.js → chunk-QFVGPKJI.js} +1 -1
- package/dist/{chunk-VIUH5CHQ.js → chunk-SFRYRW37.js} +50 -33
- package/dist/{chunk-L27YYONJ.js → chunk-SSNZ6ZNN.js} +1 -1
- package/dist/{chunk-GWVFDUNB.js → chunk-TGJHGVBZ.js} +8 -10
- package/dist/{chunk-6HGEZJDH.js → chunk-U3EJJ7JN.js} +2 -2
- package/dist/{chunk-4DSGN4FL.js → chunk-UC6BNMGT.js} +43 -13
- package/dist/{chunk-JBIFXB6I.js → chunk-WBBKYUGF.js} +3 -5
- package/dist/{chunk-FBBS4BVR.js → chunk-X67VT4QO.js} +1 -1
- package/dist/{chunk-DAIIMBLB.js → chunk-YQOYZ6G7.js} +3 -5
- package/dist/{chunk-JCWB2ITW.js → chunk-ZUG76D3P.js} +35 -5
- package/dist/cli.js +34 -27
- package/dist/{clock-6JZL2FS6.js → clock-LH7UFNLJ.js} +4 -3
- package/dist/{cloud-sync-VFDTNNCA.js → cloud-sync-IDJFVDEP.js} +22 -19
- package/dist/{config-RPSLA2HX.js → config-UGJS57VT.js} +3 -3
- package/dist/{conversations-TUM5PJJ4.js → conversations-S2232Y5I.js} +8 -4
- package/dist/{create-Z5FSDNVZ.js → create-2M7DAHQX.js} +2 -2
- package/dist/{create-VIQVP3QB.js → create-M727VUDF.js} +2 -1
- package/dist/{daemon-client-ZFY5MOAL.js → daemon-client-VVYFZI2D.js} +2 -1
- package/dist/{daemon-restart-54L4B6AQ.js → daemon-restart-2AAGYFHE.js} +6 -7
- package/dist/daemon.js +1625 -529
- package/dist/{db-F6UMFRYD.js → db-QAY45Z56.js} +2 -2
- package/dist/{delete-ESEP7NGQ.js → delete-O7PEFQDF.js} +1 -1
- package/dist/delivery-manager-B4OKWD2P.js +33 -0
- package/dist/{delivery-router-VUOL5YSA.js → delivery-router-JE7SHYNU.js} +7 -5
- package/dist/{down-6X7KX7BZ.js → down-D4IRNEMJ.js} +5 -5
- package/dist/echo-text-WYAAODBV.js +39 -0
- package/dist/{env-ZYMSCB7B.js → env-ABM5IMA7.js} +3 -2
- package/dist/{env-KUL4XKN2.js → env-AZK5X4W7.js} +3 -3
- package/dist/{exec-FBXA3CM4.js → exec-2H65LMTA.js} +3 -3
- package/dist/{export-TNBPDH77.js → export-3E3EL6W3.js} +4 -4
- package/dist/{extension-J4ZQ2GHV.js → extension-MRYJWSMV.js} +2 -1
- package/dist/{extensions-AWNO3SZF.js → extensions-7KP3X6G7.js} +22 -19
- package/dist/{files-BVBRJJ2P.js → files-6BMKVG5G.js} +5 -4
- package/dist/{import-ZUNKMCKC.js → import-UPNTEBBT.js} +8 -8
- package/dist/{isolation-OPCZQRTH.js → isolation-OKDFM6WA.js} +5 -3
- package/dist/{join-34TR2GXQ.js → join-PYA36HEC.js} +1 -1
- package/dist/{list-PMUXZNAM.js → list-4BVGH7AN.js} +8 -3
- package/dist/{login-KAKNFCW5.js → login-MOXVLULE.js} +4 -3
- package/dist/{login-LKNLCJPA.js → login-RJJTCVR3.js} +2 -1
- package/dist/{logout-YZRMZ3O2.js → logout-BYV433X6.js} +2 -1
- package/dist/{logout-RLT62RQP.js → logout-DWD3OQVT.js} +4 -3
- package/dist/message-delivery-3456PXB7.js +43 -0
- package/dist/migrate-thread-config-4A5LW36Z.js +90 -0
- package/dist/{mind-IF6CGMFW.js → mind-NYRALSV2.js} +19 -19
- package/dist/{mind-activity-tracker-YMMERB6B.js → mind-activity-tracker-L7GCYXQR.js} +4 -4
- package/dist/{mind-contacts-BKUFPUCU.js → mind-contacts-QYU4TPEA.js} +3 -2
- package/dist/{mind-history-WI5OI3BW.js → mind-history-NXXOOTX6.js} +30 -7
- package/dist/{mind-list-OTHV4ZO4.js → mind-list-SYRA7CH5.js} +3 -2
- package/dist/{mind-profile-LUG353ZT.js → mind-profile-MRCVZPYT.js} +1 -1
- package/dist/mind-service-5TA7UJAT.js +47 -0
- package/dist/{mind-sleep-VBV7YRSU.js → mind-sleep-TH6BRQ5X.js} +4 -3
- package/dist/{mind-status-BJNPGXKC.js → mind-status-Z2LQFA6F.js} +3 -2
- package/dist/{mind-wake-LVTZ55EZ.js → mind-wake-LDGPRA42.js} +2 -1
- package/dist/{package-PD67VPHB.js → package-XYSV7NUT.js} +23 -23
- package/dist/{prompts-MDHDLWEX.js → prompts-QKFPX6DQ.js} +3 -3
- package/dist/{read-YSWUXYL7.js → read-ZXGUT4QC.js} +5 -3
- package/dist/{register-TWQDKQRT.js → register-OX4HRSGC.js} +2 -1
- package/dist/{registry-3ELJPUGJ.js → registry-4UF6OCOE.js} +6 -4
- package/dist/{reject-SXGCYN4A.js → reject-TWB4QJHG.js} +2 -1
- package/dist/{restart-5F5QDBOG.js → restart-AGEGNSYQ.js} +2 -1
- package/dist/{sandbox-XPYA7UIF.js → sandbox-EGEU5GGE.js} +7 -4
- package/dist/scheduler-UV6RUJ7P.js +25 -0
- package/dist/{schema-6YONVTKZ.js → schema-C2GIPD4V.js} +3 -3
- package/dist/{seed-NQED7PSX.js → seed-3IMBOAGK.js} +1 -1
- package/dist/{seed-check-TYD7RRZY.js → seed-check-NRJEQZST.js} +14 -5
- package/dist/{seed-cmd-QF3F2G4S.js → seed-cmd-U73XVQMI.js} +3 -3
- package/dist/{seed-create-XOO4N2OE.js → seed-create-FBLNVSUP.js} +4 -4
- package/dist/{seed-readiness-3LRYASA4.js → seed-readiness-QRA46ZJQ.js} +4 -5
- package/dist/{seed-sprout-HSBBGSS2.js → seed-sprout-3B4SZX7J.js} +9 -9
- package/dist/{send-IHRJYNXA.js → send-NBRZJXKX.js} +32 -10
- package/dist/{service-YCOPCTSZ.js → service-XMXAXGJD.js} +4 -4
- package/dist/{service-install-Y46OTF55.js → service-install-MOKTEG7B.js} +5 -5
- package/dist/{setup-TCN7VH5U.js → setup-CAJFOR2N.js} +10 -3
- package/dist/{setup-F4TE64YI.js → setup-JGYAXXW7.js} +6 -7
- package/dist/{skill-4CYJLYVY.js → skill-NXIHDQCG.js} +3 -2
- package/dist/skills/dreaming/references/INSTALL.md +2 -2
- package/dist/skills/imagegen/SKILL.md +21 -4
- package/dist/skills/imagegen/scripts/imagegen.ts +124 -21
- package/dist/skills/tending/SKILL.md +7 -7
- package/dist/skills/volute-mind/SKILL.md +3 -3
- package/dist/skills/volute-mind/references/integrations.md +2 -2
- package/dist/skills/volute-mind/references/routing.md +15 -15
- package/dist/{skills-NN5UZCUU.js → skills-5MSMT7PT.js} +4 -5
- package/dist/sleep-manager-LIEPO6WG.js +41 -0
- package/dist/{spirit-SSJMYC6A.js → spirit-IL5EMWYZ.js} +10 -7
- package/dist/spirit-availability-YFEGJNJ2.js +42 -0
- package/dist/{split-JF2VWTXA.js → split-BU5VWX6P.js} +1 -1
- package/dist/{sprout-AXA2SECQ.js → sprout-UPXGJ5HX.js} +1 -1
- package/dist/{src-YH6PQ7LJ.js → src-HGNE2XG4.js} +7 -1
- package/dist/{src-ZN4LXMPI.js → src-LCSOQY6Z.js} +57 -51
- package/dist/{src-JAOVXO5W.js → src-UDMJZ6MH.js} +8 -8
- package/dist/{start-BTVMTP3K.js → start-MUMNFR52.js} +2 -1
- package/dist/{status-VJGXMQ4L.js → status-IL67CYM4.js} +7 -6
- package/dist/{stop-YORESK4U.js → stop-EKERNYTX.js} +2 -1
- package/dist/system-events-A6IGCEC3.js +46 -0
- package/dist/system-install-YZSWCPDK.js +8 -0
- package/dist/{systems-5MH2ELQE.js → systems-VDRUZXNF.js} +5 -4
- package/dist/{tailscale-AMMVKDXB.js → tailscale-SJUW2ETR.js} +2 -2
- package/dist/{template-staleness-HKGCTS3H.js → template-staleness-T27VCGYA.js} +3 -3
- package/dist/{turn-tracker-JQ2YUUY2.js → turn-tracker-6EI2EL7G.js} +6 -6
- package/dist/{up-SDLTLPAO.js → up-CLL4665A.js} +5 -6
- package/dist/{update-WEA4N2TR.js → update-HVAUCQPN.js} +5 -5
- package/dist/{update-check-DZOTONII.js → update-check-2MGBBAFF.js} +3 -3
- package/dist/{upgrade-K5FIQPQD.js → upgrade-LIE46UBB.js} +1 -1
- package/dist/variant-cleanup-HJ7Z75R6.js +22 -0
- package/dist/{version-notify-WFA5XB5A.js → version-notify-7BUHWZNT.js} +10 -27
- package/dist/web-assets/assets/index-ClsUcXiW.js +95 -0
- package/dist/web-assets/assets/index-Dz3wTbtR.css +1 -0
- package/dist/web-assets/index.html +2 -2
- package/dist/xai-P42CRCZA.js +129 -0
- package/drizzle/0014_system_events.sql +26 -0
- package/drizzle/0015_rename_session_to_thread.sql +12 -0
- package/drizzle/0016_brain_to_human.sql +4 -0
- package/drizzle/0017_event_history_rows.sql +9 -0
- package/drizzle/meta/_journal.json +28 -0
- package/package.json +23 -23
- package/packages/extensions/notes/dist/ui/assets/index-BBs8pyel.js +67 -0
- package/packages/extensions/notes/dist/ui/index.html +1 -1
- package/packages/extensions/pages/dist/ui/assets/index-B589e8Xe.js +2 -0
- package/packages/extensions/pages/dist/ui/index.html +1 -1
- package/packages/extensions/plan/dist/ui/assets/index-B8s8wxyt.js +67 -0
- package/packages/extensions/plan/dist/ui/index.html +1 -1
- package/templates/_base/.init/.config/prompts.json +1 -0
- package/templates/_base/.init/.local/hooks/startup-context.ts +24 -9
- package/templates/_base/home/.config/routes.json +2 -2
- package/templates/_base/home/VOLUTE.md +18 -5
- package/templates/_base/src/lib/daemon-client.ts +1 -1
- package/templates/_base/src/lib/event-turn.ts +39 -0
- package/templates/_base/src/lib/format-prefix.ts +21 -2
- package/templates/_base/src/lib/router.ts +6 -2
- package/templates/_base/src/lib/routing.ts +8 -8
- package/templates/_base/src/lib/startup.ts +9 -1
- package/templates/_base/src/lib/types.ts +7 -1
- package/templates/_base/src/lib/volute-server.ts +30 -1
- package/templates/claude/.init/.config/routes.json +3 -3
- package/templates/claude/.init/CLAUDE.md +1 -1
- package/templates/claude/package.json.tmpl +4 -4
- package/templates/claude/src/agent.ts +4 -1
- package/templates/claude/src/lib/hooks/reply-instructions.ts +26 -1
- package/templates/codex/.init/.config/routes.json +3 -3
- package/templates/codex/.init/AGENTS.md +1 -1
- package/templates/codex/package.json.tmpl +4 -4
- package/templates/codex/src/agent.ts +11 -11
- package/templates/codex/src/lib/turn-context.ts +51 -0
- package/templates/pi/.init/.config/routes.json +3 -3
- package/templates/pi/.init/MINDS.md +1 -1
- package/templates/pi/package.json.tmpl +6 -6
- package/templates/pi/src/agent.ts +9 -1
- package/templates/pi/src/lib/event-handler.ts +1 -1
- package/templates/pi/src/lib/reply-instructions-extension.ts +28 -1
- package/dist/chunk-NJHN6IRB.js +0 -177
- package/dist/chunk-QQBOI3QA.js +0 -5345
- package/dist/chunk-XTEKINWG.js +0 -158
- package/dist/delivery-manager-RBXBPVWM.js +0 -36
- package/dist/echo-text-B6NZ5FGE.js +0 -36
- package/dist/message-delivery-ABPV76EA.js +0 -42
- package/dist/mind-service-42GDO4KH.js +0 -44
- package/dist/notices-N6BCGHFV.js +0 -25
- package/dist/scheduler-K7Y7Z7FQ.js +0 -34
- package/dist/sleep-manager-DSPXFHGX.js +0 -38
- package/dist/system-chat-M2XOBKEM.js +0 -40
- package/dist/variant-cleanup-P4MBNLJD.js +0 -33
- package/dist/web-assets/assets/index-BostMFoR.css +0 -1
- package/dist/web-assets/assets/index-CKlqUkyG.js +0 -92
- package/packages/extensions/notes/dist/ui/assets/index-D0-DgYyj.js +0 -67
- package/packages/extensions/pages/dist/ui/assets/index-DtzftuPA.js +0 -2
- package/packages/extensions/plan/dist/ui/assets/index-Cfzs-0G9.js +0 -67
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
buildMindBaseEnv,
|
|
4
|
+
clearJsonMap,
|
|
5
|
+
generateMindToken,
|
|
6
|
+
getMindToken,
|
|
7
|
+
loadJsonMap,
|
|
8
|
+
saveJsonMapAsync
|
|
9
|
+
} from "./chunk-ALIVXDM4.js";
|
|
10
|
+
import {
|
|
11
|
+
deliverEvent
|
|
12
|
+
} from "./chunk-7OZWKHA3.js";
|
|
13
|
+
import {
|
|
14
|
+
isSandboxEnabled,
|
|
15
|
+
wrapForSandbox
|
|
16
|
+
} from "./chunk-ZUG76D3P.js";
|
|
17
|
+
import {
|
|
18
|
+
readVoluteConfig,
|
|
19
|
+
writeVoluteConfig
|
|
20
|
+
} from "./chunk-WCFSBH33.js";
|
|
21
|
+
import {
|
|
22
|
+
exec
|
|
23
|
+
} from "./chunk-AJWFDIEY.js";
|
|
24
|
+
import {
|
|
25
|
+
loadMergedEnv
|
|
26
|
+
} from "./chunk-7UWXAJMK.js";
|
|
27
|
+
import {
|
|
28
|
+
logger_default
|
|
29
|
+
} from "./chunk-QUSMYGNQ.js";
|
|
30
|
+
import {
|
|
31
|
+
findMind,
|
|
32
|
+
mindDir,
|
|
33
|
+
mindTmpDir,
|
|
34
|
+
mindTmpEnv,
|
|
35
|
+
stateDir,
|
|
36
|
+
voluteSystemDir
|
|
37
|
+
} from "./chunk-CAIYV33P.js";
|
|
38
|
+
|
|
39
|
+
// packages/daemon/src/lib/daemon/scheduler.ts
|
|
40
|
+
import { resolve } from "path";
|
|
41
|
+
import { CronExpressionParser } from "cron-parser";
|
|
42
|
+
var slog = logger_default.child("scheduler");
|
|
43
|
+
var CATCHUP_STALE_MINUTES = 10;
|
|
44
|
+
var Scheduler = class {
|
|
45
|
+
schedules = /* @__PURE__ */ new Map();
|
|
46
|
+
mindDirs = /* @__PURE__ */ new Map();
|
|
47
|
+
// mindName → dir override
|
|
48
|
+
interval = null;
|
|
49
|
+
lastFired = /* @__PURE__ */ new Map();
|
|
50
|
+
// "mind:scheduleId" → epoch minute
|
|
51
|
+
get statePath() {
|
|
52
|
+
return resolve(voluteSystemDir(), "scheduler-state.json");
|
|
53
|
+
}
|
|
54
|
+
start() {
|
|
55
|
+
this.loadState();
|
|
56
|
+
this.interval = setInterval(() => {
|
|
57
|
+
this.tick().catch((err) => slog.error("scheduler tick failed", logger_default.errorData(err)));
|
|
58
|
+
}, 6e4);
|
|
59
|
+
}
|
|
60
|
+
stop() {
|
|
61
|
+
if (this.interval) clearInterval(this.interval);
|
|
62
|
+
}
|
|
63
|
+
loadState() {
|
|
64
|
+
this.lastFired = loadJsonMap(this.statePath);
|
|
65
|
+
}
|
|
66
|
+
async saveState() {
|
|
67
|
+
await saveJsonMapAsync(this.statePath, this.lastFired);
|
|
68
|
+
}
|
|
69
|
+
clearState() {
|
|
70
|
+
clearJsonMap(this.statePath, this.lastFired);
|
|
71
|
+
}
|
|
72
|
+
loadSchedules(mindName, dir) {
|
|
73
|
+
if (dir) this.mindDirs.set(mindName, dir);
|
|
74
|
+
const resolvedDir = this.mindDirs.get(mindName) ?? mindDir(mindName);
|
|
75
|
+
const config = readVoluteConfig(resolvedDir);
|
|
76
|
+
if (!config) return;
|
|
77
|
+
const schedules = config.schedules ?? [];
|
|
78
|
+
if (schedules.length > 0) {
|
|
79
|
+
this.schedules.set(mindName, schedules);
|
|
80
|
+
} else {
|
|
81
|
+
this.schedules.delete(mindName);
|
|
82
|
+
}
|
|
83
|
+
const epochMinute = Math.floor(Date.now() / 6e4);
|
|
84
|
+
const validKeys = new Set(schedules.map((s) => `${mindName}:${s.id}`));
|
|
85
|
+
let changed = false;
|
|
86
|
+
for (const key of this.lastFired.keys()) {
|
|
87
|
+
if (key.startsWith(`${mindName}:`) && !validKeys.has(key)) {
|
|
88
|
+
this.lastFired.delete(key);
|
|
89
|
+
changed = true;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
for (const key of validKeys) {
|
|
93
|
+
if (!this.lastFired.has(key)) {
|
|
94
|
+
this.lastFired.set(key, epochMinute);
|
|
95
|
+
changed = true;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (changed) {
|
|
99
|
+
this.saveState().catch(
|
|
100
|
+
(err) => slog.warn(`failed to persist scheduler state for ${mindName}`, logger_default.errorData(err))
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
unloadSchedules(mindName) {
|
|
105
|
+
this.schedules.delete(mindName);
|
|
106
|
+
this.mindDirs.delete(mindName);
|
|
107
|
+
}
|
|
108
|
+
async tick() {
|
|
109
|
+
const now = /* @__PURE__ */ new Date();
|
|
110
|
+
const epochMinute = Math.floor(now.getTime() / 6e4);
|
|
111
|
+
const cronCache = /* @__PURE__ */ new Map();
|
|
112
|
+
let anyFired = false;
|
|
113
|
+
for (const [mind, schedules] of this.schedules) {
|
|
114
|
+
for (const schedule of schedules) {
|
|
115
|
+
if (!schedule.enabled) continue;
|
|
116
|
+
if (this.shouldFire(schedule, epochMinute, mind, cronCache)) {
|
|
117
|
+
anyFired = true;
|
|
118
|
+
this.fire(mind, schedule);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (anyFired) await this.saveState();
|
|
123
|
+
}
|
|
124
|
+
shouldFire(schedule, epochMinute, mind, cronCache) {
|
|
125
|
+
const key = `${mind}:${schedule.id}`;
|
|
126
|
+
if (this.lastFired.get(key) === epochMinute) return false;
|
|
127
|
+
if (schedule.fireAt) {
|
|
128
|
+
const fireTime = Math.floor(new Date(schedule.fireAt).getTime() / 6e4);
|
|
129
|
+
if (epochMinute >= fireTime) {
|
|
130
|
+
this.lastFired.set(key, epochMinute);
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
if (!schedule.cron) return false;
|
|
136
|
+
let prevMinute = cronCache.get(schedule.cron);
|
|
137
|
+
if (prevMinute === void 0) {
|
|
138
|
+
try {
|
|
139
|
+
const interval = CronExpressionParser.parse(schedule.cron);
|
|
140
|
+
const prev = interval.prev().toDate();
|
|
141
|
+
prevMinute = Math.floor(prev.getTime() / 6e4);
|
|
142
|
+
cronCache.set(schedule.cron, prevMinute);
|
|
143
|
+
} catch (err) {
|
|
144
|
+
slog.warn(`invalid cron "${schedule.cron}" for ${mind}:${schedule.id}`, logger_default.errorData(err));
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const lastFired = this.lastFired.get(key) ?? epochMinute;
|
|
149
|
+
if (prevMinute <= lastFired) return false;
|
|
150
|
+
this.lastFired.set(key, prevMinute);
|
|
151
|
+
if (epochMinute - prevMinute > CATCHUP_STALE_MINUTES) {
|
|
152
|
+
slog.info(`skipping stale catch-up fire for ${key} (${epochMinute - prevMinute}min late)`);
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
async fire(mindName, schedule) {
|
|
158
|
+
try {
|
|
159
|
+
let text;
|
|
160
|
+
if (schedule.script) {
|
|
161
|
+
const homeDir = resolve(this.mindDirs.get(mindName) ?? mindDir(mindName), "home");
|
|
162
|
+
try {
|
|
163
|
+
const output = await this.runScript(schedule.script, homeDir, mindName);
|
|
164
|
+
if (!output.trim()) {
|
|
165
|
+
slog.info(`fired script "${schedule.id}" for ${mindName} (no output)`);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
text = output;
|
|
169
|
+
} catch (err) {
|
|
170
|
+
const stderr = err.stderr ?? "";
|
|
171
|
+
text = `[script error] ${err.message}${stderr ? `
|
|
172
|
+
${stderr}` : ""}`;
|
|
173
|
+
slog.warn(`script "${schedule.id}" failed for ${mindName}`, logger_default.errorData(err));
|
|
174
|
+
}
|
|
175
|
+
} else if (Array.isArray(schedule.messages) && schedule.messages.length > 0) {
|
|
176
|
+
const pick = schedule.messages[Math.floor(Math.random() * schedule.messages.length)];
|
|
177
|
+
if (typeof pick !== "string" || !pick.trim()) {
|
|
178
|
+
slog.warn(`schedule "${schedule.id}" for ${mindName} has a malformed messages pool`);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
text = pick;
|
|
182
|
+
} else if (schedule.message) {
|
|
183
|
+
text = schedule.message;
|
|
184
|
+
} else {
|
|
185
|
+
slog.warn(`schedule "${schedule.id}" for ${mindName} has no message, messages, or script`);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
const { id, delivered } = await this.deliverSystem(mindName, schedule.id, text, {
|
|
189
|
+
// Default schedule fires to "queue" while asleep so an unadorned cron
|
|
190
|
+
// schedule doesn't inherit the DM wake-trigger fallback and wake the mind.
|
|
191
|
+
whileSleeping: schedule.whileSleeping ?? "queue",
|
|
192
|
+
thread: schedule.thread
|
|
193
|
+
});
|
|
194
|
+
slog.info(`fired "${schedule.id}" for ${mindName}${delivered ? "" : " (event pending)"}`);
|
|
195
|
+
if (schedule.fireAt && id != null) {
|
|
196
|
+
this.removeSchedule(mindName, schedule.id);
|
|
197
|
+
}
|
|
198
|
+
} catch (err) {
|
|
199
|
+
slog.warn(`failed to fire "${schedule.id}" for ${mindName}`, logger_default.errorData(err));
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
removeSchedule(mindName, scheduleId) {
|
|
203
|
+
const memSchedules = this.schedules.get(mindName);
|
|
204
|
+
if (memSchedules) {
|
|
205
|
+
const filtered = memSchedules.filter((s) => s.id !== scheduleId);
|
|
206
|
+
if (filtered.length > 0) {
|
|
207
|
+
this.schedules.set(mindName, filtered);
|
|
208
|
+
} else {
|
|
209
|
+
this.schedules.delete(mindName);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
try {
|
|
213
|
+
const dir = this.mindDirs.get(mindName) ?? mindDir(mindName);
|
|
214
|
+
const config = readVoluteConfig(dir);
|
|
215
|
+
if (!config?.schedules) return;
|
|
216
|
+
config.schedules = config.schedules.filter((s) => s.id !== scheduleId);
|
|
217
|
+
if (config.schedules.length === 0) config.schedules = void 0;
|
|
218
|
+
writeVoluteConfig(dir, config);
|
|
219
|
+
slog.info(`removed one-time schedule "${scheduleId}" for ${mindName}`);
|
|
220
|
+
} catch (err) {
|
|
221
|
+
slog.error(
|
|
222
|
+
`failed to persist removal of schedule "${scheduleId}" for ${mindName} (removed from memory)`,
|
|
223
|
+
logger_default.errorData(err)
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
async runScript(script, cwd, mindName) {
|
|
228
|
+
const env = await this.buildScriptEnv(mindName);
|
|
229
|
+
if (isSandboxEnabled()) {
|
|
230
|
+
const dir = this.mindDirs.get(mindName) ?? mindDir(mindName);
|
|
231
|
+
const [cmd, args] = await wrapForSandbox("bash", ["-c", script], dir, mindName, [
|
|
232
|
+
dir,
|
|
233
|
+
mindTmpDir(dir)
|
|
234
|
+
]);
|
|
235
|
+
return exec(cmd, args, { cwd, env });
|
|
236
|
+
}
|
|
237
|
+
return exec("bash", ["-c", script], { cwd, mindName, env });
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Build the environment for a scheduled script — mirrors the mind process env
|
|
241
|
+
* (allowlisted base + merged mind env + VOLUTE_* runtime vars), authenticated
|
|
242
|
+
* with the mind's own per-mind, non-admin token. Reuses the running mind's
|
|
243
|
+
* token when present; otherwise mints one (stable across fires, regenerated on
|
|
244
|
+
* next mind start). The daemon admin token is never included.
|
|
245
|
+
*/
|
|
246
|
+
async buildScriptEnv(mindName) {
|
|
247
|
+
const dir = this.mindDirs.get(mindName) ?? mindDir(mindName);
|
|
248
|
+
const entry = await findMind(mindName);
|
|
249
|
+
const token = getMindToken(mindName) ?? generateMindToken(mindName);
|
|
250
|
+
const mindLocalBin = resolve(dir, "home", ".local", "bin");
|
|
251
|
+
const currentPath = process.env.PATH ?? "";
|
|
252
|
+
return {
|
|
253
|
+
...buildMindBaseEnv(),
|
|
254
|
+
...loadMergedEnv(mindName),
|
|
255
|
+
VOLUTE_MIND: mindName,
|
|
256
|
+
VOLUTE_STATE_DIR: stateDir(mindName),
|
|
257
|
+
VOLUTE_MIND_DIR: dir,
|
|
258
|
+
VOLUTE_MIND_PORT: entry ? String(entry.port) : void 0,
|
|
259
|
+
VOLUTE_MIND_TOKEN: token,
|
|
260
|
+
...mindTmpEnv(dir),
|
|
261
|
+
PATH: `${mindLocalBin}:${currentPath}`
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
async deliverSystem(mindName, scheduleId, text, opts) {
|
|
265
|
+
return deliverEvent(mindName, {
|
|
266
|
+
type: "schedule",
|
|
267
|
+
body: text,
|
|
268
|
+
meta: { scheduleId },
|
|
269
|
+
thread: opts?.thread,
|
|
270
|
+
whileSleeping: opts?.whileSleeping
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
var instance = null;
|
|
275
|
+
function initScheduler() {
|
|
276
|
+
if (instance) throw new Error("Scheduler already initialized");
|
|
277
|
+
instance = new Scheduler();
|
|
278
|
+
return instance;
|
|
279
|
+
}
|
|
280
|
+
function getScheduler() {
|
|
281
|
+
if (!instance) throw new Error("Scheduler not initialized \u2014 call initScheduler() first");
|
|
282
|
+
return instance;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export {
|
|
286
|
+
Scheduler,
|
|
287
|
+
initScheduler,
|
|
288
|
+
getScheduler
|
|
289
|
+
};
|