volute 0.33.0 → 0.35.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 +7 -6
- package/dist/accept-ZBDVVCEU.js +42 -0
- package/dist/activity-events-ZW4SDL2C.js +15 -0
- package/dist/{ai-service-SBY2WG7O.js → ai-service-LURBEDDB.js} +6 -6
- package/dist/{api-client-YPKOZP2O.js → api-client-3A77HMH7.js} +2 -2
- package/dist/api.d.ts +1 -5195
- package/dist/{archive-INXYFVCW.js → archive-ESU2FUN4.js} +4 -4
- package/dist/{auth-GKCDSO4T.js → auth-WX4TESEI.js} +6 -6
- package/dist/bridge-PXIO6PS2.js +206 -0
- package/dist/chat-QXAJF3FU.js +51 -0
- package/dist/{chunk-NNB4WIG7.js → chunk-2TGZJFAT.js} +3 -3
- package/dist/{chunk-6LXAAQ43.js → chunk-33ODGMFZ.js} +1 -1
- package/dist/{chunk-RPZZSXV3.js → chunk-5N7Y5WAM.js} +21 -2
- package/dist/chunk-5T5YMX6S.js +23 -0
- package/dist/{chunk-7J3HEVR7.js → chunk-5XJYUFZH.js} +28 -16
- package/dist/chunk-7KJOFUNN.js +22 -0
- package/dist/{chunk-2NGTS5UU.js → chunk-A2ZLHBHG.js} +2 -2
- package/dist/{chunk-KIEPMIM5.js → chunk-AN2W47GW.js} +2 -2
- package/dist/{chunk-G53F3JA4.js → chunk-AOB6GVRM.js} +1 -1
- package/dist/{chunk-LRCG2JLP.js → chunk-BDYXIWA5.js} +9 -5
- package/dist/{chunk-YUIHSKR6.js → chunk-BKF4WQCY.js} +2 -2
- package/dist/{chunk-N432I7QH.js → chunk-BMZQYACC.js} +2 -2
- package/dist/{chunk-NAOW2CLO.js → chunk-BTY4WNFE.js} +1 -1
- package/dist/{chunk-ALEF47VT.js → chunk-BV65KRHM.js} +2 -2
- package/dist/{chunk-KVK2DLWI.js → chunk-CORXD635.js} +4 -4
- package/dist/{chunk-PVY5W6QN.js → chunk-F7ZNLYKZ.js} +2 -2
- package/dist/{chunk-QTUVYI7W.js → chunk-FT5KETXZ.js} +3 -3
- package/dist/{chunk-C7I35G4R.js → chunk-IJHIXLVN.js} +44 -8
- package/dist/{chunk-JUKK7FPS.js → chunk-J6CJQDWI.js} +37 -28
- package/dist/{chunk-4RQBJWQX.js → chunk-LOPXTW6H.js} +1 -1
- package/dist/{chunk-RSX4OPZY.js → chunk-MDJGMOSD.js} +8 -137
- package/dist/{chunk-LOEJ4HPQ.js → chunk-N446KRP7.js} +3 -3
- package/dist/{chunk-I5KY25PQ.js → chunk-N5LMGYXX.js} +2 -2
- package/dist/{chunk-G6BSYHPK.js → chunk-NJK5SDGR.js} +1 -1
- package/dist/{chunk-D424ZQGI.js → chunk-O7IGP7ZW.js} +11 -3
- package/dist/{chunk-M7UL5S3Q.js → chunk-OTC67N2Z.js} +2 -2
- package/dist/{chunk-GY5HBI7A.js → chunk-PWQ2ITYG.js} +4 -4
- package/dist/{chunk-KTLFDYPT.js → chunk-QCH6K235.js} +1 -1
- package/dist/chunk-QHG4OMZL.js +145 -0
- package/dist/{chunk-SKLSMHXO.js → chunk-QWTR6AWZ.js} +3 -3
- package/dist/chunk-TXSA4Q3V.js +116 -0
- package/dist/{chunk-VH33ZWMW.js → chunk-VHJRZM2S.js} +2 -2
- package/dist/{chunk-SSI47XP2.js → chunk-VHWGEJ4V.js} +1 -1
- package/dist/chunk-VY3RB2V7.js +164 -0
- package/dist/chunk-WJPROOU5.js +8314 -0
- package/dist/{chunk-RVGLDGMI.js → chunk-WZRZFFCL.js} +25 -27
- package/dist/{chunk-JYVGHWEJ.js → chunk-XRQSAMX2.js} +4 -4
- package/dist/{chunk-OYAKCAVY.js → chunk-ZSR72JB3.js} +1 -1
- package/dist/{chunk-UKVWJRKN.js → chunk-ZX7EAV5J.js} +17 -7
- package/dist/cli.js +90 -29
- package/dist/clock-HSEKS5AR.js +289 -0
- package/dist/{cloud-sync-4NWLMFVH.js → cloud-sync-6JL4C24T.js} +22 -23
- package/dist/config-UTS7QULS.js +76 -0
- package/dist/connectors/discord-bridge.js +4 -4
- package/dist/connectors/slack-bridge.js +4 -4
- package/dist/connectors/telegram-bridge.js +4 -4
- package/dist/{conversations-AWI5SZW2.js → conversations-2PW57WO2.js} +6 -6
- package/dist/create-5BPOOJAN.js +75 -0
- package/dist/create-UVCK2CS6.js +50 -0
- package/dist/daemon-client-RVIKXGFQ.js +12 -0
- package/dist/daemon-restart-HSZ3BCX5.js +65 -0
- package/dist/daemon.js +1349 -1211
- package/dist/db-BDMH4SZ2.js +20 -0
- package/dist/db-BVBJ57TU.js +9 -0
- package/dist/delete-L5PAVDGQ.js +42 -0
- package/dist/delivery-manager-H5ZVBMCQ.js +31 -0
- package/dist/{delivery-router-FL45JL7N.js → delivery-router-HEJSJAHQ.js} +5 -5
- package/dist/down-74VXM45A.js +17 -0
- package/dist/env-E4XHO2BI.js +223 -0
- package/dist/exec-PY7THYH4.js +17 -0
- package/dist/export-OAS6QVBN.js +113 -0
- package/dist/extension-D74CNM7G.js +89 -0
- package/dist/extensions-XDDFY72A.js +49 -0
- package/dist/files-CWTK6V3H.js +53 -0
- package/dist/import-5A3T7QV4.js +143 -0
- package/dist/{isolation-LLAYQYDY.js → isolation-TK5RX2WM.js} +4 -4
- package/dist/join-DF5XSJAC.js +67 -0
- package/dist/lib-DYEZMGW7.js +6588 -0
- package/dist/list-PDMQM7ZV.js +53 -0
- package/dist/login-7TE6CIZF.js +60 -0
- package/dist/login-GOTAYLXP.js +51 -0
- package/dist/logout-6KIA74EV.js +29 -0
- package/dist/logout-T4XS6LRU.js +50 -0
- package/dist/message-delivery-GRC4W6P7.js +41 -0
- package/dist/mind-5IEYKV7I.js +97 -0
- package/dist/mind-activity-tracker-QBLIV7ZJ.js +18 -0
- package/dist/mind-history-IE2QH7U5.js +275 -0
- package/dist/mind-list-GEWHWAL4.js +38 -0
- package/dist/mind-manager-HFLB5653.js +31 -0
- package/dist/mind-profile-DCBDVF5B.js +53 -0
- package/dist/mind-service-X2CAA6W6.js +37 -0
- package/dist/mind-sleep-ITCF6OQA.js +47 -0
- package/dist/mind-status-X4SX3YUG.js +65 -0
- package/dist/mind-wake-KXMKMGWX.js +42 -0
- package/dist/{package-U3VFO273.js → package-D2FSVFAX.js} +11 -8
- package/dist/read-67VRP2DO.js +91 -0
- package/dist/{read-stdin-HQJ7774D.js → read-stdin-3X5VYKNS.js} +2 -2
- package/dist/register-SB7NXCOE.js +51 -0
- package/dist/{registry-PJ4S5PHQ.js → registry-GBSNW3HG.js} +3 -3
- package/dist/reject-MUR2KWJ4.js +40 -0
- package/dist/restart-5EGG4JXU.js +42 -0
- package/dist/{sandbox-GJOK4QLQ.js → sandbox-R37VIU36.js} +6 -6
- package/dist/scheduler-Y7O4CJXL.js +31 -0
- package/dist/{schema-PA3M5ZKH.js → schema-XVZ2CLKW.js} +4 -2
- package/dist/{seed-QDYVLG74.js → seed-EQORWX77.js} +3 -3
- package/dist/seed-check-KJNTL72M.js +35 -0
- package/dist/seed-cmd-ZM2XGVU2.js +30 -0
- package/dist/seed-create-DRWGGHEI.js +113 -0
- package/dist/seed-sprout-JYXGXOP3.js +148 -0
- package/dist/send-JBJJQ7CA.js +409 -0
- package/dist/service-WNPCNHOX.js +121 -0
- package/dist/{setup-XMCBE3LF.js → setup-BJ4YAY26.js} +155 -129
- package/dist/{setup-TISPCO22.js → setup-RHJRFURI.js} +4 -4
- package/dist/skill-TAAKEYBV.js +389 -0
- package/dist/skills/plan-coordinator/SKILL.md +60 -0
- package/dist/skills/volute-mind/SKILL.md +9 -227
- package/dist/skills/volute-mind/references/extensions.md +34 -0
- package/dist/skills/volute-mind/references/integrations.md +48 -0
- package/dist/skills/volute-mind/references/routing.md +86 -0
- package/dist/skills/volute-mind/references/sleep.md +33 -0
- package/dist/skills/volute-mind/references/variants.md +31 -0
- package/dist/{skills-7FV7EJTE.js → skills-EKMCQ46K.js} +12 -8
- package/dist/sleep-manager-7KFK3USC.js +35 -0
- package/dist/spirit-ZFRDXMG7.js +23 -0
- package/dist/split-AWVOYOPZ.js +64 -0
- package/dist/{sprout-WKLZXUIQ.js → sprout-HE4TITMK.js} +3 -3
- package/dist/start-3UXOPXQG.js +39 -0
- package/dist/status-ZK34WYIM.js +125 -0
- package/dist/stop-3XYIBGFM.js +41 -0
- package/dist/system-chat-IDPHYHY4.js +35 -0
- package/dist/systems-O43WGQY6.js +52 -0
- package/dist/{tailscale-XHQBZROW.js → tailscale-ZIZ2HWJ5.js} +5 -5
- package/dist/template-hash-A7FNHTB7.js +9 -0
- package/dist/up-77ICEDEW.js +19 -0
- package/dist/update-ANE5ZM7F.js +225 -0
- package/dist/{update-check-ZD6OOIYQ.js → update-check-UV55CBEP.js} +4 -4
- package/dist/upgrade-ZMDGC7M2.js +74 -0
- package/dist/variant-QWL2WSRI.js +62 -0
- package/dist/{version-notify-NBI2MTJO.js → version-notify-FXSEMXWW.js} +29 -28
- package/dist/{volute-config-HD7WWUQC.js → volute-config-D2XVS2YI.js} +2 -2
- package/dist/web-assets/assets/index-BhxWKvbB.css +1 -0
- package/dist/web-assets/assets/index-CHVKJ9II.js +75 -0
- package/dist/web-assets/ext-theme.css +48 -9
- package/dist/web-assets/index.html +2 -2
- package/dist/web-assets/sw.js +117 -0
- package/drizzle/0005_meta_summaries.sql +15 -0
- package/drizzle/meta/0005_snapshot.json +7 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +10 -7
- package/packages/extensions/pages/dist/ui/assets/index-DKZLNMED.js +2 -0
- package/packages/extensions/pages/dist/ui/index.html +1 -1
- package/packages/extensions/pages/skills/pages/SKILL.md +84 -9
- package/packages/extensions/plan/dist/ui/assets/index-CJj2gZnZ.css +1 -0
- package/packages/extensions/plan/dist/ui/assets/index-FMEJmvQz.js +61 -0
- package/packages/extensions/plan/dist/ui/index.html +14 -0
- package/packages/extensions/plan/skills/plan/SKILL.md +43 -0
- package/packages/extensions/plan/skills/plan/scripts/plan-hook.sh +37 -0
- package/templates/_base/home/VOLUTE.md +12 -19
- package/templates/_base/src/lib/auto-commit.ts +8 -8
- package/templates/_base/src/lib/context-breakdown.ts +450 -0
- package/templates/_base/src/lib/format-prefix.ts +17 -0
- package/templates/_base/src/lib/hook-loader.ts +8 -2
- package/templates/_base/src/lib/router.ts +75 -33
- package/templates/_base/src/lib/routing.ts +4 -1
- package/templates/_base/src/lib/startup.ts +16 -8
- package/templates/_base/src/lib/types.ts +2 -1
- package/templates/_base/src/lib/volute-server.ts +75 -8
- package/templates/claude/.init/CLAUDE.md +4 -10
- package/templates/claude/package.json.tmpl +1 -0
- package/templates/claude/src/agent.ts +108 -33
- package/templates/claude/src/lib/hooks/reply-instructions.ts +27 -7
- package/templates/claude/src/lib/stream-consumer.ts +2 -2
- package/templates/claude/src/server.ts +1 -0
- package/templates/codex/package.json.tmpl +1 -0
- package/templates/codex/src/agent.ts +80 -8
- package/templates/codex/src/server.ts +1 -4
- package/templates/pi/package.json.tmpl +1 -0
- package/templates/pi/src/agent.ts +115 -36
- package/templates/pi/src/lib/event-handler.ts +22 -7
- package/templates/pi/src/lib/reply-instructions-extension.ts +23 -4
- package/templates/pi/src/lib/subagents.ts +20 -17
- package/templates/pi/src/server.ts +2 -5
- package/dist/accept-D5VBM7JW.js +0 -42
- package/dist/activity-events-XJO3P4RR.js +0 -15
- package/dist/bridge-TXWWPPOJ.js +0 -207
- package/dist/chat-U5ZOME3O.js +0 -68
- package/dist/chunk-3Z2DPESO.js +0 -3634
- package/dist/chunk-A2A4KLFE.js +0 -1528
- package/dist/chunk-K3NQKI34.js +0 -10
- package/dist/chunk-NPKSDYA2.js +0 -156
- package/dist/chunk-PB65JZK2.js +0 -85
- package/dist/clock-BVH3V6E3.js +0 -266
- package/dist/config-H2H4UIF7.js +0 -72
- package/dist/create-2FK7Z46Y.js +0 -44
- package/dist/create-YWD2TIP4.js +0 -71
- package/dist/daemon-client-6QXHZ7US.js +0 -12
- package/dist/daemon-restart-GOBUKLX7.js +0 -52
- package/dist/db-F34YLV7D.js +0 -9
- package/dist/db-RA45JBFG.js +0 -16
- package/dist/delete-QTGWEDBI.js +0 -35
- package/dist/delivery-manager-PFAKEJTC.js +0 -32
- package/dist/down-FWWTEKXM.js +0 -15
- package/dist/env-JCOF2222.js +0 -191
- package/dist/export-SUYRLI5Q.js +0 -112
- package/dist/extension-OBTGKQQD.js +0 -175
- package/dist/extensions-KYNTVTMO.js +0 -30
- package/dist/files-65PMW5IK.js +0 -47
- package/dist/history-DKCDI3JO.js +0 -128
- package/dist/import-DDUFE7AY.js +0 -23
- package/dist/join-I5QEE3LG.js +0 -66
- package/dist/list-JQ463EDA.js +0 -41
- package/dist/login-D7ETSU4R.js +0 -47
- package/dist/login-RIJF2F4G.js +0 -47
- package/dist/logout-5MLHZALK.js +0 -40
- package/dist/logout-UZJRGY4Z.js +0 -21
- package/dist/message-delivery-DFF5SJRM.js +0 -42
- package/dist/mind-IOJFLEM5.js +0 -108
- package/dist/mind-activity-tracker-F6O4Q2SL.js +0 -18
- package/dist/mind-list-WUPMQDYQ.js +0 -30
- package/dist/mind-manager-NBJF5D26.js +0 -32
- package/dist/mind-profile-P67FEHOY.js +0 -47
- package/dist/mind-service-2MQ6UK5N.js +0 -38
- package/dist/mind-sleep-WW2IX7JT.js +0 -42
- package/dist/mind-status-L3EFFRPR.js +0 -56
- package/dist/mind-wake-VSSGW465.js +0 -37
- package/dist/read-EBY56C33.js +0 -75
- package/dist/register-HD74C4TT.js +0 -47
- package/dist/reject-UJKFBHRO.js +0 -40
- package/dist/restart-3UCMRUVC.js +0 -33
- package/dist/scheduler-ZZ7XGQG6.js +0 -32
- package/dist/seed-check-S2IX25RL.js +0 -32
- package/dist/seed-cmd-DKOUFEAU.js +0 -36
- package/dist/seed-create-4XBBOLRH.js +0 -112
- package/dist/seed-sprout-GQEIIQRT.js +0 -132
- package/dist/send-QIV2INHB.js +0 -373
- package/dist/skill-PSQGRRJX.js +0 -358
- package/dist/skills/shared-files/SKILL.md +0 -44
- package/dist/skills/shared-files/scripts/merge.ts +0 -72
- package/dist/skills/shared-files/scripts/pull.ts +0 -52
- package/dist/sleep-manager-JTXSN7NV.js +0 -36
- package/dist/spirit-VRONKFMF.js +0 -23
- package/dist/split-STOROBYJ.js +0 -63
- package/dist/start-K2NCUUCG.js +0 -33
- package/dist/status-3JBTFSMI.js +0 -115
- package/dist/stop-H26JZDXF.js +0 -32
- package/dist/system-chat-JAPOJ3KE.js +0 -36
- package/dist/systems-XRI52VCH.js +0 -61
- package/dist/template-hash-A6VVKOXJ.js +0 -9
- package/dist/up-M5AS6SBV.js +0 -18
- package/dist/update-UD543CXX.js +0 -215
- package/dist/upgrade-O4Q7WJM3.js +0 -67
- package/dist/variant-7TGZHOU3.js +0 -41
- package/dist/web-assets/assets/index-CWJrVveV.css +0 -1
- package/dist/web-assets/assets/index-DJt14FRI.js +0 -75
- package/packages/extensions/pages/dist/ui/assets/index-tLTROSk5.js +0 -2
package/dist/daemon.js
CHANGED
|
@@ -3,10 +3,18 @@ import {
|
|
|
3
3
|
checkForUpdate,
|
|
4
4
|
checkForUpdateCached,
|
|
5
5
|
getCurrentVersion
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-OTC67N2Z.js";
|
|
7
7
|
import {
|
|
8
8
|
computeTemplateHash
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-F7ZNLYKZ.js";
|
|
10
|
+
import {
|
|
11
|
+
acceptPending,
|
|
12
|
+
formatFileSize,
|
|
13
|
+
listPending,
|
|
14
|
+
rejectPending,
|
|
15
|
+
stageFile,
|
|
16
|
+
validateFilePath
|
|
17
|
+
} from "./chunk-BV65KRHM.js";
|
|
10
18
|
import {
|
|
11
19
|
PROMPT_DEFAULTS,
|
|
12
20
|
PROMPT_KEYS,
|
|
@@ -17,12 +25,16 @@ import {
|
|
|
17
25
|
completeOrphanedTurns,
|
|
18
26
|
completeTurn,
|
|
19
27
|
createTurn,
|
|
28
|
+
deleteSystemsConfig,
|
|
20
29
|
deliverMessage,
|
|
21
30
|
ensureSystemChannel,
|
|
22
31
|
generateSystemReply,
|
|
23
32
|
getActiveTurnId,
|
|
33
|
+
getAllDiscoveredExtensions,
|
|
24
34
|
getDeliveryManager,
|
|
35
|
+
getExtensionStandardSkills,
|
|
25
36
|
getLastToolUseEventId,
|
|
37
|
+
getLoadedExtensions,
|
|
26
38
|
getMindManager,
|
|
27
39
|
getMindPromptDefaults,
|
|
28
40
|
getPrompt,
|
|
@@ -36,33 +48,91 @@ import {
|
|
|
36
48
|
initMindManager,
|
|
37
49
|
initScheduler,
|
|
38
50
|
initSleepManager,
|
|
51
|
+
initSummarizer,
|
|
39
52
|
initTokenBudget,
|
|
53
|
+
installNpmExtension,
|
|
40
54
|
isConversationId,
|
|
41
55
|
joinSystemChannel,
|
|
42
56
|
linkToolResultToTurn,
|
|
57
|
+
loadAllExtensions,
|
|
58
|
+
notifyExtensionsDaemonStart,
|
|
59
|
+
notifyExtensionsDaemonStop,
|
|
43
60
|
publish as publish2,
|
|
44
61
|
publishTypingForChannels,
|
|
62
|
+
readSystemsConfig,
|
|
45
63
|
recordInbound,
|
|
46
64
|
recordOutbound,
|
|
47
65
|
resolveMindToken,
|
|
48
|
-
|
|
66
|
+
setExtensionEnabled,
|
|
49
67
|
startMindFull,
|
|
50
68
|
stopMindFull,
|
|
51
69
|
subscribe as subscribe3,
|
|
52
70
|
subscribeAll,
|
|
53
71
|
substitute,
|
|
72
|
+
summarizeOrphanedTurns,
|
|
73
|
+
summarizeTurn,
|
|
54
74
|
tagUntaggedInbound,
|
|
55
75
|
tagUntaggedOutbound,
|
|
56
|
-
trackToolUse
|
|
57
|
-
|
|
76
|
+
trackToolUse,
|
|
77
|
+
uninstallNpmExtension,
|
|
78
|
+
writeSystemsConfig
|
|
79
|
+
} from "./chunk-WJPROOU5.js";
|
|
80
|
+
import "./chunk-5XJYUFZH.js";
|
|
81
|
+
import {
|
|
82
|
+
applyInitFiles,
|
|
83
|
+
composeTemplate,
|
|
84
|
+
copyTemplateToDir,
|
|
85
|
+
findTemplatesRoot,
|
|
86
|
+
listFiles
|
|
87
|
+
} from "./chunk-AOB6GVRM.js";
|
|
88
|
+
import {
|
|
89
|
+
SEED_SKILLS,
|
|
90
|
+
STANDARD_SKILLS,
|
|
91
|
+
autoUpdateMindSkills,
|
|
92
|
+
getSharedSkill,
|
|
93
|
+
getStandardSkillsWithExtensions,
|
|
94
|
+
importSkillFromDir,
|
|
95
|
+
initDefaultSkills,
|
|
96
|
+
installSkill,
|
|
97
|
+
isAutoUpdateSkillsEnabled,
|
|
98
|
+
listFilesRecursive,
|
|
99
|
+
listMindSkills,
|
|
100
|
+
listSharedSkills,
|
|
101
|
+
publishSkill,
|
|
102
|
+
removeSharedSkill,
|
|
103
|
+
sharedSkillsDir,
|
|
104
|
+
syncBuiltinSkills,
|
|
105
|
+
uninstallSkill,
|
|
106
|
+
updateSkill
|
|
107
|
+
} from "./chunk-IJHIXLVN.js";
|
|
58
108
|
import {
|
|
59
109
|
extractTextContent
|
|
60
|
-
} from "./chunk-
|
|
110
|
+
} from "./chunk-QWTR6AWZ.js";
|
|
61
111
|
import {
|
|
62
112
|
getActiveMinds,
|
|
63
113
|
onMindEvent,
|
|
64
114
|
stopAll
|
|
65
|
-
} from "./chunk-
|
|
115
|
+
} from "./chunk-N446KRP7.js";
|
|
116
|
+
import "./chunk-QHG4OMZL.js";
|
|
117
|
+
import {
|
|
118
|
+
approveUser,
|
|
119
|
+
changePassword,
|
|
120
|
+
countAdmins,
|
|
121
|
+
createUser,
|
|
122
|
+
deleteMindUser as deleteMindUser2,
|
|
123
|
+
deleteUser,
|
|
124
|
+
getOrCreateMindUser,
|
|
125
|
+
getOrCreateSystemUser,
|
|
126
|
+
getUser,
|
|
127
|
+
getUserByUsername,
|
|
128
|
+
listPendingUsers,
|
|
129
|
+
listUsers,
|
|
130
|
+
listUsersByType,
|
|
131
|
+
setUserRole,
|
|
132
|
+
syncMindProfile,
|
|
133
|
+
updateUserProfile,
|
|
134
|
+
verifyUser
|
|
135
|
+
} from "./chunk-XRQSAMX2.js";
|
|
66
136
|
import {
|
|
67
137
|
addMessage,
|
|
68
138
|
createChannel,
|
|
@@ -90,28 +160,16 @@ import {
|
|
|
90
160
|
publish,
|
|
91
161
|
setConversationPrivate,
|
|
92
162
|
subscribe as subscribe2
|
|
93
|
-
} from "./chunk-
|
|
94
|
-
import "./chunk-GY5HBI7A.js";
|
|
163
|
+
} from "./chunk-WZRZFFCL.js";
|
|
95
164
|
import {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
rejectPending,
|
|
100
|
-
stageFile,
|
|
101
|
-
validateFilePath
|
|
102
|
-
} from "./chunk-ALEF47VT.js";
|
|
103
|
-
import "./chunk-7J3HEVR7.js";
|
|
104
|
-
import {
|
|
105
|
-
applyInitFiles,
|
|
106
|
-
composeTemplate,
|
|
107
|
-
copyTemplateToDir,
|
|
108
|
-
findTemplatesRoot,
|
|
109
|
-
listFiles
|
|
110
|
-
} from "./chunk-G53F3JA4.js";
|
|
165
|
+
broadcast,
|
|
166
|
+
subscribe
|
|
167
|
+
} from "./chunk-CORXD635.js";
|
|
111
168
|
import {
|
|
112
169
|
readVoluteConfig,
|
|
113
170
|
writeVoluteConfig
|
|
114
|
-
} from "./chunk-
|
|
171
|
+
} from "./chunk-ZSR72JB3.js";
|
|
172
|
+
import "./chunk-PWQ2ITYG.js";
|
|
115
173
|
import {
|
|
116
174
|
findBridgeForChannel,
|
|
117
175
|
findOpenClawSession,
|
|
@@ -125,70 +183,18 @@ import {
|
|
|
125
183
|
resolveChannelMapping,
|
|
126
184
|
setBridgeConfig,
|
|
127
185
|
setChannelMapping
|
|
128
|
-
} from "./chunk-
|
|
186
|
+
} from "./chunk-MDJGMOSD.js";
|
|
129
187
|
import {
|
|
130
188
|
loadMergedEnv,
|
|
131
189
|
mindEnvPath,
|
|
132
190
|
readEnv,
|
|
133
191
|
sharedEnvPath,
|
|
134
192
|
writeEnv
|
|
135
|
-
} from "./chunk-
|
|
193
|
+
} from "./chunk-A2ZLHBHG.js";
|
|
136
194
|
import {
|
|
137
195
|
isHomeOnlyArchive
|
|
138
|
-
} from "./chunk-
|
|
139
|
-
import {
|
|
140
|
-
deleteSystemsConfig,
|
|
141
|
-
getExtensionStandardSkills,
|
|
142
|
-
getLoadedExtensions,
|
|
143
|
-
loadAllExtensions,
|
|
144
|
-
notifyExtensionsDaemonStart,
|
|
145
|
-
notifyExtensionsDaemonStop,
|
|
146
|
-
readSystemsConfig,
|
|
147
|
-
writeSystemsConfig
|
|
148
|
-
} from "./chunk-A2A4KLFE.js";
|
|
149
|
-
import "./chunk-PB65JZK2.js";
|
|
150
|
-
import {
|
|
151
|
-
approveUser,
|
|
152
|
-
changePassword,
|
|
153
|
-
countAdmins,
|
|
154
|
-
createUser,
|
|
155
|
-
deleteMindUser as deleteMindUser2,
|
|
156
|
-
deleteUser,
|
|
157
|
-
getOrCreateMindUser,
|
|
158
|
-
getOrCreateSystemUser,
|
|
159
|
-
getUser,
|
|
160
|
-
getUserByUsername,
|
|
161
|
-
listPendingUsers,
|
|
162
|
-
listUsers,
|
|
163
|
-
listUsersByType,
|
|
164
|
-
setUserRole,
|
|
165
|
-
updateUserProfile,
|
|
166
|
-
verifyUser
|
|
167
|
-
} from "./chunk-JYVGHWEJ.js";
|
|
168
|
-
import {
|
|
169
|
-
broadcast,
|
|
170
|
-
subscribe
|
|
171
|
-
} from "./chunk-KVK2DLWI.js";
|
|
172
|
-
import {
|
|
173
|
-
SEED_SKILLS,
|
|
174
|
-
STANDARD_SKILLS,
|
|
175
|
-
getSharedSkill,
|
|
176
|
-
getStandardSkillsWithExtensions,
|
|
177
|
-
importSkillFromDir,
|
|
178
|
-
initDefaultSkills,
|
|
179
|
-
installSkill,
|
|
180
|
-
listFilesRecursive,
|
|
181
|
-
listMindSkills,
|
|
182
|
-
listSharedSkills,
|
|
183
|
-
publishSkill,
|
|
184
|
-
removeSharedSkill,
|
|
185
|
-
sharedSkillsDir,
|
|
186
|
-
syncBuiltinSkills,
|
|
187
|
-
uninstallSkill,
|
|
188
|
-
updateSkill
|
|
189
|
-
} from "./chunk-C7I35G4R.js";
|
|
196
|
+
} from "./chunk-N5LMGYXX.js";
|
|
190
197
|
import {
|
|
191
|
-
aiCompleteUtility,
|
|
192
198
|
getAiConfig,
|
|
193
199
|
getAvailableModels,
|
|
194
200
|
getConfiguredProviders,
|
|
@@ -203,38 +209,33 @@ import {
|
|
|
203
209
|
setEnabledModels,
|
|
204
210
|
setUtilityModel,
|
|
205
211
|
unqualifyModelId
|
|
206
|
-
} from "./chunk-
|
|
212
|
+
} from "./chunk-FT5KETXZ.js";
|
|
207
213
|
import {
|
|
208
214
|
logBuffer,
|
|
209
215
|
logger_default
|
|
210
|
-
} from "./chunk-
|
|
216
|
+
} from "./chunk-BKF4WQCY.js";
|
|
211
217
|
import {
|
|
212
218
|
exec,
|
|
213
219
|
gitExec,
|
|
214
220
|
resolveVoluteBin
|
|
215
|
-
} from "./chunk-
|
|
221
|
+
} from "./chunk-AN2W47GW.js";
|
|
216
222
|
import {
|
|
217
223
|
chownMindDir,
|
|
218
224
|
createMindUser,
|
|
219
225
|
deleteMindUser,
|
|
220
226
|
ensureVoluteGroup,
|
|
221
227
|
isIsolationEnabled,
|
|
222
|
-
mindUserName,
|
|
223
228
|
wrapForIsolation
|
|
224
|
-
} from "./chunk-
|
|
229
|
+
} from "./chunk-VHJRZM2S.js";
|
|
225
230
|
import {
|
|
226
231
|
isSetupComplete,
|
|
227
232
|
readGlobalConfig,
|
|
228
233
|
writeGlobalConfig
|
|
229
|
-
} from "./chunk-
|
|
230
|
-
import {
|
|
231
|
-
readSessionFile
|
|
232
|
-
} from "./chunk-UKVWJRKN.js";
|
|
233
|
-
import "./chunk-D424ZQGI.js";
|
|
234
|
+
} from "./chunk-BMZQYACC.js";
|
|
234
235
|
import {
|
|
235
236
|
buildVoluteSlug,
|
|
236
237
|
slugify
|
|
237
|
-
} from "./chunk-
|
|
238
|
+
} from "./chunk-NJK5SDGR.js";
|
|
238
239
|
import {
|
|
239
240
|
addMind,
|
|
240
241
|
addVariant,
|
|
@@ -257,34 +258,35 @@ import {
|
|
|
257
258
|
validateMindName,
|
|
258
259
|
voluteHome,
|
|
259
260
|
voluteSystemDir
|
|
260
|
-
} from "./chunk-
|
|
261
|
+
} from "./chunk-BDYXIWA5.js";
|
|
261
262
|
import {
|
|
262
263
|
activity,
|
|
264
|
+
conversationParticipants,
|
|
263
265
|
conversations,
|
|
264
|
-
messages,
|
|
265
266
|
mindHistory,
|
|
266
267
|
sessions,
|
|
268
|
+
summaries,
|
|
267
269
|
systemPrompts,
|
|
268
270
|
turns,
|
|
269
271
|
users
|
|
270
|
-
} from "./chunk-
|
|
272
|
+
} from "./chunk-5N7Y5WAM.js";
|
|
271
273
|
import {
|
|
272
274
|
__export
|
|
273
|
-
} from "./chunk-
|
|
275
|
+
} from "./chunk-7KJOFUNN.js";
|
|
274
276
|
|
|
275
|
-
// src/daemon.ts
|
|
277
|
+
// packages/daemon/src/daemon.ts
|
|
276
278
|
import { randomBytes } from "crypto";
|
|
277
|
-
import { mkdirSync as mkdirSync10, readFileSync as
|
|
279
|
+
import { mkdirSync as mkdirSync10, readFileSync as readFileSync11, unlinkSync as unlinkSync2, writeFileSync as writeFileSync9 } from "fs";
|
|
278
280
|
import { homedir as homedir3 } from "os";
|
|
279
|
-
import { resolve as
|
|
281
|
+
import { resolve as resolve17 } from "path";
|
|
280
282
|
import { format } from "util";
|
|
281
283
|
|
|
282
|
-
// src/lib/daemon/bridge-manager.ts
|
|
284
|
+
// packages/daemon/src/lib/daemon/bridge-manager.ts
|
|
283
285
|
import { spawn } from "child_process";
|
|
284
286
|
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, unlinkSync, writeFileSync } from "fs";
|
|
285
287
|
import { dirname, resolve as resolve2 } from "path";
|
|
286
288
|
|
|
287
|
-
// src/lib/bridge-defs.ts
|
|
289
|
+
// packages/daemon/src/lib/bridge-defs.ts
|
|
288
290
|
import { existsSync, readFileSync } from "fs";
|
|
289
291
|
import { resolve } from "path";
|
|
290
292
|
var BUILTIN_DEFS = {
|
|
@@ -346,7 +348,7 @@ function checkMissingBridgeEnv(def, env) {
|
|
|
346
348
|
return def.envVars.filter((v) => v.required && !env[v.name]);
|
|
347
349
|
}
|
|
348
350
|
|
|
349
|
-
// src/lib/daemon/bridge-manager.ts
|
|
351
|
+
// packages/daemon/src/lib/daemon/bridge-manager.ts
|
|
350
352
|
var blog = logger_default.child("bridges");
|
|
351
353
|
function searchUpwards(...segments) {
|
|
352
354
|
let searchDir = dirname(new URL(import.meta.url).pathname);
|
|
@@ -486,8 +488,8 @@ var BridgeManager = class {
|
|
|
486
488
|
if (!tracked) return;
|
|
487
489
|
this.stopping.add(platform);
|
|
488
490
|
this.bridges.delete(platform);
|
|
489
|
-
await new Promise((
|
|
490
|
-
tracked.child.on("exit", () =>
|
|
491
|
+
await new Promise((resolve18) => {
|
|
492
|
+
tracked.child.on("exit", () => resolve18());
|
|
491
493
|
try {
|
|
492
494
|
if (tracked.child.pid) {
|
|
493
495
|
process.kill(-tracked.child.pid, "SIGTERM");
|
|
@@ -498,7 +500,7 @@ var BridgeManager = class {
|
|
|
498
500
|
if (err instanceof Error && err.code !== "ESRCH") {
|
|
499
501
|
blog.warn(`failed to stop bridge ${platform}`, logger_default.errorData(err));
|
|
500
502
|
}
|
|
501
|
-
|
|
503
|
+
resolve18();
|
|
502
504
|
}
|
|
503
505
|
setTimeout(() => {
|
|
504
506
|
try {
|
|
@@ -509,7 +511,7 @@ var BridgeManager = class {
|
|
|
509
511
|
}
|
|
510
512
|
} catch {
|
|
511
513
|
}
|
|
512
|
-
|
|
514
|
+
resolve18();
|
|
513
515
|
}, 5e3);
|
|
514
516
|
});
|
|
515
517
|
this.stopping.delete(platform);
|
|
@@ -597,7 +599,7 @@ function getBridgeManager() {
|
|
|
597
599
|
return instance;
|
|
598
600
|
}
|
|
599
601
|
|
|
600
|
-
// src/lib/history-cleanup.ts
|
|
602
|
+
// packages/daemon/src/lib/history-cleanup.ts
|
|
601
603
|
import { and, eq, lt } from "drizzle-orm";
|
|
602
604
|
var LOG_RETENTION_MS = 24 * 60 * 60 * 1e3;
|
|
603
605
|
async function cleanExpiredLogs() {
|
|
@@ -606,173 +608,7 @@ async function cleanExpiredLogs() {
|
|
|
606
608
|
await db.delete(mindHistory).where(and(eq(mindHistory.type, "log"), lt(mindHistory.created_at, cutoff)));
|
|
607
609
|
}
|
|
608
610
|
|
|
609
|
-
// src/
|
|
610
|
-
import { execFileSync } from "child_process";
|
|
611
|
-
import { chmodSync, existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, rmSync, writeFileSync as writeFileSync2 } from "fs";
|
|
612
|
-
import { resolve as resolve3 } from "path";
|
|
613
|
-
function readWorktreeGitDir(worktreePath) {
|
|
614
|
-
const dotGit = resolve3(worktreePath, ".git");
|
|
615
|
-
if (!existsSync3(dotGit)) return null;
|
|
616
|
-
try {
|
|
617
|
-
const content = readFileSync3(dotGit, "utf-8").trim();
|
|
618
|
-
const match = content.match(/^gitdir:\s*(.+)$/);
|
|
619
|
-
return match ? match[1] : null;
|
|
620
|
-
} catch {
|
|
621
|
-
return null;
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
function sharedDir() {
|
|
625
|
-
return resolve3(voluteHome(), "shared");
|
|
626
|
-
}
|
|
627
|
-
async function ensureSharedRepo() {
|
|
628
|
-
const dir = sharedDir();
|
|
629
|
-
mkdirSync2(dir, { recursive: true });
|
|
630
|
-
if (existsSync3(resolve3(dir, ".git"))) {
|
|
631
|
-
try {
|
|
632
|
-
await gitExec(["rev-parse", "HEAD"], { cwd: dir });
|
|
633
|
-
return;
|
|
634
|
-
} catch (err) {
|
|
635
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
636
|
-
if (msg.includes("unknown revision") || msg.includes("bad default revision")) {
|
|
637
|
-
logger_default.warn("shared repo has no commits, re-initializing");
|
|
638
|
-
rmSync(resolve3(dir, ".git"), { recursive: true, force: true });
|
|
639
|
-
} else {
|
|
640
|
-
throw err;
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
const initArgs = isIsolationEnabled() ? ["init", "--shared=group"] : ["init"];
|
|
645
|
-
await gitExec(initArgs, { cwd: dir });
|
|
646
|
-
await gitExec(["checkout", "-b", "main"], { cwd: dir });
|
|
647
|
-
const pagesDir = resolve3(dir, "pages");
|
|
648
|
-
mkdirSync2(pagesDir, { recursive: true });
|
|
649
|
-
writeFileSync2(resolve3(pagesDir, ".gitkeep"), "");
|
|
650
|
-
await gitExec(["add", "-A"], { cwd: dir });
|
|
651
|
-
await gitExec(["commit", "-m", "init shared repo"], { cwd: dir });
|
|
652
|
-
if (isIsolationEnabled()) {
|
|
653
|
-
try {
|
|
654
|
-
execFileSync("chgrp", ["-R", "volute", dir], { stdio: "ignore" });
|
|
655
|
-
} catch (err) {
|
|
656
|
-
logger_default.warn("failed to chgrp shared repo to volute group", logger_default.errorData(err));
|
|
657
|
-
}
|
|
658
|
-
chmodSync(dir, 1533);
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
async function addSharedWorktree(mindName, mindDir2) {
|
|
662
|
-
const dir = sharedDir();
|
|
663
|
-
if (!existsSync3(resolve3(dir, ".git"))) return;
|
|
664
|
-
const worktreePath = resolve3(mindDir2, "home", "shared");
|
|
665
|
-
if (existsSync3(worktreePath)) return;
|
|
666
|
-
let branchExists = false;
|
|
667
|
-
try {
|
|
668
|
-
await gitExec(["rev-parse", "--verify", mindName], { cwd: dir });
|
|
669
|
-
branchExists = true;
|
|
670
|
-
} catch {
|
|
671
|
-
}
|
|
672
|
-
if (branchExists) {
|
|
673
|
-
await gitExec(["worktree", "add", worktreePath, mindName], { cwd: dir });
|
|
674
|
-
} else {
|
|
675
|
-
await gitExec(["worktree", "add", "-b", mindName, worktreePath], { cwd: dir });
|
|
676
|
-
}
|
|
677
|
-
if (isIsolationEnabled()) {
|
|
678
|
-
const worktreeGitDir = readWorktreeGitDir(worktreePath);
|
|
679
|
-
if (worktreeGitDir) {
|
|
680
|
-
try {
|
|
681
|
-
const user = mindUserName(mindName);
|
|
682
|
-
execFileSync("chown", ["-R", `${user}:volute`, worktreeGitDir], { stdio: "ignore" });
|
|
683
|
-
} catch (err) {
|
|
684
|
-
logger_default.warn(`failed to chown worktree git dir for ${mindName}`, logger_default.errorData(err));
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
async function removeSharedWorktree(mindName, mindDir2) {
|
|
690
|
-
const dir = sharedDir();
|
|
691
|
-
if (!existsSync3(resolve3(dir, ".git"))) return;
|
|
692
|
-
const worktreePath = resolve3(mindDir2, "home", "shared");
|
|
693
|
-
if (existsSync3(worktreePath)) {
|
|
694
|
-
try {
|
|
695
|
-
await gitExec(["worktree", "remove", "--force", worktreePath], { cwd: dir });
|
|
696
|
-
} catch (err) {
|
|
697
|
-
logger_default.debug(`worktree remove failed for ${mindName}`, logger_default.errorData(err));
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
try {
|
|
701
|
-
await gitExec(["worktree", "prune"], { cwd: dir });
|
|
702
|
-
} catch (err) {
|
|
703
|
-
logger_default.debug(`worktree prune failed for ${mindName}`, logger_default.errorData(err));
|
|
704
|
-
}
|
|
705
|
-
try {
|
|
706
|
-
await gitExec(["branch", "-D", mindName], { cwd: dir });
|
|
707
|
-
} catch {
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
var sharedLock = Promise.resolve();
|
|
711
|
-
function rechownWorktree(worktreePath, mindName) {
|
|
712
|
-
if (!isIsolationEnabled()) return;
|
|
713
|
-
try {
|
|
714
|
-
const user = mindUserName(mindName);
|
|
715
|
-
execFileSync("chown", ["-R", `${user}:volute`, worktreePath], { stdio: "ignore" });
|
|
716
|
-
} catch (err) {
|
|
717
|
-
logger_default.warn(`failed to rechown worktree for ${mindName}`, logger_default.errorData(err));
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
async function withSharedLock(fn) {
|
|
721
|
-
const prev = sharedLock;
|
|
722
|
-
let resolve_;
|
|
723
|
-
sharedLock = new Promise((r) => {
|
|
724
|
-
resolve_ = r;
|
|
725
|
-
});
|
|
726
|
-
await prev;
|
|
727
|
-
try {
|
|
728
|
-
return await fn();
|
|
729
|
-
} finally {
|
|
730
|
-
resolve_();
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
async function sharedMerge(mindName, mindDir2, message) {
|
|
734
|
-
return withSharedLock(async () => {
|
|
735
|
-
const dir = sharedDir();
|
|
736
|
-
const worktreePath = resolve3(mindDir2, "home", "shared");
|
|
737
|
-
const status = (await gitExec(["status", "--porcelain"], { cwd: worktreePath })).trim();
|
|
738
|
-
if (status) {
|
|
739
|
-
await gitExec(["add", "-A"], { cwd: worktreePath });
|
|
740
|
-
await gitExec(
|
|
741
|
-
["commit", "--author", `${mindName} <${mindName}@volute>`, "-m", `wip: ${mindName}`],
|
|
742
|
-
{ cwd: worktreePath }
|
|
743
|
-
);
|
|
744
|
-
}
|
|
745
|
-
const diff = (await gitExec(["diff", `main...${mindName}`, "--stat"], { cwd: dir })).trim();
|
|
746
|
-
if (!diff) {
|
|
747
|
-
return { ok: true, message: "Nothing to merge" };
|
|
748
|
-
}
|
|
749
|
-
try {
|
|
750
|
-
await gitExec(["merge", "--squash", mindName], { cwd: dir });
|
|
751
|
-
} catch {
|
|
752
|
-
try {
|
|
753
|
-
await gitExec(["reset", "--hard", "HEAD"], { cwd: dir });
|
|
754
|
-
} catch (resetErr) {
|
|
755
|
-
logger_default.error("reset after squash conflict failed in shared repo", logger_default.errorData(resetErr));
|
|
756
|
-
}
|
|
757
|
-
return { ok: false, conflicts: true, message: "Merge conflicts detected" };
|
|
758
|
-
}
|
|
759
|
-
await gitExec(["commit", "--author", `${mindName} <${mindName}@volute>`, "-m", message], {
|
|
760
|
-
cwd: dir
|
|
761
|
-
});
|
|
762
|
-
try {
|
|
763
|
-
await gitExec(["reset", "--hard", "main"], { cwd: worktreePath });
|
|
764
|
-
} catch {
|
|
765
|
-
return {
|
|
766
|
-
ok: true,
|
|
767
|
-
message: "Merged to main, but branch reset failed \u2014 run 'volute shared pull' to sync"
|
|
768
|
-
};
|
|
769
|
-
}
|
|
770
|
-
rechownWorktree(worktreePath, mindName);
|
|
771
|
-
return { ok: true };
|
|
772
|
-
});
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
// src/web/api/system.ts
|
|
611
|
+
// packages/daemon/src/web/api/system.ts
|
|
776
612
|
import { zValidator } from "@hono/zod-validator";
|
|
777
613
|
import { getProviders } from "@mariozechner/pi-ai";
|
|
778
614
|
import { getOAuthProvider, getOAuthProviders } from "@mariozechner/pi-ai/oauth";
|
|
@@ -780,7 +616,7 @@ import { Hono } from "hono";
|
|
|
780
616
|
import { streamSSE } from "hono/streaming";
|
|
781
617
|
import { z } from "zod";
|
|
782
618
|
|
|
783
|
-
// src/lib/services/imagegen.ts
|
|
619
|
+
// packages/daemon/src/lib/services/imagegen.ts
|
|
784
620
|
import Replicate from "replicate";
|
|
785
621
|
var PROVIDERS = {
|
|
786
622
|
replicate: {
|
|
@@ -975,7 +811,7 @@ async function generateImage(model, prompt) {
|
|
|
975
811
|
return PROVIDERS[providerId].generate(modelId, prompt, apiKey);
|
|
976
812
|
}
|
|
977
813
|
|
|
978
|
-
// src/web/middleware/auth.ts
|
|
814
|
+
// packages/daemon/src/web/middleware/auth.ts
|
|
979
815
|
import { timingSafeEqual } from "crypto";
|
|
980
816
|
import { eq as eq2, lt as lt2 } from "drizzle-orm";
|
|
981
817
|
import { getCookie } from "hono/cookie";
|
|
@@ -1107,7 +943,7 @@ var requireSelf = (paramName = "name") => createMiddleware(async (c, next) => {
|
|
|
1107
943
|
await next();
|
|
1108
944
|
});
|
|
1109
945
|
|
|
1110
|
-
// src/web/api/system.ts
|
|
946
|
+
// packages/daemon/src/web/api/system.ts
|
|
1111
947
|
var DEFAULT_API_URL = "https://volute.systems";
|
|
1112
948
|
var igLog = logger_default.child("imagegen");
|
|
1113
949
|
var app = new Hono().post("/restart", requireAdmin, (c) => {
|
|
@@ -1127,16 +963,23 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
|
|
|
1127
963
|
stream.writeSSE({ data: JSON.stringify(entry) }).catch(() => {
|
|
1128
964
|
});
|
|
1129
965
|
});
|
|
1130
|
-
await new Promise((
|
|
966
|
+
await new Promise((resolve18) => {
|
|
1131
967
|
stream.onAbort(() => {
|
|
1132
968
|
unsubscribe();
|
|
1133
|
-
|
|
969
|
+
resolve18();
|
|
1134
970
|
});
|
|
1135
971
|
});
|
|
1136
972
|
});
|
|
1137
973
|
}).get("/info", (c) => {
|
|
1138
974
|
const config2 = readSystemsConfig();
|
|
1139
|
-
|
|
975
|
+
const globalConfig = readGlobalConfig();
|
|
976
|
+
return c.json({ system: config2?.system ?? null, name: globalConfig.name ?? null });
|
|
977
|
+
}).put("/info", requireAdmin, zValidator("json", z.object({ name: z.string() })), (c) => {
|
|
978
|
+
const { name } = c.req.valid("json");
|
|
979
|
+
const config2 = readGlobalConfig();
|
|
980
|
+
config2.name = name.trim() || void 0;
|
|
981
|
+
writeGlobalConfig(config2);
|
|
982
|
+
return c.json({ name: config2.name ?? null });
|
|
1140
983
|
}).post(
|
|
1141
984
|
"/register",
|
|
1142
985
|
requireAdmin,
|
|
@@ -1311,7 +1154,7 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
|
|
|
1311
1154
|
const { model, prompt } = c.req.valid("json");
|
|
1312
1155
|
try {
|
|
1313
1156
|
const buf = await generateImage(model, prompt);
|
|
1314
|
-
return new Response(buf, {
|
|
1157
|
+
return new Response(new Uint8Array(buf), {
|
|
1315
1158
|
headers: {
|
|
1316
1159
|
"Content-Type": "image/png",
|
|
1317
1160
|
"Content-Length": String(buf.length)
|
|
@@ -1338,13 +1181,15 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
|
|
|
1338
1181
|
let authMethod = null;
|
|
1339
1182
|
if (providerConfig?.oauth) authMethod = "oauth";
|
|
1340
1183
|
else if (providerConfig?.apiKey) authMethod = "api_key";
|
|
1184
|
+
const health = authMethod === "oauth" ? oauthHealth.get(id) : void 0;
|
|
1341
1185
|
return {
|
|
1342
1186
|
id,
|
|
1343
1187
|
oauth: !!oauth,
|
|
1344
1188
|
oauthName: oauth?.name,
|
|
1345
1189
|
usesCallbackServer: !!oauth?.usesCallbackServer,
|
|
1346
1190
|
configured,
|
|
1347
|
-
authMethod
|
|
1191
|
+
authMethod,
|
|
1192
|
+
...health && { oauthHealthy: health.healthy, oauthError: health.error }
|
|
1348
1193
|
};
|
|
1349
1194
|
});
|
|
1350
1195
|
return c.json(result);
|
|
@@ -1382,15 +1227,17 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
|
|
|
1382
1227
|
zValidator(
|
|
1383
1228
|
"json",
|
|
1384
1229
|
z.object({
|
|
1385
|
-
spiritModel: z.string().nullable(),
|
|
1230
|
+
spiritModel: z.string().nullable().optional(),
|
|
1386
1231
|
utilityModel: z.string().nullable()
|
|
1387
1232
|
})
|
|
1388
1233
|
),
|
|
1389
1234
|
(c) => {
|
|
1390
1235
|
const { spiritModel, utilityModel } = c.req.valid("json");
|
|
1391
1236
|
const config2 = readGlobalConfig();
|
|
1392
|
-
|
|
1393
|
-
|
|
1237
|
+
if (spiritModel !== void 0) {
|
|
1238
|
+
config2.spiritModel = spiritModel ?? void 0;
|
|
1239
|
+
writeGlobalConfig(config2);
|
|
1240
|
+
}
|
|
1394
1241
|
setUtilityModel(utilityModel ?? void 0);
|
|
1395
1242
|
return c.json({ ok: true });
|
|
1396
1243
|
}
|
|
@@ -1430,8 +1277,8 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
|
|
|
1430
1277
|
createdAt: Date.now()
|
|
1431
1278
|
};
|
|
1432
1279
|
oauthFlows.set(flowId, flow);
|
|
1433
|
-
const promptPromise = needsManualCode ? new Promise((
|
|
1434
|
-
flow.resolveCode =
|
|
1280
|
+
const promptPromise = needsManualCode ? new Promise((resolve18) => {
|
|
1281
|
+
flow.resolveCode = resolve18;
|
|
1435
1282
|
}) : void 0;
|
|
1436
1283
|
oauthProvider.login({
|
|
1437
1284
|
onAuth: (info) => {
|
|
@@ -1448,10 +1295,11 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
|
|
|
1448
1295
|
return "";
|
|
1449
1296
|
},
|
|
1450
1297
|
onManualCodeInput: needsManualCode ? () => promptPromise : void 0
|
|
1451
|
-
}).then((credentials) => {
|
|
1298
|
+
}).then(async (credentials) => {
|
|
1452
1299
|
saveProviderConfig(provider, { oauth: credentials });
|
|
1453
1300
|
const existing = oauthFlows.get(flowId);
|
|
1454
1301
|
if (existing) existing.status = "complete";
|
|
1302
|
+
await restartMindsForProvider(provider);
|
|
1455
1303
|
}).catch((err) => {
|
|
1456
1304
|
const existing = oauthFlows.get(flowId);
|
|
1457
1305
|
if (existing) {
|
|
@@ -1459,7 +1307,7 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
|
|
|
1459
1307
|
existing.error = err instanceof Error ? err.message : String(err);
|
|
1460
1308
|
}
|
|
1461
1309
|
});
|
|
1462
|
-
await new Promise((
|
|
1310
|
+
await new Promise((resolve18) => setTimeout(resolve18, 2e3));
|
|
1463
1311
|
const state = oauthFlows.get(flowId);
|
|
1464
1312
|
return c.json({
|
|
1465
1313
|
flowId,
|
|
@@ -1505,7 +1353,54 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
|
|
|
1505
1353
|
setTimeout(() => oauthFlows.delete(flowId), 3e4);
|
|
1506
1354
|
}
|
|
1507
1355
|
return c.json(result);
|
|
1508
|
-
})
|
|
1356
|
+
}).get("/mind-defaults", requireAdmin, (c) => {
|
|
1357
|
+
const config2 = readGlobalConfig();
|
|
1358
|
+
return c.json(config2.mindDefaults ?? {});
|
|
1359
|
+
}).put(
|
|
1360
|
+
"/mind-defaults",
|
|
1361
|
+
requireAdmin,
|
|
1362
|
+
zValidator(
|
|
1363
|
+
"json",
|
|
1364
|
+
z.object({
|
|
1365
|
+
cognition: z.object({
|
|
1366
|
+
model: z.string().optional(),
|
|
1367
|
+
thinkingLevel: z.enum(["off", "minimal", "low", "medium", "high", "xhigh"]).optional(),
|
|
1368
|
+
maxThinkingTokens: z.number().nonnegative().optional(),
|
|
1369
|
+
tokenBudget: z.number().nonnegative().optional(),
|
|
1370
|
+
tokenBudgetPeriodMinutes: z.number().positive().optional(),
|
|
1371
|
+
compaction: z.object({ maxContextTokens: z.number().positive().optional() }).optional()
|
|
1372
|
+
}).optional(),
|
|
1373
|
+
sleep: z.object({
|
|
1374
|
+
enabled: z.boolean().optional(),
|
|
1375
|
+
schedule: z.object({ sleep: z.string(), wake: z.string() }).optional(),
|
|
1376
|
+
wakeTriggers: z.object({
|
|
1377
|
+
mentions: z.boolean().optional(),
|
|
1378
|
+
dms: z.boolean().optional(),
|
|
1379
|
+
channels: z.array(z.string()).optional(),
|
|
1380
|
+
senders: z.array(z.string()).optional()
|
|
1381
|
+
}).optional()
|
|
1382
|
+
}).optional(),
|
|
1383
|
+
schedules: z.array(
|
|
1384
|
+
z.object({
|
|
1385
|
+
id: z.string().min(1),
|
|
1386
|
+
cron: z.string().optional(),
|
|
1387
|
+
message: z.string().optional(),
|
|
1388
|
+
script: z.string().optional(),
|
|
1389
|
+
session: z.string().optional(),
|
|
1390
|
+
enabled: z.boolean(),
|
|
1391
|
+
whileSleeping: z.enum(["skip", "queue", "trigger-wake"]).optional()
|
|
1392
|
+
})
|
|
1393
|
+
).optional()
|
|
1394
|
+
})
|
|
1395
|
+
),
|
|
1396
|
+
(c) => {
|
|
1397
|
+
const mindDefaults = c.req.valid("json");
|
|
1398
|
+
const config2 = readGlobalConfig();
|
|
1399
|
+
config2.mindDefaults = mindDefaults;
|
|
1400
|
+
writeGlobalConfig(config2);
|
|
1401
|
+
return c.json({ ok: true });
|
|
1402
|
+
}
|
|
1403
|
+
);
|
|
1509
1404
|
var oauthFlows = /* @__PURE__ */ new Map();
|
|
1510
1405
|
var OAUTH_FLOW_TTL_MS = 10 * 60 * 1e3;
|
|
1511
1406
|
function cleanupOAuthFlows() {
|
|
@@ -1516,8 +1411,29 @@ function cleanupOAuthFlows() {
|
|
|
1516
1411
|
}
|
|
1517
1412
|
}
|
|
1518
1413
|
}
|
|
1414
|
+
var PROVIDER_TEMPLATES = {
|
|
1415
|
+
anthropic: (t) => !t || t === "claude"
|
|
1416
|
+
};
|
|
1417
|
+
async function restartMindsForProvider(provider) {
|
|
1418
|
+
const matchTemplate = PROVIDER_TEMPLATES[provider];
|
|
1419
|
+
if (!matchTemplate) return;
|
|
1420
|
+
const manager = getMindManager();
|
|
1421
|
+
const running = manager.getRunningMinds();
|
|
1422
|
+
for (const name of running) {
|
|
1423
|
+
try {
|
|
1424
|
+
const entry = await findMind(name);
|
|
1425
|
+
if (entry && matchTemplate(entry.template)) {
|
|
1426
|
+
slog.info(`restarting ${name} after ${provider} credential refresh`);
|
|
1427
|
+
await manager.restartMind(name);
|
|
1428
|
+
}
|
|
1429
|
+
} catch (err) {
|
|
1430
|
+
slog.warn(`failed to check mind ${name} for restart`, logger_default.errorData(err));
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1519
1434
|
var apiKeyCache = /* @__PURE__ */ new Map();
|
|
1520
|
-
var
|
|
1435
|
+
var API_KEY_CACHE_TTL_MS = 4 * 60 * 1e3;
|
|
1436
|
+
var REFRESH_CHECK_INTERVAL_MS = 6e4;
|
|
1521
1437
|
function getCachedApiKey(provider) {
|
|
1522
1438
|
const cached = apiKeyCache.get(provider);
|
|
1523
1439
|
if (cached && Date.now() < cached.expiresAt) return cached.key;
|
|
@@ -1525,15 +1441,41 @@ function getCachedApiKey(provider) {
|
|
|
1525
1441
|
}
|
|
1526
1442
|
var keyRefreshTimer = null;
|
|
1527
1443
|
var slog = logger_default.child("ai-keys");
|
|
1444
|
+
var oauthHealth = /* @__PURE__ */ new Map();
|
|
1445
|
+
function needsRefresh(oauth) {
|
|
1446
|
+
const expires = oauth.expires;
|
|
1447
|
+
if (!expires || Date.now() >= expires) return true;
|
|
1448
|
+
return expires - Date.now() < 15 * 60 * 1e3;
|
|
1449
|
+
}
|
|
1528
1450
|
async function refreshApiKeyCache() {
|
|
1529
|
-
|
|
1451
|
+
const ai = getAiConfig();
|
|
1452
|
+
const providers = getConfiguredProviders();
|
|
1453
|
+
for (const id of oauthHealth.keys()) {
|
|
1454
|
+
if (!providers.includes(id)) oauthHealth.delete(id);
|
|
1455
|
+
}
|
|
1456
|
+
for (const provider of providers) {
|
|
1457
|
+
const providerConfig = ai?.providers[provider];
|
|
1458
|
+
if (!providerConfig?.oauth) continue;
|
|
1459
|
+
if (!needsRefresh(providerConfig.oauth) && getCachedApiKey(provider)) {
|
|
1460
|
+
if (!oauthHealth.has(provider)) oauthHealth.set(provider, { healthy: true });
|
|
1461
|
+
continue;
|
|
1462
|
+
}
|
|
1530
1463
|
try {
|
|
1531
1464
|
const key = await resolveApiKey(provider);
|
|
1532
1465
|
if (key) {
|
|
1533
|
-
apiKeyCache.set(provider, { key, expiresAt: Date.now() +
|
|
1466
|
+
apiKeyCache.set(provider, { key, expiresAt: Date.now() + API_KEY_CACHE_TTL_MS });
|
|
1467
|
+
oauthHealth.set(provider, { healthy: true });
|
|
1468
|
+
} else {
|
|
1469
|
+
oauthHealth.set(provider, {
|
|
1470
|
+
healthy: false,
|
|
1471
|
+
error: "Failed to resolve credentials",
|
|
1472
|
+
since: oauthHealth.get(provider)?.since ?? Date.now()
|
|
1473
|
+
});
|
|
1534
1474
|
}
|
|
1535
1475
|
} catch (err) {
|
|
1476
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1536
1477
|
slog.warn(`API key refresh failed for ${provider}`, logger_default.errorData(err));
|
|
1478
|
+
oauthHealth.set(provider, { healthy: false, error: message, since: Date.now() });
|
|
1537
1479
|
}
|
|
1538
1480
|
}
|
|
1539
1481
|
}
|
|
@@ -1546,7 +1488,7 @@ function startApiKeyRefresh() {
|
|
|
1546
1488
|
refreshApiKeyCache().catch((err) => {
|
|
1547
1489
|
slog.warn("periodic API key cache refresh failed", logger_default.errorData(err));
|
|
1548
1490
|
});
|
|
1549
|
-
},
|
|
1491
|
+
}, REFRESH_CHECK_INTERVAL_MS);
|
|
1550
1492
|
}
|
|
1551
1493
|
function stopApiKeyRefresh() {
|
|
1552
1494
|
if (keyRefreshTimer) {
|
|
@@ -1556,13 +1498,14 @@ function stopApiKeyRefresh() {
|
|
|
1556
1498
|
}
|
|
1557
1499
|
var system_default = app;
|
|
1558
1500
|
|
|
1559
|
-
// src/web/app.ts
|
|
1560
|
-
import { Hono as
|
|
1501
|
+
// packages/daemon/src/web/app.ts
|
|
1502
|
+
import { Hono as Hono28 } from "hono";
|
|
1561
1503
|
import { bodyLimit } from "hono/body-limit";
|
|
1504
|
+
import { cors } from "hono/cors";
|
|
1562
1505
|
import { csrf } from "hono/csrf";
|
|
1563
1506
|
import { HTTPException } from "hono/http-exception";
|
|
1564
1507
|
|
|
1565
|
-
// src/web/api/activity.ts
|
|
1508
|
+
// packages/daemon/src/web/api/activity.ts
|
|
1566
1509
|
import { desc } from "drizzle-orm";
|
|
1567
1510
|
import { Hono as Hono2 } from "hono";
|
|
1568
1511
|
import { streamSSE as streamSSE2 } from "hono/streaming";
|
|
@@ -1620,8 +1563,8 @@ var app2 = new Hono2().get("/events", async (c) => {
|
|
|
1620
1563
|
});
|
|
1621
1564
|
}, 15e3);
|
|
1622
1565
|
cleanups.push(() => clearInterval(keepAlive));
|
|
1623
|
-
await new Promise((
|
|
1624
|
-
stream.onAbort(() =>
|
|
1566
|
+
await new Promise((resolve18) => {
|
|
1567
|
+
stream.onAbort(() => resolve18());
|
|
1625
1568
|
});
|
|
1626
1569
|
} finally {
|
|
1627
1570
|
for (const cleanup of cleanups) {
|
|
@@ -1635,9 +1578,9 @@ var app2 = new Hono2().get("/events", async (c) => {
|
|
|
1635
1578
|
});
|
|
1636
1579
|
var activity_default = app2;
|
|
1637
1580
|
|
|
1638
|
-
// src/web/api/auth.ts
|
|
1639
|
-
import { existsSync as
|
|
1640
|
-
import { extname, resolve as
|
|
1581
|
+
// packages/daemon/src/web/api/auth.ts
|
|
1582
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, rmSync, writeFileSync as writeFileSync2 } from "fs";
|
|
1583
|
+
import { extname, resolve as resolve3 } from "path";
|
|
1641
1584
|
import { zValidator as zValidator2 } from "@hono/zod-validator";
|
|
1642
1585
|
import { Hono as Hono3 } from "hono";
|
|
1643
1586
|
import { deleteCookie, getCookie as getCookie2, setCookie } from "hono/cookie";
|
|
@@ -1674,7 +1617,7 @@ var AVATAR_MIME = {
|
|
|
1674
1617
|
};
|
|
1675
1618
|
var MAX_AVATAR_SIZE = 2 * 1024 * 1024;
|
|
1676
1619
|
function avatarsDir() {
|
|
1677
|
-
return
|
|
1620
|
+
return resolve3(voluteHome(), "avatars");
|
|
1678
1621
|
}
|
|
1679
1622
|
var authenticated = new Hono3().use(authMiddleware).post("/change-password", zValidator2("json", changePasswordSchema), async (c) => {
|
|
1680
1623
|
const user = c.get("user");
|
|
@@ -1709,13 +1652,13 @@ var authenticated = new Hono3().use(authMiddleware).post("/change-password", zVa
|
|
|
1709
1652
|
return c.json({ error: "Invalid file type (png, jpg, gif, webp only)" }, 400);
|
|
1710
1653
|
}
|
|
1711
1654
|
const dir = avatarsDir();
|
|
1712
|
-
|
|
1655
|
+
mkdirSync2(dir, { recursive: true });
|
|
1713
1656
|
const filename = `avatar-${user.id}${ext}`;
|
|
1714
1657
|
const buffer2 = Buffer.from(await file.arrayBuffer());
|
|
1715
|
-
|
|
1658
|
+
writeFileSync2(resolve3(dir, filename), buffer2);
|
|
1716
1659
|
if (user.avatar && user.avatar !== filename) {
|
|
1717
|
-
const oldPath =
|
|
1718
|
-
|
|
1660
|
+
const oldPath = resolve3(dir, user.avatar);
|
|
1661
|
+
rmSync(oldPath, { force: true });
|
|
1719
1662
|
}
|
|
1720
1663
|
await updateUserProfile(user.id, { avatar: filename });
|
|
1721
1664
|
const sessionId = getCookie2(c, "volute_session");
|
|
@@ -1729,8 +1672,8 @@ var authenticated = new Hono3().use(authMiddleware).post("/change-password", zVa
|
|
|
1729
1672
|
}).delete("/avatar", async (c) => {
|
|
1730
1673
|
const user = c.get("user");
|
|
1731
1674
|
if (user.avatar) {
|
|
1732
|
-
const path =
|
|
1733
|
-
|
|
1675
|
+
const path = resolve3(avatarsDir(), user.avatar);
|
|
1676
|
+
rmSync(path, { force: true });
|
|
1734
1677
|
}
|
|
1735
1678
|
await updateUserProfile(user.id, { avatar: null });
|
|
1736
1679
|
const sessionId = getCookie2(c, "volute_session");
|
|
@@ -1859,7 +1802,13 @@ var app3 = new Hono3().post("/register", zValidator2("json", credentialsSchema),
|
|
|
1859
1802
|
}
|
|
1860
1803
|
return c.json({ ok: true });
|
|
1861
1804
|
}).get("/me", async (c) => {
|
|
1862
|
-
|
|
1805
|
+
let sessionId = getCookie2(c, "volute_session");
|
|
1806
|
+
if (!sessionId) {
|
|
1807
|
+
const authHeader = c.req.header("Authorization");
|
|
1808
|
+
if (authHeader?.startsWith("Bearer ")) {
|
|
1809
|
+
sessionId = authHeader.slice(7);
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1863
1812
|
if (!sessionId) return c.json({ error: "Not logged in" }, 401);
|
|
1864
1813
|
const userId = await getSessionUserId(sessionId);
|
|
1865
1814
|
if (userId == null) return c.json({ error: "Not logged in" }, 401);
|
|
@@ -1879,13 +1828,13 @@ var app3 = new Hono3().post("/register", zValidator2("json", credentialsSchema),
|
|
|
1879
1828
|
return c.json({ error: "Invalid filename" }, 400);
|
|
1880
1829
|
}
|
|
1881
1830
|
const dir = avatarsDir();
|
|
1882
|
-
const filePath =
|
|
1831
|
+
const filePath = resolve3(dir, filename);
|
|
1883
1832
|
if (!filePath.startsWith(`${dir}/`)) return c.json({ error: "Invalid path" }, 400);
|
|
1884
|
-
if (!
|
|
1833
|
+
if (!existsSync3(filePath)) return c.json({ error: "Not found" }, 404);
|
|
1885
1834
|
const ext = extname(filename).toLowerCase();
|
|
1886
1835
|
const mime = AVATAR_MIME[ext];
|
|
1887
1836
|
if (!mime) return c.json({ error: "Invalid file type" }, 400);
|
|
1888
|
-
const data =
|
|
1837
|
+
const data = readFileSync3(filePath);
|
|
1889
1838
|
return c.body(data, 200, {
|
|
1890
1839
|
"Content-Type": mime,
|
|
1891
1840
|
"Cache-Control": "public, max-age=3600",
|
|
@@ -1894,12 +1843,12 @@ var app3 = new Hono3().post("/register", zValidator2("json", credentialsSchema),
|
|
|
1894
1843
|
}).route("/", admin).route("/", authenticated);
|
|
1895
1844
|
var auth_default = app3;
|
|
1896
1845
|
|
|
1897
|
-
// src/web/api/bridges.ts
|
|
1846
|
+
// packages/daemon/src/web/api/bridges.ts
|
|
1898
1847
|
import { zValidator as zValidator3 } from "@hono/zod-validator";
|
|
1899
1848
|
import { Hono as Hono4 } from "hono";
|
|
1900
1849
|
import { z as z3 } from "zod";
|
|
1901
1850
|
|
|
1902
|
-
// src/lib/puppets.ts
|
|
1851
|
+
// packages/daemon/src/lib/puppets.ts
|
|
1903
1852
|
import { and as and2, eq as eq3 } from "drizzle-orm";
|
|
1904
1853
|
async function findOrCreatePuppet(platform, platformId, displayName) {
|
|
1905
1854
|
const username = `${platform}:${platformId}`;
|
|
@@ -1945,7 +1894,7 @@ async function findOrCreatePuppet(platform, platformId, displayName) {
|
|
|
1945
1894
|
}
|
|
1946
1895
|
}
|
|
1947
1896
|
|
|
1948
|
-
// src/web/api/bridges.ts
|
|
1897
|
+
// packages/daemon/src/web/api/bridges.ts
|
|
1949
1898
|
var inboundSchema = z3.object({
|
|
1950
1899
|
content: z3.array(
|
|
1951
1900
|
z3.union([
|
|
@@ -2020,7 +1969,7 @@ var app4 = new Hono4().post("/:platform/inbound", zValidator3("json", inboundSch
|
|
|
2020
1969
|
}
|
|
2021
1970
|
const participants = await getParticipants(channel.id);
|
|
2022
1971
|
if (!participants.some((p) => p.userId === puppet.id)) {
|
|
2023
|
-
const { addParticipant } = await import("./conversations-
|
|
1972
|
+
const { addParticipant } = await import("./conversations-2PW57WO2.js");
|
|
2024
1973
|
await addParticipant(channel.id, puppet.id);
|
|
2025
1974
|
}
|
|
2026
1975
|
const contentBlocks = body.content;
|
|
@@ -2113,8 +2062,8 @@ async function fanOutToBridgedMinds(opts) {
|
|
|
2113
2062
|
const participants = await getParticipants(opts.conversationId);
|
|
2114
2063
|
const mindParticipants = participants.filter((p) => p.userType === "mind");
|
|
2115
2064
|
const participantNames = participants.map((p) => p.username);
|
|
2116
|
-
const { getMindManager: getMindManager2 } = await import("./mind-manager-
|
|
2117
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
2065
|
+
const { getMindManager: getMindManager2 } = await import("./mind-manager-HFLB5653.js");
|
|
2066
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-7KFK3USC.js");
|
|
2118
2067
|
const manager = getMindManager2();
|
|
2119
2068
|
const sm = getSleepManagerIfReady2();
|
|
2120
2069
|
const targetMinds = mindParticipants.filter((ap) => {
|
|
@@ -2142,10 +2091,10 @@ async function fanOutToBridgedMinds(opts) {
|
|
|
2142
2091
|
}
|
|
2143
2092
|
var bridges_default = app4;
|
|
2144
2093
|
|
|
2145
|
-
// src/web/api/channels.ts
|
|
2094
|
+
// packages/daemon/src/web/api/channels.ts
|
|
2146
2095
|
import { Hono as Hono5 } from "hono";
|
|
2147
2096
|
|
|
2148
|
-
// src/lib/channels/discord.ts
|
|
2097
|
+
// packages/daemon/src/lib/channels/discord.ts
|
|
2149
2098
|
var discord_exports = {};
|
|
2150
2099
|
__export(discord_exports, {
|
|
2151
2100
|
createConversation: () => createConversation2,
|
|
@@ -2155,7 +2104,7 @@ __export(discord_exports, {
|
|
|
2155
2104
|
send: () => send
|
|
2156
2105
|
});
|
|
2157
2106
|
|
|
2158
|
-
// src/connectors/sdk.ts
|
|
2107
|
+
// packages/daemon/src/connectors/sdk.ts
|
|
2159
2108
|
function splitMessage(text, maxLength) {
|
|
2160
2109
|
const chunks = [];
|
|
2161
2110
|
while (text.length > maxLength) {
|
|
@@ -2168,7 +2117,7 @@ function splitMessage(text, maxLength) {
|
|
|
2168
2117
|
return chunks;
|
|
2169
2118
|
}
|
|
2170
2119
|
|
|
2171
|
-
// src/lib/channels/discord.ts
|
|
2120
|
+
// packages/daemon/src/lib/channels/discord.ts
|
|
2172
2121
|
var DISCORD_MAX_LENGTH = 2e3;
|
|
2173
2122
|
var API_BASE = "https://discord.com/api/v10";
|
|
2174
2123
|
function requireToken(env) {
|
|
@@ -2194,8 +2143,8 @@ async function read(env, channelSlug, limit) {
|
|
|
2194
2143
|
if (!res.ok) {
|
|
2195
2144
|
throw new Error(`Discord API error: ${res.status} ${res.statusText}`);
|
|
2196
2145
|
}
|
|
2197
|
-
const
|
|
2198
|
-
return
|
|
2146
|
+
const messages = await res.json();
|
|
2147
|
+
return messages.reverse().map((m) => `${m.author.username}: ${m.content}`).join("\n");
|
|
2199
2148
|
}
|
|
2200
2149
|
async function send(env, channelSlug, message, images) {
|
|
2201
2150
|
const token = requireToken(env);
|
|
@@ -2315,7 +2264,7 @@ async function createConversation2(env, participants, _name) {
|
|
|
2315
2264
|
return `discord:${dmChannel.id}`;
|
|
2316
2265
|
}
|
|
2317
2266
|
|
|
2318
|
-
// src/lib/channels/slack.ts
|
|
2267
|
+
// packages/daemon/src/lib/channels/slack.ts
|
|
2319
2268
|
var slack_exports = {};
|
|
2320
2269
|
__export(slack_exports, {
|
|
2321
2270
|
createConversation: () => createConversation3,
|
|
@@ -2486,7 +2435,7 @@ async function createConversation3(env, participants, name) {
|
|
|
2486
2435
|
return `slack:${openData.channel.id}`;
|
|
2487
2436
|
}
|
|
2488
2437
|
|
|
2489
|
-
// src/lib/channels/telegram.ts
|
|
2438
|
+
// packages/daemon/src/lib/channels/telegram.ts
|
|
2490
2439
|
var telegram_exports = {};
|
|
2491
2440
|
__export(telegram_exports, {
|
|
2492
2441
|
createConversation: () => createConversation4,
|
|
@@ -2582,7 +2531,7 @@ async function createConversation4() {
|
|
|
2582
2531
|
);
|
|
2583
2532
|
}
|
|
2584
2533
|
|
|
2585
|
-
// src/lib/channels/volute.ts
|
|
2534
|
+
// packages/daemon/src/lib/channels/volute.ts
|
|
2586
2535
|
var volute_exports = {};
|
|
2587
2536
|
__export(volute_exports, {
|
|
2588
2537
|
createConversation: () => createConversation5,
|
|
@@ -2591,16 +2540,28 @@ __export(volute_exports, {
|
|
|
2591
2540
|
read: () => read4,
|
|
2592
2541
|
send: () => send4
|
|
2593
2542
|
});
|
|
2594
|
-
import { existsSync as
|
|
2595
|
-
import { resolve as
|
|
2543
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
|
|
2544
|
+
import { resolve as resolve4 } from "path";
|
|
2545
|
+
function readSessionFile(mindDir2) {
|
|
2546
|
+
try {
|
|
2547
|
+
const p = resolve4(mindDir2, ".mind", "current-session");
|
|
2548
|
+
if (existsSync4(p)) return readFileSync4(p, "utf-8").trim() || void 0;
|
|
2549
|
+
} catch (err) {
|
|
2550
|
+
const code = err.code;
|
|
2551
|
+
if (code !== "ENOENT") {
|
|
2552
|
+
console.error(`[volute] failed to read session file: ${code ?? err}`);
|
|
2553
|
+
}
|
|
2554
|
+
}
|
|
2555
|
+
return void 0;
|
|
2556
|
+
}
|
|
2596
2557
|
function getDaemonConfig() {
|
|
2597
|
-
const configPath =
|
|
2598
|
-
if (!
|
|
2558
|
+
const configPath = resolve4(voluteSystemDir(), "daemon.json");
|
|
2559
|
+
if (!existsSync4(configPath)) {
|
|
2599
2560
|
throw new Error("Volute daemon is not running");
|
|
2600
2561
|
}
|
|
2601
2562
|
let config2;
|
|
2602
2563
|
try {
|
|
2603
|
-
config2 = JSON.parse(
|
|
2564
|
+
config2 = JSON.parse(readFileSync4(configPath, "utf-8"));
|
|
2604
2565
|
} catch (err) {
|
|
2605
2566
|
throw new Error(`Failed to parse ${configPath}: ${err}`);
|
|
2606
2567
|
}
|
|
@@ -2748,7 +2709,7 @@ async function createConversation5(env, participants, name) {
|
|
|
2748
2709
|
return conv.id;
|
|
2749
2710
|
}
|
|
2750
2711
|
|
|
2751
|
-
// src/lib/channels.ts
|
|
2712
|
+
// packages/daemon/src/lib/channels.ts
|
|
2752
2713
|
var CHANNELS = {
|
|
2753
2714
|
volute: {
|
|
2754
2715
|
name: "volute",
|
|
@@ -2782,7 +2743,7 @@ function resolveChannelId(slug) {
|
|
|
2782
2743
|
return colonIdx !== -1 ? slug.slice(colonIdx + 1) : slug;
|
|
2783
2744
|
}
|
|
2784
2745
|
|
|
2785
|
-
// src/web/api/channels.ts
|
|
2746
|
+
// packages/daemon/src/web/api/channels.ts
|
|
2786
2747
|
function buildEnv(name) {
|
|
2787
2748
|
return { ...loadMergedEnv(name), VOLUTE_MIND: name, VOLUTE_MIND_DIR: mindDir(name) };
|
|
2788
2749
|
}
|
|
@@ -2810,7 +2771,7 @@ var app5 = new Hono5().post("/:name/channels/create", requireSelf(), async (c) =
|
|
|
2810
2771
|
});
|
|
2811
2772
|
var channels_default = app5;
|
|
2812
2773
|
|
|
2813
|
-
// src/web/api/config.ts
|
|
2774
|
+
// packages/daemon/src/web/api/config.ts
|
|
2814
2775
|
import { Hono as Hono6 } from "hono";
|
|
2815
2776
|
var config = new Hono6();
|
|
2816
2777
|
config.get("/models", (c) => {
|
|
@@ -2838,7 +2799,7 @@ config.get("/status", (c) => {
|
|
|
2838
2799
|
});
|
|
2839
2800
|
var config_default = config;
|
|
2840
2801
|
|
|
2841
|
-
// src/web/api/env.ts
|
|
2802
|
+
// packages/daemon/src/web/api/env.ts
|
|
2842
2803
|
import { Hono as Hono7 } from "hono";
|
|
2843
2804
|
var app6 = new Hono7().get("/:name/env", async (c) => {
|
|
2844
2805
|
const name = c.req.param("name");
|
|
@@ -2912,22 +2873,60 @@ var sharedEnvApp = new Hono7().get("/", (c) => {
|
|
|
2912
2873
|
});
|
|
2913
2874
|
var env_default = app6;
|
|
2914
2875
|
|
|
2915
|
-
// src/web/api/extensions.ts
|
|
2876
|
+
// packages/daemon/src/web/api/extensions.ts
|
|
2916
2877
|
import { Hono as Hono8 } from "hono";
|
|
2917
2878
|
var app7 = new Hono8().get("/", (c) => {
|
|
2918
2879
|
return c.json(getLoadedExtensions());
|
|
2880
|
+
}).get("/all", (c) => {
|
|
2881
|
+
return c.json(getAllDiscoveredExtensions());
|
|
2882
|
+
}).put("/:id/enabled", async (c) => {
|
|
2883
|
+
const { id } = c.req.param();
|
|
2884
|
+
const body = await c.req.json().catch(() => null);
|
|
2885
|
+
if (!body || typeof body.enabled !== "boolean") {
|
|
2886
|
+
return c.json({ error: "enabled must be a boolean" }, 400);
|
|
2887
|
+
}
|
|
2888
|
+
try {
|
|
2889
|
+
setExtensionEnabled(id, body.enabled);
|
|
2890
|
+
} catch (err) {
|
|
2891
|
+
return c.json({ error: err.message }, 404);
|
|
2892
|
+
}
|
|
2893
|
+
return c.json({ ok: true, requiresRestart: true });
|
|
2894
|
+
}).post("/install", async (c) => {
|
|
2895
|
+
const body = await c.req.json();
|
|
2896
|
+
const pkg = body.package?.trim();
|
|
2897
|
+
if (!pkg) {
|
|
2898
|
+
return c.json({ error: "package is required" }, 400);
|
|
2899
|
+
}
|
|
2900
|
+
try {
|
|
2901
|
+
await installNpmExtension(pkg);
|
|
2902
|
+
return c.json({ ok: true, requiresRestart: true });
|
|
2903
|
+
} catch (err) {
|
|
2904
|
+
const message = err.message;
|
|
2905
|
+
const isValidation = message.includes("already installed") || message.includes("Invalid package");
|
|
2906
|
+
return c.json({ error: message }, isValidation ? 400 : 500);
|
|
2907
|
+
}
|
|
2908
|
+
}).delete("/uninstall/:package", async (c) => {
|
|
2909
|
+
const pkg = c.req.param("package");
|
|
2910
|
+
try {
|
|
2911
|
+
await uninstallNpmExtension(pkg);
|
|
2912
|
+
return c.json({ ok: true, requiresRestart: true });
|
|
2913
|
+
} catch (err) {
|
|
2914
|
+
const message = err.message;
|
|
2915
|
+
const isValidation = message.includes("not installed");
|
|
2916
|
+
return c.json({ error: message }, isValidation ? 400 : 500);
|
|
2917
|
+
}
|
|
2919
2918
|
});
|
|
2920
2919
|
var extensions_default = app7;
|
|
2921
2920
|
|
|
2922
|
-
// src/web/api/file-sharing.ts
|
|
2923
|
-
import { readFileSync as
|
|
2924
|
-
import { resolve as
|
|
2921
|
+
// packages/daemon/src/web/api/file-sharing.ts
|
|
2922
|
+
import { readFileSync as readFileSync5, statSync } from "fs";
|
|
2923
|
+
import { resolve as resolve5 } from "path";
|
|
2925
2924
|
import { Hono as Hono9 } from "hono";
|
|
2926
2925
|
async function notifyMind(mindName, message) {
|
|
2927
2926
|
const entry = await findMind(mindName);
|
|
2928
2927
|
if (!entry) return;
|
|
2929
2928
|
try {
|
|
2930
|
-
const { sendSystemMessage } = await import("./system-chat-
|
|
2929
|
+
const { sendSystemMessage } = await import("./system-chat-IDPHYHY4.js");
|
|
2931
2930
|
await sendSystemMessage(mindName, message);
|
|
2932
2931
|
} catch (err) {
|
|
2933
2932
|
logger_default.warn(`[file-sharing] notify mind ${mindName} failed`, logger_default.errorData(err));
|
|
@@ -2946,21 +2945,21 @@ var app8 = new Hono9().post("/:name/files/send", requireSelf(), async (c) => {
|
|
|
2946
2945
|
const pathErr = validateFilePath(body.filePath);
|
|
2947
2946
|
if (pathErr) return c.json({ error: pathErr }, 400);
|
|
2948
2947
|
const senderDir = mindDir(senderName);
|
|
2949
|
-
const filePath =
|
|
2948
|
+
const filePath = resolve5(senderDir, "home", body.filePath);
|
|
2950
2949
|
const MAX_FILE_SIZE3 = 50 * 1024 * 1024;
|
|
2951
|
-
const
|
|
2952
|
-
if (!
|
|
2953
|
-
if (
|
|
2950
|
+
const stat3 = statSync(filePath, { throwIfNoEntry: false });
|
|
2951
|
+
if (!stat3) return c.json({ error: `File not found: ${body.filePath}` }, 404);
|
|
2952
|
+
if (stat3.size > MAX_FILE_SIZE3) {
|
|
2954
2953
|
return c.json(
|
|
2955
2954
|
{
|
|
2956
|
-
error: `File too large (${formatFileSize(
|
|
2955
|
+
error: `File too large (${formatFileSize(stat3.size)}, max ${formatFileSize(MAX_FILE_SIZE3)})`
|
|
2957
2956
|
},
|
|
2958
2957
|
413
|
|
2959
2958
|
);
|
|
2960
2959
|
}
|
|
2961
2960
|
let content;
|
|
2962
2961
|
try {
|
|
2963
|
-
content =
|
|
2962
|
+
content = readFileSync5(filePath);
|
|
2964
2963
|
} catch (err) {
|
|
2965
2964
|
const code = err.code;
|
|
2966
2965
|
if (code === "ENOENT") {
|
|
@@ -3049,9 +3048,10 @@ var app8 = new Hono9().post("/:name/files/send", requireSelf(), async (c) => {
|
|
|
3049
3048
|
});
|
|
3050
3049
|
var file_sharing_default = app8;
|
|
3051
3050
|
|
|
3052
|
-
// src/web/api/files.ts
|
|
3053
|
-
import {
|
|
3054
|
-
import {
|
|
3051
|
+
// packages/daemon/src/web/api/files.ts
|
|
3052
|
+
import { mkdirSync as mkdirSync3, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
3053
|
+
import { readdir, readFile, realpath, stat } from "fs/promises";
|
|
3054
|
+
import { extname as extname2, resolve as resolve6 } from "path";
|
|
3055
3055
|
import { Hono as Hono10 } from "hono";
|
|
3056
3056
|
var AVATAR_MIME2 = {
|
|
3057
3057
|
".png": "image/png",
|
|
@@ -3061,18 +3061,71 @@ var AVATAR_MIME2 = {
|
|
|
3061
3061
|
".webp": "image/webp"
|
|
3062
3062
|
};
|
|
3063
3063
|
var MAX_AVATAR_SIZE2 = 2 * 1024 * 1024;
|
|
3064
|
-
var
|
|
3064
|
+
var MAX_FILE_SIZE = 50 * 1024 * 1024;
|
|
3065
|
+
var MIME_TYPES = {
|
|
3066
|
+
".html": "text/html",
|
|
3067
|
+
".css": "text/css",
|
|
3068
|
+
".js": "application/javascript",
|
|
3069
|
+
".json": "application/json",
|
|
3070
|
+
".svg": "image/svg+xml",
|
|
3071
|
+
".png": "image/png",
|
|
3072
|
+
".jpg": "image/jpeg",
|
|
3073
|
+
".jpeg": "image/jpeg",
|
|
3074
|
+
".gif": "image/gif",
|
|
3075
|
+
".webp": "image/webp",
|
|
3076
|
+
".txt": "text/plain",
|
|
3077
|
+
".md": "text/markdown",
|
|
3078
|
+
".xml": "application/xml",
|
|
3079
|
+
".woff": "font/woff",
|
|
3080
|
+
".woff2": "font/woff2"
|
|
3081
|
+
};
|
|
3082
|
+
var app9 = new Hono10().post("/:name/avatar", requireSelf(), async (c) => {
|
|
3065
3083
|
const name = c.req.param("name");
|
|
3066
3084
|
const entry = await findMind(name);
|
|
3067
3085
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
3068
|
-
const
|
|
3086
|
+
const body = await c.req.parseBody();
|
|
3087
|
+
const file = body.file;
|
|
3088
|
+
if (!(file instanceof File)) {
|
|
3089
|
+
return c.json({ error: "No file uploaded" }, 400);
|
|
3090
|
+
}
|
|
3091
|
+
if (file.size > MAX_AVATAR_SIZE2) {
|
|
3092
|
+
return c.json({ error: "File too large (max 2MB)" }, 400);
|
|
3093
|
+
}
|
|
3094
|
+
const ext = extname2(file.name).toLowerCase();
|
|
3095
|
+
if (!AVATAR_MIME2[ext]) {
|
|
3096
|
+
return c.json({ error: "Invalid file type (png, jpg, gif, webp only)" }, 400);
|
|
3097
|
+
}
|
|
3098
|
+
const dir = entry.dir ?? mindDir(name);
|
|
3099
|
+
const homeDir = resolve6(dir, "home");
|
|
3100
|
+
const filename = `avatar${ext}`;
|
|
3101
|
+
const avatarPath = resolve6(homeDir, filename);
|
|
3102
|
+
const config2 = readVoluteConfig(dir) ?? {};
|
|
3103
|
+
const oldAvatar = config2.profile?.avatar;
|
|
3104
|
+
if (oldAvatar && oldAvatar !== filename) {
|
|
3105
|
+
rmSync2(resolve6(homeDir, oldAvatar), { force: true });
|
|
3106
|
+
}
|
|
3107
|
+
const buffer2 = Buffer.from(await file.arrayBuffer());
|
|
3108
|
+
mkdirSync3(homeDir, { recursive: true });
|
|
3109
|
+
writeFileSync3(avatarPath, buffer2);
|
|
3110
|
+
const profile = config2.profile ?? {};
|
|
3111
|
+
profile.avatar = filename;
|
|
3112
|
+
config2.profile = profile;
|
|
3113
|
+
writeVoluteConfig(dir, config2);
|
|
3114
|
+
await syncMindProfile(name, profile);
|
|
3115
|
+
broadcast({ type: "profile_updated", mind: name, summary: `${name} avatar updated` });
|
|
3116
|
+
return c.json({ ok: true, avatar: filename });
|
|
3117
|
+
}).get("/:name/avatar", async (c) => {
|
|
3118
|
+
const name = c.req.param("name");
|
|
3119
|
+
const entry = await findMind(name);
|
|
3120
|
+
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
3121
|
+
const dir = entry.dir ?? mindDir(name);
|
|
3069
3122
|
const config2 = readVoluteConfig(dir);
|
|
3070
3123
|
if (!config2?.profile?.avatar) return c.json({ error: "No avatar configured" }, 404);
|
|
3071
3124
|
const ext = extname2(config2.profile.avatar).toLowerCase();
|
|
3072
3125
|
const mime = AVATAR_MIME2[ext];
|
|
3073
3126
|
if (!mime) return c.json({ error: "Invalid avatar extension" }, 400);
|
|
3074
|
-
const homeDir =
|
|
3075
|
-
const avatarPath =
|
|
3127
|
+
const homeDir = resolve6(dir, "home");
|
|
3128
|
+
const avatarPath = resolve6(homeDir, config2.profile.avatar);
|
|
3076
3129
|
if (!avatarPath.startsWith(`${homeDir}/`)) return c.json({ error: "Invalid avatar path" }, 400);
|
|
3077
3130
|
let realAvatarPath;
|
|
3078
3131
|
try {
|
|
@@ -3096,29 +3149,89 @@ var app9 = new Hono10().get("/:name/avatar", async (c) => {
|
|
|
3096
3149
|
} catch {
|
|
3097
3150
|
return c.json({ error: "Failed to read avatar file" }, 500);
|
|
3098
3151
|
}
|
|
3152
|
+
}).get("/:name/files/", requireAdmin, async (c) => {
|
|
3153
|
+
const name = c.req.param("name");
|
|
3154
|
+
const entry = await findMind(name);
|
|
3155
|
+
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
3156
|
+
const homeDir = resolve6(entry.dir ?? mindDir(name), "home");
|
|
3157
|
+
let entries;
|
|
3158
|
+
try {
|
|
3159
|
+
entries = await readdir(homeDir, { withFileTypes: true });
|
|
3160
|
+
} catch (err) {
|
|
3161
|
+
if (err.code === "ENOENT")
|
|
3162
|
+
return c.json({ error: "Mind home directory not found" }, 404);
|
|
3163
|
+
return c.json({ error: "Failed to read directory" }, 500);
|
|
3164
|
+
}
|
|
3165
|
+
const items = entries.filter((e) => !e.name.startsWith(".")).map((e) => ({ name: e.name, type: e.isDirectory() ? "directory" : "file" }));
|
|
3166
|
+
return c.json(items);
|
|
3167
|
+
}).get("/:name/files/*", requireAdmin, async (c) => {
|
|
3168
|
+
const name = c.req.param("name");
|
|
3169
|
+
const entry = await findMind(name);
|
|
3170
|
+
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
3171
|
+
const homeDir = resolve6(entry.dir ?? mindDir(name), "home");
|
|
3172
|
+
const wildcard = c.req.path.replace(new RegExp(`^.*/minds/${name}/files`), "") || "/";
|
|
3173
|
+
const relativePath = wildcard.slice(1);
|
|
3174
|
+
const requestedPath = resolve6(homeDir, relativePath);
|
|
3175
|
+
if (requestedPath !== homeDir && !requestedPath.startsWith(`${homeDir}/`))
|
|
3176
|
+
return c.text("Forbidden", 403);
|
|
3177
|
+
if (relativePath.split("/").some((seg) => seg.startsWith("."))) return c.text("Forbidden", 403);
|
|
3178
|
+
let resolvedPath;
|
|
3179
|
+
try {
|
|
3180
|
+
const realHome = await realpath(homeDir);
|
|
3181
|
+
resolvedPath = await realpath(requestedPath);
|
|
3182
|
+
if (resolvedPath !== realHome && !resolvedPath.startsWith(`${realHome}/`))
|
|
3183
|
+
return c.text("Forbidden", 403);
|
|
3184
|
+
} catch (err) {
|
|
3185
|
+
if (err.code === "ENOENT") return c.text("Not found", 404);
|
|
3186
|
+
return c.text("Internal server error", 500);
|
|
3187
|
+
}
|
|
3188
|
+
let fileStat;
|
|
3189
|
+
try {
|
|
3190
|
+
fileStat = await stat(resolvedPath);
|
|
3191
|
+
} catch (err) {
|
|
3192
|
+
if (err.code === "ENOENT") return c.text("Not found", 404);
|
|
3193
|
+
console.error(`[files] stat failed for ${resolvedPath}:`, err);
|
|
3194
|
+
return c.text("Internal server error", 500);
|
|
3195
|
+
}
|
|
3196
|
+
if (fileStat.isDirectory()) {
|
|
3197
|
+
if (!c.req.path.endsWith("/")) return c.redirect(`${c.req.path}/`);
|
|
3198
|
+
const dirEntries = await readdir(resolvedPath, { withFileTypes: true }).catch(
|
|
3199
|
+
() => []
|
|
3200
|
+
);
|
|
3201
|
+
const items = dirEntries.filter((e) => !e.name.startsWith(".")).map((e) => ({ name: e.name, type: e.isDirectory() ? "directory" : "file" }));
|
|
3202
|
+
return c.json(items);
|
|
3203
|
+
}
|
|
3204
|
+
if (fileStat.size > MAX_FILE_SIZE) return c.text("File too large", 413);
|
|
3205
|
+
const body = await readFile(resolvedPath);
|
|
3206
|
+
const mime = MIME_TYPES[extname2(resolvedPath).toLowerCase()] || "application/octet-stream";
|
|
3207
|
+
return c.body(body, 200, { "Content-Type": mime });
|
|
3099
3208
|
});
|
|
3100
3209
|
var files_default = app9;
|
|
3101
3210
|
|
|
3102
|
-
// src/web/api/history.ts
|
|
3103
|
-
import { and as and3, desc as desc2, eq as eq4, inArray, sql } from "drizzle-orm";
|
|
3211
|
+
// packages/daemon/src/web/api/history.ts
|
|
3212
|
+
import { and as and3, desc as desc2, eq as eq4, gte, inArray, sql } from "drizzle-orm";
|
|
3104
3213
|
import { Hono as Hono11 } from "hono";
|
|
3105
3214
|
var history = new Hono11().get("/turns", async (c) => {
|
|
3106
3215
|
const mindFilter = c.req.query("mind");
|
|
3107
3216
|
const turnIdFilter = c.req.query("turnId");
|
|
3217
|
+
const turnIdsFilter = c.req.query("turnIds");
|
|
3108
3218
|
const limit = Math.min(Math.max(parseInt(c.req.query("limit") ?? "50", 10) || 50, 1), 200);
|
|
3109
3219
|
const offset = Math.max(parseInt(c.req.query("offset") ?? "0", 10) || 0, 0);
|
|
3110
3220
|
const db = await getDb();
|
|
3111
3221
|
const conditions = [];
|
|
3112
3222
|
if (mindFilter) conditions.push(eq4(turns.mind, mindFilter));
|
|
3113
3223
|
if (turnIdFilter) conditions.push(eq4(turns.id, turnIdFilter));
|
|
3224
|
+
if (turnIdsFilter) {
|
|
3225
|
+
const ids = turnIdsFilter.split(",").filter(Boolean);
|
|
3226
|
+
if (ids.length > 0) conditions.push(inArray(turns.id, ids));
|
|
3227
|
+
}
|
|
3114
3228
|
const turnRows = await db.select().from(turns).where(conditions.length > 0 ? and3(...conditions) : void 0).orderBy(desc2(turns.created_at)).limit(limit).offset(offset);
|
|
3115
3229
|
if (turnRows.length === 0) return c.json([]);
|
|
3116
3230
|
const turnIds = turnRows.map((t) => t.id);
|
|
3117
|
-
const summaryRows = await db.select().from(
|
|
3231
|
+
const summaryRows = await db.select().from(summaries).where(and3(eq4(summaries.period, "turn"), inArray(summaries.period_key, turnIds)));
|
|
3118
3232
|
const summaryByTurn = /* @__PURE__ */ new Map();
|
|
3119
3233
|
for (const s of summaryRows) {
|
|
3120
|
-
|
|
3121
|
-
summaryByTurn.set(s.turn_id, { content: s.content ?? "", metadata: s.metadata });
|
|
3234
|
+
summaryByTurn.set(s.period_key, { content: s.content, metadata: s.metadata });
|
|
3122
3235
|
}
|
|
3123
3236
|
const historyMsgRows = await db.select({
|
|
3124
3237
|
id: mindHistory.id,
|
|
@@ -3191,13 +3304,92 @@ var history = new Hono11().get("/turns", async (c) => {
|
|
|
3191
3304
|
}).from(mindHistory).where(inArray(mindHistory.id, triggerIds));
|
|
3192
3305
|
for (const r of triggerRows) triggerMap.set(r.id, r);
|
|
3193
3306
|
}
|
|
3307
|
+
const allChannelSlugs = /* @__PURE__ */ new Set();
|
|
3308
|
+
for (const [, channels] of msgsByTurnChannel) {
|
|
3309
|
+
for (const ch of channels.keys()) allChannelSlugs.add(ch);
|
|
3310
|
+
}
|
|
3311
|
+
const dmSlugMinds = /* @__PURE__ */ new Map();
|
|
3312
|
+
for (const [turnId, channels] of msgsByTurnChannel) {
|
|
3313
|
+
const mindName = turnMindMap.get(turnId);
|
|
3314
|
+
if (!mindName) continue;
|
|
3315
|
+
for (const ch of channels.keys()) {
|
|
3316
|
+
if (ch.startsWith("@")) {
|
|
3317
|
+
let minds = dmSlugMinds.get(ch);
|
|
3318
|
+
if (!minds) {
|
|
3319
|
+
minds = /* @__PURE__ */ new Set();
|
|
3320
|
+
dmSlugMinds.set(ch, minds);
|
|
3321
|
+
}
|
|
3322
|
+
minds.add(mindName);
|
|
3323
|
+
}
|
|
3324
|
+
}
|
|
3325
|
+
}
|
|
3326
|
+
const channelIdMap = /* @__PURE__ */ new Map();
|
|
3327
|
+
try {
|
|
3328
|
+
if (allChannelSlugs.size > 0) {
|
|
3329
|
+
const channelNames = [...allChannelSlugs].filter((s) => !s.startsWith("@")).map((s) => {
|
|
3330
|
+
let name = s;
|
|
3331
|
+
if (name.startsWith("#")) name = name.slice(1);
|
|
3332
|
+
const colonIdx = name.indexOf(":");
|
|
3333
|
+
if (colonIdx >= 0) name = name.substring(colonIdx + 1);
|
|
3334
|
+
return { slug: s, name };
|
|
3335
|
+
});
|
|
3336
|
+
if (channelNames.length > 0) {
|
|
3337
|
+
const channelRows = await db.select({ id: conversations.id, name: conversations.name }).from(conversations).where(
|
|
3338
|
+
and3(
|
|
3339
|
+
eq4(conversations.type, "channel"),
|
|
3340
|
+
inArray(
|
|
3341
|
+
conversations.name,
|
|
3342
|
+
channelNames.map((c2) => c2.name)
|
|
3343
|
+
)
|
|
3344
|
+
)
|
|
3345
|
+
);
|
|
3346
|
+
const nameToId = new Map(channelRows.map((r) => [r.name, r.id]));
|
|
3347
|
+
for (const { slug, name } of channelNames) {
|
|
3348
|
+
const id = nameToId.get(name);
|
|
3349
|
+
if (id) channelIdMap.set(slug, id);
|
|
3350
|
+
}
|
|
3351
|
+
}
|
|
3352
|
+
const dmSlugs = [...dmSlugMinds.keys()];
|
|
3353
|
+
if (dmSlugs.length > 0) {
|
|
3354
|
+
const targetNames = dmSlugs.map((s) => s.slice(1));
|
|
3355
|
+
const cp2 = db.$with("cp2").as(
|
|
3356
|
+
db.select({
|
|
3357
|
+
conversation_id: conversationParticipants.conversation_id,
|
|
3358
|
+
username: users.username
|
|
3359
|
+
}).from(conversationParticipants).innerJoin(users, eq4(conversationParticipants.user_id, users.id))
|
|
3360
|
+
);
|
|
3361
|
+
const dmRows = await db.with(cp2).select({
|
|
3362
|
+
id: conversations.id,
|
|
3363
|
+
targetUsername: users.username
|
|
3364
|
+
}).from(conversations).innerJoin(
|
|
3365
|
+
conversationParticipants,
|
|
3366
|
+
eq4(conversations.id, conversationParticipants.conversation_id)
|
|
3367
|
+
).innerJoin(users, eq4(conversationParticipants.user_id, users.id)).where(and3(eq4(conversations.type, "dm"), inArray(users.username, targetNames)));
|
|
3368
|
+
for (const row of dmRows) {
|
|
3369
|
+
const slug = `@${row.targetUsername}`;
|
|
3370
|
+
const mindNames = dmSlugMinds.get(slug);
|
|
3371
|
+
if (mindNames && !channelIdMap.has(slug)) {
|
|
3372
|
+
const mindCheck = await db.select({ id: conversationParticipants.user_id }).from(conversationParticipants).innerJoin(users, eq4(conversationParticipants.user_id, users.id)).where(
|
|
3373
|
+
and3(
|
|
3374
|
+
eq4(conversationParticipants.conversation_id, row.id),
|
|
3375
|
+
inArray(users.username, [...mindNames])
|
|
3376
|
+
)
|
|
3377
|
+
).get();
|
|
3378
|
+
if (mindCheck) channelIdMap.set(slug, row.id);
|
|
3379
|
+
}
|
|
3380
|
+
}
|
|
3381
|
+
}
|
|
3382
|
+
}
|
|
3383
|
+
} catch (err) {
|
|
3384
|
+
logger_default.warn("Failed to resolve channel slugs to conversation IDs", logger_default.errorData(err));
|
|
3385
|
+
}
|
|
3194
3386
|
const result = turnRows.map((t) => {
|
|
3195
3387
|
const summary = summaryByTurn.get(t.id);
|
|
3196
3388
|
const turnChannels = msgsByTurnChannel.get(t.id) ?? /* @__PURE__ */ new Map();
|
|
3197
3389
|
const convEntries = [...turnChannels.entries()].map(([channel, evts]) => {
|
|
3198
3390
|
const { label, type } = getChannelLabel(channel);
|
|
3199
3391
|
return {
|
|
3200
|
-
id: channel,
|
|
3392
|
+
id: channelIdMap.get(channel) ?? channel,
|
|
3201
3393
|
label,
|
|
3202
3394
|
type,
|
|
3203
3395
|
messages: evts.map((m) => ({
|
|
@@ -3305,25 +3497,78 @@ var history = new Hono11().get("/turns", async (c) => {
|
|
|
3305
3497
|
Connection: "keep-alive"
|
|
3306
3498
|
}
|
|
3307
3499
|
});
|
|
3500
|
+
}).get("/summaries", async (c) => {
|
|
3501
|
+
const mind = c.req.query("mind") ?? "_system";
|
|
3502
|
+
const period = c.req.query("period");
|
|
3503
|
+
const ids = c.req.query("ids");
|
|
3504
|
+
const from = c.req.query("from");
|
|
3505
|
+
const to = c.req.query("to");
|
|
3506
|
+
const limit = Math.min(Math.max(parseInt(c.req.query("limit") ?? "50", 10) || 50, 1), 200);
|
|
3507
|
+
if (ids) {
|
|
3508
|
+
const db2 = await getDb();
|
|
3509
|
+
const idList = ids.split(",").map((s) => parseInt(s, 10)).filter((n) => !isNaN(n));
|
|
3510
|
+
if (idList.length === 0) return c.json([]);
|
|
3511
|
+
const rows2 = await db2.select().from(summaries).where(inArray(summaries.id, idList));
|
|
3512
|
+
const result2 = rows2.map((r) => {
|
|
3513
|
+
let metadata = null;
|
|
3514
|
+
if (r.metadata) {
|
|
3515
|
+
try {
|
|
3516
|
+
metadata = JSON.parse(r.metadata);
|
|
3517
|
+
} catch (err) {
|
|
3518
|
+
logger_default.debug(`malformed summary metadata for id ${r.id}`, logger_default.errorData(err));
|
|
3519
|
+
}
|
|
3520
|
+
}
|
|
3521
|
+
return { ...r, metadata };
|
|
3522
|
+
});
|
|
3523
|
+
return c.json(result2);
|
|
3524
|
+
}
|
|
3525
|
+
if (!period || !["turn", "hour", "day", "week", "month"].includes(period)) {
|
|
3526
|
+
return c.json({ error: "period is required (turn, hour, day, week, month)" }, 400);
|
|
3527
|
+
}
|
|
3528
|
+
const db = await getDb();
|
|
3529
|
+
const conditions = [eq4(summaries.mind, mind), eq4(summaries.period, period)];
|
|
3530
|
+
if (from) conditions.push(gte(summaries.period_key, from));
|
|
3531
|
+
if (to) conditions.push(sql`${summaries.period_key} <= ${to}`);
|
|
3532
|
+
const rows = await db.select({
|
|
3533
|
+
id: summaries.id,
|
|
3534
|
+
mind: summaries.mind,
|
|
3535
|
+
period: summaries.period,
|
|
3536
|
+
period_key: summaries.period_key,
|
|
3537
|
+
content: summaries.content,
|
|
3538
|
+
metadata: summaries.metadata,
|
|
3539
|
+
created_at: summaries.created_at
|
|
3540
|
+
}).from(summaries).where(and3(...conditions)).orderBy(desc2(summaries.period_key)).limit(limit);
|
|
3541
|
+
const result = rows.map((r) => {
|
|
3542
|
+
let metadata = null;
|
|
3543
|
+
if (r.metadata) {
|
|
3544
|
+
try {
|
|
3545
|
+
metadata = JSON.parse(r.metadata);
|
|
3546
|
+
} catch (err) {
|
|
3547
|
+
logger_default.debug(`malformed meta_summary metadata for id ${r.id}`, logger_default.errorData(err));
|
|
3548
|
+
}
|
|
3549
|
+
}
|
|
3550
|
+
return { ...r, metadata };
|
|
3551
|
+
});
|
|
3552
|
+
return c.json(result);
|
|
3308
3553
|
});
|
|
3309
3554
|
var history_default = history;
|
|
3310
3555
|
|
|
3311
|
-
// src/web/api/keys.ts
|
|
3556
|
+
// packages/daemon/src/web/api/keys.ts
|
|
3312
3557
|
import { Hono as Hono12 } from "hono";
|
|
3313
3558
|
|
|
3314
|
-
// src/lib/identity.ts
|
|
3559
|
+
// packages/daemon/src/lib/identity.ts
|
|
3315
3560
|
import { createHash, generateKeyPairSync, sign, verify } from "crypto";
|
|
3316
|
-
import { existsSync as
|
|
3317
|
-
import { resolve as
|
|
3561
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
3562
|
+
import { resolve as resolve7 } from "path";
|
|
3318
3563
|
function generateIdentity(mindDir2) {
|
|
3319
|
-
const identityDir =
|
|
3564
|
+
const identityDir = resolve7(mindDir2, ".mind/identity");
|
|
3320
3565
|
mkdirSync4(identityDir, { recursive: true });
|
|
3321
3566
|
const { publicKey, privateKey } = generateKeyPairSync("ed25519", {
|
|
3322
3567
|
publicKeyEncoding: { type: "spki", format: "pem" },
|
|
3323
3568
|
privateKeyEncoding: { type: "pkcs8", format: "pem" }
|
|
3324
3569
|
});
|
|
3325
|
-
const privatePath =
|
|
3326
|
-
const publicPath =
|
|
3570
|
+
const privatePath = resolve7(identityDir, "private.pem");
|
|
3571
|
+
const publicPath = resolve7(identityDir, "public.pem");
|
|
3327
3572
|
writeFileSync4(privatePath, privateKey, { mode: 384 });
|
|
3328
3573
|
writeFileSync4(publicPath, publicKey, { mode: 420 });
|
|
3329
3574
|
const config2 = readVoluteConfig(mindDir2) ?? {};
|
|
@@ -3338,9 +3583,9 @@ function getPublicKey(mindDir2) {
|
|
|
3338
3583
|
const config2 = readVoluteConfig(mindDir2);
|
|
3339
3584
|
const relPath = config2?.identity?.publicKey;
|
|
3340
3585
|
if (!relPath) return null;
|
|
3341
|
-
const fullPath =
|
|
3342
|
-
if (!
|
|
3343
|
-
return
|
|
3586
|
+
const fullPath = resolve7(mindDir2, relPath);
|
|
3587
|
+
if (!existsSync5(fullPath)) return null;
|
|
3588
|
+
return readFileSync6(fullPath, "utf-8");
|
|
3344
3589
|
}
|
|
3345
3590
|
function getFingerprint(publicKeyPem) {
|
|
3346
3591
|
return createHash("sha256").update(publicKeyPem).digest("hex");
|
|
@@ -3368,7 +3613,7 @@ async function publishPublicKey(mindName, publicKeyPem) {
|
|
|
3368
3613
|
}
|
|
3369
3614
|
}
|
|
3370
3615
|
|
|
3371
|
-
// src/web/api/keys.ts
|
|
3616
|
+
// packages/daemon/src/web/api/keys.ts
|
|
3372
3617
|
var app10 = new Hono12().get("/:fingerprint", async (c) => {
|
|
3373
3618
|
const fingerprint = c.req.param("fingerprint");
|
|
3374
3619
|
for (const entry of await readRegistry()) {
|
|
@@ -3385,18 +3630,18 @@ var app10 = new Hono12().get("/:fingerprint", async (c) => {
|
|
|
3385
3630
|
});
|
|
3386
3631
|
var keys_default = app10;
|
|
3387
3632
|
|
|
3388
|
-
// src/web/api/logs.ts
|
|
3633
|
+
// packages/daemon/src/web/api/logs.ts
|
|
3389
3634
|
import { spawn as spawn2 } from "child_process";
|
|
3390
|
-
import { existsSync as
|
|
3391
|
-
import { resolve as
|
|
3635
|
+
import { existsSync as existsSync6 } from "fs";
|
|
3636
|
+
import { resolve as resolve8 } from "path";
|
|
3392
3637
|
import { Hono as Hono13 } from "hono";
|
|
3393
3638
|
import { streamSSE as streamSSE3 } from "hono/streaming";
|
|
3394
3639
|
var app11 = new Hono13().get("/:name/logs", async (c) => {
|
|
3395
3640
|
const name = c.req.param("name");
|
|
3396
3641
|
const entry = await findMind(name);
|
|
3397
3642
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
3398
|
-
const logFile =
|
|
3399
|
-
if (!
|
|
3643
|
+
const logFile = resolve8(stateDir(name), "logs", "mind.log");
|
|
3644
|
+
if (!existsSync6(logFile)) {
|
|
3400
3645
|
return c.json({ error: "No log file found" }, 404);
|
|
3401
3646
|
}
|
|
3402
3647
|
return streamSSE3(c, async (stream) => {
|
|
@@ -3414,17 +3659,17 @@ var app11 = new Hono13().get("/:name/logs", async (c) => {
|
|
|
3414
3659
|
stream.onAbort(() => {
|
|
3415
3660
|
tail.kill();
|
|
3416
3661
|
});
|
|
3417
|
-
await new Promise((
|
|
3418
|
-
tail.on("exit",
|
|
3419
|
-
stream.onAbort(
|
|
3662
|
+
await new Promise((resolve18) => {
|
|
3663
|
+
tail.on("exit", resolve18);
|
|
3664
|
+
stream.onAbort(resolve18);
|
|
3420
3665
|
});
|
|
3421
3666
|
});
|
|
3422
3667
|
}).get("/:name/logs/tail", async (c) => {
|
|
3423
3668
|
const name = c.req.param("name");
|
|
3424
3669
|
const entry = await findMind(name);
|
|
3425
3670
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
3426
|
-
const logFile =
|
|
3427
|
-
if (!
|
|
3671
|
+
const logFile = resolve8(stateDir(name), "logs", "mind.log");
|
|
3672
|
+
if (!existsSync6(logFile)) {
|
|
3428
3673
|
return c.json({ error: "No log file found" }, 404);
|
|
3429
3674
|
}
|
|
3430
3675
|
const nParam = parseInt(c.req.query("n") ?? "50", 10);
|
|
@@ -3434,14 +3679,14 @@ var app11 = new Hono13().get("/:name/logs", async (c) => {
|
|
|
3434
3679
|
tail.stdout.on("data", (data) => {
|
|
3435
3680
|
output += data.toString();
|
|
3436
3681
|
});
|
|
3437
|
-
await new Promise((
|
|
3438
|
-
tail.on("exit",
|
|
3682
|
+
await new Promise((resolve18) => {
|
|
3683
|
+
tail.on("exit", resolve18);
|
|
3439
3684
|
});
|
|
3440
3685
|
return c.text(output);
|
|
3441
3686
|
});
|
|
3442
3687
|
var logs_default = app11;
|
|
3443
3688
|
|
|
3444
|
-
// src/web/api/mind-skills.ts
|
|
3689
|
+
// packages/daemon/src/web/api/mind-skills.ts
|
|
3445
3690
|
import { zValidator as zValidator4 } from "@hono/zod-validator";
|
|
3446
3691
|
import { Hono as Hono14 } from "hono";
|
|
3447
3692
|
import { z as z4 } from "zod";
|
|
@@ -3449,7 +3694,7 @@ var app12 = new Hono14().get("/:name/skills", async (c) => {
|
|
|
3449
3694
|
const name = c.req.param("name");
|
|
3450
3695
|
const entry = await findMind(name);
|
|
3451
3696
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
3452
|
-
const dir = mindDir(name);
|
|
3697
|
+
const dir = entry.dir ?? mindDir(name);
|
|
3453
3698
|
const skills = await listMindSkills(dir);
|
|
3454
3699
|
return c.json(skills);
|
|
3455
3700
|
}).post(
|
|
@@ -3461,7 +3706,7 @@ var app12 = new Hono14().get("/:name/skills", async (c) => {
|
|
|
3461
3706
|
const entry = await findMind(name);
|
|
3462
3707
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
3463
3708
|
const { skillId } = c.req.valid("json");
|
|
3464
|
-
const dir = mindDir(name);
|
|
3709
|
+
const dir = entry.dir ?? mindDir(name);
|
|
3465
3710
|
try {
|
|
3466
3711
|
const result = await installSkill(name, dir, skillId);
|
|
3467
3712
|
return c.json({ ok: true, ...result });
|
|
@@ -3479,7 +3724,7 @@ var app12 = new Hono14().get("/:name/skills", async (c) => {
|
|
|
3479
3724
|
const entry = await findMind(name);
|
|
3480
3725
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
3481
3726
|
const { skillId } = c.req.valid("json");
|
|
3482
|
-
const dir = mindDir(name);
|
|
3727
|
+
const dir = entry.dir ?? mindDir(name);
|
|
3483
3728
|
try {
|
|
3484
3729
|
const result = await updateSkill(name, dir, skillId);
|
|
3485
3730
|
return c.json(result);
|
|
@@ -3497,7 +3742,7 @@ var app12 = new Hono14().get("/:name/skills", async (c) => {
|
|
|
3497
3742
|
const entry = await findMind(name);
|
|
3498
3743
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
3499
3744
|
const { skillId } = c.req.valid("json");
|
|
3500
|
-
const dir = mindDir(name);
|
|
3745
|
+
const dir = entry.dir ?? mindDir(name);
|
|
3501
3746
|
try {
|
|
3502
3747
|
const skill = await publishSkill(name, dir, skillId);
|
|
3503
3748
|
return c.json(skill);
|
|
@@ -3511,7 +3756,7 @@ var app12 = new Hono14().get("/:name/skills", async (c) => {
|
|
|
3511
3756
|
const skillName = c.req.param("skill");
|
|
3512
3757
|
const entry = await findMind(name);
|
|
3513
3758
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
3514
|
-
const dir = mindDir(name);
|
|
3759
|
+
const dir = entry.dir ?? mindDir(name);
|
|
3515
3760
|
try {
|
|
3516
3761
|
await uninstallSkill(name, dir, skillName);
|
|
3517
3762
|
} catch (e) {
|
|
@@ -3522,36 +3767,36 @@ var app12 = new Hono14().get("/:name/skills", async (c) => {
|
|
|
3522
3767
|
});
|
|
3523
3768
|
var mind_skills_default = app12;
|
|
3524
3769
|
|
|
3525
|
-
// src/web/api/minds.ts
|
|
3770
|
+
// packages/daemon/src/web/api/minds.ts
|
|
3526
3771
|
import {
|
|
3527
3772
|
cpSync,
|
|
3528
|
-
existsSync as
|
|
3773
|
+
existsSync as existsSync8,
|
|
3529
3774
|
mkdirSync as mkdirSync6,
|
|
3530
3775
|
readdirSync as readdirSync2,
|
|
3531
|
-
readFileSync as
|
|
3776
|
+
readFileSync as readFileSync9,
|
|
3532
3777
|
rmSync as rmSync4,
|
|
3533
3778
|
writeFileSync as writeFileSync7
|
|
3534
3779
|
} from "fs";
|
|
3535
|
-
import { resolve as
|
|
3780
|
+
import { resolve as resolve11 } from "path";
|
|
3536
3781
|
import { zValidator as zValidator5 } from "@hono/zod-validator";
|
|
3537
|
-
import { and as
|
|
3782
|
+
import { and as and4, desc as desc3, eq as eq5, sql as sql2 } from "drizzle-orm";
|
|
3538
3783
|
import { Hono as Hono15 } from "hono";
|
|
3539
3784
|
import { z as z5 } from "zod";
|
|
3540
3785
|
|
|
3541
|
-
// src/lib/consolidate.ts
|
|
3542
|
-
import { readdirSync, readFileSync as
|
|
3543
|
-
import { resolve as
|
|
3786
|
+
// packages/daemon/src/lib/consolidate.ts
|
|
3787
|
+
import { readdirSync, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
|
|
3788
|
+
import { resolve as resolve9 } from "path";
|
|
3544
3789
|
async function consolidateMemory(mindDir2) {
|
|
3545
|
-
const soulPath =
|
|
3546
|
-
const memoryPath =
|
|
3547
|
-
const memoryDir =
|
|
3548
|
-
const soul =
|
|
3790
|
+
const soulPath = resolve9(mindDir2, "home/SOUL.md");
|
|
3791
|
+
const memoryPath = resolve9(mindDir2, "home/MEMORY.md");
|
|
3792
|
+
const memoryDir = resolve9(mindDir2, "home/memory");
|
|
3793
|
+
const soul = readFileSync7(soulPath, "utf-8");
|
|
3549
3794
|
const logs = [];
|
|
3550
3795
|
try {
|
|
3551
3796
|
const files = readdirSync(memoryDir).filter((f) => /^\d{4}-\d{2}-\d{2}\.md$/.test(f)).sort();
|
|
3552
3797
|
for (const filename of files) {
|
|
3553
3798
|
const date = filename.replace(".md", "");
|
|
3554
|
-
const content2 =
|
|
3799
|
+
const content2 = readFileSync7(resolve9(memoryDir, filename), "utf-8").trim();
|
|
3555
3800
|
if (content2) {
|
|
3556
3801
|
logs.push(`### ${date}
|
|
3557
3802
|
|
|
@@ -3609,26 +3854,26 @@ ${content2}`);
|
|
|
3609
3854
|
}
|
|
3610
3855
|
}
|
|
3611
3856
|
|
|
3612
|
-
// src/lib/convert-session.ts
|
|
3857
|
+
// packages/daemon/src/lib/convert-session.ts
|
|
3613
3858
|
import { randomUUID } from "crypto";
|
|
3614
|
-
import { mkdirSync as mkdirSync5, readFileSync as
|
|
3859
|
+
import { mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "fs";
|
|
3615
3860
|
import { homedir } from "os";
|
|
3616
|
-
import { resolve as
|
|
3861
|
+
import { resolve as resolve10 } from "path";
|
|
3617
3862
|
function convertSession(opts) {
|
|
3618
|
-
const lines =
|
|
3863
|
+
const lines = readFileSync8(opts.sessionPath, "utf-8").trim().split("\n");
|
|
3619
3864
|
const sessionId = randomUUID();
|
|
3620
3865
|
const idMap = /* @__PURE__ */ new Map();
|
|
3621
|
-
const
|
|
3866
|
+
const messages = [];
|
|
3622
3867
|
for (const line of lines) {
|
|
3623
3868
|
const event = JSON.parse(line);
|
|
3624
3869
|
if (event.type === "message" && event.message) {
|
|
3625
|
-
|
|
3870
|
+
messages.push(event);
|
|
3626
3871
|
}
|
|
3627
3872
|
}
|
|
3628
3873
|
const sdkEvents = [];
|
|
3629
3874
|
let lastSdkUuid = null;
|
|
3630
|
-
for (let i = 0; i <
|
|
3631
|
-
const event =
|
|
3875
|
+
for (let i = 0; i < messages.length; i++) {
|
|
3876
|
+
const event = messages[i];
|
|
3632
3877
|
const msg = event.message;
|
|
3633
3878
|
if (msg.role === "user") {
|
|
3634
3879
|
const uuid = randomUUID();
|
|
@@ -3689,8 +3934,8 @@ function convertSession(opts) {
|
|
|
3689
3934
|
let lastToolResultId = event.id;
|
|
3690
3935
|
let lastTimestamp = event.timestamp;
|
|
3691
3936
|
let j = i;
|
|
3692
|
-
while (j <
|
|
3693
|
-
const tr =
|
|
3937
|
+
while (j < messages.length && messages[j].message.role === "toolResult") {
|
|
3938
|
+
const tr = messages[j];
|
|
3694
3939
|
const trMsg = tr.message;
|
|
3695
3940
|
lastToolResultId = tr.id;
|
|
3696
3941
|
lastTimestamp = tr.timestamp;
|
|
@@ -3729,9 +3974,9 @@ function convertSession(opts) {
|
|
|
3729
3974
|
}
|
|
3730
3975
|
}
|
|
3731
3976
|
const projectId = opts.projectDir.replace(/\//g, "-");
|
|
3732
|
-
const sdkDir =
|
|
3977
|
+
const sdkDir = resolve10(homedir(), ".claude", "projects", projectId);
|
|
3733
3978
|
mkdirSync5(sdkDir, { recursive: true });
|
|
3734
|
-
const sdkPath =
|
|
3979
|
+
const sdkPath = resolve10(sdkDir, `${sessionId}.jsonl`);
|
|
3735
3980
|
writeFileSync6(sdkPath, `${sdkEvents.join("\n")}
|
|
3736
3981
|
`);
|
|
3737
3982
|
console.log(`Converted ${sdkEvents.length} messages \u2192 ${sdkPath}`);
|
|
@@ -3761,280 +4006,29 @@ function mapUsage(usage) {
|
|
|
3761
4006
|
input_tokens: usage.input ?? usage.input_tokens ?? 0,
|
|
3762
4007
|
output_tokens: usage.output ?? usage.output_tokens ?? 0,
|
|
3763
4008
|
cache_read_input_tokens: usage.cacheRead ?? usage.cache_read_input_tokens ?? 0,
|
|
3764
|
-
cache_creation_input_tokens: usage.cacheWrite ?? usage.cache_creation_input_tokens ?? 0
|
|
3765
|
-
};
|
|
3766
|
-
}
|
|
3767
|
-
function convertAssistantContent(content) {
|
|
3768
|
-
const result = [];
|
|
3769
|
-
for (const block of content) {
|
|
3770
|
-
if (block.type === "thinking") {
|
|
3771
|
-
} else if (block.type === "toolCall") {
|
|
3772
|
-
result.push({
|
|
3773
|
-
type: "tool_use",
|
|
3774
|
-
id: block.id,
|
|
3775
|
-
name: block.name,
|
|
3776
|
-
input: block.arguments ?? block.input ?? {},
|
|
3777
|
-
caller: { type: "direct" }
|
|
3778
|
-
});
|
|
3779
|
-
} else {
|
|
3780
|
-
result.push(block);
|
|
3781
|
-
}
|
|
3782
|
-
}
|
|
3783
|
-
return result;
|
|
3784
|
-
}
|
|
3785
|
-
|
|
3786
|
-
// src/lib/daemon/turn-summarizer.ts
|
|
3787
|
-
import { and as and4, desc as desc3, eq as eq5, gt, lt as lt3, sql as sql2 } from "drizzle-orm";
|
|
3788
|
-
|
|
3789
|
-
// src/lib/format-tool.ts
|
|
3790
|
-
function summarizeTool(name, input) {
|
|
3791
|
-
if (input && typeof input === "object") {
|
|
3792
|
-
const args = input;
|
|
3793
|
-
const val = args.path ?? args.command ?? args.query ?? args.url;
|
|
3794
|
-
if (typeof val === "string") {
|
|
3795
|
-
const brief = val.length > 60 ? `${val.slice(0, 57)}...` : val;
|
|
3796
|
-
return `[${name} ${brief}]`;
|
|
3797
|
-
}
|
|
3798
|
-
}
|
|
3799
|
-
return `[${name}]`;
|
|
3800
|
-
}
|
|
3801
|
-
|
|
3802
|
-
// src/lib/daemon/turn-summarizer.ts
|
|
3803
|
-
var sLog = logger_default.child("turn-summarizer");
|
|
3804
|
-
async function gatherTurnEvents(mind, session, doneId) {
|
|
3805
|
-
const db = await getDb();
|
|
3806
|
-
const conditions = [
|
|
3807
|
-
eq5(mindHistory.mind, mind),
|
|
3808
|
-
eq5(mindHistory.type, "done"),
|
|
3809
|
-
lt3(mindHistory.id, doneId)
|
|
3810
|
-
];
|
|
3811
|
-
if (session) {
|
|
3812
|
-
conditions.push(eq5(mindHistory.session, session));
|
|
3813
|
-
}
|
|
3814
|
-
const prevDone = await db.select({ id: mindHistory.id }).from(mindHistory).where(and4(...conditions)).orderBy(desc3(mindHistory.id)).limit(1);
|
|
3815
|
-
const prevDoneId = prevDone.length > 0 ? prevDone[0].id : 0;
|
|
3816
|
-
const turnConditions = [
|
|
3817
|
-
eq5(mindHistory.mind, mind),
|
|
3818
|
-
gt(mindHistory.id, prevDoneId),
|
|
3819
|
-
sql2`${mindHistory.id} <= ${doneId}`
|
|
3820
|
-
];
|
|
3821
|
-
if (session) {
|
|
3822
|
-
turnConditions.push(eq5(mindHistory.session, session));
|
|
3823
|
-
}
|
|
3824
|
-
const events = await db.select({
|
|
3825
|
-
id: mindHistory.id,
|
|
3826
|
-
type: mindHistory.type,
|
|
3827
|
-
channel: mindHistory.channel,
|
|
3828
|
-
session: mindHistory.session,
|
|
3829
|
-
content: mindHistory.content,
|
|
3830
|
-
metadata: mindHistory.metadata,
|
|
3831
|
-
created_at: mindHistory.created_at
|
|
3832
|
-
}).from(mindHistory).where(and4(...turnConditions)).orderBy(mindHistory.id);
|
|
3833
|
-
return {
|
|
3834
|
-
events,
|
|
3835
|
-
fromId: events.length > 0 ? events[0].id : doneId,
|
|
3836
|
-
toId: doneId
|
|
3837
|
-
};
|
|
3838
|
-
}
|
|
3839
|
-
function buildDeterministicSummary(events) {
|
|
3840
|
-
const channels = /* @__PURE__ */ new Set();
|
|
3841
|
-
const tools = [];
|
|
3842
|
-
let hasInbound = false;
|
|
3843
|
-
let hasOutbound = false;
|
|
3844
|
-
for (const ev of events) {
|
|
3845
|
-
if (ev.type === "inbound") {
|
|
3846
|
-
hasInbound = true;
|
|
3847
|
-
if (ev.channel) channels.add(ev.channel);
|
|
3848
|
-
}
|
|
3849
|
-
if (ev.type === "outbound" || ev.type === "text") {
|
|
3850
|
-
hasOutbound = true;
|
|
3851
|
-
}
|
|
3852
|
-
if (ev.type === "tool_use" && ev.metadata) {
|
|
3853
|
-
try {
|
|
3854
|
-
const meta = JSON.parse(ev.metadata);
|
|
3855
|
-
if (meta.name) tools.push(meta.name);
|
|
3856
|
-
} catch (err) {
|
|
3857
|
-
sLog.debug(`failed to parse tool_use metadata for event ${ev.id}`, logger_default.errorData(err));
|
|
3858
|
-
}
|
|
3859
|
-
}
|
|
3860
|
-
}
|
|
3861
|
-
const parts = [];
|
|
3862
|
-
if (hasInbound) {
|
|
3863
|
-
const channelList = [...channels];
|
|
3864
|
-
parts.push(
|
|
3865
|
-
channelList.length > 0 ? `Received message on ${channelList.join(", ")}` : "Received message"
|
|
3866
|
-
);
|
|
3867
|
-
}
|
|
3868
|
-
if (tools.length > 0) {
|
|
3869
|
-
const unique = [...new Set(tools)];
|
|
3870
|
-
parts.push(`Used ${unique.join(", ")}`);
|
|
3871
|
-
}
|
|
3872
|
-
if (hasOutbound) {
|
|
3873
|
-
parts.push("Sent response");
|
|
3874
|
-
}
|
|
3875
|
-
return parts.length > 0 ? `${parts.join(". ")}.` : "Turn completed.";
|
|
3876
|
-
}
|
|
3877
|
-
function buildTranscript(events) {
|
|
3878
|
-
const lines = [];
|
|
3879
|
-
for (const ev of events) {
|
|
3880
|
-
switch (ev.type) {
|
|
3881
|
-
case "inbound":
|
|
3882
|
-
lines.push(`[inbound${ev.channel ? ` ${ev.channel}` : ""}] ${ev.content ?? ""}`);
|
|
3883
|
-
break;
|
|
3884
|
-
case "outbound":
|
|
3885
|
-
case "text":
|
|
3886
|
-
lines.push(`[response] ${(ev.content ?? "").slice(0, 500)}`);
|
|
3887
|
-
break;
|
|
3888
|
-
case "tool_use": {
|
|
3889
|
-
let toolInfo = "tool";
|
|
3890
|
-
if (ev.metadata) {
|
|
3891
|
-
try {
|
|
3892
|
-
const meta = JSON.parse(ev.metadata);
|
|
3893
|
-
toolInfo = summarizeTool(meta.name ?? "tool", meta.input ?? {});
|
|
3894
|
-
} catch (err) {
|
|
3895
|
-
sLog.debug(`failed to parse tool_use metadata for event ${ev.id}`, logger_default.errorData(err));
|
|
3896
|
-
}
|
|
3897
|
-
}
|
|
3898
|
-
lines.push(toolInfo);
|
|
3899
|
-
break;
|
|
3900
|
-
}
|
|
3901
|
-
case "tool_result": {
|
|
3902
|
-
const content = ev.content ?? "";
|
|
3903
|
-
let isError = false;
|
|
3904
|
-
if (ev.metadata) {
|
|
3905
|
-
try {
|
|
3906
|
-
const meta = JSON.parse(ev.metadata);
|
|
3907
|
-
isError = !!meta.is_error;
|
|
3908
|
-
} catch (err) {
|
|
3909
|
-
sLog.debug(
|
|
3910
|
-
`failed to parse tool_result metadata for event ${ev.id}`,
|
|
3911
|
-
logger_default.errorData(err)
|
|
3912
|
-
);
|
|
3913
|
-
}
|
|
3914
|
-
}
|
|
3915
|
-
lines.push(isError ? "[result error]" : `[result] ${content.slice(0, 200)}`);
|
|
3916
|
-
break;
|
|
3917
|
-
}
|
|
3918
|
-
case "thinking":
|
|
3919
|
-
lines.push(`[thinking] ${(ev.content ?? "").slice(0, 300)}`);
|
|
3920
|
-
break;
|
|
3921
|
-
}
|
|
3922
|
-
}
|
|
3923
|
-
return lines.join("\n");
|
|
3924
|
-
}
|
|
3925
|
-
async function gatherTurnEventsByTurnId(turnId) {
|
|
3926
|
-
const db = await getDb();
|
|
3927
|
-
const events = await db.select({
|
|
3928
|
-
id: mindHistory.id,
|
|
3929
|
-
type: mindHistory.type,
|
|
3930
|
-
channel: mindHistory.channel,
|
|
3931
|
-
session: mindHistory.session,
|
|
3932
|
-
content: mindHistory.content,
|
|
3933
|
-
metadata: mindHistory.metadata,
|
|
3934
|
-
created_at: mindHistory.created_at
|
|
3935
|
-
}).from(mindHistory).where(eq5(mindHistory.turn_id, turnId)).orderBy(mindHistory.id);
|
|
3936
|
-
return {
|
|
3937
|
-
events,
|
|
3938
|
-
fromId: events.length > 0 ? events[0].id : 0,
|
|
3939
|
-
toId: events.length > 0 ? events[events.length - 1].id : 0
|
|
4009
|
+
cache_creation_input_tokens: usage.cacheWrite ?? usage.cache_creation_input_tokens ?? 0
|
|
3940
4010
|
};
|
|
3941
4011
|
}
|
|
3942
|
-
|
|
3943
|
-
const
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
await db2.update(mindHistory).set({ turn_id: null }).where(and4(eq5(mindHistory.turn_id, turnId), eq5(mindHistory.type, "inbound")));
|
|
3955
|
-
await db2.update(messages).set({ turn_id: null }).where(eq5(messages.turn_id, turnId));
|
|
3956
|
-
} catch (err) {
|
|
3957
|
-
sLog.error(`failed to un-tag events for interrupted turn ${turnId}`, logger_default.errorData(err));
|
|
3958
|
-
}
|
|
3959
|
-
}
|
|
3960
|
-
return;
|
|
3961
|
-
}
|
|
3962
|
-
const tools = [];
|
|
3963
|
-
for (const ev of events) {
|
|
3964
|
-
if (ev.type === "tool_use" && ev.metadata) {
|
|
3965
|
-
try {
|
|
3966
|
-
const meta = JSON.parse(ev.metadata);
|
|
3967
|
-
if (meta.name) tools.push(meta.name);
|
|
3968
|
-
} catch (err) {
|
|
3969
|
-
sLog.debug(`failed to parse tool_use metadata for event ${ev.id}`, logger_default.errorData(err));
|
|
3970
|
-
}
|
|
3971
|
-
}
|
|
3972
|
-
}
|
|
3973
|
-
const fromTime = events[0].created_at;
|
|
3974
|
-
const toTime = events[events.length - 1].created_at;
|
|
3975
|
-
let summaryText;
|
|
3976
|
-
let deterministic;
|
|
3977
|
-
const transcript = buildTranscript(events);
|
|
3978
|
-
if (transcript.trim()) {
|
|
3979
|
-
const summaryPrompt = await getPrompt("turn_summary");
|
|
3980
|
-
const aiResult = await aiCompleteUtility(summaryPrompt, transcript);
|
|
3981
|
-
if (aiResult) {
|
|
3982
|
-
summaryText = aiResult;
|
|
3983
|
-
deterministic = false;
|
|
4012
|
+
function convertAssistantContent(content) {
|
|
4013
|
+
const result = [];
|
|
4014
|
+
for (const block of content) {
|
|
4015
|
+
if (block.type === "thinking") {
|
|
4016
|
+
} else if (block.type === "toolCall") {
|
|
4017
|
+
result.push({
|
|
4018
|
+
type: "tool_use",
|
|
4019
|
+
id: block.id,
|
|
4020
|
+
name: block.name,
|
|
4021
|
+
input: block.arguments ?? block.input ?? {},
|
|
4022
|
+
caller: { type: "direct" }
|
|
4023
|
+
});
|
|
3984
4024
|
} else {
|
|
3985
|
-
|
|
3986
|
-
deterministic = true;
|
|
4025
|
+
result.push(block);
|
|
3987
4026
|
}
|
|
3988
|
-
} else {
|
|
3989
|
-
summaryText = buildDeterministicSummary(events);
|
|
3990
|
-
deterministic = true;
|
|
3991
|
-
}
|
|
3992
|
-
const metadata = {
|
|
3993
|
-
deterministic,
|
|
3994
|
-
tool_count: tools.length,
|
|
3995
|
-
tools: [...new Set(tools)],
|
|
3996
|
-
from_id: fromId,
|
|
3997
|
-
to_id: toId,
|
|
3998
|
-
from_time: fromTime,
|
|
3999
|
-
to_time: toTime
|
|
4000
|
-
};
|
|
4001
|
-
const db = await getDb();
|
|
4002
|
-
let summaryId;
|
|
4003
|
-
try {
|
|
4004
|
-
const result = await db.insert(mindHistory).values({
|
|
4005
|
-
mind,
|
|
4006
|
-
type: "summary",
|
|
4007
|
-
session: session ?? null,
|
|
4008
|
-
channel: channel ?? null,
|
|
4009
|
-
content: summaryText,
|
|
4010
|
-
metadata: JSON.stringify(metadata),
|
|
4011
|
-
turn_id: turnId ?? null
|
|
4012
|
-
}).returning({ id: mindHistory.id });
|
|
4013
|
-
summaryId = result[0]?.id;
|
|
4014
|
-
} catch (err) {
|
|
4015
|
-
sLog.error(
|
|
4016
|
-
`failed to persist summary for ${mind} (events ${fromId}-${toId})`,
|
|
4017
|
-
logger_default.errorData(err)
|
|
4018
|
-
);
|
|
4019
|
-
return;
|
|
4020
|
-
}
|
|
4021
|
-
if (turnId && summaryId != null) {
|
|
4022
|
-
setSummaryEventId(turnId, summaryId).catch((err) => {
|
|
4023
|
-
sLog.error(`failed to link summary to turn ${turnId}`, logger_default.errorData(err));
|
|
4024
|
-
});
|
|
4025
4027
|
}
|
|
4026
|
-
|
|
4027
|
-
mind,
|
|
4028
|
-
type: "summary",
|
|
4029
|
-
session,
|
|
4030
|
-
channel,
|
|
4031
|
-
content: summaryText,
|
|
4032
|
-
metadata,
|
|
4033
|
-
turnId
|
|
4034
|
-
});
|
|
4028
|
+
return result;
|
|
4035
4029
|
}
|
|
4036
4030
|
|
|
4037
|
-
// src/lib/health.ts
|
|
4031
|
+
// packages/daemon/src/lib/health.ts
|
|
4038
4032
|
async function checkHealth(port) {
|
|
4039
4033
|
try {
|
|
4040
4034
|
const res = await fetch(`http://127.0.0.1:${port}/health`, {
|
|
@@ -4048,8 +4042,8 @@ async function checkHealth(port) {
|
|
|
4048
4042
|
}
|
|
4049
4043
|
}
|
|
4050
4044
|
|
|
4051
|
-
// src/lib/variant-cleanup.ts
|
|
4052
|
-
import { existsSync as
|
|
4045
|
+
// packages/daemon/src/lib/variant-cleanup.ts
|
|
4046
|
+
import { existsSync as existsSync7, rmSync as rmSync3 } from "fs";
|
|
4053
4047
|
async function cleanupVariant(variantName, projectRoot, variantPath, opts) {
|
|
4054
4048
|
if (opts?.stop) {
|
|
4055
4049
|
try {
|
|
@@ -4058,10 +4052,10 @@ async function cleanupVariant(variantName, projectRoot, variantPath, opts) {
|
|
|
4058
4052
|
logger_default.warn(`failed to stop variant ${variantName}`, logger_default.errorData(err));
|
|
4059
4053
|
}
|
|
4060
4054
|
}
|
|
4061
|
-
const { findMind: findMind2 } = await import("./registry-
|
|
4055
|
+
const { findMind: findMind2 } = await import("./registry-GBSNW3HG.js");
|
|
4062
4056
|
const variantEntry = await findMind2(variantName);
|
|
4063
4057
|
const branchName = variantEntry?.branch ?? variantName;
|
|
4064
|
-
if (
|
|
4058
|
+
if (existsSync7(variantPath)) {
|
|
4065
4059
|
try {
|
|
4066
4060
|
await gitExec(["worktree", "remove", "--force", variantPath], { cwd: projectRoot });
|
|
4067
4061
|
} catch {
|
|
@@ -4094,7 +4088,7 @@ async function cleanupVariant(variantName, projectRoot, variantPath, opts) {
|
|
|
4094
4088
|
}
|
|
4095
4089
|
}
|
|
4096
4090
|
|
|
4097
|
-
// src/lib/variants.ts
|
|
4091
|
+
// packages/daemon/src/lib/variants.ts
|
|
4098
4092
|
var SAFE_BRANCH_RE = /^[a-zA-Z0-9._\-/]+$/;
|
|
4099
4093
|
function validateBranchName(branch) {
|
|
4100
4094
|
if (!SAFE_BRANCH_RE.test(branch)) {
|
|
@@ -4106,13 +4100,13 @@ function validateBranchName(branch) {
|
|
|
4106
4100
|
return null;
|
|
4107
4101
|
}
|
|
4108
4102
|
|
|
4109
|
-
// src/web/api/minds.ts
|
|
4103
|
+
// packages/daemon/src/web/api/minds.ts
|
|
4110
4104
|
var SUBSTANTIVE_TYPES = /* @__PURE__ */ new Set(["thinking", "text", "tool_use", "tool_result", "outbound"]);
|
|
4111
4105
|
async function getMindStatus(name, port) {
|
|
4112
4106
|
const manager = getMindManager();
|
|
4113
4107
|
let status = "stopped";
|
|
4114
4108
|
try {
|
|
4115
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
4109
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-7KFK3USC.js");
|
|
4116
4110
|
if (getSleepManagerIfReady2()?.isSleeping(name)) {
|
|
4117
4111
|
status = "sleeping";
|
|
4118
4112
|
}
|
|
@@ -4158,7 +4152,7 @@ async function initTemplateBranch(projectRoot, composedDir, manifest, mindName,
|
|
|
4158
4152
|
await gitExec(["commit", "-m", "initial commit"], opts);
|
|
4159
4153
|
}
|
|
4160
4154
|
async function updateTemplateBranch(projectRoot, template, mindName) {
|
|
4161
|
-
const tempWorktree =
|
|
4155
|
+
const tempWorktree = resolve11(projectRoot, ".variants", "_template_update");
|
|
4162
4156
|
let branchExists = false;
|
|
4163
4157
|
try {
|
|
4164
4158
|
await gitExec(["rev-parse", "--verify", TEMPLATE_BRANCH], { cwd: projectRoot });
|
|
@@ -4169,7 +4163,7 @@ async function updateTemplateBranch(projectRoot, template, mindName) {
|
|
|
4169
4163
|
await gitExec(["worktree", "remove", "--force", tempWorktree], { cwd: projectRoot });
|
|
4170
4164
|
} catch {
|
|
4171
4165
|
}
|
|
4172
|
-
if (
|
|
4166
|
+
if (existsSync8(tempWorktree)) {
|
|
4173
4167
|
rmSync4(tempWorktree, { recursive: true, force: true });
|
|
4174
4168
|
}
|
|
4175
4169
|
const templatesRoot = findTemplatesRoot();
|
|
@@ -4190,15 +4184,15 @@ async function updateTemplateBranch(projectRoot, template, mindName) {
|
|
|
4190
4184
|
});
|
|
4191
4185
|
}
|
|
4192
4186
|
copyTemplateToDir(composedDir, tempWorktree, mindName, manifest);
|
|
4193
|
-
const initDir =
|
|
4194
|
-
if (
|
|
4187
|
+
const initDir = resolve11(tempWorktree, ".init");
|
|
4188
|
+
if (existsSync8(initDir)) {
|
|
4195
4189
|
rmSync4(initDir, { recursive: true, force: true });
|
|
4196
4190
|
}
|
|
4197
|
-
const homeDir =
|
|
4198
|
-
if (
|
|
4191
|
+
const homeDir = resolve11(tempWorktree, "home");
|
|
4192
|
+
if (existsSync8(homeDir)) {
|
|
4199
4193
|
for (const entry of readdirSync2(homeDir)) {
|
|
4200
4194
|
if (entry !== "VOLUTE.md") {
|
|
4201
|
-
rmSync4(
|
|
4195
|
+
rmSync4(resolve11(homeDir, entry), { recursive: true, force: true });
|
|
4202
4196
|
}
|
|
4203
4197
|
}
|
|
4204
4198
|
}
|
|
@@ -4213,7 +4207,7 @@ async function updateTemplateBranch(projectRoot, template, mindName) {
|
|
|
4213
4207
|
await gitExec(["worktree", "remove", "--force", tempWorktree], { cwd: projectRoot });
|
|
4214
4208
|
} catch {
|
|
4215
4209
|
}
|
|
4216
|
-
if (
|
|
4210
|
+
if (existsSync8(tempWorktree)) {
|
|
4217
4211
|
rmSync4(tempWorktree, { recursive: true, force: true });
|
|
4218
4212
|
}
|
|
4219
4213
|
rmSync4(composedDir, { recursive: true, force: true });
|
|
@@ -4239,7 +4233,7 @@ async function mergeTemplateBranch(worktreeDir) {
|
|
|
4239
4233
|
async function npmInstallAsMind(cwd, mindName) {
|
|
4240
4234
|
if (isIsolationEnabled()) {
|
|
4241
4235
|
const [cmd, args] = await wrapForIsolation("npm", ["install"], mindName);
|
|
4242
|
-
await exec(cmd, args, { cwd, env: { ...process.env, HOME:
|
|
4236
|
+
await exec(cmd, args, { cwd, env: { ...process.env, HOME: resolve11(cwd, "home") } });
|
|
4243
4237
|
} else {
|
|
4244
4238
|
await exec("npm", ["install"], { cwd });
|
|
4245
4239
|
}
|
|
@@ -4290,8 +4284,8 @@ async function mergeUpgradeAndRestart(mindName, dir, worktreeDir, upgradeVariant
|
|
|
4290
4284
|
return { ok: true };
|
|
4291
4285
|
}
|
|
4292
4286
|
async function importFromArchive(c, tempDir, nameOverride, manifest) {
|
|
4293
|
-
const extractedMindDir =
|
|
4294
|
-
if (!
|
|
4287
|
+
const extractedMindDir = resolve11(tempDir, "mind");
|
|
4288
|
+
if (!existsSync8(extractedMindDir)) {
|
|
4295
4289
|
return c.json({ error: "Invalid archive: missing mind/ directory" }, 400);
|
|
4296
4290
|
}
|
|
4297
4291
|
if (!manifest?.includes || !manifest.name || !manifest.template) {
|
|
@@ -4309,7 +4303,7 @@ async function importFromFullArchive(c, tempDir, extractedMindDir, nameOverride,
|
|
|
4309
4303
|
if (await findMind(name)) return c.json({ error: `Mind already exists: ${name}` }, 409);
|
|
4310
4304
|
ensureVoluteHome();
|
|
4311
4305
|
const dest = mindDir(name);
|
|
4312
|
-
if (
|
|
4306
|
+
if (existsSync8(dest)) return c.json({ error: "Mind directory already exists" }, 409);
|
|
4313
4307
|
try {
|
|
4314
4308
|
cpSync(extractedMindDir, dest, { recursive: true });
|
|
4315
4309
|
if (!manifest.includes.identity) {
|
|
@@ -4317,9 +4311,9 @@ async function importFromFullArchive(c, tempDir, extractedMindDir, nameOverride,
|
|
|
4317
4311
|
}
|
|
4318
4312
|
const state = stateDir(name);
|
|
4319
4313
|
mkdirSync6(state, { recursive: true });
|
|
4320
|
-
const envJson =
|
|
4321
|
-
if (
|
|
4322
|
-
cpSync(envJson,
|
|
4314
|
+
const envJson = resolve11(tempDir, "state/env.json");
|
|
4315
|
+
if (existsSync8(envJson)) {
|
|
4316
|
+
cpSync(envJson, resolve11(state, "env.json"));
|
|
4323
4317
|
}
|
|
4324
4318
|
const port = await nextPort();
|
|
4325
4319
|
await addMind(name, port, manifest.stage, manifest.template);
|
|
@@ -4328,30 +4322,30 @@ async function importFromFullArchive(c, tempDir, extractedMindDir, nameOverride,
|
|
|
4328
4322
|
} catch (err) {
|
|
4329
4323
|
logger_default.warn(`failed to set template hash for ${name}`, logger_default.errorData(err));
|
|
4330
4324
|
}
|
|
4331
|
-
const homeDir =
|
|
4325
|
+
const homeDir = resolve11(dest, "home");
|
|
4332
4326
|
ensureVoluteGroup();
|
|
4333
4327
|
createMindUser(name, homeDir);
|
|
4334
4328
|
chownMindDir(dest, name);
|
|
4335
4329
|
await npmInstallAsMind(dest, name);
|
|
4336
4330
|
await importHistoryFromArchive(name, tempDir);
|
|
4337
4331
|
importSessionsFromArchive(dest, tempDir);
|
|
4338
|
-
if (!
|
|
4332
|
+
if (!existsSync8(resolve11(dest, ".git"))) {
|
|
4339
4333
|
try {
|
|
4340
|
-
const env = isIsolationEnabled() ? { ...process.env, HOME:
|
|
4334
|
+
const env = isIsolationEnabled() ? { ...process.env, HOME: resolve11(dest, "home") } : void 0;
|
|
4341
4335
|
await gitExec(["init"], { cwd: dest, mindName: name, env });
|
|
4342
4336
|
await configureGitIdentity(name, { cwd: dest, mindName: name, env });
|
|
4343
4337
|
await gitExec(["add", "-A"], { cwd: dest, mindName: name, env });
|
|
4344
4338
|
await gitExec(["commit", "-m", "import from archive"], { cwd: dest, mindName: name, env });
|
|
4345
4339
|
} catch (err) {
|
|
4346
4340
|
logger_default.error(`git setup failed for imported mind ${name}`, logger_default.errorData(err));
|
|
4347
|
-
rmSync4(
|
|
4341
|
+
rmSync4(resolve11(dest, ".git"), { recursive: true, force: true });
|
|
4348
4342
|
}
|
|
4349
4343
|
}
|
|
4350
4344
|
chownMindDir(dest, name);
|
|
4351
4345
|
rmSync4(tempDir, { recursive: true, force: true });
|
|
4352
4346
|
return c.json({ ok: true, name, port, message: `Imported mind: ${name} (port ${port})` });
|
|
4353
4347
|
} catch (err) {
|
|
4354
|
-
if (
|
|
4348
|
+
if (existsSync8(dest)) rmSync4(dest, { recursive: true, force: true });
|
|
4355
4349
|
try {
|
|
4356
4350
|
await removeMind(name);
|
|
4357
4351
|
} catch (cleanupErr) {
|
|
@@ -4368,7 +4362,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
|
|
|
4368
4362
|
if (await findMind(name)) return c.json({ error: `Mind already exists: ${name}` }, 409);
|
|
4369
4363
|
ensureVoluteHome();
|
|
4370
4364
|
const dest = mindDir(name);
|
|
4371
|
-
if (
|
|
4365
|
+
if (existsSync8(dest)) return c.json({ error: "Mind directory already exists" }, 409);
|
|
4372
4366
|
const templatesRoot = findTemplatesRoot();
|
|
4373
4367
|
const { composedDir, manifest: templateManifest } = composeTemplate(
|
|
4374
4368
|
templatesRoot,
|
|
@@ -4377,36 +4371,36 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
|
|
|
4377
4371
|
try {
|
|
4378
4372
|
copyTemplateToDir(composedDir, dest, name, templateManifest);
|
|
4379
4373
|
applyInitFiles(dest);
|
|
4380
|
-
const extractedHome =
|
|
4381
|
-
if (
|
|
4382
|
-
cpSync(extractedHome,
|
|
4374
|
+
const extractedHome = resolve11(extractedMindDir, "home");
|
|
4375
|
+
if (existsSync8(extractedHome)) {
|
|
4376
|
+
cpSync(extractedHome, resolve11(dest, "home"), { recursive: true });
|
|
4383
4377
|
}
|
|
4384
|
-
const extractedMindInternal =
|
|
4385
|
-
if (
|
|
4386
|
-
cpSync(extractedMindInternal,
|
|
4378
|
+
const extractedMindInternal = resolve11(extractedMindDir, ".mind");
|
|
4379
|
+
if (existsSync8(extractedMindInternal)) {
|
|
4380
|
+
cpSync(extractedMindInternal, resolve11(dest, ".mind"), { recursive: true });
|
|
4387
4381
|
}
|
|
4388
|
-
const identityDir =
|
|
4382
|
+
const identityDir = resolve11(dest, ".mind/identity");
|
|
4389
4383
|
let publicKeyPem;
|
|
4390
|
-
if (!manifest.includes.identity || !
|
|
4384
|
+
if (!manifest.includes.identity || !existsSync8(resolve11(identityDir, "private.pem"))) {
|
|
4391
4385
|
({ publicKeyPem } = generateIdentity(dest));
|
|
4392
4386
|
} else {
|
|
4393
|
-
publicKeyPem =
|
|
4387
|
+
publicKeyPem = readFileSync9(resolve11(identityDir, "public.pem"), "utf-8");
|
|
4394
4388
|
}
|
|
4395
|
-
const promptsPath =
|
|
4396
|
-
if (!
|
|
4389
|
+
const promptsPath = resolve11(dest, "home/.config/prompts.json");
|
|
4390
|
+
if (!existsSync8(promptsPath)) {
|
|
4397
4391
|
const mindPrompts = await getMindPromptDefaults();
|
|
4398
4392
|
writeFileSync7(promptsPath, `${JSON.stringify(mindPrompts, null, 2)}
|
|
4399
4393
|
`);
|
|
4400
4394
|
}
|
|
4401
4395
|
const state = stateDir(name);
|
|
4402
4396
|
mkdirSync6(state, { recursive: true });
|
|
4403
|
-
const envJson =
|
|
4404
|
-
if (
|
|
4405
|
-
cpSync(envJson,
|
|
4397
|
+
const envJson = resolve11(tempDir, "state/env.json");
|
|
4398
|
+
if (existsSync8(envJson)) {
|
|
4399
|
+
cpSync(envJson, resolve11(state, "env.json"));
|
|
4406
4400
|
}
|
|
4407
4401
|
const port = await nextPort();
|
|
4408
4402
|
await addMind(name, port, manifest.stage, manifest.template);
|
|
4409
|
-
const homeDir =
|
|
4403
|
+
const homeDir = resolve11(dest, "home");
|
|
4410
4404
|
ensureVoluteGroup();
|
|
4411
4405
|
createMindUser(name, homeDir);
|
|
4412
4406
|
chownMindDir(dest, name);
|
|
@@ -4419,14 +4413,9 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
|
|
|
4419
4413
|
await initTemplateBranch(dest, composedDir, templateManifest, name, env);
|
|
4420
4414
|
} catch (err) {
|
|
4421
4415
|
logger_default.error(`git setup failed for imported mind ${name}`, logger_default.errorData(err));
|
|
4422
|
-
rmSync4(
|
|
4416
|
+
rmSync4(resolve11(dest, ".git"), { recursive: true, force: true });
|
|
4423
4417
|
gitWarning = "Git setup failed \u2014 variants and upgrades won't be available until git is initialized.";
|
|
4424
4418
|
}
|
|
4425
|
-
try {
|
|
4426
|
-
await addSharedWorktree(name, dest);
|
|
4427
|
-
} catch (err) {
|
|
4428
|
-
logger_default.warn(`failed to add shared worktree for ${name}`, logger_default.errorData(err));
|
|
4429
|
-
}
|
|
4430
4419
|
const skillSet = manifest.stage === "seed" ? SEED_SKILLS : getStandardSkillsWithExtensions();
|
|
4431
4420
|
const skillWarnings = [];
|
|
4432
4421
|
for (const skillId of skillSet) {
|
|
@@ -4454,7 +4443,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
|
|
|
4454
4443
|
...skillWarnings.length > 0 && { skillWarnings }
|
|
4455
4444
|
});
|
|
4456
4445
|
} catch (err) {
|
|
4457
|
-
if (
|
|
4446
|
+
if (existsSync8(dest)) rmSync4(dest, { recursive: true, force: true });
|
|
4458
4447
|
try {
|
|
4459
4448
|
await removeMind(name);
|
|
4460
4449
|
} catch (cleanupErr) {
|
|
@@ -4467,11 +4456,11 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
|
|
|
4467
4456
|
}
|
|
4468
4457
|
}
|
|
4469
4458
|
async function importHistoryFromArchive(name, tempDir) {
|
|
4470
|
-
const historyJsonl =
|
|
4471
|
-
if (!
|
|
4459
|
+
const historyJsonl = resolve11(tempDir, "history.jsonl");
|
|
4460
|
+
if (!existsSync8(historyJsonl)) return;
|
|
4472
4461
|
try {
|
|
4473
4462
|
const db = await getDb();
|
|
4474
|
-
const lines =
|
|
4463
|
+
const lines = readFileSync9(historyJsonl, "utf-8").trim().split("\n");
|
|
4475
4464
|
let imported = 0;
|
|
4476
4465
|
let failed = 0;
|
|
4477
4466
|
for (const line of lines) {
|
|
@@ -4507,13 +4496,13 @@ async function importHistoryFromArchive(name, tempDir) {
|
|
|
4507
4496
|
}
|
|
4508
4497
|
}
|
|
4509
4498
|
function importSessionsFromArchive(dest, tempDir) {
|
|
4510
|
-
const sessionsDir =
|
|
4511
|
-
if (!
|
|
4499
|
+
const sessionsDir = resolve11(tempDir, "sessions");
|
|
4500
|
+
if (!existsSync8(sessionsDir)) return;
|
|
4512
4501
|
try {
|
|
4513
|
-
const destSessions =
|
|
4502
|
+
const destSessions = resolve11(dest, ".mind/sessions");
|
|
4514
4503
|
mkdirSync6(destSessions, { recursive: true });
|
|
4515
4504
|
for (const file of readdirSync2(sessionsDir)) {
|
|
4516
|
-
cpSync(
|
|
4505
|
+
cpSync(resolve11(sessionsDir, file), resolve11(destSessions, file));
|
|
4517
4506
|
}
|
|
4518
4507
|
} catch (err) {
|
|
4519
4508
|
logger_default.error("Failed to import sessions from archive", logger_default.errorData(err));
|
|
@@ -4548,7 +4537,7 @@ var app13 = new Hono15().post("/", requireAdminOrSystem, zValidator5("json", cre
|
|
|
4548
4537
|
if (await findMind(name)) return c.json({ error: `Mind already exists: ${name}` }, 409);
|
|
4549
4538
|
ensureVoluteHome();
|
|
4550
4539
|
const dest = mindDir(name);
|
|
4551
|
-
if (
|
|
4540
|
+
if (existsSync8(dest)) return c.json({ error: "Mind directory already exists" }, 409);
|
|
4552
4541
|
const templatesRoot = findTemplatesRoot();
|
|
4553
4542
|
const { composedDir, manifest } = composeTemplate(templatesRoot, template);
|
|
4554
4543
|
try {
|
|
@@ -4556,19 +4545,21 @@ var app13 = new Hono15().post("/", requireAdminOrSystem, zValidator5("json", cre
|
|
|
4556
4545
|
applyInitFiles(dest);
|
|
4557
4546
|
const { publicKeyPem } = generateIdentity(dest);
|
|
4558
4547
|
{
|
|
4548
|
+
const { readGlobalConfig: readGlobal } = await import("./setup-RHJRFURI.js");
|
|
4549
|
+
const mindDefaults = readGlobal().mindDefaults;
|
|
4559
4550
|
const config2 = readVoluteConfig(dest);
|
|
4560
4551
|
if (!config2) throw new Error("Failed to read volute.json after identity generation");
|
|
4561
4552
|
if (body.description) {
|
|
4562
4553
|
config2.profile = { ...config2.profile, description: body.description };
|
|
4563
4554
|
}
|
|
4564
4555
|
if (!config2.sleep) {
|
|
4565
|
-
config2.sleep = {
|
|
4556
|
+
config2.sleep = mindDefaults?.sleep ?? {
|
|
4566
4557
|
enabled: true,
|
|
4567
4558
|
schedule: { sleep: "0 0 * * *", wake: "0 8 * * *" }
|
|
4568
4559
|
};
|
|
4569
4560
|
}
|
|
4570
4561
|
if (!config2.schedules || config2.schedules.length === 0) {
|
|
4571
|
-
config2.schedules = [
|
|
4562
|
+
config2.schedules = mindDefaults?.schedules ?? [
|
|
4572
4563
|
{
|
|
4573
4564
|
id: "heartbeat",
|
|
4574
4565
|
cron: "0 12,16,20 * * *",
|
|
@@ -4578,18 +4569,35 @@ var app13 = new Hono15().post("/", requireAdminOrSystem, zValidator5("json", cre
|
|
|
4578
4569
|
}
|
|
4579
4570
|
];
|
|
4580
4571
|
}
|
|
4572
|
+
const cog = mindDefaults?.cognition;
|
|
4573
|
+
if (cog) {
|
|
4574
|
+
if (cog.thinkingLevel != null && !config2.thinkingLevel)
|
|
4575
|
+
config2.thinkingLevel = cog.thinkingLevel;
|
|
4576
|
+
if (cog.maxThinkingTokens != null && config2.maxThinkingTokens == null)
|
|
4577
|
+
config2.maxThinkingTokens = cog.maxThinkingTokens;
|
|
4578
|
+
if (cog.tokenBudget != null && config2.tokenBudget == null)
|
|
4579
|
+
config2.tokenBudget = cog.tokenBudget;
|
|
4580
|
+
if (cog.tokenBudgetPeriodMinutes != null && config2.tokenBudgetPeriodMinutes == null)
|
|
4581
|
+
config2.tokenBudgetPeriodMinutes = cog.tokenBudgetPeriodMinutes;
|
|
4582
|
+
}
|
|
4581
4583
|
writeVoluteConfig(dest, config2);
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4584
|
+
const modelId = body.model ?? cog?.model;
|
|
4585
|
+
const sdkConfigPath = resolve11(dest, "home/.config/config.json");
|
|
4586
|
+
if (modelId || cog?.compaction) {
|
|
4587
|
+
const existing = existsSync8(sdkConfigPath) ? JSON.parse(readFileSync9(sdkConfigPath, "utf-8")) : {};
|
|
4588
|
+
if (modelId) {
|
|
4589
|
+
existing.model = template === "pi" ? qualifyModelId(modelId) : unqualifyModelId(modelId);
|
|
4590
|
+
}
|
|
4591
|
+
if (cog?.compaction && !existing.compaction) {
|
|
4592
|
+
existing.compaction = cog.compaction;
|
|
4593
|
+
}
|
|
4594
|
+
writeFileSync7(sdkConfigPath, `${JSON.stringify(existing, null, 2)}
|
|
4588
4595
|
`);
|
|
4596
|
+
}
|
|
4589
4597
|
}
|
|
4590
4598
|
const mindPrompts = await getMindPromptDefaults();
|
|
4591
4599
|
writeFileSync7(
|
|
4592
|
-
|
|
4600
|
+
resolve11(dest, "home/.config/prompts.json"),
|
|
4593
4601
|
`${JSON.stringify(mindPrompts, null, 2)}
|
|
4594
4602
|
`
|
|
4595
4603
|
);
|
|
@@ -4601,7 +4609,7 @@ var app13 = new Hono15().post("/", requireAdminOrSystem, zValidator5("json", cre
|
|
|
4601
4609
|
} catch (err) {
|
|
4602
4610
|
logger_default.warn(`failed to set template hash for ${name}`, logger_default.errorData(err));
|
|
4603
4611
|
}
|
|
4604
|
-
const homeDir =
|
|
4612
|
+
const homeDir = resolve11(dest, "home");
|
|
4605
4613
|
ensureVoluteGroup();
|
|
4606
4614
|
createMindUser(name, homeDir);
|
|
4607
4615
|
chownMindDir(dest, name);
|
|
@@ -4614,14 +4622,9 @@ var app13 = new Hono15().post("/", requireAdminOrSystem, zValidator5("json", cre
|
|
|
4614
4622
|
await initTemplateBranch(dest, composedDir, manifest, name, env);
|
|
4615
4623
|
} catch (err) {
|
|
4616
4624
|
logger_default.error(`git setup failed for ${name}`, logger_default.errorData(err));
|
|
4617
|
-
rmSync4(
|
|
4625
|
+
rmSync4(resolve11(dest, ".git"), { recursive: true, force: true });
|
|
4618
4626
|
gitWarning = "Git setup failed \u2014 variants and upgrades won't be available until git is initialized.";
|
|
4619
4627
|
}
|
|
4620
|
-
try {
|
|
4621
|
-
await addSharedWorktree(name, dest);
|
|
4622
|
-
} catch (err) {
|
|
4623
|
-
logger_default.warn(`failed to add shared worktree for ${name}`, logger_default.errorData(err));
|
|
4624
|
-
}
|
|
4625
4628
|
chownMindDir(dest, name);
|
|
4626
4629
|
if (body.stage === "seed") {
|
|
4627
4630
|
const descLine = body.description ? `
|
|
@@ -4629,11 +4632,11 @@ The human who planted you described you as: "${body.description}"
|
|
|
4629
4632
|
` : "";
|
|
4630
4633
|
const seedSoulRaw = body.seedSoul ?? await getPrompt("seed_soul", { name, description: descLine });
|
|
4631
4634
|
const seedSoul = body.seedSoul ? substitute(seedSoulRaw, { name, description: descLine }) : seedSoulRaw;
|
|
4632
|
-
writeFileSync7(
|
|
4635
|
+
writeFileSync7(resolve11(dest, "home/SOUL.md"), seedSoul);
|
|
4633
4636
|
}
|
|
4634
4637
|
let skillSet = body.skills ?? (body.stage === "seed" ? SEED_SKILLS : getStandardSkillsWithExtensions());
|
|
4635
4638
|
if (body.stage === "seed" && !body.skills) {
|
|
4636
|
-
const { isImagegenEnabled } = await import("./setup-
|
|
4639
|
+
const { isImagegenEnabled } = await import("./setup-RHJRFURI.js");
|
|
4637
4640
|
if (isImagegenEnabled()) {
|
|
4638
4641
|
skillSet = [...skillSet, "imagegen"];
|
|
4639
4642
|
}
|
|
@@ -4651,7 +4654,7 @@ The human who planted you described you as: "${body.description}"
|
|
|
4651
4654
|
try {
|
|
4652
4655
|
const spiritEntry = await findMind("volute");
|
|
4653
4656
|
if (spiritEntry) {
|
|
4654
|
-
const { spiritDir } = await import("./spirit-
|
|
4657
|
+
const { spiritDir } = await import("./spirit-ZFRDXMG7.js");
|
|
4655
4658
|
const sDir = spiritEntry.dir ?? spiritDir();
|
|
4656
4659
|
const spiritConfig = readVoluteConfig(sDir) ?? {};
|
|
4657
4660
|
const schedules = spiritConfig.schedules ?? [];
|
|
@@ -4666,7 +4669,7 @@ The human who planted you described you as: "${body.description}"
|
|
|
4666
4669
|
});
|
|
4667
4670
|
spiritConfig.schedules = schedules;
|
|
4668
4671
|
writeVoluteConfig(sDir, spiritConfig);
|
|
4669
|
-
const { getScheduler: getScheduler2 } = await import("./scheduler-
|
|
4672
|
+
const { getScheduler: getScheduler2 } = await import("./scheduler-Y7O4CJXL.js");
|
|
4670
4673
|
getScheduler2().loadSchedules("volute", sDir);
|
|
4671
4674
|
}
|
|
4672
4675
|
}
|
|
@@ -4677,11 +4680,11 @@ The human who planted you described you as: "${body.description}"
|
|
|
4677
4680
|
if (body.stage !== "seed") {
|
|
4678
4681
|
const customSoul = await getPromptIfCustom("default_soul");
|
|
4679
4682
|
if (customSoul) {
|
|
4680
|
-
writeFileSync7(
|
|
4683
|
+
writeFileSync7(resolve11(dest, "home/SOUL.md"), customSoul.replace(/\{\{name\}\}/g, name));
|
|
4681
4684
|
}
|
|
4682
4685
|
const customMemory = await getPromptIfCustom("default_memory");
|
|
4683
4686
|
if (customMemory) {
|
|
4684
|
-
writeFileSync7(
|
|
4687
|
+
writeFileSync7(resolve11(dest, "home/MEMORY.md"), customMemory);
|
|
4685
4688
|
}
|
|
4686
4689
|
}
|
|
4687
4690
|
publishPublicKey(name, publicKeyPem).catch(
|
|
@@ -4710,7 +4713,7 @@ The human who planted you described you as: "${body.description}"
|
|
|
4710
4713
|
...skillWarnings.length > 0 && { skillWarnings }
|
|
4711
4714
|
});
|
|
4712
4715
|
} catch (err) {
|
|
4713
|
-
if (
|
|
4716
|
+
if (existsSync8(dest)) rmSync4(dest, { recursive: true, force: true });
|
|
4714
4717
|
try {
|
|
4715
4718
|
await removeMind(name);
|
|
4716
4719
|
} catch {
|
|
@@ -4730,13 +4733,13 @@ The human who planted you described you as: "${body.description}"
|
|
|
4730
4733
|
return importFromArchive(c, body.archivePath, body.name, body.manifest);
|
|
4731
4734
|
}
|
|
4732
4735
|
const wsDir = body.workspacePath;
|
|
4733
|
-
if (!wsDir || !
|
|
4736
|
+
if (!wsDir || !existsSync8(resolve11(wsDir, "SOUL.md")) || !existsSync8(resolve11(wsDir, "IDENTITY.md"))) {
|
|
4734
4737
|
return c.json({ error: "Invalid workspace: missing SOUL.md or IDENTITY.md" }, 400);
|
|
4735
4738
|
}
|
|
4736
|
-
const soul =
|
|
4737
|
-
const identity =
|
|
4738
|
-
const userPath =
|
|
4739
|
-
const user =
|
|
4739
|
+
const soul = readFileSync9(resolve11(wsDir, "SOUL.md"), "utf-8");
|
|
4740
|
+
const identity = readFileSync9(resolve11(wsDir, "IDENTITY.md"), "utf-8");
|
|
4741
|
+
const userPath = resolve11(wsDir, "USER.md");
|
|
4742
|
+
const user = existsSync8(userPath) ? readFileSync9(userPath, "utf-8") : "";
|
|
4740
4743
|
const name = body.name ?? parseNameFromIdentity(identity) ?? "imported-mind";
|
|
4741
4744
|
const template = body.template ?? "claude";
|
|
4742
4745
|
const nameErr = validateMindName(name);
|
|
@@ -4756,33 +4759,33 @@ ${user.trimEnd()}
|
|
|
4756
4759
|
` : "";
|
|
4757
4760
|
ensureVoluteHome();
|
|
4758
4761
|
const dest = mindDir(name);
|
|
4759
|
-
if (
|
|
4762
|
+
if (existsSync8(dest)) return c.json({ error: "Mind directory already exists" }, 409);
|
|
4760
4763
|
const templatesRoot = findTemplatesRoot();
|
|
4761
4764
|
const { composedDir, manifest } = composeTemplate(templatesRoot, template);
|
|
4762
4765
|
try {
|
|
4763
4766
|
copyTemplateToDir(composedDir, dest, name, manifest);
|
|
4764
4767
|
applyInitFiles(dest);
|
|
4765
4768
|
const { publicKeyPem: importPublicKey } = generateIdentity(dest);
|
|
4766
|
-
writeFileSync7(
|
|
4767
|
-
const wsMemoryPath =
|
|
4768
|
-
const hasMemory =
|
|
4769
|
+
writeFileSync7(resolve11(dest, "home/SOUL.md"), mergedSoul);
|
|
4770
|
+
const wsMemoryPath = resolve11(wsDir, "MEMORY.md");
|
|
4771
|
+
const hasMemory = existsSync8(wsMemoryPath);
|
|
4769
4772
|
if (hasMemory) {
|
|
4770
|
-
const existingMemory =
|
|
4773
|
+
const existingMemory = readFileSync9(wsMemoryPath, "utf-8");
|
|
4771
4774
|
writeFileSync7(
|
|
4772
|
-
|
|
4775
|
+
resolve11(dest, "home/MEMORY.md"),
|
|
4773
4776
|
`${existingMemory.trimEnd()}${mergedMemoryExtra}`
|
|
4774
4777
|
);
|
|
4775
4778
|
} else if (user) {
|
|
4776
|
-
writeFileSync7(
|
|
4779
|
+
writeFileSync7(resolve11(dest, "home/MEMORY.md"), `${user.trimEnd()}
|
|
4777
4780
|
`);
|
|
4778
4781
|
}
|
|
4779
|
-
const wsMemoryDir =
|
|
4782
|
+
const wsMemoryDir = resolve11(wsDir, "memory");
|
|
4780
4783
|
let dailyLogCount = 0;
|
|
4781
|
-
if (
|
|
4782
|
-
const destMemoryDir =
|
|
4784
|
+
if (existsSync8(wsMemoryDir)) {
|
|
4785
|
+
const destMemoryDir = resolve11(dest, "home/memory");
|
|
4783
4786
|
const files = readdirSync2(wsMemoryDir).filter((f) => f.endsWith(".md"));
|
|
4784
4787
|
for (const file of files) {
|
|
4785
|
-
cpSync(
|
|
4788
|
+
cpSync(resolve11(wsMemoryDir, file), resolve11(destMemoryDir, file));
|
|
4786
4789
|
}
|
|
4787
4790
|
dailyLogCount = files.length;
|
|
4788
4791
|
}
|
|
@@ -4793,7 +4796,7 @@ ${user.trimEnd()}
|
|
|
4793
4796
|
} catch (err) {
|
|
4794
4797
|
logger_default.warn(`failed to set template hash for ${name}`, logger_default.errorData(err));
|
|
4795
4798
|
}
|
|
4796
|
-
const homeDir =
|
|
4799
|
+
const homeDir = resolve11(dest, "home");
|
|
4797
4800
|
ensureVoluteGroup();
|
|
4798
4801
|
createMindUser(name, homeDir);
|
|
4799
4802
|
chownMindDir(dest, name);
|
|
@@ -4801,35 +4804,30 @@ ${user.trimEnd()}
|
|
|
4801
4804
|
if (!hasMemory && dailyLogCount > 0) {
|
|
4802
4805
|
await consolidateMemory(dest);
|
|
4803
4806
|
}
|
|
4804
|
-
const env = isIsolationEnabled() ? { ...process.env, HOME:
|
|
4807
|
+
const env = isIsolationEnabled() ? { ...process.env, HOME: resolve11(dest, "home") } : void 0;
|
|
4805
4808
|
await gitExec(["init"], { cwd: dest, mindName: name, env });
|
|
4806
4809
|
await configureGitIdentity(name, { cwd: dest, mindName: name, env });
|
|
4807
4810
|
await gitExec(["add", "-A"], { cwd: dest, mindName: name, env });
|
|
4808
4811
|
await gitExec(["commit", "-m", "import from OpenClaw"], { cwd: dest, mindName: name, env });
|
|
4809
|
-
const sessionFile = body.sessionPath ?
|
|
4810
|
-
if (sessionFile &&
|
|
4812
|
+
const sessionFile = body.sessionPath ? resolve11(body.sessionPath) : findOpenClawSession(wsDir);
|
|
4813
|
+
if (sessionFile && existsSync8(sessionFile)) {
|
|
4811
4814
|
if (template === "pi") {
|
|
4812
4815
|
importPiSession(sessionFile, dest);
|
|
4813
4816
|
} else if (template === "claude") {
|
|
4814
4817
|
const sessionId = convertSession({ sessionPath: sessionFile, projectDir: dest });
|
|
4815
|
-
const mindRuntimeDir =
|
|
4818
|
+
const mindRuntimeDir = resolve11(dest, ".mind");
|
|
4816
4819
|
mkdirSync6(mindRuntimeDir, { recursive: true });
|
|
4817
|
-
writeFileSync7(
|
|
4820
|
+
writeFileSync7(resolve11(mindRuntimeDir, "session.json"), JSON.stringify({ sessionId }));
|
|
4818
4821
|
}
|
|
4819
4822
|
}
|
|
4820
4823
|
importOpenClawConnectors(name, dest);
|
|
4821
|
-
try {
|
|
4822
|
-
await addSharedWorktree(name, dest);
|
|
4823
|
-
} catch (err) {
|
|
4824
|
-
logger_default.warn(`failed to add shared worktree for ${name}`, logger_default.errorData(err));
|
|
4825
|
-
}
|
|
4826
4824
|
chownMindDir(dest, name);
|
|
4827
4825
|
publishPublicKey(name, importPublicKey).catch(
|
|
4828
4826
|
(err) => logger_default.warn(`failed to publish key for ${name}`, { error: err.message })
|
|
4829
4827
|
);
|
|
4830
4828
|
return c.json({ ok: true, name, port, message: `Imported mind: ${name} (port ${port})` });
|
|
4831
4829
|
} catch (err) {
|
|
4832
|
-
if (
|
|
4830
|
+
if (existsSync8(dest)) rmSync4(dest, { recursive: true, force: true });
|
|
4833
4831
|
try {
|
|
4834
4832
|
await removeMind(name);
|
|
4835
4833
|
} catch {
|
|
@@ -4845,7 +4843,7 @@ ${user.trimEnd()}
|
|
|
4845
4843
|
const db = await getDb();
|
|
4846
4844
|
const lastActiveRows = await db.select({
|
|
4847
4845
|
mind: mindHistory.mind,
|
|
4848
|
-
lastActiveAt:
|
|
4846
|
+
lastActiveAt: sql2`MAX(${mindHistory.created_at})`
|
|
4849
4847
|
}).from(mindHistory).groupBy(mindHistory.mind);
|
|
4850
4848
|
lastActiveMap = new Map(lastActiveRows.map((r) => [r.mind, r.lastActiveAt]));
|
|
4851
4849
|
} catch {
|
|
@@ -4853,7 +4851,7 @@ ${user.trimEnd()}
|
|
|
4853
4851
|
const minds = await Promise.all(
|
|
4854
4852
|
entries.map(async (entry) => {
|
|
4855
4853
|
const mindStatus = await getMindStatus(entry.name, entry.port);
|
|
4856
|
-
const hasPages =
|
|
4854
|
+
const hasPages = existsSync8(resolve11(mindDir(entry.name), "home", "pages"));
|
|
4857
4855
|
return {
|
|
4858
4856
|
...entry,
|
|
4859
4857
|
...mindStatus,
|
|
@@ -4868,7 +4866,7 @@ ${user.trimEnd()}
|
|
|
4868
4866
|
const entry = await findMind(name);
|
|
4869
4867
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
4870
4868
|
const dir = entry.dir ?? mindDir(entry.parent ?? name);
|
|
4871
|
-
if (!
|
|
4869
|
+
if (!existsSync8(dir)) return c.json({ error: "Mind directory missing" }, 404);
|
|
4872
4870
|
const mindStatus = await getMindStatus(name, entry.port);
|
|
4873
4871
|
const variants = await findVariants(name);
|
|
4874
4872
|
const manager = getMindManager();
|
|
@@ -4882,8 +4880,34 @@ ${user.trimEnd()}
|
|
|
4882
4880
|
return { name: s.name, port: s.port, status: variantStatus };
|
|
4883
4881
|
})
|
|
4884
4882
|
);
|
|
4885
|
-
const hasPages =
|
|
4883
|
+
const hasPages = existsSync8(resolve11(mindDir(name), "home", "pages"));
|
|
4886
4884
|
return c.json({ ...entry, ...mindStatus, variants: variantStatuses, hasPages });
|
|
4885
|
+
}).get("/:name/context", async (c) => {
|
|
4886
|
+
const name = c.req.param("name");
|
|
4887
|
+
const entry = await findMind(name);
|
|
4888
|
+
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
4889
|
+
if (!getMindManager().isRunning(name)) {
|
|
4890
|
+
return c.json({ error: "Mind is not running" }, 503);
|
|
4891
|
+
}
|
|
4892
|
+
try {
|
|
4893
|
+
const res = await fetch(`http://127.0.0.1:${entry.port}/context`, {
|
|
4894
|
+
signal: AbortSignal.timeout(3e3)
|
|
4895
|
+
});
|
|
4896
|
+
if (!res.ok) {
|
|
4897
|
+
const status = res.status >= 500 ? 502 : 404;
|
|
4898
|
+
return c.json(
|
|
4899
|
+
{
|
|
4900
|
+
error: res.status >= 500 ? "Mind context handler errored" : "Context endpoint not available"
|
|
4901
|
+
},
|
|
4902
|
+
status
|
|
4903
|
+
);
|
|
4904
|
+
}
|
|
4905
|
+
const data = await res.json();
|
|
4906
|
+
return c.json(data);
|
|
4907
|
+
} catch (err) {
|
|
4908
|
+
console.error(`context proxy for ${name}:`, err);
|
|
4909
|
+
return c.json({ error: "Failed to reach mind" }, 503);
|
|
4910
|
+
}
|
|
4887
4911
|
}).post("/:name/start", requireSelf(), async (c) => {
|
|
4888
4912
|
const name = c.req.param("name");
|
|
4889
4913
|
const entry = await findMind(name);
|
|
@@ -4893,7 +4917,7 @@ ${user.trimEnd()}
|
|
|
4893
4917
|
if (!entry.dir) return c.json({ error: `Variant ${name} has no directory` }, 404);
|
|
4894
4918
|
} else {
|
|
4895
4919
|
const dir = mindDir(name);
|
|
4896
|
-
if (!
|
|
4920
|
+
if (!existsSync8(dir)) return c.json({ error: "Mind directory missing" }, 404);
|
|
4897
4921
|
}
|
|
4898
4922
|
if (getMindManager().isRunning(name)) {
|
|
4899
4923
|
return c.json({ error: "Mind already running" }, 409);
|
|
@@ -4914,7 +4938,7 @@ ${user.trimEnd()}
|
|
|
4914
4938
|
if (!entry.dir) return c.json({ error: `Variant ${name} has no directory` }, 404);
|
|
4915
4939
|
} else {
|
|
4916
4940
|
const dir = mindDir(name);
|
|
4917
|
-
if (!
|
|
4941
|
+
if (!existsSync8(dir)) return c.json({ error: "Mind directory missing" }, 404);
|
|
4918
4942
|
}
|
|
4919
4943
|
let context;
|
|
4920
4944
|
const contentType = c.req.header("content-type");
|
|
@@ -4929,7 +4953,7 @@ ${user.trimEnd()}
|
|
|
4929
4953
|
const manager = getMindManager();
|
|
4930
4954
|
try {
|
|
4931
4955
|
if (context?.type === "reload") {
|
|
4932
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
4956
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-7KFK3USC.js");
|
|
4933
4957
|
const sleepState = getSleepManagerIfReady2()?.getState(name);
|
|
4934
4958
|
if (sleepState?.sleeping) {
|
|
4935
4959
|
logger_default.info(`skipping reload for ${name} during sleep \u2014 will apply on next wake`);
|
|
@@ -4949,7 +4973,7 @@ ${user.trimEnd()}
|
|
|
4949
4973
|
const variantEntry = await findMind(mergeVariantName);
|
|
4950
4974
|
if (variantEntry && variantEntry.parent === baseName && variantEntry.dir && variantEntry.branch) {
|
|
4951
4975
|
const projectRoot = mindDir(baseName);
|
|
4952
|
-
if (
|
|
4976
|
+
if (existsSync8(variantEntry.dir)) {
|
|
4953
4977
|
const status = (await gitExec(["status", "--porcelain"], { cwd: variantEntry.dir })).trim();
|
|
4954
4978
|
if (status) {
|
|
4955
4979
|
try {
|
|
@@ -4991,7 +5015,7 @@ ${user.trimEnd()}
|
|
|
4991
5015
|
if (context?.type === "sprouted" && !entry.parent) {
|
|
4992
5016
|
try {
|
|
4993
5017
|
const db = await getDb();
|
|
4994
|
-
const activeConvs = await db.select({ id: conversations.id, channel: conversations.channel }).from(conversations).where(
|
|
5018
|
+
const activeConvs = await db.select({ id: conversations.id, channel: conversations.channel }).from(conversations).where(eq5(conversations.mind_name, baseName)).all();
|
|
4995
5019
|
for (const conv of activeConvs) {
|
|
4996
5020
|
await recordInbound(baseName, conv.channel, "system", "[seed has sprouted]");
|
|
4997
5021
|
await addMessage(conv.id, "assistant", "system", [
|
|
@@ -5025,7 +5049,7 @@ ${user.trimEnd()}
|
|
|
5025
5049
|
const name = c.req.param("name");
|
|
5026
5050
|
const entry = await findMind(name);
|
|
5027
5051
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
5028
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
5052
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-7KFK3USC.js");
|
|
5029
5053
|
const sm = getSleepManagerIfReady2();
|
|
5030
5054
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
5031
5055
|
return c.json(sm.getState(name));
|
|
@@ -5033,7 +5057,7 @@ ${user.trimEnd()}
|
|
|
5033
5057
|
const name = c.req.param("name");
|
|
5034
5058
|
const entry = await findMind(name);
|
|
5035
5059
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
5036
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
5060
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-7KFK3USC.js");
|
|
5037
5061
|
const sm = getSleepManagerIfReady2();
|
|
5038
5062
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
5039
5063
|
if (sm.isSleeping(name)) return c.json({ error: "Mind is already sleeping" }, 409);
|
|
@@ -5053,7 +5077,7 @@ ${user.trimEnd()}
|
|
|
5053
5077
|
const name = c.req.param("name");
|
|
5054
5078
|
const entry = await findMind(name);
|
|
5055
5079
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
5056
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
5080
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-7KFK3USC.js");
|
|
5057
5081
|
const sm = getSleepManagerIfReady2();
|
|
5058
5082
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
5059
5083
|
const sleepState = sm.getState(name);
|
|
@@ -5068,7 +5092,7 @@ ${user.trimEnd()}
|
|
|
5068
5092
|
const name = c.req.param("name");
|
|
5069
5093
|
const entry = await findMind(name);
|
|
5070
5094
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
5071
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
5095
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-7KFK3USC.js");
|
|
5072
5096
|
const sm = getSleepManagerIfReady2();
|
|
5073
5097
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
5074
5098
|
const flushed = await sm.flushQueuedMessages(name);
|
|
@@ -5086,8 +5110,8 @@ ${user.trimEnd()}
|
|
|
5086
5110
|
if (body.avatar !== void 0) profile.avatar = body.avatar;
|
|
5087
5111
|
config2.profile = profile;
|
|
5088
5112
|
writeVoluteConfig(dir, config2);
|
|
5089
|
-
const { syncMindProfile } = await import("./auth-
|
|
5090
|
-
await
|
|
5113
|
+
const { syncMindProfile: syncMindProfile2 } = await import("./auth-WX4TESEI.js");
|
|
5114
|
+
await syncMindProfile2(name, profile);
|
|
5091
5115
|
broadcast({ type: "profile_updated", mind: name, summary: `${name} profile updated` });
|
|
5092
5116
|
return c.json({ ok: true });
|
|
5093
5117
|
}).get("/:name/seed-check", requireSelf(), async (c) => {
|
|
@@ -5102,21 +5126,21 @@ ${user.trimEnd()}
|
|
|
5102
5126
|
const rawSpirit = Number(process.env.VOLUTE_NURTURE_SPIRIT_MINUTES);
|
|
5103
5127
|
const spiritThreshold = Number.isNaN(rawSpirit) ? 15 : rawSpirit;
|
|
5104
5128
|
const lastCreatorMsg = await db.select({ created_at: mindHistory.created_at }).from(mindHistory).where(
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5129
|
+
and4(
|
|
5130
|
+
eq5(mindHistory.mind, name),
|
|
5131
|
+
eq5(mindHistory.type, "inbound"),
|
|
5132
|
+
sql2`${mindHistory.sender} != 'volute'`,
|
|
5133
|
+
sql2`${mindHistory.sender} != ${name}`,
|
|
5134
|
+
sql2`${mindHistory.sender} IS NOT NULL`
|
|
5111
5135
|
)
|
|
5112
|
-
).orderBy(
|
|
5136
|
+
).orderBy(desc3(mindHistory.created_at)).limit(1);
|
|
5113
5137
|
const lastSpiritMsg = await db.select({ created_at: mindHistory.created_at }).from(mindHistory).where(
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5138
|
+
and4(
|
|
5139
|
+
eq5(mindHistory.mind, name),
|
|
5140
|
+
eq5(mindHistory.type, "inbound"),
|
|
5141
|
+
eq5(mindHistory.sender, "volute")
|
|
5118
5142
|
)
|
|
5119
|
-
).orderBy(
|
|
5143
|
+
).orderBy(desc3(mindHistory.created_at)).limit(1);
|
|
5120
5144
|
const now = Date.now();
|
|
5121
5145
|
const creatorTime = lastCreatorMsg[0] ? new Date(lastCreatorMsg[0].created_at).getTime() : 0;
|
|
5122
5146
|
const spiritTime = lastSpiritMsg[0] ? new Date(lastSpiritMsg[0].created_at).getTime() : 0;
|
|
@@ -5126,14 +5150,14 @@ ${user.trimEnd()}
|
|
|
5126
5150
|
return c.json({ output: "" });
|
|
5127
5151
|
}
|
|
5128
5152
|
const dir = entry.dir ?? mindDir(name);
|
|
5129
|
-
const soulPath =
|
|
5130
|
-
const memoryPath =
|
|
5131
|
-
const soulCustom =
|
|
5132
|
-
const memoryWritten =
|
|
5153
|
+
const soulPath = resolve11(dir, "home/SOUL.md");
|
|
5154
|
+
const memoryPath = resolve11(dir, "home/MEMORY.md");
|
|
5155
|
+
const soulCustom = existsSync8(soulPath) && !readFileSync9(soulPath, "utf-8").includes(ORIENTATION_MARKER);
|
|
5156
|
+
const memoryWritten = existsSync8(memoryPath) && readFileSync9(memoryPath, "utf-8").trim().length > 0;
|
|
5133
5157
|
const config2 = readVoluteConfig(dir);
|
|
5134
5158
|
const displayNameSet = !!config2?.profile?.displayName;
|
|
5135
5159
|
const avatarSet = !!config2?.profile?.avatar;
|
|
5136
|
-
const { isImagegenEnabled } = await import("./setup-
|
|
5160
|
+
const { isImagegenEnabled } = await import("./setup-RHJRFURI.js");
|
|
5137
5161
|
const imagegenEnabled = isImagegenEnabled();
|
|
5138
5162
|
const done = [];
|
|
5139
5163
|
const remaining = [];
|
|
@@ -5171,7 +5195,7 @@ ${user.trimEnd()}
|
|
|
5171
5195
|
try {
|
|
5172
5196
|
const spiritEntry = await findMind("volute");
|
|
5173
5197
|
if (spiritEntry) {
|
|
5174
|
-
const { spiritDir } = await import("./spirit-
|
|
5198
|
+
const { spiritDir } = await import("./spirit-ZFRDXMG7.js");
|
|
5175
5199
|
const sDir = spiritEntry.dir ?? spiritDir();
|
|
5176
5200
|
const spiritConfig = readVoluteConfig(sDir);
|
|
5177
5201
|
if (spiritConfig?.schedules) {
|
|
@@ -5179,7 +5203,7 @@ ${user.trimEnd()}
|
|
|
5179
5203
|
spiritConfig.schedules = spiritConfig.schedules.filter((s) => s.id !== nurtureId);
|
|
5180
5204
|
if (spiritConfig.schedules.length === 0) spiritConfig.schedules = void 0;
|
|
5181
5205
|
writeVoluteConfig(sDir, spiritConfig);
|
|
5182
|
-
const { getScheduler: getScheduler2 } = await import("./scheduler-
|
|
5206
|
+
const { getScheduler: getScheduler2 } = await import("./scheduler-Y7O4CJXL.js");
|
|
5183
5207
|
getScheduler2().loadSchedules("volute", sDir);
|
|
5184
5208
|
}
|
|
5185
5209
|
}
|
|
@@ -5203,18 +5227,13 @@ ${user.trimEnd()}
|
|
|
5203
5227
|
await cleanupVariant(s.name, dir, s.dir, { stop: true });
|
|
5204
5228
|
}
|
|
5205
5229
|
}
|
|
5206
|
-
try {
|
|
5207
|
-
await removeSharedWorktree(name, dir);
|
|
5208
|
-
} catch (err) {
|
|
5209
|
-
logger_default.warn(`failed to clean up shared worktree for ${name}`, logger_default.errorData(err));
|
|
5210
|
-
}
|
|
5211
5230
|
await removeMind(name);
|
|
5212
5231
|
await deleteMindUser2(name);
|
|
5213
5232
|
const state = stateDir(name);
|
|
5214
|
-
if (
|
|
5233
|
+
if (existsSync8(state)) {
|
|
5215
5234
|
rmSync4(state, { recursive: true, force: true });
|
|
5216
5235
|
}
|
|
5217
|
-
if (force &&
|
|
5236
|
+
if (force && existsSync8(dir)) {
|
|
5218
5237
|
rmSync4(dir, { recursive: true, force: true });
|
|
5219
5238
|
deleteMindUser(name);
|
|
5220
5239
|
}
|
|
@@ -5229,7 +5248,7 @@ ${user.trimEnd()}
|
|
|
5229
5248
|
const entry = await findMind(mindName);
|
|
5230
5249
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
5231
5250
|
const dir = mindDir(mindName);
|
|
5232
|
-
if (!
|
|
5251
|
+
if (!existsSync8(dir)) return c.json({ error: "Mind directory missing" }, 404);
|
|
5233
5252
|
let body = {};
|
|
5234
5253
|
try {
|
|
5235
5254
|
body = await c.req.json();
|
|
@@ -5238,16 +5257,16 @@ ${user.trimEnd()}
|
|
|
5238
5257
|
const template = body.template ?? entry.template ?? "claude";
|
|
5239
5258
|
const UPGRADE_BRANCH = "upgrade";
|
|
5240
5259
|
const upgradeVariantName = `${mindName}-upgrade`;
|
|
5241
|
-
const worktreeDir =
|
|
5260
|
+
const worktreeDir = resolve11(dir, ".variants", UPGRADE_BRANCH);
|
|
5242
5261
|
if (body.abort) {
|
|
5243
|
-
if (!
|
|
5262
|
+
if (!existsSync8(worktreeDir)) {
|
|
5244
5263
|
return c.json({ error: "No upgrade in progress" }, 400);
|
|
5245
5264
|
}
|
|
5246
5265
|
try {
|
|
5247
5266
|
try {
|
|
5248
|
-
const gitDirContent =
|
|
5267
|
+
const gitDirContent = readFileSync9(resolve11(worktreeDir, ".git"), "utf-8").trim();
|
|
5249
5268
|
const gitDir = gitDirContent.replace("gitdir: ", "");
|
|
5250
|
-
if (
|
|
5269
|
+
if (existsSync8(resolve11(gitDir, "MERGE_HEAD"))) {
|
|
5251
5270
|
await gitExec(["merge", "--abort"], { cwd: worktreeDir });
|
|
5252
5271
|
}
|
|
5253
5272
|
} catch {
|
|
@@ -5266,7 +5285,7 @@ ${user.trimEnd()}
|
|
|
5266
5285
|
}
|
|
5267
5286
|
}
|
|
5268
5287
|
if (body.continue) {
|
|
5269
|
-
if (!
|
|
5288
|
+
if (!existsSync8(worktreeDir)) {
|
|
5270
5289
|
return c.json({ error: "No upgrade in progress" }, 400);
|
|
5271
5290
|
}
|
|
5272
5291
|
const status = await gitExec(["status", "--porcelain"], { cwd: worktreeDir });
|
|
@@ -5315,7 +5334,7 @@ ${user.trimEnd()}
|
|
|
5315
5334
|
}
|
|
5316
5335
|
}
|
|
5317
5336
|
if (body.accept) {
|
|
5318
|
-
if (
|
|
5337
|
+
if (existsSync8(worktreeDir)) {
|
|
5319
5338
|
try {
|
|
5320
5339
|
await cleanupVariant(upgradeVariantName, dir, worktreeDir, { stop: true });
|
|
5321
5340
|
} catch (err) {
|
|
@@ -5330,7 +5349,7 @@ ${user.trimEnd()}
|
|
|
5330
5349
|
}
|
|
5331
5350
|
if (body.diff) {
|
|
5332
5351
|
try {
|
|
5333
|
-
if (!
|
|
5352
|
+
if (!existsSync8(resolve11(dir, ".git"))) {
|
|
5334
5353
|
return c.json({ error: "Mind has no git history \u2014 nothing to diff against" }, 400);
|
|
5335
5354
|
}
|
|
5336
5355
|
await updateTemplateBranch(dir, template, mindName);
|
|
@@ -5348,22 +5367,22 @@ ${user.trimEnd()}
|
|
|
5348
5367
|
);
|
|
5349
5368
|
}
|
|
5350
5369
|
}
|
|
5351
|
-
if (
|
|
5370
|
+
if (existsSync8(worktreeDir)) {
|
|
5352
5371
|
return c.json(
|
|
5353
5372
|
{ error: "Upgrade variant already exists. Use continue or delete it first." },
|
|
5354
5373
|
409
|
|
5355
5374
|
);
|
|
5356
5375
|
}
|
|
5357
|
-
if (!
|
|
5376
|
+
if (!existsSync8(resolve11(dir, ".git"))) {
|
|
5358
5377
|
try {
|
|
5359
|
-
const env = isIsolationEnabled() ? { ...process.env, HOME:
|
|
5378
|
+
const env = isIsolationEnabled() ? { ...process.env, HOME: resolve11(dir, "home") } : void 0;
|
|
5360
5379
|
await gitExec(["init"], { cwd: dir, mindName, env });
|
|
5361
5380
|
await configureGitIdentity(mindName, { cwd: dir, mindName, env });
|
|
5362
5381
|
await gitExec(["add", "-A"], { cwd: dir, mindName, env });
|
|
5363
5382
|
await gitExec(["commit", "-m", "initial commit"], { cwd: dir, mindName, env });
|
|
5364
5383
|
chownMindDir(dir, mindName);
|
|
5365
5384
|
} catch (err) {
|
|
5366
|
-
rmSync4(
|
|
5385
|
+
rmSync4(resolve11(dir, ".git"), { recursive: true, force: true });
|
|
5367
5386
|
return c.json(
|
|
5368
5387
|
{
|
|
5369
5388
|
error: `Git initialization failed: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -5377,19 +5396,9 @@ ${user.trimEnd()}
|
|
|
5377
5396
|
await gitExec(["branch", "-D", UPGRADE_BRANCH], { cwd: dir });
|
|
5378
5397
|
} catch {
|
|
5379
5398
|
}
|
|
5380
|
-
if (!existsSync9(resolve12(dir, "home", "shared"))) {
|
|
5381
|
-
try {
|
|
5382
|
-
await addSharedWorktree(mindName, dir);
|
|
5383
|
-
} catch (err) {
|
|
5384
|
-
logger_default.warn(
|
|
5385
|
-
`failed to add shared worktree during upgrade for ${mindName}`,
|
|
5386
|
-
logger_default.errorData(err)
|
|
5387
|
-
);
|
|
5388
|
-
}
|
|
5389
|
-
}
|
|
5390
5399
|
await updateTemplateBranch(dir, template, mindName);
|
|
5391
|
-
const parentDir =
|
|
5392
|
-
if (!
|
|
5400
|
+
const parentDir = resolve11(dir, ".variants");
|
|
5401
|
+
if (!existsSync8(parentDir)) {
|
|
5393
5402
|
mkdirSync6(parentDir, { recursive: true });
|
|
5394
5403
|
}
|
|
5395
5404
|
await gitExec(["worktree", "add", "-b", UPGRADE_BRANCH, worktreeDir], { cwd: dir });
|
|
@@ -5517,18 +5526,26 @@ ${user.trimEnd()}
|
|
|
5517
5526
|
const name = c.req.param("name");
|
|
5518
5527
|
const entry = await findMind(name);
|
|
5519
5528
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
5520
|
-
const dir = mindDir(name);
|
|
5521
|
-
if (!
|
|
5529
|
+
const dir = entry.dir ?? mindDir(name);
|
|
5530
|
+
if (!existsSync8(dir)) return c.json({ error: "Mind directory missing" }, 404);
|
|
5522
5531
|
let config2 = readVoluteConfig(dir);
|
|
5523
5532
|
if (!config2 && entry.template === "pi") {
|
|
5524
|
-
const piConfigPath =
|
|
5525
|
-
if (
|
|
5533
|
+
const piConfigPath = resolve11(dir, "home/.config/config.json");
|
|
5534
|
+
if (existsSync8(piConfigPath)) {
|
|
5526
5535
|
try {
|
|
5527
|
-
config2 = JSON.parse(
|
|
5536
|
+
config2 = JSON.parse(readFileSync9(piConfigPath, "utf-8"));
|
|
5528
5537
|
} catch {
|
|
5529
5538
|
}
|
|
5530
5539
|
}
|
|
5531
5540
|
}
|
|
5541
|
+
let templateConfig = {};
|
|
5542
|
+
const configJsonPath = resolve11(dir, "home/.config/config.json");
|
|
5543
|
+
if (existsSync8(configJsonPath)) {
|
|
5544
|
+
try {
|
|
5545
|
+
templateConfig = JSON.parse(readFileSync9(configJsonPath, "utf-8"));
|
|
5546
|
+
} catch {
|
|
5547
|
+
}
|
|
5548
|
+
}
|
|
5532
5549
|
return c.json({
|
|
5533
5550
|
registry: {
|
|
5534
5551
|
name: entry.name,
|
|
@@ -5538,10 +5555,26 @@ ${user.trimEnd()}
|
|
|
5538
5555
|
template: entry.template
|
|
5539
5556
|
},
|
|
5540
5557
|
config: {
|
|
5541
|
-
model: config2?.model ?? null,
|
|
5542
|
-
thinkingLevel:
|
|
5558
|
+
model: config2?.model ?? templateConfig.model ?? null,
|
|
5559
|
+
thinkingLevel: (() => {
|
|
5560
|
+
if (config2?.thinkingLevel) return config2.thinkingLevel;
|
|
5561
|
+
const tc = templateConfig;
|
|
5562
|
+
if (tc.thinkingLevel) return tc.thinkingLevel;
|
|
5563
|
+
if (tc.reasoningEffort) return tc.reasoningEffort;
|
|
5564
|
+
const mtt = tc.maxThinkingTokens;
|
|
5565
|
+
if (mtt) {
|
|
5566
|
+
if (mtt <= 1024) return "minimal";
|
|
5567
|
+
if (mtt <= 4096) return "low";
|
|
5568
|
+
if (mtt <= 1e4) return "medium";
|
|
5569
|
+
if (mtt <= 32e3) return "high";
|
|
5570
|
+
return "xhigh";
|
|
5571
|
+
}
|
|
5572
|
+
return null;
|
|
5573
|
+
})(),
|
|
5574
|
+
maxThinkingTokens: config2?.maxThinkingTokens ?? templateConfig.maxThinkingTokens ?? null,
|
|
5543
5575
|
tokenBudget: config2?.tokenBudget ?? null,
|
|
5544
|
-
tokenBudgetPeriodMinutes: config2?.tokenBudgetPeriodMinutes ?? null
|
|
5576
|
+
tokenBudgetPeriodMinutes: config2?.tokenBudgetPeriodMinutes ?? null,
|
|
5577
|
+
compaction: templateConfig.compaction ?? null
|
|
5545
5578
|
}
|
|
5546
5579
|
});
|
|
5547
5580
|
}).put(
|
|
@@ -5552,16 +5585,18 @@ ${user.trimEnd()}
|
|
|
5552
5585
|
z5.object({
|
|
5553
5586
|
model: z5.string().optional(),
|
|
5554
5587
|
thinkingLevel: z5.enum(["off", "minimal", "low", "medium", "high", "xhigh"]).optional(),
|
|
5588
|
+
maxThinkingTokens: z5.number().int().positive().nullable().optional(),
|
|
5555
5589
|
tokenBudget: z5.number().int().positive().nullable().optional(),
|
|
5556
|
-
tokenBudgetPeriodMinutes: z5.number().int().positive().nullable().optional()
|
|
5590
|
+
tokenBudgetPeriodMinutes: z5.number().int().positive().nullable().optional(),
|
|
5591
|
+
compaction: z5.object({ maxContextTokens: z5.number().int().positive().nullable().optional() }).nullable().optional()
|
|
5557
5592
|
})
|
|
5558
5593
|
),
|
|
5559
5594
|
async (c) => {
|
|
5560
5595
|
const name = c.req.param("name");
|
|
5561
5596
|
const entry = await findMind(name);
|
|
5562
5597
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
5563
|
-
const dir = mindDir(name);
|
|
5564
|
-
if (!
|
|
5598
|
+
const dir = entry.dir ?? mindDir(name);
|
|
5599
|
+
if (!existsSync8(dir)) return c.json({ error: "Mind directory missing" }, 404);
|
|
5565
5600
|
const body = c.req.valid("json");
|
|
5566
5601
|
const existing = readVoluteConfig(dir) ?? {};
|
|
5567
5602
|
if (body.model !== void 0) existing.model = body.model;
|
|
@@ -5582,7 +5617,96 @@ ${user.trimEnd()}
|
|
|
5582
5617
|
existing.tokenBudgetPeriodMinutes = body.tokenBudgetPeriodMinutes;
|
|
5583
5618
|
}
|
|
5584
5619
|
}
|
|
5620
|
+
if (body.maxThinkingTokens !== void 0) {
|
|
5621
|
+
if (body.maxThinkingTokens === null) {
|
|
5622
|
+
delete existing.maxThinkingTokens;
|
|
5623
|
+
} else {
|
|
5624
|
+
existing.maxThinkingTokens = body.maxThinkingTokens;
|
|
5625
|
+
}
|
|
5626
|
+
}
|
|
5585
5627
|
writeVoluteConfig(dir, existing);
|
|
5628
|
+
const needsConfigJson = body.model !== void 0 || body.thinkingLevel !== void 0 || body.maxThinkingTokens !== void 0 || body.compaction !== void 0;
|
|
5629
|
+
if (needsConfigJson) {
|
|
5630
|
+
const configJsonPath = resolve11(dir, "home/.config/config.json");
|
|
5631
|
+
let templateConfig = {};
|
|
5632
|
+
if (existsSync8(configJsonPath)) {
|
|
5633
|
+
try {
|
|
5634
|
+
templateConfig = JSON.parse(readFileSync9(configJsonPath, "utf-8"));
|
|
5635
|
+
} catch {
|
|
5636
|
+
}
|
|
5637
|
+
}
|
|
5638
|
+
if (body.model !== void 0) {
|
|
5639
|
+
templateConfig.model = body.model;
|
|
5640
|
+
}
|
|
5641
|
+
const tmpl = entry.template ?? "claude";
|
|
5642
|
+
if (body.thinkingLevel !== void 0) {
|
|
5643
|
+
if (tmpl === "claude") {
|
|
5644
|
+
const claudeThinkingTokens = {
|
|
5645
|
+
off: null,
|
|
5646
|
+
minimal: 1024,
|
|
5647
|
+
low: 4096,
|
|
5648
|
+
medium: 1e4,
|
|
5649
|
+
high: 32e3,
|
|
5650
|
+
xhigh: 128e3
|
|
5651
|
+
};
|
|
5652
|
+
const tokens = claudeThinkingTokens[body.thinkingLevel] ?? null;
|
|
5653
|
+
if (tokens === null) {
|
|
5654
|
+
delete templateConfig.maxThinkingTokens;
|
|
5655
|
+
} else {
|
|
5656
|
+
templateConfig.maxThinkingTokens = tokens;
|
|
5657
|
+
}
|
|
5658
|
+
} else if (tmpl === "codex") {
|
|
5659
|
+
const codexMap = {
|
|
5660
|
+
off: null,
|
|
5661
|
+
minimal: "minimal",
|
|
5662
|
+
low: "low",
|
|
5663
|
+
medium: "medium",
|
|
5664
|
+
high: "high",
|
|
5665
|
+
xhigh: "xhigh"
|
|
5666
|
+
};
|
|
5667
|
+
const effort = codexMap[body.thinkingLevel] ?? null;
|
|
5668
|
+
if (effort === null) {
|
|
5669
|
+
delete templateConfig.reasoningEffort;
|
|
5670
|
+
} else {
|
|
5671
|
+
templateConfig.reasoningEffort = effort;
|
|
5672
|
+
}
|
|
5673
|
+
} else {
|
|
5674
|
+
templateConfig.thinkingLevel = body.thinkingLevel;
|
|
5675
|
+
}
|
|
5676
|
+
}
|
|
5677
|
+
if (body.maxThinkingTokens !== void 0) {
|
|
5678
|
+
if (body.maxThinkingTokens === null) {
|
|
5679
|
+
delete templateConfig.maxThinkingTokens;
|
|
5680
|
+
} else {
|
|
5681
|
+
templateConfig.maxThinkingTokens = body.maxThinkingTokens;
|
|
5682
|
+
}
|
|
5683
|
+
}
|
|
5684
|
+
if (body.compaction !== void 0) {
|
|
5685
|
+
if (body.compaction === null) {
|
|
5686
|
+
delete templateConfig.compaction;
|
|
5687
|
+
} else {
|
|
5688
|
+
const comp = templateConfig.compaction ?? {};
|
|
5689
|
+
if (body.compaction.maxContextTokens === null) {
|
|
5690
|
+
delete comp.maxContextTokens;
|
|
5691
|
+
} else if (body.compaction.maxContextTokens !== void 0) {
|
|
5692
|
+
comp.maxContextTokens = body.compaction.maxContextTokens;
|
|
5693
|
+
}
|
|
5694
|
+
templateConfig.compaction = comp;
|
|
5695
|
+
}
|
|
5696
|
+
}
|
|
5697
|
+
writeFileSync7(configJsonPath, `${JSON.stringify(templateConfig, null, 2)}
|
|
5698
|
+
`);
|
|
5699
|
+
}
|
|
5700
|
+
if (entry.mindType === "spirit" && body.model !== void 0) {
|
|
5701
|
+
try {
|
|
5702
|
+
const { readGlobalConfig: readGlobalConfig2, writeGlobalConfig: writeGlobalConfig2 } = await import("./setup-RHJRFURI.js");
|
|
5703
|
+
const globalConfig = readGlobalConfig2();
|
|
5704
|
+
globalConfig.spiritModel = body.model;
|
|
5705
|
+
writeGlobalConfig2(globalConfig);
|
|
5706
|
+
} catch (err) {
|
|
5707
|
+
logger_default.warn("failed to sync spirit model to global config", logger_default.errorData(err));
|
|
5708
|
+
}
|
|
5709
|
+
}
|
|
5586
5710
|
return c.json({ ok: true });
|
|
5587
5711
|
}
|
|
5588
5712
|
).get("/:name/delivery/pending", async (c) => {
|
|
@@ -5603,7 +5727,7 @@ ${user.trimEnd()}
|
|
|
5603
5727
|
if (!body.systemPrompt || !body.message) {
|
|
5604
5728
|
return c.json({ error: "systemPrompt and message required" }, 400);
|
|
5605
5729
|
}
|
|
5606
|
-
const { aiComplete: aiCompleteFn, isAiConfigured } = await import("./ai-service-
|
|
5730
|
+
const { aiComplete: aiCompleteFn, isAiConfigured } = await import("./ai-service-LURBEDDB.js");
|
|
5607
5731
|
if (!isAiConfigured()) {
|
|
5608
5732
|
return c.json({ error: "AI service not configured" }, 503);
|
|
5609
5733
|
}
|
|
@@ -5837,32 +5961,32 @@ ${user.trimEnd()}
|
|
|
5837
5961
|
const db = await getDb();
|
|
5838
5962
|
const rows = await db.select({
|
|
5839
5963
|
session: mindHistory.session,
|
|
5840
|
-
started_at:
|
|
5841
|
-
event_count:
|
|
5842
|
-
message_count:
|
|
5843
|
-
tool_count:
|
|
5844
|
-
}).from(mindHistory).where(
|
|
5964
|
+
started_at: sql2`MIN(${mindHistory.created_at})`,
|
|
5965
|
+
event_count: sql2`COUNT(*)`,
|
|
5966
|
+
message_count: sql2`SUM(CASE WHEN ${mindHistory.type} IN ('inbound','outbound') THEN 1 ELSE 0 END)`,
|
|
5967
|
+
tool_count: sql2`SUM(CASE WHEN ${mindHistory.type}='tool_use' THEN 1 ELSE 0 END)`
|
|
5968
|
+
}).from(mindHistory).where(and4(eq5(mindHistory.mind, name), sql2`${mindHistory.session} IS NOT NULL`)).groupBy(mindHistory.session).orderBy(sql2`MIN(${mindHistory.created_at}) DESC`);
|
|
5845
5969
|
return c.json(rows);
|
|
5846
5970
|
}).get("/:name/history/channels", async (c) => {
|
|
5847
5971
|
const name = c.req.param("name");
|
|
5848
5972
|
const db = await getDb();
|
|
5849
|
-
const rows = await db.selectDistinct({ channel: mindHistory.channel }).from(mindHistory).where(
|
|
5973
|
+
const rows = await db.selectDistinct({ channel: mindHistory.channel }).from(mindHistory).where(eq5(mindHistory.mind, name));
|
|
5850
5974
|
return c.json(rows.map((r) => r.channel));
|
|
5851
5975
|
}).get("/:name/history/export", async (c) => {
|
|
5852
5976
|
const name = c.req.param("name");
|
|
5853
5977
|
if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
|
|
5854
5978
|
const db = await getDb();
|
|
5855
|
-
const rows = await db.select().from(mindHistory).where(
|
|
5979
|
+
const rows = await db.select().from(mindHistory).where(eq5(mindHistory.mind, name));
|
|
5856
5980
|
return c.json(rows);
|
|
5857
5981
|
}).get("/:name/history/turn", async (c) => {
|
|
5858
5982
|
const name = c.req.param("name");
|
|
5859
5983
|
const turnId = c.req.query("turn_id");
|
|
5860
5984
|
const detail = c.req.query("detail") === "1";
|
|
5861
5985
|
const db = await getDb();
|
|
5862
|
-
const typeFilter = detail ? void 0 :
|
|
5986
|
+
const typeFilter = detail ? void 0 : sql2`${mindHistory.type} IN ('inbound','outbound','tool_use','tool_result','text','thinking','activity')`;
|
|
5863
5987
|
let rows;
|
|
5864
5988
|
if (turnId) {
|
|
5865
|
-
rows = await db.select().from(mindHistory).where(
|
|
5989
|
+
rows = await db.select().from(mindHistory).where(and4(eq5(mindHistory.mind, name), eq5(mindHistory.turn_id, turnId), typeFilter)).orderBy(mindHistory.id);
|
|
5866
5990
|
} else {
|
|
5867
5991
|
const session = c.req.query("session");
|
|
5868
5992
|
const fromId = parseInt(c.req.query("from_id") ?? "", 10);
|
|
@@ -5871,11 +5995,11 @@ ${user.trimEnd()}
|
|
|
5871
5995
|
return c.json({ error: "turn_id, or session with from_id and to_id, required" }, 400);
|
|
5872
5996
|
}
|
|
5873
5997
|
rows = await db.select().from(mindHistory).where(
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
|
|
5998
|
+
and4(
|
|
5999
|
+
eq5(mindHistory.mind, name),
|
|
6000
|
+
eq5(mindHistory.session, session),
|
|
6001
|
+
sql2`${mindHistory.id} >= ${fromId}`,
|
|
6002
|
+
sql2`${mindHistory.id} <= ${toId}`,
|
|
5879
6003
|
typeFilter
|
|
5880
6004
|
)
|
|
5881
6005
|
).orderBy(mindHistory.id);
|
|
@@ -5888,14 +6012,14 @@ ${user.trimEnd()}
|
|
|
5888
6012
|
let sinceTimestamp = null;
|
|
5889
6013
|
if (currentSession) {
|
|
5890
6014
|
const lastTurn = await db.select({ turn_id: mindHistory.turn_id }).from(mindHistory).where(
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
|
|
6015
|
+
and4(
|
|
6016
|
+
eq5(mindHistory.mind, name),
|
|
6017
|
+
eq5(mindHistory.session, currentSession),
|
|
6018
|
+
sql2`${mindHistory.turn_id} IS NOT NULL`
|
|
5895
6019
|
)
|
|
5896
|
-
).orderBy(
|
|
6020
|
+
).orderBy(desc3(mindHistory.created_at)).limit(1);
|
|
5897
6021
|
if (lastTurn.length > 0 && lastTurn[0].turn_id) {
|
|
5898
|
-
const firstEvent = await db.select({ created_at: mindHistory.created_at }).from(mindHistory).where(
|
|
6022
|
+
const firstEvent = await db.select({ created_at: mindHistory.created_at }).from(mindHistory).where(eq5(mindHistory.turn_id, lastTurn[0].turn_id)).orderBy(mindHistory.created_at).limit(1);
|
|
5899
6023
|
if (firstEvent.length > 0) {
|
|
5900
6024
|
sinceTimestamp = firstEvent[0].created_at;
|
|
5901
6025
|
}
|
|
@@ -5905,18 +6029,18 @@ ${user.trimEnd()}
|
|
|
5905
6029
|
sinceTimestamp = new Date(Date.now() - 36e5).toISOString().replace("T", " ").slice(0, 19);
|
|
5906
6030
|
}
|
|
5907
6031
|
const conditions = [
|
|
5908
|
-
|
|
5909
|
-
|
|
5910
|
-
|
|
6032
|
+
eq5(summaries.mind, name),
|
|
6033
|
+
eq5(summaries.period, "turn"),
|
|
6034
|
+
sql2`${summaries.created_at} > ${sinceTimestamp}`
|
|
5911
6035
|
];
|
|
5912
6036
|
if (currentSession) {
|
|
5913
|
-
conditions.push(
|
|
6037
|
+
conditions.push(sql2`${turns.session} != ${currentSession}`);
|
|
5914
6038
|
}
|
|
5915
6039
|
const rows = await db.select({
|
|
5916
|
-
session:
|
|
5917
|
-
content:
|
|
5918
|
-
created_at:
|
|
5919
|
-
}).from(
|
|
6040
|
+
session: turns.session,
|
|
6041
|
+
content: summaries.content,
|
|
6042
|
+
created_at: summaries.created_at
|
|
6043
|
+
}).from(summaries).innerJoin(turns, eq5(turns.id, summaries.period_key)).where(and4(...conditions)).orderBy(desc3(summaries.created_at)).limit(50);
|
|
5920
6044
|
if (rows.length === 0) {
|
|
5921
6045
|
return c.json({ context: null });
|
|
5922
6046
|
}
|
|
@@ -5936,37 +6060,81 @@ ${lines.join("\n")}` });
|
|
|
5936
6060
|
const limit = Math.min(Math.max(parseInt(c.req.query("limit") ?? "50", 10) || 50, 1), 200);
|
|
5937
6061
|
const offset = Math.max(parseInt(c.req.query("offset") ?? "0", 10) || 0, 0);
|
|
5938
6062
|
const db = await getDb();
|
|
5939
|
-
const conditions = [
|
|
6063
|
+
const conditions = [eq5(mindHistory.mind, name)];
|
|
5940
6064
|
if (channel) {
|
|
5941
|
-
conditions.push(
|
|
6065
|
+
conditions.push(eq5(mindHistory.channel, channel));
|
|
5942
6066
|
}
|
|
5943
6067
|
if (session) {
|
|
5944
|
-
conditions.push(
|
|
6068
|
+
conditions.push(eq5(mindHistory.session, session));
|
|
5945
6069
|
}
|
|
5946
6070
|
const effectivePreset = full ? "all" : preset;
|
|
6071
|
+
if (!effectivePreset || effectivePreset === "summary") {
|
|
6072
|
+
const sumConditions = [eq5(summaries.mind, name), eq5(summaries.period, "turn")];
|
|
6073
|
+
if (session) {
|
|
6074
|
+
sumConditions.push(eq5(turns.session, session));
|
|
6075
|
+
const sumRows2 = await db.select({
|
|
6076
|
+
id: summaries.id,
|
|
6077
|
+
mind: summaries.mind,
|
|
6078
|
+
period: summaries.period,
|
|
6079
|
+
period_key: summaries.period_key,
|
|
6080
|
+
content: summaries.content,
|
|
6081
|
+
metadata: summaries.metadata,
|
|
6082
|
+
created_at: summaries.created_at,
|
|
6083
|
+
session: turns.session
|
|
6084
|
+
}).from(summaries).innerJoin(turns, eq5(turns.id, summaries.period_key)).where(and4(...sumConditions)).orderBy(desc3(summaries.created_at)).limit(limit).offset(offset);
|
|
6085
|
+
return c.json(
|
|
6086
|
+
sumRows2.map((r) => ({
|
|
6087
|
+
id: r.id,
|
|
6088
|
+
mind: r.mind,
|
|
6089
|
+
type: "summary",
|
|
6090
|
+
channel: null,
|
|
6091
|
+
session: r.session,
|
|
6092
|
+
sender: null,
|
|
6093
|
+
message_id: null,
|
|
6094
|
+
content: r.content,
|
|
6095
|
+
metadata: r.metadata,
|
|
6096
|
+
turn_id: r.period_key,
|
|
6097
|
+
created_at: r.created_at
|
|
6098
|
+
}))
|
|
6099
|
+
);
|
|
6100
|
+
}
|
|
6101
|
+
const sumRows = await db.select().from(summaries).where(and4(...sumConditions)).orderBy(desc3(summaries.created_at)).limit(limit).offset(offset);
|
|
6102
|
+
return c.json(
|
|
6103
|
+
sumRows.map((r) => ({
|
|
6104
|
+
id: r.id,
|
|
6105
|
+
mind: r.mind,
|
|
6106
|
+
type: "summary",
|
|
6107
|
+
channel: null,
|
|
6108
|
+
session: null,
|
|
6109
|
+
sender: null,
|
|
6110
|
+
message_id: null,
|
|
6111
|
+
content: r.content,
|
|
6112
|
+
metadata: r.metadata,
|
|
6113
|
+
turn_id: r.period_key,
|
|
6114
|
+
created_at: r.created_at
|
|
6115
|
+
}))
|
|
6116
|
+
);
|
|
6117
|
+
}
|
|
5947
6118
|
switch (effectivePreset) {
|
|
5948
6119
|
case "all":
|
|
5949
6120
|
break;
|
|
5950
6121
|
case "conversation":
|
|
5951
|
-
conditions.push(
|
|
6122
|
+
conditions.push(sql2`${mindHistory.type} IN ('inbound','outbound','tool_use')`);
|
|
5952
6123
|
break;
|
|
5953
6124
|
case "detailed":
|
|
5954
6125
|
conditions.push(
|
|
5955
|
-
|
|
6126
|
+
sql2`${mindHistory.type} IN ('inbound','outbound','tool_use','tool_result','text','thinking')`
|
|
5956
6127
|
);
|
|
5957
6128
|
break;
|
|
5958
|
-
default:
|
|
5959
|
-
conditions.push(sql3`${mindHistory.type} IN ('summary')`);
|
|
5960
|
-
break;
|
|
5961
6129
|
}
|
|
5962
|
-
const rows = await db.select().from(mindHistory).where(
|
|
6130
|
+
const rows = await db.select().from(mindHistory).where(and4(...conditions)).orderBy(desc3(mindHistory.created_at)).limit(limit).offset(offset);
|
|
5963
6131
|
return c.json(rows);
|
|
5964
6132
|
});
|
|
5965
6133
|
var minds_default = app13;
|
|
5966
6134
|
|
|
5967
|
-
// src/web/api/prompts.ts
|
|
6135
|
+
// packages/daemon/src/web/api/prompts.ts
|
|
5968
6136
|
import { zValidator as zValidator6 } from "@hono/zod-validator";
|
|
5969
|
-
import { eq as
|
|
6137
|
+
import { eq as eq6, sql as sql3 } from "drizzle-orm";
|
|
5970
6138
|
import { Hono as Hono16 } from "hono";
|
|
5971
6139
|
import { z as z6 } from "zod";
|
|
5972
6140
|
var app14 = new Hono16().get("/", async (c) => {
|
|
@@ -5999,9 +6167,9 @@ var app14 = new Hono16().get("/", async (c) => {
|
|
|
5999
6167
|
}
|
|
6000
6168
|
const { content } = c.req.valid("json");
|
|
6001
6169
|
const db = await getDb();
|
|
6002
|
-
await db.insert(systemPrompts).values({ key, content, updated_at:
|
|
6170
|
+
await db.insert(systemPrompts).values({ key, content, updated_at: sql3`(datetime('now'))` }).onConflictDoUpdate({
|
|
6003
6171
|
target: systemPrompts.key,
|
|
6004
|
-
set: { content, updated_at:
|
|
6172
|
+
set: { content, updated_at: sql3`(datetime('now'))` }
|
|
6005
6173
|
});
|
|
6006
6174
|
return c.json({ ok: true });
|
|
6007
6175
|
}).delete("/:key", requireAdmin, async (c) => {
|
|
@@ -6010,103 +6178,14 @@ var app14 = new Hono16().get("/", async (c) => {
|
|
|
6010
6178
|
return c.json({ error: "Unknown prompt key" }, 404);
|
|
6011
6179
|
}
|
|
6012
6180
|
const db = await getDb();
|
|
6013
|
-
await db.delete(systemPrompts).where(
|
|
6181
|
+
await db.delete(systemPrompts).where(eq6(systemPrompts.key, key));
|
|
6014
6182
|
return c.json({ ok: true });
|
|
6015
6183
|
});
|
|
6016
6184
|
var prompts_default = app14;
|
|
6017
6185
|
|
|
6018
|
-
// src/web/api/
|
|
6019
|
-
import { readdir, readFile as readFile2, stat as stat2 } from "fs/promises";
|
|
6020
|
-
import { extname as extname3, resolve as resolve13 } from "path";
|
|
6021
|
-
import { Hono as Hono17 } from "hono";
|
|
6022
|
-
var MAX_FILE_SIZE = 50 * 1024 * 1024;
|
|
6023
|
-
async function resolvePublicRoot(name) {
|
|
6024
|
-
if (name === "_system") return resolve13(voluteHome(), "shared");
|
|
6025
|
-
if (!await findMind(name)) return null;
|
|
6026
|
-
return resolve13(mindDir(name), "home", "public");
|
|
6027
|
-
}
|
|
6028
|
-
function hasDotSegment(relativePath) {
|
|
6029
|
-
return relativePath.split("/").some((seg) => seg.startsWith("."));
|
|
6030
|
-
}
|
|
6031
|
-
var MIME_TYPES = {
|
|
6032
|
-
".html": "text/html",
|
|
6033
|
-
".js": "application/javascript",
|
|
6034
|
-
".css": "text/css",
|
|
6035
|
-
".json": "application/json",
|
|
6036
|
-
".svg": "image/svg+xml",
|
|
6037
|
-
".png": "image/png",
|
|
6038
|
-
".jpg": "image/jpeg",
|
|
6039
|
-
".jpeg": "image/jpeg",
|
|
6040
|
-
".gif": "image/gif",
|
|
6041
|
-
".ico": "image/x-icon",
|
|
6042
|
-
".woff": "font/woff",
|
|
6043
|
-
".woff2": "font/woff2",
|
|
6044
|
-
".txt": "text/plain",
|
|
6045
|
-
".xml": "application/xml",
|
|
6046
|
-
".md": "text/markdown",
|
|
6047
|
-
".webp": "image/webp"
|
|
6048
|
-
};
|
|
6049
|
-
async function listDir(dirPath) {
|
|
6050
|
-
let entries;
|
|
6051
|
-
try {
|
|
6052
|
-
entries = await readdir(dirPath, { withFileTypes: true });
|
|
6053
|
-
} catch (err) {
|
|
6054
|
-
if (err?.code === "ENOENT") return [];
|
|
6055
|
-
throw err;
|
|
6056
|
-
}
|
|
6057
|
-
return entries.filter((e) => !e.name.startsWith(".")).map((e) => ({
|
|
6058
|
-
name: e.name,
|
|
6059
|
-
type: e.isDirectory() ? "directory" : "file"
|
|
6060
|
-
}));
|
|
6061
|
-
}
|
|
6062
|
-
var app15 = new Hono17().get("/:name/", async (c) => {
|
|
6063
|
-
const name = c.req.param("name");
|
|
6064
|
-
const publicRoot = await resolvePublicRoot(name);
|
|
6065
|
-
if (!publicRoot) return c.json({ error: "Not found" }, 404);
|
|
6066
|
-
return c.json(await listDir(publicRoot));
|
|
6067
|
-
}).get("/:name/*", async (c) => {
|
|
6068
|
-
const name = c.req.param("name");
|
|
6069
|
-
const publicRoot = await resolvePublicRoot(name);
|
|
6070
|
-
if (!publicRoot) return c.text("Not found", 404);
|
|
6071
|
-
const wildcard = c.req.path.replace(`/public/${name}`, "") || "/";
|
|
6072
|
-
const relativePath = wildcard.slice(1);
|
|
6073
|
-
const requestedPath = resolve13(publicRoot, relativePath);
|
|
6074
|
-
if (!requestedPath.startsWith(publicRoot)) return c.text("Forbidden", 403);
|
|
6075
|
-
if (hasDotSegment(relativePath)) return c.text("Forbidden", 403);
|
|
6076
|
-
let fileStat;
|
|
6077
|
-
try {
|
|
6078
|
-
fileStat = await stat2(requestedPath);
|
|
6079
|
-
} catch (err) {
|
|
6080
|
-
if (err?.code === "ENOENT") return c.text("Not found", 404);
|
|
6081
|
-
if (err?.code === "EACCES") return c.text("Forbidden", 403);
|
|
6082
|
-
return c.text("Internal server error", 500);
|
|
6083
|
-
}
|
|
6084
|
-
if (fileStat.isDirectory()) {
|
|
6085
|
-
if (wildcard.endsWith("/")) {
|
|
6086
|
-
return c.json(await listDir(requestedPath));
|
|
6087
|
-
}
|
|
6088
|
-
return c.text("Not found", 404);
|
|
6089
|
-
}
|
|
6090
|
-
if (fileStat.isFile()) {
|
|
6091
|
-
if (fileStat.size > MAX_FILE_SIZE) return c.text("File too large", 413);
|
|
6092
|
-
const ext = extname3(requestedPath);
|
|
6093
|
-
const mime = MIME_TYPES[ext] || "application/octet-stream";
|
|
6094
|
-
try {
|
|
6095
|
-
const body = await readFile2(requestedPath);
|
|
6096
|
-
return c.body(body, 200, { "Content-Type": mime });
|
|
6097
|
-
} catch (err) {
|
|
6098
|
-
if (err?.code === "ENOENT") return c.text("Not found", 404);
|
|
6099
|
-
if (err?.code === "EACCES") return c.text("Forbidden", 403);
|
|
6100
|
-
return c.text("Failed to read file", 500);
|
|
6101
|
-
}
|
|
6102
|
-
}
|
|
6103
|
-
return c.text("Not found", 404);
|
|
6104
|
-
});
|
|
6105
|
-
var public_files_default = app15;
|
|
6106
|
-
|
|
6107
|
-
// src/web/api/schedules.ts
|
|
6186
|
+
// packages/daemon/src/web/api/schedules.ts
|
|
6108
6187
|
import { CronExpressionParser } from "cron-parser";
|
|
6109
|
-
import { Hono as
|
|
6188
|
+
import { Hono as Hono17 } from "hono";
|
|
6110
6189
|
var slog2 = logger_default.child("schedules");
|
|
6111
6190
|
function readSchedules(name) {
|
|
6112
6191
|
return readVoluteConfig(mindDir(name))?.schedules ?? [];
|
|
@@ -6124,7 +6203,7 @@ function writeSchedules(name, schedules) {
|
|
|
6124
6203
|
data: { schedules }
|
|
6125
6204
|
});
|
|
6126
6205
|
}
|
|
6127
|
-
var
|
|
6206
|
+
var app15 = new Hono17().get("/:name/clock/status", async (c) => {
|
|
6128
6207
|
const name = c.req.param("name");
|
|
6129
6208
|
if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
|
|
6130
6209
|
const sleepManager = getSleepManagerIfReady();
|
|
@@ -6132,23 +6211,20 @@ var app16 = new Hono18().get("/:name/clock/status", async (c) => {
|
|
|
6132
6211
|
const sleepConfig = sleepManager?.getSleepConfig(name) ?? null;
|
|
6133
6212
|
const schedules = readSchedules(name);
|
|
6134
6213
|
const now = /* @__PURE__ */ new Date();
|
|
6135
|
-
const in24h = new Date(now.getTime() + 24 * 60 * 6e4);
|
|
6136
6214
|
const upcoming = [];
|
|
6137
6215
|
const previous = [];
|
|
6138
6216
|
for (const s of schedules) {
|
|
6139
6217
|
if (!s.enabled) continue;
|
|
6140
6218
|
if (s.fireAt) {
|
|
6141
6219
|
const fireDate = new Date(s.fireAt);
|
|
6142
|
-
if (fireDate >= now
|
|
6220
|
+
if (fireDate >= now) {
|
|
6143
6221
|
upcoming.push({ id: s.id, at: fireDate.toISOString(), type: "timer" });
|
|
6144
6222
|
}
|
|
6145
6223
|
} else if (s.cron) {
|
|
6146
6224
|
try {
|
|
6147
6225
|
const interval = CronExpressionParser.parse(s.cron);
|
|
6148
6226
|
const next = interval.next().toDate();
|
|
6149
|
-
|
|
6150
|
-
upcoming.push({ id: s.id, at: next.toISOString(), type: "cron" });
|
|
6151
|
-
}
|
|
6227
|
+
upcoming.push({ id: s.id, at: next.toISOString(), type: "cron" });
|
|
6152
6228
|
} catch {
|
|
6153
6229
|
slog2.warn(`invalid cron "${s.cron}" for schedule "${s.id}" of ${name}`);
|
|
6154
6230
|
}
|
|
@@ -6160,9 +6236,61 @@ var app16 = new Hono18().get("/:name/clock/status", async (c) => {
|
|
|
6160
6236
|
}
|
|
6161
6237
|
}
|
|
6162
6238
|
}
|
|
6239
|
+
if (sleepState?.sleeping) {
|
|
6240
|
+
if (sleepState.scheduledWakeAt) {
|
|
6241
|
+
upcoming.push({ id: "sleep", at: sleepState.scheduledWakeAt, type: "cron" });
|
|
6242
|
+
}
|
|
6243
|
+
if (sleepState.sleepingSince) {
|
|
6244
|
+
previous.push({ id: "sleep", at: sleepState.sleepingSince });
|
|
6245
|
+
}
|
|
6246
|
+
} else if (sleepConfig?.enabled && sleepConfig.schedule) {
|
|
6247
|
+
try {
|
|
6248
|
+
const sleepInterval = CronExpressionParser.parse(sleepConfig.schedule.sleep);
|
|
6249
|
+
const nextSleep = sleepInterval.next().toDate();
|
|
6250
|
+
upcoming.push({ id: "sleep", at: nextSleep.toISOString(), type: "cron" });
|
|
6251
|
+
} catch {
|
|
6252
|
+
}
|
|
6253
|
+
try {
|
|
6254
|
+
const prevWakeInterval = CronExpressionParser.parse(sleepConfig.schedule.wake);
|
|
6255
|
+
const prevWake = prevWakeInterval.prev().toDate();
|
|
6256
|
+
previous.push({ id: "sleep", at: prevWake.toISOString() });
|
|
6257
|
+
} catch {
|
|
6258
|
+
}
|
|
6259
|
+
}
|
|
6163
6260
|
upcoming.sort((a, b) => a.at.localeCompare(b.at));
|
|
6164
6261
|
previous.sort((a, b) => b.at.localeCompare(a.at));
|
|
6165
6262
|
return c.json({ sleep: sleepState, sleepConfig, schedules, upcoming, previous });
|
|
6263
|
+
}).get("/:name/sleep/config", async (c) => {
|
|
6264
|
+
const name = c.req.param("name");
|
|
6265
|
+
if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
|
|
6266
|
+
const config2 = readVoluteConfig(mindDir(name));
|
|
6267
|
+
return c.json(config2?.sleep ?? { enabled: false });
|
|
6268
|
+
}).put("/:name/sleep/config", requireSelf(), async (c) => {
|
|
6269
|
+
const name = c.req.param("name");
|
|
6270
|
+
const entry = await findMind(name);
|
|
6271
|
+
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
6272
|
+
const body = await c.req.json();
|
|
6273
|
+
if (body.schedule) {
|
|
6274
|
+
for (const field of ["sleep", "wake"]) {
|
|
6275
|
+
if (body.schedule[field]) {
|
|
6276
|
+
try {
|
|
6277
|
+
CronExpressionParser.parse(body.schedule[field]);
|
|
6278
|
+
} catch {
|
|
6279
|
+
return c.json({ error: `Invalid ${field} cron: ${body.schedule[field]}` }, 400);
|
|
6280
|
+
}
|
|
6281
|
+
}
|
|
6282
|
+
}
|
|
6283
|
+
}
|
|
6284
|
+
const dir = mindDir(name);
|
|
6285
|
+
const config2 = readVoluteConfig(dir) ?? {};
|
|
6286
|
+
const sleep = config2.sleep ?? {};
|
|
6287
|
+
if (body.enabled !== void 0) sleep.enabled = body.enabled;
|
|
6288
|
+
if (body.schedule !== void 0) sleep.schedule = body.schedule;
|
|
6289
|
+
if (body.wakeTriggers !== void 0) sleep.wakeTriggers = body.wakeTriggers;
|
|
6290
|
+
config2.sleep = sleep;
|
|
6291
|
+
writeVoluteConfig(dir, config2);
|
|
6292
|
+
getSleepManagerIfReady()?.invalidateSleepConfig(name);
|
|
6293
|
+
return c.json({ ok: true });
|
|
6166
6294
|
}).get("/:name/schedules", async (c) => {
|
|
6167
6295
|
const name = c.req.param("name");
|
|
6168
6296
|
if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
|
|
@@ -6290,7 +6418,7 @@ var app16 = new Hono18().get("/:name/clock/status", async (c) => {
|
|
|
6290
6418
|
const body = await c.req.text();
|
|
6291
6419
|
const message = `[webhook: ${event}] ${body}`;
|
|
6292
6420
|
try {
|
|
6293
|
-
const { sendSystemMessage } = await import("./system-chat-
|
|
6421
|
+
const { sendSystemMessage } = await import("./system-chat-IDPHYHY4.js");
|
|
6294
6422
|
await sendSystemMessage(name, message);
|
|
6295
6423
|
return c.json({ ok: true });
|
|
6296
6424
|
} catch (err) {
|
|
@@ -6298,19 +6426,19 @@ var app16 = new Hono18().get("/:name/clock/status", async (c) => {
|
|
|
6298
6426
|
return c.json({ error: "Failed to reach mind" }, 502);
|
|
6299
6427
|
}
|
|
6300
6428
|
});
|
|
6301
|
-
var schedules_default =
|
|
6429
|
+
var schedules_default = app15;
|
|
6302
6430
|
|
|
6303
|
-
// src/web/api/setup.ts
|
|
6431
|
+
// packages/daemon/src/web/api/setup.ts
|
|
6304
6432
|
import { mkdirSync as mkdirSync7 } from "fs";
|
|
6305
6433
|
import { homedir as homedir2 } from "os";
|
|
6306
|
-
import { resolve as
|
|
6307
|
-
import { Hono as
|
|
6434
|
+
import { resolve as resolve12 } from "path";
|
|
6435
|
+
import { Hono as Hono18 } from "hono";
|
|
6308
6436
|
import { setCookie as setCookie2 } from "hono/cookie";
|
|
6309
6437
|
var DEFAULT_API_URL2 = "https://volute.systems";
|
|
6310
|
-
var setup = new
|
|
6438
|
+
var setup = new Hono18();
|
|
6311
6439
|
function writeSetupConfig(systemName, description) {
|
|
6312
|
-
const configHome = process.env.VOLUTE_HOME ??
|
|
6313
|
-
const mindsDir =
|
|
6440
|
+
const configHome = process.env.VOLUTE_HOME ?? resolve12(homedir2(), ".volute");
|
|
6441
|
+
const mindsDir = resolve12(configHome, "minds");
|
|
6314
6442
|
mkdirSync7(configHome, { recursive: true });
|
|
6315
6443
|
mkdirSync7(mindsDir, { recursive: true });
|
|
6316
6444
|
const existingConfig = readGlobalConfig();
|
|
@@ -6343,7 +6471,7 @@ setup.get("/status", async (c) => {
|
|
|
6343
6471
|
let hasAccount = false;
|
|
6344
6472
|
if (hasSystem) {
|
|
6345
6473
|
try {
|
|
6346
|
-
const { listUsersByType: listUsersByType2 } = await import("./auth-
|
|
6474
|
+
const { listUsersByType: listUsersByType2 } = await import("./auth-WX4TESEI.js");
|
|
6347
6475
|
const brains = await listUsersByType2("brain");
|
|
6348
6476
|
hasAccount = brains.length > 0;
|
|
6349
6477
|
} catch (err) {
|
|
@@ -6392,7 +6520,11 @@ setup.post("/system", async (c) => {
|
|
|
6392
6520
|
return c.json({ error: "System name is required" }, 400);
|
|
6393
6521
|
}
|
|
6394
6522
|
try {
|
|
6395
|
-
writeSetupConfig(body.name.trim(), body.description?.trim());
|
|
6523
|
+
const config2 = writeSetupConfig(body.name.trim(), body.description?.trim());
|
|
6524
|
+
if (body.remote) {
|
|
6525
|
+
config2.hostname = "0.0.0.0";
|
|
6526
|
+
writeGlobalConfig(config2);
|
|
6527
|
+
}
|
|
6396
6528
|
return c.json({ ok: true });
|
|
6397
6529
|
} catch (err) {
|
|
6398
6530
|
return c.json({ error: `Failed to write configuration: ${err.message}` }, 500);
|
|
@@ -6528,7 +6660,7 @@ setup.post("/account", async (c) => {
|
|
|
6528
6660
|
}
|
|
6529
6661
|
}
|
|
6530
6662
|
try {
|
|
6531
|
-
const { createUser: createUser2, updateUserProfile: updateUserProfile2 } = await import("./auth-
|
|
6663
|
+
const { createUser: createUser2, updateUserProfile: updateUserProfile2 } = await import("./auth-WX4TESEI.js");
|
|
6532
6664
|
const user = await createUser2(body.username.trim(), body.password);
|
|
6533
6665
|
if (body.displayName?.trim()) {
|
|
6534
6666
|
await updateUserProfile2(user.id, { display_name: body.displayName.trim() });
|
|
@@ -6574,7 +6706,7 @@ setup.post("/models", async (c) => {
|
|
|
6574
6706
|
return c.json({ error: "Spirit model is required" }, 400);
|
|
6575
6707
|
}
|
|
6576
6708
|
try {
|
|
6577
|
-
const { setEnabledModels: setEnabledModels3, setUtilityModel: setUtilityModel2 } = await import("./ai-service-
|
|
6709
|
+
const { setEnabledModels: setEnabledModels3, setUtilityModel: setUtilityModel2 } = await import("./ai-service-LURBEDDB.js");
|
|
6578
6710
|
setEnabledModels3(body.models);
|
|
6579
6711
|
const config2 = readGlobalConfig();
|
|
6580
6712
|
config2.spiritModel = body.spiritModel.trim();
|
|
@@ -6592,8 +6724,8 @@ setup.post("/complete", async (c) => {
|
|
|
6592
6724
|
return c.json({ error: "Setup already complete" }, 400);
|
|
6593
6725
|
}
|
|
6594
6726
|
try {
|
|
6595
|
-
const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-
|
|
6596
|
-
const { startSpiritFull } = await import("./mind-service-
|
|
6727
|
+
const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-ZFRDXMG7.js");
|
|
6728
|
+
const { startSpiritFull } = await import("./mind-service-X2CAA6W6.js");
|
|
6597
6729
|
await ensureSpiritProject();
|
|
6598
6730
|
await syncSpiritTemplate();
|
|
6599
6731
|
const warnings = [];
|
|
@@ -6609,8 +6741,8 @@ setup.post("/complete", async (c) => {
|
|
|
6609
6741
|
}
|
|
6610
6742
|
let spiritConversationId;
|
|
6611
6743
|
try {
|
|
6612
|
-
const { getOrCreateMindUser: getOrCreateMindUser2, listUsersByType: listUsersByType2 } = await import("./auth-
|
|
6613
|
-
const { createConversation: createConversation6, findDMConversation: findDMConversation2 } = await import("./conversations-
|
|
6744
|
+
const { getOrCreateMindUser: getOrCreateMindUser2, listUsersByType: listUsersByType2 } = await import("./auth-WX4TESEI.js");
|
|
6745
|
+
const { createConversation: createConversation6, findDMConversation: findDMConversation2 } = await import("./conversations-2PW57WO2.js");
|
|
6614
6746
|
const spiritUser = await getOrCreateMindUser2("volute");
|
|
6615
6747
|
const brains = await listUsersByType2("brain");
|
|
6616
6748
|
const admin2 = brains.find((u) => u.role === "admin");
|
|
@@ -6633,8 +6765,8 @@ setup.post("/complete", async (c) => {
|
|
|
6633
6765
|
logger_default.info("setup complete state", { spiritConversationId, spiritStarted });
|
|
6634
6766
|
if (spiritConversationId && spiritStarted) {
|
|
6635
6767
|
try {
|
|
6636
|
-
const { deliverMessage: deliverMessage2 } = await import("./message-delivery-
|
|
6637
|
-
const { listUsersByType: listUsers6 } = await import("./auth-
|
|
6768
|
+
const { deliverMessage: deliverMessage2 } = await import("./message-delivery-GRC4W6P7.js");
|
|
6769
|
+
const { listUsersByType: listUsers6 } = await import("./auth-WX4TESEI.js");
|
|
6638
6770
|
const admins = await listUsers6("brain");
|
|
6639
6771
|
const admin2 = admins.find((u) => u.role === "admin");
|
|
6640
6772
|
const adminName = admin2?.display_name || admin2?.username || "the admin";
|
|
@@ -6670,35 +6802,13 @@ setup.post("/complete", async (c) => {
|
|
|
6670
6802
|
});
|
|
6671
6803
|
var setup_default = setup;
|
|
6672
6804
|
|
|
6673
|
-
// src/web/api/
|
|
6674
|
-
import {
|
|
6675
|
-
var app17 = new Hono20().post("/:name/shared/merge", requireAdmin, async (c) => {
|
|
6676
|
-
const name = c.req.param("name");
|
|
6677
|
-
const entry = await findMind(name);
|
|
6678
|
-
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
6679
|
-
let body;
|
|
6680
|
-
try {
|
|
6681
|
-
body = await c.req.json();
|
|
6682
|
-
} catch {
|
|
6683
|
-
return c.json({ error: "Invalid JSON in request body" }, 400);
|
|
6684
|
-
}
|
|
6685
|
-
const message = body.message || `shared: merge from ${name}`;
|
|
6686
|
-
try {
|
|
6687
|
-
const result = await sharedMerge(name, mindDir(name), message);
|
|
6688
|
-
return c.json(result);
|
|
6689
|
-
} catch (err) {
|
|
6690
|
-
return c.json({ error: err instanceof Error ? err.message : "Merge failed" }, 500);
|
|
6691
|
-
}
|
|
6692
|
-
});
|
|
6693
|
-
var shared_default = app17;
|
|
6694
|
-
|
|
6695
|
-
// src/web/api/skills.ts
|
|
6696
|
-
import { existsSync as existsSync10, mkdtempSync, readdirSync as readdirSync3, rmSync as rmSync5 } from "fs";
|
|
6805
|
+
// packages/daemon/src/web/api/skills.ts
|
|
6806
|
+
import { existsSync as existsSync9, mkdtempSync, readdirSync as readdirSync3, rmSync as rmSync5 } from "fs";
|
|
6697
6807
|
import { tmpdir } from "os";
|
|
6698
|
-
import { join, resolve as
|
|
6808
|
+
import { join, resolve as resolve13 } from "path";
|
|
6699
6809
|
import AdmZip from "adm-zip";
|
|
6700
|
-
import { Hono as
|
|
6701
|
-
var
|
|
6810
|
+
import { Hono as Hono19 } from "hono";
|
|
6811
|
+
var app16 = new Hono19().get("/", async (c) => {
|
|
6702
6812
|
const skills = await listSharedSkills();
|
|
6703
6813
|
return c.json(skills);
|
|
6704
6814
|
}).get("/defaults/list", async (c) => {
|
|
@@ -6747,6 +6857,16 @@ var app18 = new Hono21().get("/", async (c) => {
|
|
|
6747
6857
|
removed.add(skill);
|
|
6748
6858
|
writeGlobalConfig({ ...config2, defaultSkills: updated, removedDefaultSkills: [...removed] });
|
|
6749
6859
|
return c.json({ skills: updated });
|
|
6860
|
+
}).get("/auto-update", (c) => {
|
|
6861
|
+
return c.json({ enabled: isAutoUpdateSkillsEnabled() });
|
|
6862
|
+
}).put("/auto-update", requireAdmin, async (c) => {
|
|
6863
|
+
const body = await c.req.json();
|
|
6864
|
+
if (typeof body.enabled !== "boolean") {
|
|
6865
|
+
return c.json({ error: "body.enabled must be a boolean" }, 400);
|
|
6866
|
+
}
|
|
6867
|
+
const config2 = readGlobalConfig();
|
|
6868
|
+
writeGlobalConfig({ ...config2, autoUpdateSkills: body.enabled });
|
|
6869
|
+
return c.json({ enabled: body.enabled });
|
|
6750
6870
|
}).post("/upload", requireAdmin, async (c) => {
|
|
6751
6871
|
const body = await c.req.parseBody();
|
|
6752
6872
|
const file = body.file;
|
|
@@ -6761,19 +6881,19 @@ var app18 = new Hono21().get("/", async (c) => {
|
|
|
6761
6881
|
try {
|
|
6762
6882
|
const zip = new AdmZip(buffer2);
|
|
6763
6883
|
for (const entry of zip.getEntries()) {
|
|
6764
|
-
const target =
|
|
6884
|
+
const target = resolve13(tmpDir, entry.entryName);
|
|
6765
6885
|
if (!target.startsWith(tmpDir)) {
|
|
6766
6886
|
return c.json({ error: "Invalid zip: paths must not escape archive" }, 400);
|
|
6767
6887
|
}
|
|
6768
6888
|
}
|
|
6769
6889
|
zip.extractAllTo(tmpDir, true);
|
|
6770
6890
|
let skillDir = null;
|
|
6771
|
-
if (
|
|
6891
|
+
if (existsSync9(join(tmpDir, "SKILL.md"))) {
|
|
6772
6892
|
skillDir = tmpDir;
|
|
6773
6893
|
} else {
|
|
6774
6894
|
const entries = readdirSync3(tmpDir, { withFileTypes: true }).filter((e) => e.isDirectory());
|
|
6775
6895
|
for (const entry of entries) {
|
|
6776
|
-
if (
|
|
6896
|
+
if (existsSync9(join(tmpDir, entry.name, "SKILL.md"))) {
|
|
6777
6897
|
skillDir = join(tmpDir, entry.name);
|
|
6778
6898
|
break;
|
|
6779
6899
|
}
|
|
@@ -6809,18 +6929,18 @@ var app18 = new Hono21().get("/", async (c) => {
|
|
|
6809
6929
|
}
|
|
6810
6930
|
return c.json({ ok: true });
|
|
6811
6931
|
});
|
|
6812
|
-
var skills_default =
|
|
6932
|
+
var skills_default = app16;
|
|
6813
6933
|
|
|
6814
|
-
// src/web/api/typing.ts
|
|
6934
|
+
// packages/daemon/src/web/api/typing.ts
|
|
6815
6935
|
import { zValidator as zValidator7 } from "@hono/zod-validator";
|
|
6816
|
-
import { Hono as
|
|
6936
|
+
import { Hono as Hono20 } from "hono";
|
|
6817
6937
|
import { z as z7 } from "zod";
|
|
6818
6938
|
var typingSchema = z7.object({
|
|
6819
6939
|
channel: z7.string().min(1),
|
|
6820
6940
|
sender: z7.string().min(1),
|
|
6821
6941
|
active: z7.boolean()
|
|
6822
6942
|
});
|
|
6823
|
-
var
|
|
6943
|
+
var app17 = new Hono20().post("/:name/typing", zValidator7("json", typingSchema), (c) => {
|
|
6824
6944
|
const { channel, sender, active } = c.req.valid("json");
|
|
6825
6945
|
const map = getTypingMap();
|
|
6826
6946
|
if (active) {
|
|
@@ -6840,13 +6960,13 @@ var app19 = new Hono22().post("/:name/typing", zValidator7("json", typingSchema)
|
|
|
6840
6960
|
const map = getTypingMap();
|
|
6841
6961
|
return c.json({ typing: map.get(channel) });
|
|
6842
6962
|
});
|
|
6843
|
-
var typing_default =
|
|
6963
|
+
var typing_default = app17;
|
|
6844
6964
|
|
|
6845
|
-
// src/web/api/update.ts
|
|
6965
|
+
// packages/daemon/src/web/api/update.ts
|
|
6846
6966
|
import { spawn as spawn3 } from "child_process";
|
|
6847
|
-
import { Hono as
|
|
6967
|
+
import { Hono as Hono21 } from "hono";
|
|
6848
6968
|
var bin;
|
|
6849
|
-
var
|
|
6969
|
+
var app18 = new Hono21().get("/update", async (c) => {
|
|
6850
6970
|
const result = await checkForUpdate();
|
|
6851
6971
|
return c.json(result);
|
|
6852
6972
|
}).post("/update", requireAdmin, async (c) => {
|
|
@@ -6861,17 +6981,17 @@ var app20 = new Hono23().get("/update", async (c) => {
|
|
|
6861
6981
|
child.unref();
|
|
6862
6982
|
return c.json({ ok: true, message: "Updating..." });
|
|
6863
6983
|
});
|
|
6864
|
-
var update_default =
|
|
6984
|
+
var update_default = app18;
|
|
6865
6985
|
|
|
6866
|
-
// src/web/api/v1/conversations.ts
|
|
6986
|
+
// packages/daemon/src/web/api/v1/conversations.ts
|
|
6867
6987
|
import { zValidator as zValidator8 } from "@hono/zod-validator";
|
|
6868
|
-
import { Hono as
|
|
6988
|
+
import { Hono as Hono22 } from "hono";
|
|
6869
6989
|
import { z as z8 } from "zod";
|
|
6870
6990
|
var createSchema = z8.object({
|
|
6871
6991
|
title: z8.string().optional(),
|
|
6872
6992
|
participantNames: z8.array(z8.string()).min(1)
|
|
6873
6993
|
});
|
|
6874
|
-
var
|
|
6994
|
+
var app19 = new Hono22().use("*", authMiddleware).get("/", async (c) => {
|
|
6875
6995
|
const user = c.get("user");
|
|
6876
6996
|
const convs = await listConversationsWithParticipants(user.id);
|
|
6877
6997
|
return c.json(convs);
|
|
@@ -6956,14 +7076,14 @@ var app21 = new Hono24().use("*", authMiddleware).get("/", async (c) => {
|
|
|
6956
7076
|
if (!deleted) return c.json({ error: "Conversation not found" }, 404);
|
|
6957
7077
|
return c.json({ ok: true });
|
|
6958
7078
|
});
|
|
6959
|
-
var conversations_default =
|
|
7079
|
+
var conversations_default = app19;
|
|
6960
7080
|
|
|
6961
|
-
// src/web/api/v1/events.ts
|
|
6962
|
-
import { desc as
|
|
6963
|
-
import { Hono as
|
|
7081
|
+
// packages/daemon/src/web/api/v1/events.ts
|
|
7082
|
+
import { desc as desc4 } from "drizzle-orm";
|
|
7083
|
+
import { Hono as Hono23 } from "hono";
|
|
6964
7084
|
import { streamSSE as streamSSE4 } from "hono/streaming";
|
|
6965
7085
|
|
|
6966
|
-
// src/lib/events/brain-presence.ts
|
|
7086
|
+
// packages/daemon/src/lib/events/brain-presence.ts
|
|
6967
7087
|
var connections = /* @__PURE__ */ new Map();
|
|
6968
7088
|
function addConnection(username) {
|
|
6969
7089
|
const count = connections.get(username) ?? 0;
|
|
@@ -6986,7 +7106,7 @@ function getOnlineBrains() {
|
|
|
6986
7106
|
return [...connections.keys()];
|
|
6987
7107
|
}
|
|
6988
7108
|
|
|
6989
|
-
// src/lib/events/event-sequencer.ts
|
|
7109
|
+
// packages/daemon/src/lib/events/event-sequencer.ts
|
|
6990
7110
|
var BUFFER_SIZE = 1e3;
|
|
6991
7111
|
var MAX_AGE_MS = 5 * 60 * 1e3;
|
|
6992
7112
|
var nextId = 1;
|
|
@@ -7006,8 +7126,8 @@ function getEventsSince(sinceId) {
|
|
|
7006
7126
|
return buffer.slice(startIdx).filter((e) => now - e.timestamp < MAX_AGE_MS);
|
|
7007
7127
|
}
|
|
7008
7128
|
|
|
7009
|
-
// src/web/api/v1/events.ts
|
|
7010
|
-
var
|
|
7129
|
+
// packages/daemon/src/web/api/v1/events.ts
|
|
7130
|
+
var app20 = new Hono23().use("*", authMiddleware).get("/", async (c) => {
|
|
7011
7131
|
const user = c.get("user");
|
|
7012
7132
|
const since = c.req.query("since");
|
|
7013
7133
|
const sinceId = since ? Number(since) : 0;
|
|
@@ -7030,7 +7150,7 @@ var app22 = new Hono25().use("*", authMiddleware).get("/", async (c) => {
|
|
|
7030
7150
|
let recentActivity = [];
|
|
7031
7151
|
try {
|
|
7032
7152
|
const db = await getDb();
|
|
7033
|
-
recentActivity = await db.select().from(activity).orderBy(
|
|
7153
|
+
recentActivity = await db.select().from(activity).orderBy(desc4(activity.created_at)).limit(50);
|
|
7034
7154
|
recentActivity = recentActivity.map((row) => ({
|
|
7035
7155
|
...row,
|
|
7036
7156
|
metadata: row.metadata ? JSON.parse(row.metadata) : null
|
|
@@ -7097,8 +7217,8 @@ var app22 = new Hono25().use("*", authMiddleware).get("/", async (c) => {
|
|
|
7097
7217
|
});
|
|
7098
7218
|
}, 15e3);
|
|
7099
7219
|
cleanups.push(() => clearInterval(keepAlive));
|
|
7100
|
-
await new Promise((
|
|
7101
|
-
stream.onAbort(() =>
|
|
7220
|
+
await new Promise((resolve18) => {
|
|
7221
|
+
stream.onAbort(() => resolve18());
|
|
7102
7222
|
});
|
|
7103
7223
|
} finally {
|
|
7104
7224
|
for (const cleanup of cleanups) {
|
|
@@ -7110,19 +7230,19 @@ var app22 = new Hono25().use("*", authMiddleware).get("/", async (c) => {
|
|
|
7110
7230
|
}
|
|
7111
7231
|
});
|
|
7112
7232
|
});
|
|
7113
|
-
var events_default =
|
|
7233
|
+
var events_default = app20;
|
|
7114
7234
|
|
|
7115
|
-
// src/web/api/variants.ts
|
|
7116
|
-
import { existsSync as
|
|
7117
|
-
import { resolve as
|
|
7118
|
-
import { Hono as
|
|
7235
|
+
// packages/daemon/src/web/api/variants.ts
|
|
7236
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync9, writeFileSync as writeFileSync8 } from "fs";
|
|
7237
|
+
import { resolve as resolve15 } from "path";
|
|
7238
|
+
import { Hono as Hono24 } from "hono";
|
|
7119
7239
|
|
|
7120
|
-
// src/lib/spawn-server.ts
|
|
7240
|
+
// packages/daemon/src/lib/spawn-server.ts
|
|
7121
7241
|
import { spawn as spawn4 } from "child_process";
|
|
7122
|
-
import { closeSync, mkdirSync as mkdirSync8, openSync, readFileSync as
|
|
7123
|
-
import { resolve as
|
|
7242
|
+
import { closeSync, mkdirSync as mkdirSync8, openSync, readFileSync as readFileSync10 } from "fs";
|
|
7243
|
+
import { resolve as resolve14 } from "path";
|
|
7124
7244
|
function tsxBin(cwd) {
|
|
7125
|
-
return
|
|
7245
|
+
return resolve14(cwd, "node_modules", ".bin", "tsx");
|
|
7126
7246
|
}
|
|
7127
7247
|
function spawnServer(cwd, port, options) {
|
|
7128
7248
|
if (options?.detached) {
|
|
@@ -7135,31 +7255,31 @@ function spawnAttached(cwd, port) {
|
|
|
7135
7255
|
cwd,
|
|
7136
7256
|
stdio: ["ignore", "pipe", "pipe"]
|
|
7137
7257
|
});
|
|
7138
|
-
return new Promise((
|
|
7139
|
-
const timeout = setTimeout(() =>
|
|
7258
|
+
return new Promise((resolve18) => {
|
|
7259
|
+
const timeout = setTimeout(() => resolve18(null), 3e4);
|
|
7140
7260
|
function checkOutput(data) {
|
|
7141
7261
|
const match = data.toString().match(/listening on :(\d+)/);
|
|
7142
7262
|
if (match) {
|
|
7143
7263
|
clearTimeout(timeout);
|
|
7144
|
-
|
|
7264
|
+
resolve18({ child, actualPort: parseInt(match[1], 10) });
|
|
7145
7265
|
}
|
|
7146
7266
|
}
|
|
7147
7267
|
child.stdout?.on("data", checkOutput);
|
|
7148
7268
|
child.stderr?.on("data", checkOutput);
|
|
7149
7269
|
child.on("error", () => {
|
|
7150
7270
|
clearTimeout(timeout);
|
|
7151
|
-
|
|
7271
|
+
resolve18(null);
|
|
7152
7272
|
});
|
|
7153
7273
|
child.on("exit", () => {
|
|
7154
7274
|
clearTimeout(timeout);
|
|
7155
|
-
|
|
7275
|
+
resolve18(null);
|
|
7156
7276
|
});
|
|
7157
7277
|
});
|
|
7158
7278
|
}
|
|
7159
7279
|
function spawnDetached(cwd, port, logDir) {
|
|
7160
|
-
const logsDir = logDir ??
|
|
7280
|
+
const logsDir = logDir ?? resolve14(cwd, ".mind", "logs");
|
|
7161
7281
|
mkdirSync8(logsDir, { recursive: true });
|
|
7162
|
-
const logPath =
|
|
7282
|
+
const logPath = resolve14(logsDir, "mind.log");
|
|
7163
7283
|
const logFd = openSync(logPath, "a");
|
|
7164
7284
|
const child = spawn4(tsxBin(cwd), ["src/server.ts", "--port", String(port)], {
|
|
7165
7285
|
cwd,
|
|
@@ -7179,7 +7299,7 @@ function spawnDetached(cwd, port, logDir) {
|
|
|
7179
7299
|
}
|
|
7180
7300
|
const interval = setInterval(() => {
|
|
7181
7301
|
try {
|
|
7182
|
-
const content =
|
|
7302
|
+
const content = readFileSync10(logPath, "utf-8");
|
|
7183
7303
|
const match = content.match(/listening on :(\d+)/);
|
|
7184
7304
|
if (match) {
|
|
7185
7305
|
finish({ child, actualPort: parseInt(match[1], 10) });
|
|
@@ -7193,7 +7313,7 @@ function spawnDetached(cwd, port, logDir) {
|
|
|
7193
7313
|
});
|
|
7194
7314
|
}
|
|
7195
7315
|
|
|
7196
|
-
// src/lib/verify.ts
|
|
7316
|
+
// packages/daemon/src/lib/verify.ts
|
|
7197
7317
|
async function verify2(port) {
|
|
7198
7318
|
const health = await checkHealth(port);
|
|
7199
7319
|
if (!health.ok) {
|
|
@@ -7230,8 +7350,8 @@ async function verify2(port) {
|
|
|
7230
7350
|
}
|
|
7231
7351
|
}
|
|
7232
7352
|
|
|
7233
|
-
// src/web/api/variants.ts
|
|
7234
|
-
var
|
|
7353
|
+
// packages/daemon/src/web/api/variants.ts
|
|
7354
|
+
var app21 = new Hono24().get("/:name/variants", async (c) => {
|
|
7235
7355
|
const name = c.req.param("name");
|
|
7236
7356
|
const entry = await findMind(name);
|
|
7237
7357
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
@@ -7275,11 +7395,11 @@ var app23 = new Hono26().get("/:name/variants", async (c) => {
|
|
|
7275
7395
|
return c.json({ error: `Name already in use: ${variantName}` }, 409);
|
|
7276
7396
|
}
|
|
7277
7397
|
const projectRoot = mindDir(mindName);
|
|
7278
|
-
const variantDir =
|
|
7279
|
-
if (
|
|
7398
|
+
const variantDir = resolve15(projectRoot, ".variants", variantName);
|
|
7399
|
+
if (existsSync10(variantDir)) {
|
|
7280
7400
|
return c.json({ error: `Variant directory already exists: ${variantDir}` }, 409);
|
|
7281
7401
|
}
|
|
7282
|
-
mkdirSync9(
|
|
7402
|
+
mkdirSync9(resolve15(projectRoot, ".variants"), { recursive: true });
|
|
7283
7403
|
try {
|
|
7284
7404
|
await gitExec(["worktree", "add", "-b", variantName, variantDir], { cwd: projectRoot });
|
|
7285
7405
|
} catch (e) {
|
|
@@ -7292,7 +7412,7 @@ var app23 = new Hono26().get("/:name/variants", async (c) => {
|
|
|
7292
7412
|
const [cmd, args] = await wrapForIsolation("npm", ["install"], mindName);
|
|
7293
7413
|
await exec(cmd, args, {
|
|
7294
7414
|
cwd: variantDir,
|
|
7295
|
-
env: { ...process.env, HOME:
|
|
7415
|
+
env: { ...process.env, HOME: resolve15(variantDir, "home") }
|
|
7296
7416
|
});
|
|
7297
7417
|
} else {
|
|
7298
7418
|
await exec("npm", ["install"], { cwd: variantDir });
|
|
@@ -7302,7 +7422,7 @@ var app23 = new Hono26().get("/:name/variants", async (c) => {
|
|
|
7302
7422
|
return c.json({ error: `npm install failed: ${msg}` }, 500);
|
|
7303
7423
|
}
|
|
7304
7424
|
if (body.soul) {
|
|
7305
|
-
writeFileSync8(
|
|
7425
|
+
writeFileSync8(resolve15(variantDir, "home/SOUL.md"), body.soul);
|
|
7306
7426
|
}
|
|
7307
7427
|
const variantPort = body.port ?? await nextPort();
|
|
7308
7428
|
await addVariant(variantName, mindName, variantPort, variantDir, variantName);
|
|
@@ -7337,7 +7457,7 @@ var app23 = new Hono26().get("/:name/variants", async (c) => {
|
|
|
7337
7457
|
} catch {
|
|
7338
7458
|
}
|
|
7339
7459
|
const projectRoot = mindDir(mindName);
|
|
7340
|
-
if (
|
|
7460
|
+
if (existsSync10(variantEntry.dir)) {
|
|
7341
7461
|
const status = (await gitExec(["status", "--porcelain"], { cwd: variantEntry.dir })).trim();
|
|
7342
7462
|
if (status) {
|
|
7343
7463
|
try {
|
|
@@ -7397,8 +7517,8 @@ var app23 = new Hono26().get("/:name/variants", async (c) => {
|
|
|
7397
7517
|
await cleanupVariant(variantName, projectRoot, variantEntry.dir);
|
|
7398
7518
|
if (variantName.endsWith("-upgrade") || variantName === "upgrade") {
|
|
7399
7519
|
try {
|
|
7400
|
-
const { computeTemplateHash: computeTemplateHash2 } = await import("./template-hash-
|
|
7401
|
-
const { setMindTemplateHash: setMindTemplateHash2 } = await import("./registry-
|
|
7520
|
+
const { computeTemplateHash: computeTemplateHash2 } = await import("./template-hash-A7FNHTB7.js");
|
|
7521
|
+
const { setMindTemplateHash: setMindTemplateHash2 } = await import("./registry-GBSNW3HG.js");
|
|
7402
7522
|
const tmpl = parentEntry.template ?? "claude";
|
|
7403
7523
|
await setMindTemplateHash2(mindName, computeTemplateHash2(tmpl));
|
|
7404
7524
|
} catch (err) {
|
|
@@ -7411,7 +7531,7 @@ var app23 = new Hono26().get("/:name/variants", async (c) => {
|
|
|
7411
7531
|
const [cmd, args] = await wrapForIsolation("npm", ["install"], mindName);
|
|
7412
7532
|
await exec(cmd, args, {
|
|
7413
7533
|
cwd: projectRoot,
|
|
7414
|
-
env: { ...process.env, HOME:
|
|
7534
|
+
env: { ...process.env, HOME: resolve15(projectRoot, "home") }
|
|
7415
7535
|
});
|
|
7416
7536
|
} else {
|
|
7417
7537
|
await exec("npm", ["install"], { cwd: projectRoot });
|
|
@@ -7453,11 +7573,11 @@ var app23 = new Hono26().get("/:name/variants", async (c) => {
|
|
|
7453
7573
|
await cleanupVariant(variantName, projectRoot, variantEntry.dir, { stop: true });
|
|
7454
7574
|
return c.json({ ok: true });
|
|
7455
7575
|
});
|
|
7456
|
-
var variants_default =
|
|
7576
|
+
var variants_default = app21;
|
|
7457
7577
|
|
|
7458
|
-
// src/web/api/volute/channels.ts
|
|
7578
|
+
// packages/daemon/src/web/api/volute/channels.ts
|
|
7459
7579
|
import { zValidator as zValidator9 } from "@hono/zod-validator";
|
|
7460
|
-
import { Hono as
|
|
7580
|
+
import { Hono as Hono25 } from "hono";
|
|
7461
7581
|
import { z as z9 } from "zod";
|
|
7462
7582
|
var createSchema2 = z9.object({
|
|
7463
7583
|
name: z9.string().min(1).max(50).regex(/^[a-z0-9][a-z0-9-]*$/, "Channel names must be lowercase alphanumeric with hyphens")
|
|
@@ -7465,7 +7585,7 @@ var createSchema2 = z9.object({
|
|
|
7465
7585
|
var inviteSchema = z9.object({
|
|
7466
7586
|
username: z9.string().min(1)
|
|
7467
7587
|
});
|
|
7468
|
-
var
|
|
7588
|
+
var app22 = new Hono25().get("/", async (c) => {
|
|
7469
7589
|
const user = c.get("user");
|
|
7470
7590
|
const channels = await listChannels();
|
|
7471
7591
|
const results = await Promise.all(
|
|
@@ -7535,15 +7655,15 @@ var app24 = new Hono27().get("/", async (c) => {
|
|
|
7535
7655
|
]);
|
|
7536
7656
|
return c.json({ ok: true });
|
|
7537
7657
|
});
|
|
7538
|
-
var channels_default2 =
|
|
7658
|
+
var channels_default2 = app22;
|
|
7539
7659
|
|
|
7540
|
-
// src/web/api/volute/chat.ts
|
|
7660
|
+
// packages/daemon/src/web/api/volute/chat.ts
|
|
7541
7661
|
import { zValidator as zValidator10 } from "@hono/zod-validator";
|
|
7542
|
-
import { Hono as
|
|
7662
|
+
import { Hono as Hono26 } from "hono";
|
|
7543
7663
|
import { streamSSE as streamSSE5 } from "hono/streaming";
|
|
7544
7664
|
import { z as z10 } from "zod";
|
|
7545
7665
|
|
|
7546
|
-
// src/lib/bridge-outbound.ts
|
|
7666
|
+
// packages/daemon/src/lib/bridge-outbound.ts
|
|
7547
7667
|
function extractContent(contentBlocks) {
|
|
7548
7668
|
const text = contentBlocks.filter((b) => b.type === "text").map((b) => b.text).join("\n");
|
|
7549
7669
|
const images = contentBlocks.filter((b) => b.type === "image").map((b) => ({ media_type: b.media_type, data: b.data }));
|
|
@@ -7614,7 +7734,7 @@ async function routeDMOutbound(conversationId, senderName, contentBlocks) {
|
|
|
7614
7734
|
}
|
|
7615
7735
|
}
|
|
7616
7736
|
|
|
7617
|
-
// src/web/api/volute/chat.ts
|
|
7737
|
+
// packages/daemon/src/web/api/volute/chat.ts
|
|
7618
7738
|
async function fanOutToMinds(opts) {
|
|
7619
7739
|
const participants = await getParticipants(opts.conversationId);
|
|
7620
7740
|
const mindParticipants = participants.filter(
|
|
@@ -7622,8 +7742,8 @@ async function fanOutToMinds(opts) {
|
|
|
7622
7742
|
);
|
|
7623
7743
|
const participantNames = participants.map((p) => p.username);
|
|
7624
7744
|
const isDM = opts.isDM ?? participants.length === 2;
|
|
7625
|
-
const { getMindManager: getMindManager2 } = await import("./mind-manager-
|
|
7626
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
7745
|
+
const { getMindManager: getMindManager2 } = await import("./mind-manager-HFLB5653.js");
|
|
7746
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-7KFK3USC.js");
|
|
7627
7747
|
const manager = getMindManager2();
|
|
7628
7748
|
const sm = getSleepManagerIfReady2();
|
|
7629
7749
|
const targetMinds = mindParticipants.map((ap) => {
|
|
@@ -7703,7 +7823,7 @@ function stageFilesForMinds(files, targetMinds, senderName) {
|
|
|
7703
7823
|
}
|
|
7704
7824
|
return { notifications };
|
|
7705
7825
|
}
|
|
7706
|
-
var unifiedChatApp = new
|
|
7826
|
+
var unifiedChatApp = new Hono26().post(
|
|
7707
7827
|
"/chat",
|
|
7708
7828
|
zValidator10("json", chatSchema),
|
|
7709
7829
|
async (c) => {
|
|
@@ -7841,7 +7961,7 @@ var unifiedChatApp = new Hono28().post(
|
|
|
7841
7961
|
return c.json({ ok: true, conversationId, outboundId });
|
|
7842
7962
|
}
|
|
7843
7963
|
);
|
|
7844
|
-
var
|
|
7964
|
+
var app23 = new Hono26().get("/:name/conversations/:id/events", async (c) => {
|
|
7845
7965
|
const conversationId = c.req.param("id");
|
|
7846
7966
|
const user = c.get("user");
|
|
7847
7967
|
if (user.id !== 0 && !await isParticipantOrOwner(conversationId, user.id)) {
|
|
@@ -7858,27 +7978,27 @@ var app25 = new Hono28().get("/:name/conversations/:id/events", async (c) => {
|
|
|
7858
7978
|
if (!stream.aborted) console.error("[chat] SSE ping error:", err);
|
|
7859
7979
|
});
|
|
7860
7980
|
}, 15e3);
|
|
7861
|
-
await new Promise((
|
|
7981
|
+
await new Promise((resolve18) => {
|
|
7862
7982
|
stream.onAbort(() => {
|
|
7863
7983
|
unsubscribe();
|
|
7864
7984
|
clearInterval(keepAlive);
|
|
7865
|
-
|
|
7985
|
+
resolve18();
|
|
7866
7986
|
});
|
|
7867
7987
|
});
|
|
7868
7988
|
});
|
|
7869
7989
|
});
|
|
7870
|
-
var chat_default =
|
|
7990
|
+
var chat_default = app23;
|
|
7871
7991
|
|
|
7872
|
-
// src/web/api/volute/conversations.ts
|
|
7992
|
+
// packages/daemon/src/web/api/volute/conversations.ts
|
|
7873
7993
|
import { zValidator as zValidator11 } from "@hono/zod-validator";
|
|
7874
|
-
import { Hono as
|
|
7994
|
+
import { Hono as Hono27 } from "hono";
|
|
7875
7995
|
import { z as z11 } from "zod";
|
|
7876
7996
|
var createConvSchema = z11.object({
|
|
7877
7997
|
title: z11.string().optional(),
|
|
7878
7998
|
participantIds: z11.array(z11.number()).optional(),
|
|
7879
7999
|
participantNames: z11.array(z11.string()).optional()
|
|
7880
8000
|
});
|
|
7881
|
-
var
|
|
8001
|
+
var app24 = new Hono27().get("/:name/conversations", async (c) => {
|
|
7882
8002
|
const name = c.req.param("name");
|
|
7883
8003
|
const user = c.get("user");
|
|
7884
8004
|
let lookupId = user.id;
|
|
@@ -7986,12 +8106,12 @@ var app26 = new Hono29().get("/:name/conversations", async (c) => {
|
|
|
7986
8106
|
if (!deleted) return c.json({ error: "Conversation not found" }, 404);
|
|
7987
8107
|
return c.json({ ok: true });
|
|
7988
8108
|
});
|
|
7989
|
-
var conversations_default2 =
|
|
8109
|
+
var conversations_default2 = app24;
|
|
7990
8110
|
|
|
7991
|
-
// src/web/app.ts
|
|
8111
|
+
// packages/daemon/src/web/app.ts
|
|
7992
8112
|
var httpLog = logger_default.child("http");
|
|
7993
|
-
var
|
|
7994
|
-
|
|
8113
|
+
var app25 = new Hono28();
|
|
8114
|
+
app25.onError((err, c) => {
|
|
7995
8115
|
if (err instanceof HTTPException) {
|
|
7996
8116
|
return err.getResponse();
|
|
7997
8117
|
}
|
|
@@ -8002,10 +8122,10 @@ app27.onError((err, c) => {
|
|
|
8002
8122
|
});
|
|
8003
8123
|
return c.json({ error: "Internal server error" }, 500);
|
|
8004
8124
|
});
|
|
8005
|
-
|
|
8125
|
+
app25.notFound((c) => {
|
|
8006
8126
|
return c.json({ error: "Not found" }, 404);
|
|
8007
8127
|
});
|
|
8008
|
-
|
|
8128
|
+
app25.use("*", async (c, next) => {
|
|
8009
8129
|
const start = Date.now();
|
|
8010
8130
|
await next();
|
|
8011
8131
|
const duration = Date.now() - start;
|
|
@@ -8016,7 +8136,22 @@ app27.use("*", async (c, next) => {
|
|
|
8016
8136
|
httpLog.debug("request", data);
|
|
8017
8137
|
}
|
|
8018
8138
|
});
|
|
8019
|
-
|
|
8139
|
+
app25.use("/api/*", bodyLimit({ maxSize: 10 * 1024 * 1024 }));
|
|
8140
|
+
app25.use(
|
|
8141
|
+
"/api/*",
|
|
8142
|
+
cors({
|
|
8143
|
+
origin: (origin) => origin,
|
|
8144
|
+
allowHeaders: ["Authorization", "Content-Type", "X-Volute-Session"],
|
|
8145
|
+
allowMethods: ["GET", "POST", "PUT", "PATCH", "DELETE"],
|
|
8146
|
+
credentials: false
|
|
8147
|
+
})
|
|
8148
|
+
);
|
|
8149
|
+
app25.use("/api/*", async (c, next) => {
|
|
8150
|
+
const auth = c.req.header("Authorization");
|
|
8151
|
+
if (auth?.startsWith("Bearer ") && auth.length > 7) return next();
|
|
8152
|
+
return csrf()(c, next);
|
|
8153
|
+
});
|
|
8154
|
+
app25.get("/api/health", (c) => {
|
|
8020
8155
|
let version = "unknown";
|
|
8021
8156
|
let cached = null;
|
|
8022
8157
|
try {
|
|
@@ -8031,45 +8166,42 @@ app27.get("/api/health", (c) => {
|
|
|
8031
8166
|
...cached?.updateAvailable ? { updateAvailable: true, latest: cached.latest } : {}
|
|
8032
8167
|
});
|
|
8033
8168
|
});
|
|
8034
|
-
|
|
8035
|
-
|
|
8036
|
-
|
|
8037
|
-
|
|
8038
|
-
|
|
8039
|
-
|
|
8040
|
-
|
|
8041
|
-
|
|
8042
|
-
|
|
8043
|
-
|
|
8044
|
-
|
|
8045
|
-
|
|
8046
|
-
|
|
8047
|
-
|
|
8048
|
-
|
|
8049
|
-
|
|
8050
|
-
|
|
8051
|
-
|
|
8052
|
-
|
|
8053
|
-
|
|
8054
|
-
|
|
8055
|
-
|
|
8056
|
-
|
|
8057
|
-
|
|
8058
|
-
|
|
8059
|
-
|
|
8060
|
-
|
|
8061
|
-
|
|
8062
|
-
|
|
8063
|
-
|
|
8064
|
-
app27.route("/api/v1/env", sharedEnvApp);
|
|
8065
|
-
app27.route("/api/conversations", conversations_default);
|
|
8066
|
-
var app_default = app27;
|
|
8169
|
+
app25.use("/api/activity/*", authMiddleware);
|
|
8170
|
+
app25.use("/api/minds/*", authMiddleware);
|
|
8171
|
+
app25.use("/api/conversations/*", authMiddleware);
|
|
8172
|
+
app25.use("/api/system/*", authMiddleware);
|
|
8173
|
+
app25.use("/api/env/*", authMiddleware);
|
|
8174
|
+
app25.use("/api/prompts/*", authMiddleware);
|
|
8175
|
+
app25.use("/api/skills/*", authMiddleware);
|
|
8176
|
+
app25.use("/api/extensions/*", authMiddleware);
|
|
8177
|
+
app25.use("/api/bridges/*", authMiddleware);
|
|
8178
|
+
app25.use("/api/config/*", authMiddleware);
|
|
8179
|
+
app25.use("/api/v1/*", authMiddleware);
|
|
8180
|
+
app25.route("/api/setup", setup_default);
|
|
8181
|
+
app25.route("/api/config", config_default);
|
|
8182
|
+
var routes = app25.route("/api/activity", activity_default).route("/api/keys", keys_default).route("/api/auth", auth_default).route("/api/system", system_default).route("/api/system", update_default).route("/api/minds", minds_default).route("/api/minds", chat_default).route("/api/minds", schedules_default).route("/api/minds", logs_default).route("/api/minds", typing_default).route("/api/minds", variants_default).route("/api/minds", file_sharing_default).route("/api/minds", files_default).route("/api/minds", channels_default).route("/api/minds", env_default).route("/api/minds", mind_skills_default).route("/api/minds", conversations_default2).route("/api/env", sharedEnvApp).route("/api/prompts", prompts_default).route("/api/skills", skills_default).route("/api/bridges", bridges_default).route("/api/extensions", extensions_default).route("/api/v1/conversations", conversations_default).route("/api/v1/events", events_default).route("/api/v1", unifiedChatApp).route("/api/v1/channels", channels_default2).route("/api/v1/history", history_default);
|
|
8183
|
+
app25.route("/api/v1/minds", minds_default);
|
|
8184
|
+
app25.route("/api/v1/minds", chat_default);
|
|
8185
|
+
app25.route("/api/v1/minds", typing_default);
|
|
8186
|
+
app25.route("/api/v1/minds", variants_default);
|
|
8187
|
+
app25.route("/api/v1/minds", files_default);
|
|
8188
|
+
app25.route("/api/v1/minds", env_default);
|
|
8189
|
+
app25.route("/api/v1/minds", mind_skills_default);
|
|
8190
|
+
app25.route("/api/v1/minds", schedules_default);
|
|
8191
|
+
app25.route("/api/v1/minds", logs_default);
|
|
8192
|
+
app25.route("/api/v1/system", system_default);
|
|
8193
|
+
app25.route("/api/v1/system", update_default);
|
|
8194
|
+
app25.route("/api/v1/prompts", prompts_default);
|
|
8195
|
+
app25.route("/api/v1/skills", skills_default);
|
|
8196
|
+
app25.route("/api/v1/env", sharedEnvApp);
|
|
8197
|
+
app25.route("/api/conversations", conversations_default);
|
|
8198
|
+
var app_default = app25;
|
|
8067
8199
|
|
|
8068
|
-
// src/web/server.ts
|
|
8069
|
-
import { existsSync as
|
|
8070
|
-
import { readFile as
|
|
8200
|
+
// packages/daemon/src/web/server.ts
|
|
8201
|
+
import { existsSync as existsSync11 } from "fs";
|
|
8202
|
+
import { readFile as readFile2, stat as stat2 } from "fs/promises";
|
|
8071
8203
|
import { createServer as createHttpsServer } from "https";
|
|
8072
|
-
import { dirname as dirname2, extname as
|
|
8204
|
+
import { dirname as dirname2, extname as extname3, resolve as resolve16 } from "path";
|
|
8073
8205
|
import { serve } from "@hono/node-server";
|
|
8074
8206
|
var MIME_TYPES2 = {
|
|
8075
8207
|
".html": "text/html",
|
|
@@ -8088,8 +8220,8 @@ async function startServer({
|
|
|
8088
8220
|
let assetsDir = "";
|
|
8089
8221
|
let searchDir = dirname2(new URL(import.meta.url).pathname);
|
|
8090
8222
|
for (let i = 0; i < 5; i++) {
|
|
8091
|
-
const candidate =
|
|
8092
|
-
if (
|
|
8223
|
+
const candidate = resolve16(searchDir, "dist", "web-assets");
|
|
8224
|
+
if (existsSync11(candidate)) {
|
|
8093
8225
|
assetsDir = candidate;
|
|
8094
8226
|
break;
|
|
8095
8227
|
}
|
|
@@ -8099,19 +8231,19 @@ async function startServer({
|
|
|
8099
8231
|
app_default.get("*", async (c) => {
|
|
8100
8232
|
const urlPath = new URL(c.req.url).pathname;
|
|
8101
8233
|
if (urlPath.startsWith("/api/") || urlPath.startsWith("/ext/")) return c.notFound();
|
|
8102
|
-
const filePath =
|
|
8234
|
+
const filePath = resolve16(assetsDir, urlPath.slice(1));
|
|
8103
8235
|
if (!filePath.startsWith(assetsDir)) return c.text("Forbidden", 403);
|
|
8104
|
-
const s = await
|
|
8236
|
+
const s = await stat2(filePath).catch(() => null);
|
|
8105
8237
|
if (s?.isFile()) {
|
|
8106
|
-
const ext =
|
|
8238
|
+
const ext = extname3(filePath);
|
|
8107
8239
|
const mime = MIME_TYPES2[ext] || "application/octet-stream";
|
|
8108
|
-
const body = await
|
|
8240
|
+
const body = await readFile2(filePath);
|
|
8109
8241
|
return c.body(body, 200, { "Content-Type": mime });
|
|
8110
8242
|
}
|
|
8111
|
-
const indexPath =
|
|
8112
|
-
const indexStat = await
|
|
8243
|
+
const indexPath = resolve16(assetsDir, "index.html");
|
|
8244
|
+
const indexStat = await stat2(indexPath).catch(() => null);
|
|
8113
8245
|
if (indexStat?.isFile()) {
|
|
8114
|
-
const body = await
|
|
8246
|
+
const body = await readFile2(indexPath, "utf-8");
|
|
8115
8247
|
return c.html(body);
|
|
8116
8248
|
}
|
|
8117
8249
|
return c.text("Not found", 404);
|
|
@@ -8125,10 +8257,10 @@ async function startServer({
|
|
|
8125
8257
|
createServer: createHttpsServer,
|
|
8126
8258
|
serverOptions: { key: tls.key, cert: tls.cert }
|
|
8127
8259
|
});
|
|
8128
|
-
await new Promise((
|
|
8260
|
+
await new Promise((resolve18, reject) => {
|
|
8129
8261
|
server2.on("listening", () => {
|
|
8130
8262
|
logger_default.info("Volute UI running (https)", { hostname, port });
|
|
8131
|
-
|
|
8263
|
+
resolve18();
|
|
8132
8264
|
});
|
|
8133
8265
|
server2.on("error", (err) => {
|
|
8134
8266
|
reject(err);
|
|
@@ -8136,13 +8268,13 @@ async function startServer({
|
|
|
8136
8268
|
});
|
|
8137
8269
|
const internalPort = port + 1;
|
|
8138
8270
|
const internalServer = serve({ fetch: app_default.fetch, port: internalPort, hostname: "127.0.0.1" });
|
|
8139
|
-
await new Promise((
|
|
8271
|
+
await new Promise((resolve18, reject) => {
|
|
8140
8272
|
internalServer.on("listening", () => {
|
|
8141
8273
|
logger_default.info("Volute API running (http, internal)", {
|
|
8142
8274
|
hostname: "127.0.0.1",
|
|
8143
8275
|
port: internalPort
|
|
8144
8276
|
});
|
|
8145
|
-
|
|
8277
|
+
resolve18();
|
|
8146
8278
|
});
|
|
8147
8279
|
internalServer.on("error", (err) => {
|
|
8148
8280
|
reject(err);
|
|
@@ -8151,10 +8283,10 @@ async function startServer({
|
|
|
8151
8283
|
return { server: server2, internalPort };
|
|
8152
8284
|
}
|
|
8153
8285
|
const server = serve({ fetch: app_default.fetch, port, hostname });
|
|
8154
|
-
await new Promise((
|
|
8286
|
+
await new Promise((resolve18, reject) => {
|
|
8155
8287
|
server.on("listening", () => {
|
|
8156
8288
|
logger_default.info("Volute API running (http)", { hostname, port });
|
|
8157
|
-
|
|
8289
|
+
resolve18();
|
|
8158
8290
|
});
|
|
8159
8291
|
server.on("error", (err) => {
|
|
8160
8292
|
reject(err);
|
|
@@ -8163,9 +8295,9 @@ async function startServer({
|
|
|
8163
8295
|
return { server };
|
|
8164
8296
|
}
|
|
8165
8297
|
|
|
8166
|
-
// src/daemon.ts
|
|
8298
|
+
// packages/daemon/src/daemon.ts
|
|
8167
8299
|
if (!process.env.VOLUTE_HOME) {
|
|
8168
|
-
process.env.VOLUTE_HOME =
|
|
8300
|
+
process.env.VOLUTE_HOME = resolve17(homedir3(), ".volute");
|
|
8169
8301
|
}
|
|
8170
8302
|
if (process.env.VOLUTE_TIMEZONE && !process.env.TZ) {
|
|
8171
8303
|
process.env.TZ = process.env.VOLUTE_TIMEZONE;
|
|
@@ -8176,7 +8308,7 @@ async function startDaemon(opts) {
|
|
|
8176
8308
|
const home = voluteHome();
|
|
8177
8309
|
const systemDir = voluteSystemDir();
|
|
8178
8310
|
if (!opts.foreground) {
|
|
8179
|
-
const rotatingLog = new RotatingLog(
|
|
8311
|
+
const rotatingLog = new RotatingLog(resolve17(systemDir, "daemon.log"));
|
|
8180
8312
|
logger_default.setOutput((line) => rotatingLog.write(`${line}
|
|
8181
8313
|
`));
|
|
8182
8314
|
const write = (...args) => rotatingLog.write(`${format(...args)}
|
|
@@ -8186,27 +8318,22 @@ async function startDaemon(opts) {
|
|
|
8186
8318
|
console.warn = write;
|
|
8187
8319
|
console.info = write;
|
|
8188
8320
|
}
|
|
8189
|
-
const DAEMON_PID_PATH =
|
|
8190
|
-
const DAEMON_JSON_PATH =
|
|
8321
|
+
const DAEMON_PID_PATH = resolve17(systemDir, "daemon.pid");
|
|
8322
|
+
const DAEMON_JSON_PATH = resolve17(systemDir, "daemon.json");
|
|
8191
8323
|
mkdirSync10(home, { recursive: true });
|
|
8192
8324
|
ensureSystemDir();
|
|
8193
|
-
|
|
8194
|
-
await ensureSharedRepo();
|
|
8195
|
-
} catch (err) {
|
|
8196
|
-
logger_default.warn("failed to initialize shared repo", logger_default.errorData(err));
|
|
8197
|
-
}
|
|
8198
|
-
const { migrateSetupCompleted } = await import("./setup-TISPCO22.js");
|
|
8325
|
+
const { migrateSetupCompleted } = await import("./setup-RHJRFURI.js");
|
|
8199
8326
|
migrateSetupCompleted();
|
|
8200
|
-
await (await import("./db-
|
|
8327
|
+
await (await import("./db-BVBJ57TU.js")).getDb();
|
|
8201
8328
|
try {
|
|
8202
|
-
const { eq:
|
|
8203
|
-
const { users: users2 } = await import("./schema-
|
|
8204
|
-
const db = await (await import("./db-
|
|
8205
|
-
await db.update(users2).set({ role: "system" }).where(
|
|
8329
|
+
const { eq: eq7, and: and5 } = await import("drizzle-orm");
|
|
8330
|
+
const { users: users2 } = await import("./schema-XVZ2CLKW.js");
|
|
8331
|
+
const db = await (await import("./db-BVBJ57TU.js")).getDb();
|
|
8332
|
+
await db.update(users2).set({ role: "system" }).where(and5(eq7(users2.user_type, "system"), eq7(users2.role, "user")));
|
|
8206
8333
|
} catch (err) {
|
|
8207
8334
|
logger_default.warn("failed to migrate system user role", logger_default.errorData(err));
|
|
8208
8335
|
}
|
|
8209
|
-
const { initSandbox } = await import("./sandbox-
|
|
8336
|
+
const { initSandbox } = await import("./sandbox-R37VIU36.js");
|
|
8210
8337
|
await initSandbox();
|
|
8211
8338
|
try {
|
|
8212
8339
|
await syncBuiltinSkills();
|
|
@@ -8220,13 +8347,20 @@ async function startDaemon(opts) {
|
|
|
8220
8347
|
logger_default.error("failed to load extensions", logger_default.errorData(err));
|
|
8221
8348
|
}
|
|
8222
8349
|
await initDefaultSkills();
|
|
8350
|
+
if (isAutoUpdateSkillsEnabled()) {
|
|
8351
|
+
try {
|
|
8352
|
+
await autoUpdateMindSkills();
|
|
8353
|
+
} catch (err) {
|
|
8354
|
+
logger_default.error("failed to auto-update mind skills", logger_default.errorData(err));
|
|
8355
|
+
}
|
|
8356
|
+
}
|
|
8223
8357
|
try {
|
|
8224
8358
|
await ensureSystemChannel();
|
|
8225
8359
|
} catch (err) {
|
|
8226
8360
|
logger_default.warn("failed to ensure #system channel", logger_default.errorData(err));
|
|
8227
8361
|
}
|
|
8228
8362
|
try {
|
|
8229
|
-
const { getOrCreateSystemUser: getOrCreateSystemUser2 } = await import("./auth-
|
|
8363
|
+
const { getOrCreateSystemUser: getOrCreateSystemUser2 } = await import("./auth-WX4TESEI.js");
|
|
8230
8364
|
await getOrCreateSystemUser2();
|
|
8231
8365
|
} catch (err) {
|
|
8232
8366
|
logger_default.warn(
|
|
@@ -8237,7 +8371,7 @@ async function startDaemon(opts) {
|
|
|
8237
8371
|
const token = process.env.VOLUTE_DAEMON_TOKEN || randomBytes(32).toString("hex");
|
|
8238
8372
|
let tls;
|
|
8239
8373
|
if (opts.tailscale) {
|
|
8240
|
-
const { getTailscaleTls } = await import("./tailscale-
|
|
8374
|
+
const { getTailscaleTls } = await import("./tailscale-ZIZ2HWJ5.js");
|
|
8241
8375
|
const tlsConfig = await getTailscaleTls();
|
|
8242
8376
|
tls = { key: tlsConfig.key, cert: tlsConfig.cert };
|
|
8243
8377
|
logger_default.info("Tailscale HTTPS enabled", { hostname: tlsConfig.hostname });
|
|
@@ -8276,8 +8410,11 @@ async function startDaemon(opts) {
|
|
|
8276
8410
|
tokenBudget.start();
|
|
8277
8411
|
const sleepManager = initSleepManager();
|
|
8278
8412
|
sleepManager.start();
|
|
8413
|
+
const summarizer = initSummarizer();
|
|
8414
|
+
summarizer.start();
|
|
8279
8415
|
const unsubscribeWebhook = initWebhook();
|
|
8280
|
-
await completeOrphanedTurns();
|
|
8416
|
+
const orphanedTurns = await completeOrphanedTurns();
|
|
8417
|
+
summarizeOrphanedTurns(orphanedTurns);
|
|
8281
8418
|
const allMinds = await readAllMinds();
|
|
8282
8419
|
const runningEntries = allMinds.filter((e) => e.running && e.mindType !== "spirit");
|
|
8283
8420
|
{
|
|
@@ -8307,10 +8444,10 @@ async function startDaemon(opts) {
|
|
|
8307
8444
|
await Promise.all(workers);
|
|
8308
8445
|
}
|
|
8309
8446
|
try {
|
|
8310
|
-
const { isSetupComplete: isSetupComplete2 } = await import("./setup-
|
|
8447
|
+
const { isSetupComplete: isSetupComplete2 } = await import("./setup-RHJRFURI.js");
|
|
8311
8448
|
if (isSetupComplete2()) {
|
|
8312
|
-
const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-
|
|
8313
|
-
const { startSpiritFull } = await import("./mind-service-
|
|
8449
|
+
const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-ZFRDXMG7.js");
|
|
8450
|
+
const { startSpiritFull } = await import("./mind-service-X2CAA6W6.js");
|
|
8314
8451
|
await ensureSpiritProject();
|
|
8315
8452
|
await syncSpiritTemplate();
|
|
8316
8453
|
const spiritEntry = await findMind("volute");
|
|
@@ -8327,7 +8464,7 @@ async function startDaemon(opts) {
|
|
|
8327
8464
|
bridgeManager.startBridges(daemonPort).catch((err) => {
|
|
8328
8465
|
logger_default.warn("failed to start bridges", logger_default.errorData(err));
|
|
8329
8466
|
});
|
|
8330
|
-
import("./cloud-sync-
|
|
8467
|
+
import("./cloud-sync-6JL4C24T.js").then(
|
|
8331
8468
|
({ consumeQueuedMessages }) => consumeQueuedMessages().catch((err) => {
|
|
8332
8469
|
logger_default.warn("failed to consume queued cloud messages", logger_default.errorData(err));
|
|
8333
8470
|
})
|
|
@@ -8335,7 +8472,7 @@ async function startDaemon(opts) {
|
|
|
8335
8472
|
logger_default.warn("failed to load cloud-sync module", logger_default.errorData(err));
|
|
8336
8473
|
});
|
|
8337
8474
|
try {
|
|
8338
|
-
const { backfillTemplateHashes, notifyVersionUpdate } = await import("./version-notify-
|
|
8475
|
+
const { backfillTemplateHashes, notifyVersionUpdate } = await import("./version-notify-FXSEMXWW.js");
|
|
8339
8476
|
backfillTemplateHashes();
|
|
8340
8477
|
notifyVersionUpdate().catch((err) => {
|
|
8341
8478
|
logger_default.warn("failed to send version update notifications", logger_default.errorData(err));
|
|
@@ -8356,13 +8493,13 @@ async function startDaemon(opts) {
|
|
|
8356
8493
|
logger_default.info(`running on ${hostname}:${port}, pid ${myPid}`);
|
|
8357
8494
|
function cleanup() {
|
|
8358
8495
|
try {
|
|
8359
|
-
if (
|
|
8496
|
+
if (readFileSync11(DAEMON_PID_PATH, "utf-8").trim() === myPid) {
|
|
8360
8497
|
unlinkSync2(DAEMON_PID_PATH);
|
|
8361
8498
|
}
|
|
8362
8499
|
} catch {
|
|
8363
8500
|
}
|
|
8364
8501
|
try {
|
|
8365
|
-
const data = JSON.parse(
|
|
8502
|
+
const data = JSON.parse(readFileSync11(DAEMON_JSON_PATH, "utf-8"));
|
|
8366
8503
|
if (data.token === token) {
|
|
8367
8504
|
unlinkSync2(DAEMON_JSON_PATH);
|
|
8368
8505
|
}
|
|
@@ -8393,6 +8530,7 @@ async function startDaemon(opts) {
|
|
|
8393
8530
|
safe("scheduler.saveState", () => scheduler.saveState());
|
|
8394
8531
|
safe("mailPoller.stop", () => mailPoller.stop());
|
|
8395
8532
|
safe("tokenBudget.stop", () => tokenBudget.stop());
|
|
8533
|
+
safe("summarizer.stop", () => summarizer.stop());
|
|
8396
8534
|
safe("stopApiKeyRefresh", stopApiKeyRefresh);
|
|
8397
8535
|
safe("delivery.dispose", () => delivery.dispose());
|
|
8398
8536
|
await safe("bridgeManager.stopAll", () => bridgeManager.stopAll());
|