volute 0.34.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-BN7V6KCC.js → activity-events-ZW4SDL2C.js} +4 -4
- package/dist/{ai-service-PSILB5WD.js → ai-service-LURBEDDB.js} +5 -5
- package/dist/{api-client-XUXOB7LI.js → api-client-3A77HMH7.js} +1 -1
- package/dist/api.d.ts +1 -5618
- package/dist/{archive-C2VEMQOR.js → archive-ESU2FUN4.js} +3 -3
- package/dist/{auth-ZFZXJZDQ.js → auth-WX4TESEI.js} +5 -5
- package/dist/bridge-PXIO6PS2.js +206 -0
- package/dist/chat-QXAJF3FU.js +51 -0
- package/dist/{chunk-7F2SW2KD.js → chunk-2TGZJFAT.js} +3 -3
- package/dist/{chunk-6LXAAQ43.js → chunk-33ODGMFZ.js} +1 -1
- package/dist/{chunk-4JSR7YO7.js → chunk-5N7Y5WAM.js} +1 -1
- package/dist/{chunk-FYCALD4Q.js → chunk-5T5YMX6S.js} +1 -1
- package/dist/{chunk-B2BVAIZ4.js → chunk-5XJYUFZH.js} +21 -15
- package/dist/{chunk-M3K5AARV.js → chunk-A2ZLHBHG.js} +2 -2
- package/dist/{chunk-U5BTYSAL.js → chunk-AN2W47GW.js} +2 -2
- package/dist/{chunk-G53F3JA4.js → chunk-AOB6GVRM.js} +1 -1
- package/dist/{chunk-N7BLAHNE.js → chunk-BDYXIWA5.js} +5 -5
- package/dist/{chunk-YUIHSKR6.js → chunk-BKF4WQCY.js} +2 -2
- package/dist/{chunk-6OWJXUAR.js → chunk-BMZQYACC.js} +2 -2
- package/dist/{chunk-NAOW2CLO.js → chunk-BTY4WNFE.js} +1 -1
- package/dist/{chunk-MLOQKQNB.js → chunk-BV65KRHM.js} +2 -2
- package/dist/{chunk-XWXBJQBE.js → chunk-CORXD635.js} +4 -4
- package/dist/{chunk-PVY5W6QN.js → chunk-F7ZNLYKZ.js} +2 -2
- package/dist/{chunk-BFWHBQK4.js → chunk-FT5KETXZ.js} +3 -3
- package/dist/{chunk-N3DNFPVA.js → chunk-IJHIXLVN.js} +8 -8
- package/dist/{chunk-V6ZCNULL.js → chunk-J6CJQDWI.js} +37 -28
- package/dist/{chunk-4RQBJWQX.js → chunk-LOPXTW6H.js} +1 -1
- package/dist/{chunk-47ZPNLF4.js → chunk-MDJGMOSD.js} +8 -137
- package/dist/{chunk-BTWAGDV5.js → chunk-N446KRP7.js} +3 -3
- package/dist/{chunk-6WAWMWR5.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-2IOP6PHB.js → chunk-OTC67N2Z.js} +2 -2
- package/dist/{chunk-V45JXOWY.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-IS7WJ56Q.js → chunk-QWTR6AWZ.js} +3 -3
- package/dist/chunk-TXSA4Q3V.js +116 -0
- package/dist/{chunk-BDK73LK6.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-E5C7OWZ2.js → chunk-WZRZFFCL.js} +8 -8
- package/dist/{chunk-BM474GX6.js → chunk-XRQSAMX2.js} +4 -4
- package/dist/{chunk-OYAKCAVY.js → chunk-ZSR72JB3.js} +1 -1
- package/dist/{chunk-PLDWHR4D.js → chunk-ZX7EAV5J.js} +17 -7
- package/dist/cli.js +90 -29
- package/dist/clock-HSEKS5AR.js +289 -0
- package/dist/{cloud-sync-TG3TIX5H.js → cloud-sync-6JL4C24T.js} +21 -22
- package/dist/config-UTS7QULS.js +76 -0
- package/dist/connectors/discord-bridge.js +3 -3
- package/dist/connectors/slack-bridge.js +3 -3
- package/dist/connectors/telegram-bridge.js +3 -3
- package/dist/{conversations-HL2JP5GI.js → conversations-2PW57WO2.js} +5 -5
- 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 +845 -1766
- package/dist/{db-PLEDCBHZ.js → db-BDMH4SZ2.js} +7 -3
- package/dist/{db-RYX3SS2W.js → db-BVBJ57TU.js} +2 -2
- package/dist/delete-L5PAVDGQ.js +42 -0
- package/dist/delivery-manager-H5ZVBMCQ.js +31 -0
- package/dist/{delivery-router-D5ELDMS2.js → delivery-router-HEJSJAHQ.js} +4 -4
- package/dist/down-74VXM45A.js +17 -0
- package/dist/env-E4XHO2BI.js +223 -0
- package/dist/{exec-DVLXKRIO.js → exec-PY7THYH4.js} +4 -4
- package/dist/export-OAS6QVBN.js +113 -0
- package/dist/{extension-PM42QCID.js → extension-D74CNM7G.js} +25 -33
- package/dist/{extensions-BBGVL5JC.js → extensions-XDDFY72A.js} +22 -11
- package/dist/files-CWTK6V3H.js +53 -0
- package/dist/import-5A3T7QV4.js +143 -0
- package/dist/{isolation-62MKDZN3.js → isolation-TK5RX2WM.js} +3 -3
- package/dist/join-DF5XSJAC.js +67 -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-2ACNHA7B.js → mind-activity-tracker-QBLIV7ZJ.js} +5 -5
- package/dist/{mind-history-WOYFLQAI.js → mind-history-IE2QH7U5.js} +82 -71
- 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-V2WHWVG6.js → package-D2FSVFAX.js} +5 -5
- package/dist/read-67VRP2DO.js +91 -0
- package/dist/{read-stdin-PIRM6A2Y.js → read-stdin-3X5VYKNS.js} +1 -1
- package/dist/register-SB7NXCOE.js +51 -0
- package/dist/{registry-UYV5S6QT.js → registry-GBSNW3HG.js} +2 -2
- package/dist/reject-MUR2KWJ4.js +40 -0
- package/dist/restart-5EGG4JXU.js +42 -0
- package/dist/{sandbox-SI5HMBP3.js → sandbox-R37VIU36.js} +5 -5
- package/dist/scheduler-Y7O4CJXL.js +31 -0
- package/dist/{schema-ETMABTW4.js → schema-XVZ2CLKW.js} +1 -1
- package/dist/{seed-WNGI6PNW.js → seed-EQORWX77.js} +2 -2
- 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-Z3DEVWV7.js → setup-BJ4YAY26.js} +153 -127
- package/dist/{setup-GGMKENLN.js → setup-RHJRFURI.js} +3 -3
- package/dist/skill-TAAKEYBV.js +389 -0
- package/dist/skills/volute-mind/SKILL.md +3 -7
- package/dist/skills/volute-mind/references/extensions.md +8 -11
- package/dist/{skills-Q6VZ2UGD.js → skills-EKMCQ46K.js} +7 -7
- 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-E3HJIV2Z.js → sprout-HE4TITMK.js} +2 -2
- 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-ZEUK7GKZ.js → tailscale-ZIZ2HWJ5.js} +4 -4
- package/dist/{template-hash-EJRTKE36.js → template-hash-A7FNHTB7.js} +2 -2
- package/dist/up-77ICEDEW.js +19 -0
- package/dist/update-ANE5ZM7F.js +225 -0
- package/dist/{update-check-X3YG4WVP.js → update-check-UV55CBEP.js} +3 -3
- package/dist/upgrade-ZMDGC7M2.js +74 -0
- package/dist/variant-QWL2WSRI.js +62 -0
- package/dist/{version-notify-YCH4UVQ2.js → version-notify-FXSEMXWW.js} +28 -27
- package/dist/{volute-config-WBKYJGYQ.js → volute-config-D2XVS2YI.js} +1 -1
- package/dist/web-assets/assets/index-BhxWKvbB.css +1 -0
- package/dist/web-assets/assets/index-CHVKJ9II.js +75 -0
- package/dist/web-assets/index.html +2 -2
- package/dist/web-assets/sw.js +117 -0
- package/package.json +5 -5
- 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/templates/_base/src/lib/auto-commit.ts +8 -8
- package/templates/_base/src/lib/volute-server.ts +6 -0
- package/templates/claude/src/agent.ts +8 -1
- package/dist/accept-TW6V4WI4.js +0 -42
- package/dist/bridge-O753D5F4.js +0 -207
- package/dist/chat-BHYX7DJ4.js +0 -68
- package/dist/chunk-47XDEWWV.js +0 -156
- package/dist/chunk-CVL5IGIR.js +0 -2084
- package/dist/chunk-PB65JZK2.js +0 -85
- package/dist/chunk-TAHX36HZ.js +0 -3679
- package/dist/clock-3X4DSC2N.js +0 -281
- package/dist/config-OROA5DUA.js +0 -72
- package/dist/create-3SEKKI6P.js +0 -71
- package/dist/create-UOSOQ2HN.js +0 -44
- package/dist/daemon-client-WOAQXXBM.js +0 -12
- package/dist/daemon-restart-5ABHNXJZ.js +0 -52
- package/dist/delete-KYOVWR23.js +0 -35
- package/dist/delivery-manager-2BR5NZKF.js +0 -32
- package/dist/down-QVFN4UPK.js +0 -15
- package/dist/env-R34DT7XL.js +0 -195
- package/dist/export-6ZXAXATG.js +0 -112
- package/dist/files-VQV2VZQO.js +0 -47
- package/dist/import-MK2I2T6F.js +0 -23
- package/dist/join-DGYHTJUH.js +0 -66
- package/dist/list-C644WTHV.js +0 -49
- package/dist/login-IIGEQPHL.js +0 -47
- package/dist/login-KZQLMAWE.js +0 -47
- package/dist/logout-AGTZVRGP.js +0 -40
- package/dist/logout-KD6GXIJJ.js +0 -21
- package/dist/message-delivery-V3R6NXJP.js +0 -42
- package/dist/mind-BI4EPBVZ.js +0 -108
- package/dist/mind-list-6VPM7GUQ.js +0 -30
- package/dist/mind-manager-MWW3BTS4.js +0 -32
- package/dist/mind-profile-WPG42U5Y.js +0 -47
- package/dist/mind-service-VIKZJK2M.js +0 -38
- package/dist/mind-sleep-XDISJY74.js +0 -42
- package/dist/mind-status-7FTZWPZF.js +0 -56
- package/dist/mind-wake-KIIKEI3A.js +0 -37
- package/dist/read-H5C26YO7.js +0 -85
- package/dist/register-J27WP33N.js +0 -47
- package/dist/reject-OEANJYIA.js +0 -40
- package/dist/restart-V5EGYBJG.js +0 -33
- package/dist/scheduler-AGG3L2FO.js +0 -32
- package/dist/seed-check-PXTH7YXS.js +0 -32
- package/dist/seed-cmd-VENFTGS3.js +0 -36
- package/dist/seed-create-663ALOKH.js +0 -112
- package/dist/seed-sprout-EH3AGKAI.js +0 -132
- package/dist/send-7FUUUZZH.js +0 -386
- package/dist/skill-DKNYJS4P.js +0 -362
- 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-BJK2ROPX.js +0 -36
- package/dist/spirit-4JP4TY4C.js +0 -23
- package/dist/split-3YPMS2CL.js +0 -63
- package/dist/start-W3TPKX4D.js +0 -33
- package/dist/status-4OVFXFEJ.js +0 -115
- package/dist/stop-GTT6YWYO.js +0 -32
- package/dist/system-channel-DXD2JBOU.js +0 -36
- package/dist/system-chat-TYLOL7SX.js +0 -36
- package/dist/systems-AYLO727G.js +0 -61
- package/dist/up-PA7F2CXE.js +0 -18
- package/dist/update-HG4LCUSG.js +0 -215
- package/dist/upgrade-YGNIDICG.js +0 -67
- package/dist/variant-MZUMRTQO.js +0 -41
- package/dist/web-assets/assets/index-DiiwC-CZ.css +0 -1
- package/dist/web-assets/assets/index-d6y5b9Ij.js +0 -75
- package/packages/extensions/pages/dist/ui/assets/index-tLTROSk5.js +0 -2
package/dist/daemon.js
CHANGED
|
@@ -3,10 +3,10 @@ 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
10
|
import {
|
|
11
11
|
acceptPending,
|
|
12
12
|
formatFileSize,
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
rejectPending,
|
|
15
15
|
stageFile,
|
|
16
16
|
validateFilePath
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-BV65KRHM.js";
|
|
18
18
|
import {
|
|
19
19
|
PROMPT_DEFAULTS,
|
|
20
20
|
PROMPT_KEYS,
|
|
@@ -25,12 +25,16 @@ import {
|
|
|
25
25
|
completeOrphanedTurns,
|
|
26
26
|
completeTurn,
|
|
27
27
|
createTurn,
|
|
28
|
+
deleteSystemsConfig,
|
|
28
29
|
deliverMessage,
|
|
29
30
|
ensureSystemChannel,
|
|
30
31
|
generateSystemReply,
|
|
31
32
|
getActiveTurnId,
|
|
33
|
+
getAllDiscoveredExtensions,
|
|
32
34
|
getDeliveryManager,
|
|
35
|
+
getExtensionStandardSkills,
|
|
33
36
|
getLastToolUseEventId,
|
|
37
|
+
getLoadedExtensions,
|
|
34
38
|
getMindManager,
|
|
35
39
|
getMindPromptDefaults,
|
|
36
40
|
getPrompt,
|
|
@@ -44,46 +48,43 @@ import {
|
|
|
44
48
|
initMindManager,
|
|
45
49
|
initScheduler,
|
|
46
50
|
initSleepManager,
|
|
51
|
+
initSummarizer,
|
|
47
52
|
initTokenBudget,
|
|
53
|
+
installNpmExtension,
|
|
48
54
|
isConversationId,
|
|
49
55
|
joinSystemChannel,
|
|
50
56
|
linkToolResultToTurn,
|
|
57
|
+
loadAllExtensions,
|
|
58
|
+
notifyExtensionsDaemonStart,
|
|
59
|
+
notifyExtensionsDaemonStop,
|
|
51
60
|
publish as publish2,
|
|
52
61
|
publishTypingForChannels,
|
|
62
|
+
readSystemsConfig,
|
|
53
63
|
recordInbound,
|
|
54
64
|
recordOutbound,
|
|
55
65
|
resolveMindToken,
|
|
66
|
+
setExtensionEnabled,
|
|
56
67
|
startMindFull,
|
|
57
68
|
stopMindFull,
|
|
58
69
|
subscribe as subscribe3,
|
|
59
70
|
subscribeAll,
|
|
60
71
|
substitute,
|
|
72
|
+
summarizeOrphanedTurns,
|
|
73
|
+
summarizeTurn,
|
|
61
74
|
tagUntaggedInbound,
|
|
62
75
|
tagUntaggedOutbound,
|
|
63
|
-
trackToolUse
|
|
64
|
-
} from "./chunk-TAHX36HZ.js";
|
|
65
|
-
import {
|
|
66
|
-
deleteSystemsConfig,
|
|
67
|
-
getAllDiscoveredExtensions,
|
|
68
|
-
getExtensionStandardSkills,
|
|
69
|
-
getLoadedExtensions,
|
|
70
|
-
installNpmExtension,
|
|
71
|
-
loadAllExtensions,
|
|
72
|
-
notifyExtensionsDaemonStart,
|
|
73
|
-
notifyExtensionsDaemonStop,
|
|
74
|
-
readSystemsConfig,
|
|
75
|
-
setExtensionEnabled,
|
|
76
|
+
trackToolUse,
|
|
76
77
|
uninstallNpmExtension,
|
|
77
78
|
writeSystemsConfig
|
|
78
|
-
} from "./chunk-
|
|
79
|
-
import "./chunk-
|
|
79
|
+
} from "./chunk-WJPROOU5.js";
|
|
80
|
+
import "./chunk-5XJYUFZH.js";
|
|
80
81
|
import {
|
|
81
82
|
applyInitFiles,
|
|
82
83
|
composeTemplate,
|
|
83
84
|
copyTemplateToDir,
|
|
84
85
|
findTemplatesRoot,
|
|
85
86
|
listFiles
|
|
86
|
-
} from "./chunk-
|
|
87
|
+
} from "./chunk-AOB6GVRM.js";
|
|
87
88
|
import {
|
|
88
89
|
SEED_SKILLS,
|
|
89
90
|
STANDARD_SKILLS,
|
|
@@ -103,45 +104,16 @@ import {
|
|
|
103
104
|
syncBuiltinSkills,
|
|
104
105
|
uninstallSkill,
|
|
105
106
|
updateSkill
|
|
106
|
-
} from "./chunk-
|
|
107
|
-
import {
|
|
108
|
-
readVoluteConfig,
|
|
109
|
-
writeVoluteConfig
|
|
110
|
-
} from "./chunk-OYAKCAVY.js";
|
|
111
|
-
import "./chunk-V45JXOWY.js";
|
|
107
|
+
} from "./chunk-IJHIXLVN.js";
|
|
112
108
|
import {
|
|
113
109
|
extractTextContent
|
|
114
|
-
} from "./chunk-
|
|
110
|
+
} from "./chunk-QWTR6AWZ.js";
|
|
115
111
|
import {
|
|
116
112
|
getActiveMinds,
|
|
117
113
|
onMindEvent,
|
|
118
114
|
stopAll
|
|
119
|
-
} from "./chunk-
|
|
120
|
-
import
|
|
121
|
-
findBridgeForChannel,
|
|
122
|
-
findOpenClawSession,
|
|
123
|
-
getBridgeConfig,
|
|
124
|
-
importOpenClawConnectors,
|
|
125
|
-
importPiSession,
|
|
126
|
-
parseNameFromIdentity,
|
|
127
|
-
readBridgesConfig,
|
|
128
|
-
removeBridgeConfig,
|
|
129
|
-
removeChannelMapping,
|
|
130
|
-
resolveChannelMapping,
|
|
131
|
-
setBridgeConfig,
|
|
132
|
-
setChannelMapping
|
|
133
|
-
} from "./chunk-47ZPNLF4.js";
|
|
134
|
-
import {
|
|
135
|
-
loadMergedEnv,
|
|
136
|
-
mindEnvPath,
|
|
137
|
-
readEnv,
|
|
138
|
-
sharedEnvPath,
|
|
139
|
-
writeEnv
|
|
140
|
-
} from "./chunk-M3K5AARV.js";
|
|
141
|
-
import {
|
|
142
|
-
isHomeOnlyArchive
|
|
143
|
-
} from "./chunk-6WAWMWR5.js";
|
|
144
|
-
import "./chunk-PB65JZK2.js";
|
|
115
|
+
} from "./chunk-N446KRP7.js";
|
|
116
|
+
import "./chunk-QHG4OMZL.js";
|
|
145
117
|
import {
|
|
146
118
|
approveUser,
|
|
147
119
|
changePassword,
|
|
@@ -160,7 +132,7 @@ import {
|
|
|
160
132
|
syncMindProfile,
|
|
161
133
|
updateUserProfile,
|
|
162
134
|
verifyUser
|
|
163
|
-
} from "./chunk-
|
|
135
|
+
} from "./chunk-XRQSAMX2.js";
|
|
164
136
|
import {
|
|
165
137
|
addMessage,
|
|
166
138
|
createChannel,
|
|
@@ -188,13 +160,41 @@ import {
|
|
|
188
160
|
publish,
|
|
189
161
|
setConversationPrivate,
|
|
190
162
|
subscribe as subscribe2
|
|
191
|
-
} from "./chunk-
|
|
163
|
+
} from "./chunk-WZRZFFCL.js";
|
|
192
164
|
import {
|
|
193
165
|
broadcast,
|
|
194
166
|
subscribe
|
|
195
|
-
} from "./chunk-
|
|
167
|
+
} from "./chunk-CORXD635.js";
|
|
168
|
+
import {
|
|
169
|
+
readVoluteConfig,
|
|
170
|
+
writeVoluteConfig
|
|
171
|
+
} from "./chunk-ZSR72JB3.js";
|
|
172
|
+
import "./chunk-PWQ2ITYG.js";
|
|
173
|
+
import {
|
|
174
|
+
findBridgeForChannel,
|
|
175
|
+
findOpenClawSession,
|
|
176
|
+
getBridgeConfig,
|
|
177
|
+
importOpenClawConnectors,
|
|
178
|
+
importPiSession,
|
|
179
|
+
parseNameFromIdentity,
|
|
180
|
+
readBridgesConfig,
|
|
181
|
+
removeBridgeConfig,
|
|
182
|
+
removeChannelMapping,
|
|
183
|
+
resolveChannelMapping,
|
|
184
|
+
setBridgeConfig,
|
|
185
|
+
setChannelMapping
|
|
186
|
+
} from "./chunk-MDJGMOSD.js";
|
|
187
|
+
import {
|
|
188
|
+
loadMergedEnv,
|
|
189
|
+
mindEnvPath,
|
|
190
|
+
readEnv,
|
|
191
|
+
sharedEnvPath,
|
|
192
|
+
writeEnv
|
|
193
|
+
} from "./chunk-A2ZLHBHG.js";
|
|
194
|
+
import {
|
|
195
|
+
isHomeOnlyArchive
|
|
196
|
+
} from "./chunk-N5LMGYXX.js";
|
|
196
197
|
import {
|
|
197
|
-
aiCompleteUtility,
|
|
198
198
|
getAiConfig,
|
|
199
199
|
getAvailableModels,
|
|
200
200
|
getConfiguredProviders,
|
|
@@ -209,38 +209,33 @@ import {
|
|
|
209
209
|
setEnabledModels,
|
|
210
210
|
setUtilityModel,
|
|
211
211
|
unqualifyModelId
|
|
212
|
-
} from "./chunk-
|
|
212
|
+
} from "./chunk-FT5KETXZ.js";
|
|
213
213
|
import {
|
|
214
214
|
logBuffer,
|
|
215
215
|
logger_default
|
|
216
|
-
} from "./chunk-
|
|
217
|
-
import "./chunk-D424ZQGI.js";
|
|
216
|
+
} from "./chunk-BKF4WQCY.js";
|
|
218
217
|
import {
|
|
219
218
|
exec,
|
|
220
219
|
gitExec,
|
|
221
220
|
resolveVoluteBin
|
|
222
|
-
} from "./chunk-
|
|
221
|
+
} from "./chunk-AN2W47GW.js";
|
|
223
222
|
import {
|
|
224
223
|
chownMindDir,
|
|
225
224
|
createMindUser,
|
|
226
225
|
deleteMindUser,
|
|
227
226
|
ensureVoluteGroup,
|
|
228
227
|
isIsolationEnabled,
|
|
229
|
-
mindUserName,
|
|
230
228
|
wrapForIsolation
|
|
231
|
-
} from "./chunk-
|
|
229
|
+
} from "./chunk-VHJRZM2S.js";
|
|
232
230
|
import {
|
|
233
231
|
isSetupComplete,
|
|
234
232
|
readGlobalConfig,
|
|
235
233
|
writeGlobalConfig
|
|
236
|
-
} from "./chunk-
|
|
237
|
-
import {
|
|
238
|
-
readSessionFile
|
|
239
|
-
} from "./chunk-PLDWHR4D.js";
|
|
234
|
+
} from "./chunk-BMZQYACC.js";
|
|
240
235
|
import {
|
|
241
236
|
buildVoluteSlug,
|
|
242
237
|
slugify
|
|
243
|
-
} from "./chunk-
|
|
238
|
+
} from "./chunk-NJK5SDGR.js";
|
|
244
239
|
import {
|
|
245
240
|
addMind,
|
|
246
241
|
addVariant,
|
|
@@ -263,36 +258,35 @@ import {
|
|
|
263
258
|
validateMindName,
|
|
264
259
|
voluteHome,
|
|
265
260
|
voluteSystemDir
|
|
266
|
-
} from "./chunk-
|
|
261
|
+
} from "./chunk-BDYXIWA5.js";
|
|
267
262
|
import {
|
|
268
263
|
activity,
|
|
269
264
|
conversationParticipants,
|
|
270
265
|
conversations,
|
|
271
|
-
messages,
|
|
272
266
|
mindHistory,
|
|
273
267
|
sessions,
|
|
274
268
|
summaries,
|
|
275
269
|
systemPrompts,
|
|
276
270
|
turns,
|
|
277
271
|
users
|
|
278
|
-
} from "./chunk-
|
|
272
|
+
} from "./chunk-5N7Y5WAM.js";
|
|
279
273
|
import {
|
|
280
274
|
__export
|
|
281
275
|
} from "./chunk-7KJOFUNN.js";
|
|
282
276
|
|
|
283
|
-
// src/daemon.ts
|
|
277
|
+
// packages/daemon/src/daemon.ts
|
|
284
278
|
import { randomBytes } from "crypto";
|
|
285
|
-
import { mkdirSync as
|
|
279
|
+
import { mkdirSync as mkdirSync10, readFileSync as readFileSync11, unlinkSync as unlinkSync2, writeFileSync as writeFileSync9 } from "fs";
|
|
286
280
|
import { homedir as homedir3 } from "os";
|
|
287
|
-
import { resolve as
|
|
281
|
+
import { resolve as resolve17 } from "path";
|
|
288
282
|
import { format } from "util";
|
|
289
283
|
|
|
290
|
-
// src/lib/daemon/bridge-manager.ts
|
|
284
|
+
// packages/daemon/src/lib/daemon/bridge-manager.ts
|
|
291
285
|
import { spawn } from "child_process";
|
|
292
286
|
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, unlinkSync, writeFileSync } from "fs";
|
|
293
287
|
import { dirname, resolve as resolve2 } from "path";
|
|
294
288
|
|
|
295
|
-
// src/lib/bridge-defs.ts
|
|
289
|
+
// packages/daemon/src/lib/bridge-defs.ts
|
|
296
290
|
import { existsSync, readFileSync } from "fs";
|
|
297
291
|
import { resolve } from "path";
|
|
298
292
|
var BUILTIN_DEFS = {
|
|
@@ -354,7 +348,7 @@ function checkMissingBridgeEnv(def, env) {
|
|
|
354
348
|
return def.envVars.filter((v) => v.required && !env[v.name]);
|
|
355
349
|
}
|
|
356
350
|
|
|
357
|
-
// src/lib/daemon/bridge-manager.ts
|
|
351
|
+
// packages/daemon/src/lib/daemon/bridge-manager.ts
|
|
358
352
|
var blog = logger_default.child("bridges");
|
|
359
353
|
function searchUpwards(...segments) {
|
|
360
354
|
let searchDir = dirname(new URL(import.meta.url).pathname);
|
|
@@ -494,8 +488,8 @@ var BridgeManager = class {
|
|
|
494
488
|
if (!tracked) return;
|
|
495
489
|
this.stopping.add(platform);
|
|
496
490
|
this.bridges.delete(platform);
|
|
497
|
-
await new Promise((
|
|
498
|
-
tracked.child.on("exit", () =>
|
|
491
|
+
await new Promise((resolve18) => {
|
|
492
|
+
tracked.child.on("exit", () => resolve18());
|
|
499
493
|
try {
|
|
500
494
|
if (tracked.child.pid) {
|
|
501
495
|
process.kill(-tracked.child.pid, "SIGTERM");
|
|
@@ -506,7 +500,7 @@ var BridgeManager = class {
|
|
|
506
500
|
if (err instanceof Error && err.code !== "ESRCH") {
|
|
507
501
|
blog.warn(`failed to stop bridge ${platform}`, logger_default.errorData(err));
|
|
508
502
|
}
|
|
509
|
-
|
|
503
|
+
resolve18();
|
|
510
504
|
}
|
|
511
505
|
setTimeout(() => {
|
|
512
506
|
try {
|
|
@@ -517,7 +511,7 @@ var BridgeManager = class {
|
|
|
517
511
|
}
|
|
518
512
|
} catch {
|
|
519
513
|
}
|
|
520
|
-
|
|
514
|
+
resolve18();
|
|
521
515
|
}, 5e3);
|
|
522
516
|
});
|
|
523
517
|
this.stopping.delete(platform);
|
|
@@ -567,983 +561,54 @@ var BridgeManager = class {
|
|
|
567
561
|
try {
|
|
568
562
|
const pid = parseInt(readFileSync2(pidPath, "utf-8").trim(), 10);
|
|
569
563
|
if (pid > 0) {
|
|
570
|
-
try {
|
|
571
|
-
process.kill(-pid, "SIGTERM");
|
|
572
|
-
} catch {
|
|
573
|
-
try {
|
|
574
|
-
process.kill(pid, "SIGTERM");
|
|
575
|
-
} catch {
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
blog.warn(`killed orphan bridge ${platform} (pid ${pid})`);
|
|
579
|
-
}
|
|
580
|
-
} catch (err) {
|
|
581
|
-
if (err instanceof Error && err.code !== "ESRCH") {
|
|
582
|
-
blog.debug(`orphan bridge ${platform} cleanup: ${err}`);
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
try {
|
|
586
|
-
unlinkSync(pidPath);
|
|
587
|
-
} catch (err) {
|
|
588
|
-
if (err instanceof Error && err.code !== "ENOENT") {
|
|
589
|
-
blog.warn(`failed to clean up PID file for orphan bridge ${platform}`, logger_default.errorData(err));
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
resolveBuiltinBridge(platform) {
|
|
594
|
-
return searchUpwards("connectors", `${platform}-bridge.js`);
|
|
595
|
-
}
|
|
596
|
-
};
|
|
597
|
-
var instance = null;
|
|
598
|
-
function initBridgeManager() {
|
|
599
|
-
if (instance) throw new Error("BridgeManager already initialized");
|
|
600
|
-
instance = new BridgeManager();
|
|
601
|
-
return instance;
|
|
602
|
-
}
|
|
603
|
-
function getBridgeManager() {
|
|
604
|
-
if (!instance) throw new Error("BridgeManager not initialized \u2014 call initBridgeManager() first");
|
|
605
|
-
return instance;
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
// src/lib/daemon/summarizer.ts
|
|
609
|
-
import { and, desc, eq, gte, like, lt, sql } from "drizzle-orm";
|
|
610
|
-
|
|
611
|
-
// src/lib/format-tool.ts
|
|
612
|
-
function summarizeTool(name, input) {
|
|
613
|
-
if (input && typeof input === "object") {
|
|
614
|
-
const args = input;
|
|
615
|
-
const val = args.path ?? args.command ?? args.query ?? args.url;
|
|
616
|
-
if (typeof val === "string") {
|
|
617
|
-
const brief = val.length > 60 ? `${val.slice(0, 57)}...` : val;
|
|
618
|
-
return `[${name} ${brief}]`;
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
return `[${name}]`;
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
// src/lib/daemon/summarizer.ts
|
|
625
|
-
var sLog = logger_default.child("summarizer");
|
|
626
|
-
var SYSTEM_MIND = "_system";
|
|
627
|
-
function getPeriodKey(date, period) {
|
|
628
|
-
switch (period) {
|
|
629
|
-
case "hour": {
|
|
630
|
-
const y = date.getFullYear();
|
|
631
|
-
const m = String(date.getMonth() + 1).padStart(2, "0");
|
|
632
|
-
const d = String(date.getDate()).padStart(2, "0");
|
|
633
|
-
const h = String(date.getHours()).padStart(2, "0");
|
|
634
|
-
return `${y}-${m}-${d}T${h}`;
|
|
635
|
-
}
|
|
636
|
-
case "day": {
|
|
637
|
-
const y = date.getFullYear();
|
|
638
|
-
const m = String(date.getMonth() + 1).padStart(2, "0");
|
|
639
|
-
const d = String(date.getDate()).padStart(2, "0");
|
|
640
|
-
return `${y}-${m}-${d}`;
|
|
641
|
-
}
|
|
642
|
-
case "week":
|
|
643
|
-
return getISOWeekKey(date);
|
|
644
|
-
case "month": {
|
|
645
|
-
const y = date.getFullYear();
|
|
646
|
-
const m = String(date.getMonth() + 1).padStart(2, "0");
|
|
647
|
-
return `${y}-${m}`;
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
function getISOWeekKey(date) {
|
|
652
|
-
const d = new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
|
653
|
-
d.setDate(d.getDate() + 4 - (d.getDay() || 7));
|
|
654
|
-
const yearStart = new Date(d.getFullYear(), 0, 1);
|
|
655
|
-
const weekNum = Math.ceil(((d.getTime() - yearStart.getTime()) / 864e5 + 1) / 7);
|
|
656
|
-
return `${d.getFullYear()}-W${String(weekNum).padStart(2, "0")}`;
|
|
657
|
-
}
|
|
658
|
-
function getPreviousPeriodKey(key, period) {
|
|
659
|
-
switch (period) {
|
|
660
|
-
case "hour": {
|
|
661
|
-
const d = /* @__PURE__ */ new Date(`${key.slice(0, 10)}T${key.slice(11)}:00:00`);
|
|
662
|
-
d.setHours(d.getHours() - 1);
|
|
663
|
-
return getPeriodKey(d, "hour");
|
|
664
|
-
}
|
|
665
|
-
case "day": {
|
|
666
|
-
const d = /* @__PURE__ */ new Date(`${key}T00:00:00`);
|
|
667
|
-
d.setDate(d.getDate() - 1);
|
|
668
|
-
return getPeriodKey(d, "day");
|
|
669
|
-
}
|
|
670
|
-
case "week": {
|
|
671
|
-
const d = isoWeekToDate(key);
|
|
672
|
-
d.setDate(d.getDate() - 7);
|
|
673
|
-
return getPeriodKey(d, "week");
|
|
674
|
-
}
|
|
675
|
-
case "month": {
|
|
676
|
-
const [y, m] = key.split("-").map(Number);
|
|
677
|
-
const d = new Date(y, m - 2, 1);
|
|
678
|
-
return getPeriodKey(d, "month");
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
function isoWeekToDate(weekKey) {
|
|
683
|
-
const [yearStr, weekStr] = weekKey.split("-W");
|
|
684
|
-
const year = parseInt(yearStr, 10);
|
|
685
|
-
const week = parseInt(weekStr, 10);
|
|
686
|
-
const jan4 = new Date(year, 0, 4);
|
|
687
|
-
const dayOfWeek = jan4.getDay() || 7;
|
|
688
|
-
const monday = new Date(jan4);
|
|
689
|
-
monday.setDate(jan4.getDate() - dayOfWeek + 1 + (week - 1) * 7);
|
|
690
|
-
return monday;
|
|
691
|
-
}
|
|
692
|
-
function localDateStr(d) {
|
|
693
|
-
const y = d.getFullYear();
|
|
694
|
-
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
695
|
-
const day = String(d.getDate()).padStart(2, "0");
|
|
696
|
-
return `${y}-${m}-${day}`;
|
|
697
|
-
}
|
|
698
|
-
function utcDateTimeStr(d) {
|
|
699
|
-
return d.toISOString().replace("T", " ").slice(0, 19);
|
|
700
|
-
}
|
|
701
|
-
function getTimeRange(periodKey, period) {
|
|
702
|
-
switch (period) {
|
|
703
|
-
case "hour": {
|
|
704
|
-
const d = /* @__PURE__ */ new Date(`${periodKey.slice(0, 10)}T${periodKey.slice(11)}:00:00`);
|
|
705
|
-
const dEnd = new Date(d.getTime() + 36e5);
|
|
706
|
-
return { start: utcDateTimeStr(d), end: utcDateTimeStr(dEnd) };
|
|
707
|
-
}
|
|
708
|
-
case "day":
|
|
709
|
-
return { start: `${periodKey} 00:00:00`, end: `${periodKey} 23:59:59` };
|
|
710
|
-
case "week": {
|
|
711
|
-
const monday = isoWeekToDate(periodKey);
|
|
712
|
-
const sunday = new Date(monday);
|
|
713
|
-
sunday.setDate(monday.getDate() + 6);
|
|
714
|
-
return {
|
|
715
|
-
start: `${localDateStr(monday)} 00:00:00`,
|
|
716
|
-
end: `${localDateStr(sunday)} 23:59:59`
|
|
717
|
-
};
|
|
718
|
-
}
|
|
719
|
-
case "month": {
|
|
720
|
-
const [y, m] = periodKey.split("-").map(Number);
|
|
721
|
-
const lastDay = new Date(y, m, 0).getDate();
|
|
722
|
-
return {
|
|
723
|
-
start: `${periodKey}-01 00:00:00`,
|
|
724
|
-
end: `${periodKey}-${String(lastDay).padStart(2, "0")} 23:59:59`
|
|
725
|
-
};
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
async function gatherTurnEvents(mind, session, doneId) {
|
|
730
|
-
const db = await getDb();
|
|
731
|
-
const conditions = [
|
|
732
|
-
eq(mindHistory.mind, mind),
|
|
733
|
-
eq(mindHistory.type, "done"),
|
|
734
|
-
lt(mindHistory.id, doneId)
|
|
735
|
-
];
|
|
736
|
-
if (session) {
|
|
737
|
-
conditions.push(eq(mindHistory.session, session));
|
|
738
|
-
}
|
|
739
|
-
const prevDone = await db.select({ id: mindHistory.id }).from(mindHistory).where(and(...conditions)).orderBy(desc(mindHistory.id)).limit(1);
|
|
740
|
-
const prevDoneId = prevDone.length > 0 ? prevDone[0].id : 0;
|
|
741
|
-
const turnConditions = [
|
|
742
|
-
eq(mindHistory.mind, mind),
|
|
743
|
-
sql`${mindHistory.id} > ${prevDoneId}`,
|
|
744
|
-
sql`${mindHistory.id} <= ${doneId}`
|
|
745
|
-
];
|
|
746
|
-
if (session) {
|
|
747
|
-
turnConditions.push(eq(mindHistory.session, session));
|
|
748
|
-
}
|
|
749
|
-
const events = await db.select({
|
|
750
|
-
id: mindHistory.id,
|
|
751
|
-
type: mindHistory.type,
|
|
752
|
-
channel: mindHistory.channel,
|
|
753
|
-
session: mindHistory.session,
|
|
754
|
-
content: mindHistory.content,
|
|
755
|
-
metadata: mindHistory.metadata,
|
|
756
|
-
created_at: mindHistory.created_at
|
|
757
|
-
}).from(mindHistory).where(and(...turnConditions)).orderBy(mindHistory.id);
|
|
758
|
-
return {
|
|
759
|
-
events,
|
|
760
|
-
fromId: events.length > 0 ? events[0].id : doneId,
|
|
761
|
-
toId: doneId
|
|
762
|
-
};
|
|
763
|
-
}
|
|
764
|
-
async function gatherTurnEventsByTurnId(turnId) {
|
|
765
|
-
const db = await getDb();
|
|
766
|
-
const events = await db.select({
|
|
767
|
-
id: mindHistory.id,
|
|
768
|
-
type: mindHistory.type,
|
|
769
|
-
channel: mindHistory.channel,
|
|
770
|
-
session: mindHistory.session,
|
|
771
|
-
content: mindHistory.content,
|
|
772
|
-
metadata: mindHistory.metadata,
|
|
773
|
-
created_at: mindHistory.created_at
|
|
774
|
-
}).from(mindHistory).where(eq(mindHistory.turn_id, turnId)).orderBy(mindHistory.id);
|
|
775
|
-
return {
|
|
776
|
-
events,
|
|
777
|
-
fromId: events.length > 0 ? events[0].id : 0,
|
|
778
|
-
toId: events.length > 0 ? events[events.length - 1].id : 0
|
|
779
|
-
};
|
|
780
|
-
}
|
|
781
|
-
function buildTurnDeterministicSummary(events) {
|
|
782
|
-
const channels = /* @__PURE__ */ new Set();
|
|
783
|
-
const tools = [];
|
|
784
|
-
let hasInbound = false;
|
|
785
|
-
let hasOutbound = false;
|
|
786
|
-
for (const ev of events) {
|
|
787
|
-
if (ev.type === "inbound") {
|
|
788
|
-
hasInbound = true;
|
|
789
|
-
if (ev.channel) channels.add(ev.channel);
|
|
790
|
-
}
|
|
791
|
-
if (ev.type === "outbound" || ev.type === "text") {
|
|
792
|
-
hasOutbound = true;
|
|
793
|
-
}
|
|
794
|
-
if (ev.type === "tool_use" && ev.metadata) {
|
|
795
|
-
try {
|
|
796
|
-
const meta = JSON.parse(ev.metadata);
|
|
797
|
-
if (meta.name) tools.push(meta.name);
|
|
798
|
-
} catch (err) {
|
|
799
|
-
sLog.debug(`failed to parse tool_use metadata for event ${ev.id}`, logger_default.errorData(err));
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
const parts = [];
|
|
804
|
-
if (hasInbound) {
|
|
805
|
-
const channelList = [...channels];
|
|
806
|
-
parts.push(
|
|
807
|
-
channelList.length > 0 ? `Received message on ${channelList.join(", ")}` : "Received message"
|
|
808
|
-
);
|
|
809
|
-
}
|
|
810
|
-
if (tools.length > 0) {
|
|
811
|
-
const unique = [...new Set(tools)];
|
|
812
|
-
parts.push(`Used ${unique.join(", ")}`);
|
|
813
|
-
}
|
|
814
|
-
if (hasOutbound) {
|
|
815
|
-
parts.push("Sent response");
|
|
816
|
-
}
|
|
817
|
-
return parts.length > 0 ? `${parts.join(". ")}.` : "Turn completed.";
|
|
818
|
-
}
|
|
819
|
-
function buildTranscript(events) {
|
|
820
|
-
const lines = [];
|
|
821
|
-
for (const ev of events) {
|
|
822
|
-
switch (ev.type) {
|
|
823
|
-
case "inbound":
|
|
824
|
-
lines.push(`[inbound${ev.channel ? ` ${ev.channel}` : ""}] ${ev.content ?? ""}`);
|
|
825
|
-
break;
|
|
826
|
-
case "outbound":
|
|
827
|
-
case "text":
|
|
828
|
-
lines.push(`[response] ${(ev.content ?? "").slice(0, 500)}`);
|
|
829
|
-
break;
|
|
830
|
-
case "tool_use": {
|
|
831
|
-
let toolInfo = "tool";
|
|
832
|
-
if (ev.metadata) {
|
|
833
|
-
try {
|
|
834
|
-
const meta = JSON.parse(ev.metadata);
|
|
835
|
-
toolInfo = summarizeTool(meta.name ?? "tool", meta.input ?? {});
|
|
836
|
-
} catch (err) {
|
|
837
|
-
sLog.debug(`failed to parse tool_use metadata for event ${ev.id}`, logger_default.errorData(err));
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
lines.push(toolInfo);
|
|
841
|
-
break;
|
|
842
|
-
}
|
|
843
|
-
case "tool_result": {
|
|
844
|
-
const content = ev.content ?? "";
|
|
845
|
-
let isError = false;
|
|
846
|
-
if (ev.metadata) {
|
|
847
|
-
try {
|
|
848
|
-
const meta = JSON.parse(ev.metadata);
|
|
849
|
-
isError = !!meta.is_error;
|
|
850
|
-
} catch (err) {
|
|
851
|
-
sLog.debug(
|
|
852
|
-
`failed to parse tool_result metadata for event ${ev.id}`,
|
|
853
|
-
logger_default.errorData(err)
|
|
854
|
-
);
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
lines.push(isError ? "[result error]" : `[result] ${content.slice(0, 200)}`);
|
|
858
|
-
break;
|
|
859
|
-
}
|
|
860
|
-
case "thinking":
|
|
861
|
-
lines.push(`[thinking] ${(ev.content ?? "").slice(0, 300)}`);
|
|
862
|
-
break;
|
|
863
|
-
}
|
|
864
|
-
}
|
|
865
|
-
return lines.join("\n");
|
|
866
|
-
}
|
|
867
|
-
async function summarizeTurn(mind, session, channel, doneId, turnId) {
|
|
868
|
-
const { events, fromId, toId } = turnId ? await gatherTurnEventsByTurnId(turnId) : await gatherTurnEvents(mind, session, doneId);
|
|
869
|
-
if (events.length === 0) return;
|
|
870
|
-
const substantiveTypes = /* @__PURE__ */ new Set(["text", "outbound", "tool_use", "tool_result", "thinking"]);
|
|
871
|
-
const hasSubstantiveOutput = events.some((ev) => substantiveTypes.has(ev.type));
|
|
872
|
-
if (!hasSubstantiveOutput) {
|
|
873
|
-
sLog.info(
|
|
874
|
-
`skipping summary for interrupted turn ${turnId ?? "(no turn)"} (no substantive output)`
|
|
875
|
-
);
|
|
876
|
-
if (turnId) {
|
|
877
|
-
try {
|
|
878
|
-
const db2 = await getDb();
|
|
879
|
-
await db2.update(mindHistory).set({ turn_id: null }).where(and(eq(mindHistory.turn_id, turnId), eq(mindHistory.type, "inbound")));
|
|
880
|
-
await db2.update(messages).set({ turn_id: null }).where(eq(messages.turn_id, turnId));
|
|
881
|
-
} catch (err) {
|
|
882
|
-
sLog.error(`failed to un-tag events for interrupted turn ${turnId}`, logger_default.errorData(err));
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
return;
|
|
886
|
-
}
|
|
887
|
-
const tools = [];
|
|
888
|
-
for (const ev of events) {
|
|
889
|
-
if (ev.type === "tool_use" && ev.metadata) {
|
|
890
|
-
try {
|
|
891
|
-
const meta = JSON.parse(ev.metadata);
|
|
892
|
-
if (meta.name) tools.push(meta.name);
|
|
893
|
-
} catch (err) {
|
|
894
|
-
sLog.debug(`failed to parse tool_use metadata for event ${ev.id}`, logger_default.errorData(err));
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
}
|
|
898
|
-
const fromTime = events[0].created_at;
|
|
899
|
-
const toTime = events[events.length - 1].created_at;
|
|
900
|
-
let summaryText;
|
|
901
|
-
let deterministic;
|
|
902
|
-
const transcript = buildTranscript(events);
|
|
903
|
-
if (transcript.trim()) {
|
|
904
|
-
const summaryPrompt = await getPrompt("turn_summary");
|
|
905
|
-
const aiResult = await aiCompleteUtility(summaryPrompt, transcript);
|
|
906
|
-
if (aiResult) {
|
|
907
|
-
summaryText = aiResult;
|
|
908
|
-
deterministic = false;
|
|
909
|
-
} else {
|
|
910
|
-
summaryText = buildTurnDeterministicSummary(events);
|
|
911
|
-
deterministic = true;
|
|
912
|
-
}
|
|
913
|
-
} else {
|
|
914
|
-
summaryText = buildTurnDeterministicSummary(events);
|
|
915
|
-
deterministic = true;
|
|
916
|
-
}
|
|
917
|
-
const metadata = {
|
|
918
|
-
deterministic,
|
|
919
|
-
tool_count: tools.length,
|
|
920
|
-
tools: [...new Set(tools)],
|
|
921
|
-
from_id: fromId,
|
|
922
|
-
to_id: toId,
|
|
923
|
-
from_time: fromTime,
|
|
924
|
-
to_time: toTime
|
|
925
|
-
};
|
|
926
|
-
const periodKey = turnId ?? `${mind}-${doneId}`;
|
|
927
|
-
const db = await getDb();
|
|
928
|
-
let summaryId;
|
|
929
|
-
try {
|
|
930
|
-
const result = await db.insert(summaries).values({
|
|
931
|
-
mind,
|
|
932
|
-
period: "turn",
|
|
933
|
-
period_key: periodKey,
|
|
934
|
-
content: summaryText,
|
|
935
|
-
metadata: JSON.stringify(metadata)
|
|
936
|
-
}).onConflictDoNothing().returning({ id: summaries.id });
|
|
937
|
-
summaryId = result[0]?.id;
|
|
938
|
-
if (summaryId == null) {
|
|
939
|
-
const existing = await db.select({ id: summaries.id }).from(summaries).where(
|
|
940
|
-
and(
|
|
941
|
-
eq(summaries.mind, mind),
|
|
942
|
-
eq(summaries.period, "turn"),
|
|
943
|
-
eq(summaries.period_key, periodKey)
|
|
944
|
-
)
|
|
945
|
-
).get();
|
|
946
|
-
summaryId = existing?.id;
|
|
947
|
-
}
|
|
948
|
-
} catch (err) {
|
|
949
|
-
sLog.error(
|
|
950
|
-
`failed to persist turn summary for ${mind} (events ${fromId}-${toId})`,
|
|
951
|
-
logger_default.errorData(err)
|
|
952
|
-
);
|
|
953
|
-
return;
|
|
954
|
-
}
|
|
955
|
-
if (turnId && summaryId != null) {
|
|
956
|
-
setSummaryId(turnId, summaryId).catch((err) => {
|
|
957
|
-
sLog.error(`failed to link summary to turn ${turnId}`, logger_default.errorData(err));
|
|
958
|
-
});
|
|
959
|
-
}
|
|
960
|
-
publish2(mind, {
|
|
961
|
-
mind,
|
|
962
|
-
type: "summary",
|
|
963
|
-
session,
|
|
964
|
-
channel,
|
|
965
|
-
content: summaryText,
|
|
966
|
-
metadata,
|
|
967
|
-
turnId
|
|
968
|
-
});
|
|
969
|
-
}
|
|
970
|
-
async function setSummaryId(turnId, summaryId) {
|
|
971
|
-
const db = await getDb();
|
|
972
|
-
await db.update(turns).set({ summary_id: summaryId }).where(eq(turns.id, turnId));
|
|
973
|
-
}
|
|
974
|
-
function getChildPeriod(period) {
|
|
975
|
-
switch (period) {
|
|
976
|
-
case "hour":
|
|
977
|
-
return "turn";
|
|
978
|
-
case "day":
|
|
979
|
-
return "hour";
|
|
980
|
-
case "week":
|
|
981
|
-
case "month":
|
|
982
|
-
return "day";
|
|
983
|
-
}
|
|
984
|
-
}
|
|
985
|
-
function getScopeInstruction(mind) {
|
|
986
|
-
if (mind === SYSTEM_MIND) {
|
|
987
|
-
return 'Write in third person, describing what the minds in the system did (e.g. "Alice explored...", "The system saw activity in..."). Reference minds by name.';
|
|
988
|
-
}
|
|
989
|
-
return 'Write in first person as the mind who performed the actions (e.g. "I explored...", "I worked on...").';
|
|
990
|
-
}
|
|
991
|
-
function buildPeriodicDeterministicSummary(sources, period, periodKey) {
|
|
992
|
-
if (sources.length === 0) return "";
|
|
993
|
-
switch (period) {
|
|
994
|
-
case "hour":
|
|
995
|
-
return `Activity during ${periodKey.slice(11)}:00: ${sources.join(" ")}`;
|
|
996
|
-
case "day":
|
|
997
|
-
return `Activity on ${periodKey}:
|
|
998
|
-
|
|
999
|
-
${sources.join("\n\n")}`;
|
|
1000
|
-
case "week":
|
|
1001
|
-
return `Week ${periodKey} summary:
|
|
1002
|
-
|
|
1003
|
-
${sources.join("\n\n")}`;
|
|
1004
|
-
case "month":
|
|
1005
|
-
return `${periodKey} summary:
|
|
1006
|
-
|
|
1007
|
-
${sources.join("\n\n")}`;
|
|
1008
|
-
}
|
|
1009
|
-
}
|
|
1010
|
-
async function gatherChildSummaries(mind, period, periodKey) {
|
|
1011
|
-
const db = await getDb();
|
|
1012
|
-
const childPeriod = getChildPeriod(period);
|
|
1013
|
-
if (period === "hour") {
|
|
1014
|
-
const { start: start2, end: end2 } = getTimeRange(periodKey, "hour");
|
|
1015
|
-
const rows2 = await db.select({ id: summaries.id, content: summaries.content }).from(summaries).where(
|
|
1016
|
-
and(
|
|
1017
|
-
eq(summaries.mind, mind),
|
|
1018
|
-
eq(summaries.period, childPeriod),
|
|
1019
|
-
gte(summaries.created_at, start2),
|
|
1020
|
-
lt(summaries.created_at, end2)
|
|
1021
|
-
)
|
|
1022
|
-
).orderBy(summaries.created_at);
|
|
1023
|
-
return {
|
|
1024
|
-
texts: rows2.map((r) => r.content),
|
|
1025
|
-
sourceIds: rows2.map((r) => r.id)
|
|
1026
|
-
};
|
|
1027
|
-
}
|
|
1028
|
-
if (period === "day") {
|
|
1029
|
-
const rows2 = await db.select({ id: summaries.id, content: summaries.content }).from(summaries).where(
|
|
1030
|
-
and(
|
|
1031
|
-
eq(summaries.mind, mind),
|
|
1032
|
-
eq(summaries.period, childPeriod),
|
|
1033
|
-
like(summaries.period_key, `${periodKey}%`)
|
|
1034
|
-
)
|
|
1035
|
-
).orderBy(summaries.period_key);
|
|
1036
|
-
return {
|
|
1037
|
-
texts: rows2.map((r) => r.content),
|
|
1038
|
-
sourceIds: rows2.map((r) => r.id)
|
|
1039
|
-
};
|
|
1040
|
-
}
|
|
1041
|
-
const { start, end } = getTimeRange(periodKey, period);
|
|
1042
|
-
const startKey = start.slice(0, 10);
|
|
1043
|
-
const endKey = end.slice(0, 10);
|
|
1044
|
-
const rows = await db.select({ id: summaries.id, content: summaries.content }).from(summaries).where(
|
|
1045
|
-
and(
|
|
1046
|
-
eq(summaries.mind, mind),
|
|
1047
|
-
eq(summaries.period, childPeriod),
|
|
1048
|
-
gte(summaries.period_key, startKey),
|
|
1049
|
-
sql`${summaries.period_key} <= ${endKey}`
|
|
1050
|
-
)
|
|
1051
|
-
).orderBy(summaries.period_key);
|
|
1052
|
-
return {
|
|
1053
|
-
texts: rows.map((r) => r.content),
|
|
1054
|
-
sourceIds: rows.map((r) => r.id)
|
|
1055
|
-
};
|
|
1056
|
-
}
|
|
1057
|
-
async function summarizePeriod(mind, period, periodKey) {
|
|
1058
|
-
if (await summaryExists(mind, period, periodKey)) return false;
|
|
1059
|
-
const db = await getDb();
|
|
1060
|
-
const sources = await gatherChildSummaries(mind, period, periodKey);
|
|
1061
|
-
if (sources.texts.length === 0) return false;
|
|
1062
|
-
if (sources.texts.length === 1) {
|
|
1063
|
-
try {
|
|
1064
|
-
await db.insert(summaries).values({
|
|
1065
|
-
mind,
|
|
1066
|
-
period,
|
|
1067
|
-
period_key: periodKey,
|
|
1068
|
-
content: sources.texts[0],
|
|
1069
|
-
metadata: JSON.stringify({
|
|
1070
|
-
deterministic: false,
|
|
1071
|
-
promoted: true,
|
|
1072
|
-
source_count: 1,
|
|
1073
|
-
source_ids: sources.sourceIds
|
|
1074
|
-
})
|
|
1075
|
-
}).onConflictDoNothing();
|
|
1076
|
-
} catch (err) {
|
|
1077
|
-
sLog.error(
|
|
1078
|
-
`failed to persist promoted ${period} summary for ${mind} (${periodKey})`,
|
|
1079
|
-
logger_default.errorData(err)
|
|
1080
|
-
);
|
|
1081
|
-
return false;
|
|
1082
|
-
}
|
|
1083
|
-
sLog.info(`promoted single-child ${period} summary for ${mind} (${periodKey})`);
|
|
1084
|
-
return true;
|
|
1085
|
-
}
|
|
1086
|
-
const promptKey = `meta_summary_${period}`;
|
|
1087
|
-
const scopeInstruction = getScopeInstruction(mind);
|
|
1088
|
-
const systemPrompt = await getPrompt(promptKey, { scope_instruction: scopeInstruction });
|
|
1089
|
-
const userMessage = sources.texts.join("\n\n---\n\n");
|
|
1090
|
-
let content;
|
|
1091
|
-
let deterministic;
|
|
1092
|
-
const aiResult = await aiCompleteUtility(systemPrompt, userMessage);
|
|
1093
|
-
if (aiResult) {
|
|
1094
|
-
content = aiResult;
|
|
1095
|
-
deterministic = false;
|
|
1096
|
-
} else {
|
|
1097
|
-
content = buildPeriodicDeterministicSummary(sources.texts, period, periodKey);
|
|
1098
|
-
deterministic = true;
|
|
1099
|
-
}
|
|
1100
|
-
const metadata = {
|
|
1101
|
-
deterministic,
|
|
1102
|
-
source_count: sources.texts.length,
|
|
1103
|
-
source_ids: sources.sourceIds
|
|
1104
|
-
};
|
|
1105
|
-
try {
|
|
1106
|
-
await db.insert(summaries).values({
|
|
1107
|
-
mind,
|
|
1108
|
-
period,
|
|
1109
|
-
period_key: periodKey,
|
|
1110
|
-
content,
|
|
1111
|
-
metadata: JSON.stringify(metadata)
|
|
1112
|
-
}).onConflictDoNothing();
|
|
1113
|
-
} catch (err) {
|
|
1114
|
-
sLog.error(
|
|
1115
|
-
`failed to persist ${period} summary for ${mind} (${periodKey})`,
|
|
1116
|
-
logger_default.errorData(err)
|
|
1117
|
-
);
|
|
1118
|
-
return false;
|
|
1119
|
-
}
|
|
1120
|
-
sLog.info(
|
|
1121
|
-
`generated ${period} summary for ${mind} (${periodKey})${deterministic ? " [deterministic]" : ""}`
|
|
1122
|
-
);
|
|
1123
|
-
return true;
|
|
1124
|
-
}
|
|
1125
|
-
async function summarizeSystem(period, periodKey) {
|
|
1126
|
-
if (await summaryExists(SYSTEM_MIND, period, periodKey)) return;
|
|
1127
|
-
const db = await getDb();
|
|
1128
|
-
const rows = await db.select({ mind: summaries.mind, content: summaries.content }).from(summaries).where(
|
|
1129
|
-
and(
|
|
1130
|
-
eq(summaries.period, period),
|
|
1131
|
-
eq(summaries.period_key, periodKey),
|
|
1132
|
-
sql`${summaries.mind} != ${SYSTEM_MIND}`
|
|
1133
|
-
)
|
|
1134
|
-
).orderBy(summaries.mind);
|
|
1135
|
-
if (rows.length === 0) return;
|
|
1136
|
-
const minds = [...new Set(rows.map((r) => r.mind))];
|
|
1137
|
-
const texts = rows.map((r) => `[${r.mind}] ${r.content}`);
|
|
1138
|
-
const promptKey = `meta_summary_${period}`;
|
|
1139
|
-
const scopeInstruction = getScopeInstruction(SYSTEM_MIND);
|
|
1140
|
-
const systemPrompt = await getPrompt(promptKey, { scope_instruction: scopeInstruction });
|
|
1141
|
-
const userMessage = texts.join("\n\n---\n\n");
|
|
1142
|
-
let content;
|
|
1143
|
-
let deterministic;
|
|
1144
|
-
const aiResult = await aiCompleteUtility(systemPrompt, userMessage);
|
|
1145
|
-
if (aiResult) {
|
|
1146
|
-
content = aiResult;
|
|
1147
|
-
deterministic = false;
|
|
1148
|
-
} else {
|
|
1149
|
-
content = buildPeriodicDeterministicSummary(texts, period, periodKey);
|
|
1150
|
-
deterministic = true;
|
|
1151
|
-
}
|
|
1152
|
-
try {
|
|
1153
|
-
await db.insert(summaries).values({
|
|
1154
|
-
mind: SYSTEM_MIND,
|
|
1155
|
-
period,
|
|
1156
|
-
period_key: periodKey,
|
|
1157
|
-
content,
|
|
1158
|
-
metadata: JSON.stringify({ deterministic, minds, source_count: rows.length })
|
|
1159
|
-
}).onConflictDoNothing();
|
|
1160
|
-
} catch (err) {
|
|
1161
|
-
sLog.error(`failed to persist system ${period} summary (${periodKey})`, logger_default.errorData(err));
|
|
1162
|
-
}
|
|
1163
|
-
}
|
|
1164
|
-
async function mindsWithTurnSummaries(start, end) {
|
|
1165
|
-
const db = await getDb();
|
|
1166
|
-
const rows = await db.select({ mind: summaries.mind }).from(summaries).where(
|
|
1167
|
-
and(
|
|
1168
|
-
eq(summaries.period, "turn"),
|
|
1169
|
-
gte(summaries.created_at, start),
|
|
1170
|
-
lt(summaries.created_at, end),
|
|
1171
|
-
sql`${summaries.mind} != ${SYSTEM_MIND}`
|
|
1172
|
-
)
|
|
1173
|
-
).groupBy(summaries.mind);
|
|
1174
|
-
return rows.map((r) => r.mind);
|
|
1175
|
-
}
|
|
1176
|
-
async function mindsWithSummaries(period, keyPattern) {
|
|
1177
|
-
const db = await getDb();
|
|
1178
|
-
const rows = await db.select({ mind: summaries.mind }).from(summaries).where(
|
|
1179
|
-
and(
|
|
1180
|
-
eq(summaries.period, period),
|
|
1181
|
-
like(summaries.period_key, keyPattern),
|
|
1182
|
-
sql`${summaries.mind} != ${SYSTEM_MIND}`
|
|
1183
|
-
)
|
|
1184
|
-
).groupBy(summaries.mind);
|
|
1185
|
-
return rows.map((r) => r.mind);
|
|
1186
|
-
}
|
|
1187
|
-
async function mindsWithDailySummariesInRange(startKey, endKey) {
|
|
1188
|
-
const db = await getDb();
|
|
1189
|
-
const rows = await db.select({ mind: summaries.mind }).from(summaries).where(
|
|
1190
|
-
and(
|
|
1191
|
-
eq(summaries.period, "day"),
|
|
1192
|
-
gte(summaries.period_key, startKey),
|
|
1193
|
-
sql`${summaries.period_key} <= ${endKey}`,
|
|
1194
|
-
sql`${summaries.mind} != ${SYSTEM_MIND}`
|
|
1195
|
-
)
|
|
1196
|
-
).groupBy(summaries.mind);
|
|
1197
|
-
return rows.map((r) => r.mind);
|
|
1198
|
-
}
|
|
1199
|
-
async function processHour(periodKey) {
|
|
1200
|
-
const { start, end } = getTimeRange(periodKey, "hour");
|
|
1201
|
-
const minds = await mindsWithTurnSummaries(start, end);
|
|
1202
|
-
for (const mind of minds) {
|
|
1203
|
-
try {
|
|
1204
|
-
await summarizePeriod(mind, "hour", periodKey);
|
|
1205
|
-
} catch (err) {
|
|
1206
|
-
sLog.error(`failed to summarize hour for ${mind} (${periodKey})`, logger_default.errorData(err));
|
|
1207
|
-
}
|
|
1208
|
-
}
|
|
1209
|
-
if (minds.length > 0) {
|
|
1210
|
-
await summarizeSystem("hour", periodKey);
|
|
1211
|
-
}
|
|
1212
|
-
}
|
|
1213
|
-
async function processDay(periodKey) {
|
|
1214
|
-
const minds = await mindsWithSummaries("hour", `${periodKey}%`);
|
|
1215
|
-
for (const mind of minds) {
|
|
1216
|
-
try {
|
|
1217
|
-
await summarizePeriod(mind, "day", periodKey);
|
|
1218
|
-
} catch (err) {
|
|
1219
|
-
sLog.error(`failed to summarize day for ${mind} (${periodKey})`, logger_default.errorData(err));
|
|
1220
|
-
}
|
|
1221
|
-
}
|
|
1222
|
-
if (minds.length > 0) {
|
|
1223
|
-
await summarizeSystem("day", periodKey);
|
|
1224
|
-
}
|
|
1225
|
-
}
|
|
1226
|
-
async function processWeek(periodKey) {
|
|
1227
|
-
const { start, end } = getTimeRange(periodKey, "week");
|
|
1228
|
-
const startKey = start.slice(0, 10);
|
|
1229
|
-
const endKey = end.slice(0, 10);
|
|
1230
|
-
const minds = await mindsWithDailySummariesInRange(startKey, endKey);
|
|
1231
|
-
for (const mind of minds) {
|
|
1232
|
-
try {
|
|
1233
|
-
await summarizePeriod(mind, "week", periodKey);
|
|
1234
|
-
} catch (err) {
|
|
1235
|
-
sLog.error(`failed to summarize week for ${mind} (${periodKey})`, logger_default.errorData(err));
|
|
1236
|
-
}
|
|
1237
|
-
}
|
|
1238
|
-
if (minds.length > 0) {
|
|
1239
|
-
await summarizeSystem("week", periodKey);
|
|
1240
|
-
}
|
|
1241
|
-
}
|
|
1242
|
-
async function processMonth(periodKey) {
|
|
1243
|
-
const minds = await mindsWithSummaries("day", `${periodKey}%`);
|
|
1244
|
-
for (const mind of minds) {
|
|
1245
|
-
try {
|
|
1246
|
-
await summarizePeriod(mind, "month", periodKey);
|
|
1247
|
-
} catch (err) {
|
|
1248
|
-
sLog.error(`failed to summarize month for ${mind} (${periodKey})`, logger_default.errorData(err));
|
|
1249
|
-
}
|
|
1250
|
-
}
|
|
1251
|
-
if (minds.length > 0) {
|
|
1252
|
-
await summarizeSystem("month", periodKey);
|
|
1253
|
-
}
|
|
1254
|
-
}
|
|
1255
|
-
async function summaryExists(mind, period, periodKey) {
|
|
1256
|
-
const db = await getDb();
|
|
1257
|
-
const row = await db.select({ id: summaries.id }).from(summaries).where(
|
|
1258
|
-
and(
|
|
1259
|
-
eq(summaries.mind, mind),
|
|
1260
|
-
eq(summaries.period, period),
|
|
1261
|
-
eq(summaries.period_key, periodKey)
|
|
1262
|
-
)
|
|
1263
|
-
).get();
|
|
1264
|
-
return !!row;
|
|
1265
|
-
}
|
|
1266
|
-
async function backfill() {
|
|
1267
|
-
const now = /* @__PURE__ */ new Date();
|
|
1268
|
-
for (let i = 1; i <= 48; i++) {
|
|
1269
|
-
try {
|
|
1270
|
-
const d = new Date(now);
|
|
1271
|
-
d.setHours(d.getHours() - i);
|
|
1272
|
-
const key = getPeriodKey(d, "hour");
|
|
1273
|
-
if (!await summaryExists(SYSTEM_MIND, "hour", key)) {
|
|
1274
|
-
await processHour(key);
|
|
1275
|
-
}
|
|
1276
|
-
} catch (err) {
|
|
1277
|
-
sLog.error(`backfill failed for hour -${i}`, logger_default.errorData(err));
|
|
1278
|
-
}
|
|
1279
|
-
}
|
|
1280
|
-
for (let i = 1; i <= 7; i++) {
|
|
1281
|
-
try {
|
|
1282
|
-
const d = new Date(now);
|
|
1283
|
-
d.setDate(d.getDate() - i);
|
|
1284
|
-
const key = getPeriodKey(d, "day");
|
|
1285
|
-
if (!await summaryExists(SYSTEM_MIND, "day", key)) {
|
|
1286
|
-
await processDay(key);
|
|
1287
|
-
}
|
|
1288
|
-
} catch (err) {
|
|
1289
|
-
sLog.error(`backfill failed for day -${i}`, logger_default.errorData(err));
|
|
1290
|
-
}
|
|
1291
|
-
}
|
|
1292
|
-
for (let i = 1; i <= 4; i++) {
|
|
1293
|
-
try {
|
|
1294
|
-
const d = new Date(now);
|
|
1295
|
-
d.setDate(d.getDate() - i * 7);
|
|
1296
|
-
const key = getPeriodKey(d, "week");
|
|
1297
|
-
if (!await summaryExists(SYSTEM_MIND, "week", key)) {
|
|
1298
|
-
await processWeek(key);
|
|
1299
|
-
}
|
|
1300
|
-
} catch (err) {
|
|
1301
|
-
sLog.error(`backfill failed for week -${i}`, logger_default.errorData(err));
|
|
1302
|
-
}
|
|
1303
|
-
}
|
|
1304
|
-
for (let i = 1; i <= 3; i++) {
|
|
1305
|
-
try {
|
|
1306
|
-
const d = new Date(now);
|
|
1307
|
-
d.setMonth(d.getMonth() - i);
|
|
1308
|
-
const key = getPeriodKey(d, "month");
|
|
1309
|
-
if (!await summaryExists(SYSTEM_MIND, "month", key)) {
|
|
1310
|
-
await processMonth(key);
|
|
1311
|
-
}
|
|
1312
|
-
} catch (err) {
|
|
1313
|
-
sLog.error(`backfill failed for month -${i}`, logger_default.errorData(err));
|
|
1314
|
-
}
|
|
1315
|
-
}
|
|
1316
|
-
}
|
|
1317
|
-
var Summarizer = class {
|
|
1318
|
-
interval = null;
|
|
1319
|
-
lastHourKey = null;
|
|
1320
|
-
hasBackfilled = false;
|
|
1321
|
-
start() {
|
|
1322
|
-
this.interval = setInterval(() => this.tick(), 5 * 6e4);
|
|
1323
|
-
this.tick();
|
|
1324
|
-
}
|
|
1325
|
-
stop() {
|
|
1326
|
-
if (this.interval) {
|
|
1327
|
-
clearInterval(this.interval);
|
|
1328
|
-
this.interval = null;
|
|
1329
|
-
}
|
|
1330
|
-
}
|
|
1331
|
-
async tick() {
|
|
1332
|
-
try {
|
|
1333
|
-
if (!this.hasBackfilled) {
|
|
1334
|
-
await backfill();
|
|
1335
|
-
this.hasBackfilled = true;
|
|
1336
|
-
}
|
|
1337
|
-
const now = /* @__PURE__ */ new Date();
|
|
1338
|
-
const currentHourKey = getPeriodKey(now, "hour");
|
|
1339
|
-
if (this.lastHourKey && this.lastHourKey !== currentHourKey) {
|
|
1340
|
-
await processHour(this.lastHourKey);
|
|
1341
|
-
}
|
|
1342
|
-
this.lastHourKey = currentHourKey;
|
|
1343
|
-
const yesterday = new Date(now);
|
|
1344
|
-
yesterday.setDate(yesterday.getDate() - 1);
|
|
1345
|
-
const yesterdayKey = getPeriodKey(yesterday, "day");
|
|
1346
|
-
if (!await summaryExists(SYSTEM_MIND, "day", yesterdayKey)) {
|
|
1347
|
-
await processDay(yesterdayKey);
|
|
1348
|
-
}
|
|
1349
|
-
const currentWeekKey = getPeriodKey(now, "week");
|
|
1350
|
-
const prevWeekKey = getPreviousPeriodKey(currentWeekKey, "week");
|
|
1351
|
-
if (!await summaryExists(SYSTEM_MIND, "week", prevWeekKey)) {
|
|
1352
|
-
await processWeek(prevWeekKey);
|
|
1353
|
-
}
|
|
1354
|
-
const currentMonthKey = getPeriodKey(now, "month");
|
|
1355
|
-
const prevMonthKey = getPreviousPeriodKey(currentMonthKey, "month");
|
|
1356
|
-
if (!await summaryExists(SYSTEM_MIND, "month", prevMonthKey)) {
|
|
1357
|
-
await processMonth(prevMonthKey);
|
|
1358
|
-
}
|
|
1359
|
-
} catch (err) {
|
|
1360
|
-
sLog.error("tick failed", logger_default.errorData(err));
|
|
1361
|
-
}
|
|
1362
|
-
}
|
|
1363
|
-
};
|
|
1364
|
-
var instance2 = null;
|
|
1365
|
-
function initSummarizer() {
|
|
1366
|
-
if (instance2) throw new Error("Summarizer already initialized");
|
|
1367
|
-
instance2 = new Summarizer();
|
|
1368
|
-
return instance2;
|
|
1369
|
-
}
|
|
1370
|
-
|
|
1371
|
-
// src/lib/history-cleanup.ts
|
|
1372
|
-
import { and as and2, eq as eq2, lt as lt2 } from "drizzle-orm";
|
|
1373
|
-
var LOG_RETENTION_MS = 24 * 60 * 60 * 1e3;
|
|
1374
|
-
async function cleanExpiredLogs() {
|
|
1375
|
-
const db = await getDb();
|
|
1376
|
-
const cutoff = new Date(Date.now() - LOG_RETENTION_MS).toISOString().replace("T", " ").slice(0, 19);
|
|
1377
|
-
await db.delete(mindHistory).where(and2(eq2(mindHistory.type, "log"), lt2(mindHistory.created_at, cutoff)));
|
|
1378
|
-
}
|
|
1379
|
-
|
|
1380
|
-
// src/lib/shared.ts
|
|
1381
|
-
import { execFileSync } from "child_process";
|
|
1382
|
-
import { chmodSync, existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, rmSync, writeFileSync as writeFileSync2 } from "fs";
|
|
1383
|
-
import { resolve as resolve3 } from "path";
|
|
1384
|
-
function readWorktreeGitDir(worktreePath) {
|
|
1385
|
-
const dotGit = resolve3(worktreePath, ".git");
|
|
1386
|
-
if (!existsSync3(dotGit)) return null;
|
|
1387
|
-
try {
|
|
1388
|
-
const content = readFileSync3(dotGit, "utf-8").trim();
|
|
1389
|
-
const match = content.match(/^gitdir:\s*(.+)$/);
|
|
1390
|
-
return match ? match[1] : null;
|
|
1391
|
-
} catch {
|
|
1392
|
-
return null;
|
|
1393
|
-
}
|
|
1394
|
-
}
|
|
1395
|
-
function sharedDir() {
|
|
1396
|
-
return resolve3(voluteHome(), "shared");
|
|
1397
|
-
}
|
|
1398
|
-
async function ensureSharedRepo() {
|
|
1399
|
-
const dir = sharedDir();
|
|
1400
|
-
mkdirSync2(dir, { recursive: true });
|
|
1401
|
-
if (existsSync3(resolve3(dir, ".git"))) {
|
|
1402
|
-
try {
|
|
1403
|
-
await gitExec(["rev-parse", "HEAD"], { cwd: dir });
|
|
1404
|
-
return;
|
|
1405
|
-
} catch (err) {
|
|
1406
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
1407
|
-
if (msg.includes("unknown revision") || msg.includes("bad default revision")) {
|
|
1408
|
-
logger_default.warn("shared repo has no commits, re-initializing");
|
|
1409
|
-
rmSync(resolve3(dir, ".git"), { recursive: true, force: true });
|
|
1410
|
-
} else {
|
|
1411
|
-
throw err;
|
|
1412
|
-
}
|
|
1413
|
-
}
|
|
1414
|
-
}
|
|
1415
|
-
const initArgs = isIsolationEnabled() ? ["init", "--shared=group"] : ["init"];
|
|
1416
|
-
await gitExec(initArgs, { cwd: dir });
|
|
1417
|
-
await gitExec(["checkout", "-b", "main"], { cwd: dir });
|
|
1418
|
-
const pagesDir = resolve3(dir, "pages");
|
|
1419
|
-
mkdirSync2(pagesDir, { recursive: true });
|
|
1420
|
-
writeFileSync2(resolve3(pagesDir, ".gitkeep"), "");
|
|
1421
|
-
await gitExec(["add", "-A"], { cwd: dir });
|
|
1422
|
-
await gitExec(["commit", "-m", "init shared repo"], { cwd: dir });
|
|
1423
|
-
if (isIsolationEnabled()) {
|
|
1424
|
-
try {
|
|
1425
|
-
execFileSync("chgrp", ["-R", "volute", dir], { stdio: "ignore" });
|
|
1426
|
-
} catch (err) {
|
|
1427
|
-
logger_default.warn("failed to chgrp shared repo to volute group", logger_default.errorData(err));
|
|
1428
|
-
}
|
|
1429
|
-
chmodSync(dir, 1533);
|
|
1430
|
-
}
|
|
1431
|
-
}
|
|
1432
|
-
async function addSharedWorktree(mindName, mindDir2) {
|
|
1433
|
-
const dir = sharedDir();
|
|
1434
|
-
if (!existsSync3(resolve3(dir, ".git"))) return;
|
|
1435
|
-
const worktreePath = resolve3(mindDir2, "home", "shared");
|
|
1436
|
-
if (existsSync3(worktreePath)) return;
|
|
1437
|
-
let branchExists = false;
|
|
1438
|
-
try {
|
|
1439
|
-
await gitExec(["rev-parse", "--verify", mindName], { cwd: dir });
|
|
1440
|
-
branchExists = true;
|
|
1441
|
-
} catch {
|
|
1442
|
-
}
|
|
1443
|
-
if (branchExists) {
|
|
1444
|
-
await gitExec(["worktree", "add", worktreePath, mindName], { cwd: dir });
|
|
1445
|
-
} else {
|
|
1446
|
-
await gitExec(["worktree", "add", "-b", mindName, worktreePath], { cwd: dir });
|
|
1447
|
-
}
|
|
1448
|
-
if (isIsolationEnabled()) {
|
|
1449
|
-
const worktreeGitDir = readWorktreeGitDir(worktreePath);
|
|
1450
|
-
if (worktreeGitDir) {
|
|
1451
|
-
try {
|
|
1452
|
-
const user = mindUserName(mindName);
|
|
1453
|
-
execFileSync("chown", ["-R", `${user}:volute`, worktreeGitDir], { stdio: "ignore" });
|
|
1454
|
-
} catch (err) {
|
|
1455
|
-
logger_default.warn(`failed to chown worktree git dir for ${mindName}`, logger_default.errorData(err));
|
|
564
|
+
try {
|
|
565
|
+
process.kill(-pid, "SIGTERM");
|
|
566
|
+
} catch {
|
|
567
|
+
try {
|
|
568
|
+
process.kill(pid, "SIGTERM");
|
|
569
|
+
} catch {
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
blog.warn(`killed orphan bridge ${platform} (pid ${pid})`);
|
|
573
|
+
}
|
|
574
|
+
} catch (err) {
|
|
575
|
+
if (err instanceof Error && err.code !== "ESRCH") {
|
|
576
|
+
blog.debug(`orphan bridge ${platform} cleanup: ${err}`);
|
|
1456
577
|
}
|
|
1457
578
|
}
|
|
1458
|
-
}
|
|
1459
|
-
}
|
|
1460
|
-
async function removeSharedWorktree(mindName, mindDir2) {
|
|
1461
|
-
const dir = sharedDir();
|
|
1462
|
-
if (!existsSync3(resolve3(dir, ".git"))) return;
|
|
1463
|
-
const worktreePath = resolve3(mindDir2, "home", "shared");
|
|
1464
|
-
if (existsSync3(worktreePath)) {
|
|
1465
579
|
try {
|
|
1466
|
-
|
|
580
|
+
unlinkSync(pidPath);
|
|
1467
581
|
} catch (err) {
|
|
1468
|
-
|
|
582
|
+
if (err instanceof Error && err.code !== "ENOENT") {
|
|
583
|
+
blog.warn(`failed to clean up PID file for orphan bridge ${platform}`, logger_default.errorData(err));
|
|
584
|
+
}
|
|
1469
585
|
}
|
|
1470
586
|
}
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
} catch (err) {
|
|
1474
|
-
logger_default.debug(`worktree prune failed for ${mindName}`, logger_default.errorData(err));
|
|
1475
|
-
}
|
|
1476
|
-
try {
|
|
1477
|
-
await gitExec(["branch", "-D", mindName], { cwd: dir });
|
|
1478
|
-
} catch {
|
|
1479
|
-
}
|
|
1480
|
-
}
|
|
1481
|
-
var sharedLock = Promise.resolve();
|
|
1482
|
-
function rechownWorktree(worktreePath, mindName) {
|
|
1483
|
-
if (!isIsolationEnabled()) return;
|
|
1484
|
-
try {
|
|
1485
|
-
const user = mindUserName(mindName);
|
|
1486
|
-
execFileSync("chown", ["-R", `${user}:volute`, worktreePath], { stdio: "ignore" });
|
|
1487
|
-
} catch (err) {
|
|
1488
|
-
logger_default.warn(`failed to rechown worktree for ${mindName}`, logger_default.errorData(err));
|
|
587
|
+
resolveBuiltinBridge(platform) {
|
|
588
|
+
return searchUpwards("connectors", `${platform}-bridge.js`);
|
|
1489
589
|
}
|
|
590
|
+
};
|
|
591
|
+
var instance = null;
|
|
592
|
+
function initBridgeManager() {
|
|
593
|
+
if (instance) throw new Error("BridgeManager already initialized");
|
|
594
|
+
instance = new BridgeManager();
|
|
595
|
+
return instance;
|
|
1490
596
|
}
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
sharedLock = new Promise((r) => {
|
|
1495
|
-
resolve_ = r;
|
|
1496
|
-
});
|
|
1497
|
-
await prev;
|
|
1498
|
-
try {
|
|
1499
|
-
return await fn();
|
|
1500
|
-
} finally {
|
|
1501
|
-
resolve_();
|
|
1502
|
-
}
|
|
597
|
+
function getBridgeManager() {
|
|
598
|
+
if (!instance) throw new Error("BridgeManager not initialized \u2014 call initBridgeManager() first");
|
|
599
|
+
return instance;
|
|
1503
600
|
}
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
["commit", "--author", `${mindName} <${mindName}@volute>`, "-m", `wip: ${mindName}`],
|
|
1513
|
-
{ cwd: worktreePath }
|
|
1514
|
-
);
|
|
1515
|
-
}
|
|
1516
|
-
const diff = (await gitExec(["diff", `main...${mindName}`, "--stat"], { cwd: dir })).trim();
|
|
1517
|
-
if (!diff) {
|
|
1518
|
-
return { ok: true, message: "Nothing to merge" };
|
|
1519
|
-
}
|
|
1520
|
-
try {
|
|
1521
|
-
await gitExec(["merge", "--squash", mindName], { cwd: dir });
|
|
1522
|
-
} catch {
|
|
1523
|
-
try {
|
|
1524
|
-
await gitExec(["reset", "--hard", "HEAD"], { cwd: dir });
|
|
1525
|
-
} catch (resetErr) {
|
|
1526
|
-
logger_default.error("reset after squash conflict failed in shared repo", logger_default.errorData(resetErr));
|
|
1527
|
-
}
|
|
1528
|
-
return { ok: false, conflicts: true, message: "Merge conflicts detected" };
|
|
1529
|
-
}
|
|
1530
|
-
await gitExec(["commit", "--author", `${mindName} <${mindName}@volute>`, "-m", message], {
|
|
1531
|
-
cwd: dir
|
|
1532
|
-
});
|
|
1533
|
-
try {
|
|
1534
|
-
await gitExec(["reset", "--hard", "main"], { cwd: worktreePath });
|
|
1535
|
-
} catch {
|
|
1536
|
-
return {
|
|
1537
|
-
ok: true,
|
|
1538
|
-
message: "Merged to main, but branch reset failed \u2014 run 'volute shared pull' to sync"
|
|
1539
|
-
};
|
|
1540
|
-
}
|
|
1541
|
-
rechownWorktree(worktreePath, mindName);
|
|
1542
|
-
return { ok: true };
|
|
1543
|
-
});
|
|
601
|
+
|
|
602
|
+
// packages/daemon/src/lib/history-cleanup.ts
|
|
603
|
+
import { and, eq, lt } from "drizzle-orm";
|
|
604
|
+
var LOG_RETENTION_MS = 24 * 60 * 60 * 1e3;
|
|
605
|
+
async function cleanExpiredLogs() {
|
|
606
|
+
const db = await getDb();
|
|
607
|
+
const cutoff = new Date(Date.now() - LOG_RETENTION_MS).toISOString().replace("T", " ").slice(0, 19);
|
|
608
|
+
await db.delete(mindHistory).where(and(eq(mindHistory.type, "log"), lt(mindHistory.created_at, cutoff)));
|
|
1544
609
|
}
|
|
1545
610
|
|
|
1546
|
-
// src/web/api/system.ts
|
|
611
|
+
// packages/daemon/src/web/api/system.ts
|
|
1547
612
|
import { zValidator } from "@hono/zod-validator";
|
|
1548
613
|
import { getProviders } from "@mariozechner/pi-ai";
|
|
1549
614
|
import { getOAuthProvider, getOAuthProviders } from "@mariozechner/pi-ai/oauth";
|
|
@@ -1551,7 +616,7 @@ import { Hono } from "hono";
|
|
|
1551
616
|
import { streamSSE } from "hono/streaming";
|
|
1552
617
|
import { z } from "zod";
|
|
1553
618
|
|
|
1554
|
-
// src/lib/services/imagegen.ts
|
|
619
|
+
// packages/daemon/src/lib/services/imagegen.ts
|
|
1555
620
|
import Replicate from "replicate";
|
|
1556
621
|
var PROVIDERS = {
|
|
1557
622
|
replicate: {
|
|
@@ -1746,9 +811,9 @@ async function generateImage(model, prompt) {
|
|
|
1746
811
|
return PROVIDERS[providerId].generate(modelId, prompt, apiKey);
|
|
1747
812
|
}
|
|
1748
813
|
|
|
1749
|
-
// src/web/middleware/auth.ts
|
|
814
|
+
// packages/daemon/src/web/middleware/auth.ts
|
|
1750
815
|
import { timingSafeEqual } from "crypto";
|
|
1751
|
-
import { eq as
|
|
816
|
+
import { eq as eq2, lt as lt2 } from "drizzle-orm";
|
|
1752
817
|
import { getCookie } from "hono/cookie";
|
|
1753
818
|
import { createMiddleware } from "hono/factory";
|
|
1754
819
|
function isValidDaemonToken(token) {
|
|
@@ -1771,14 +836,14 @@ async function createSession(userId) {
|
|
|
1771
836
|
async function deleteSession(sessionId) {
|
|
1772
837
|
sessionCache.delete(sessionId);
|
|
1773
838
|
const db = await getDb();
|
|
1774
|
-
await db.delete(sessions).where(
|
|
839
|
+
await db.delete(sessions).where(eq2(sessions.id, sessionId));
|
|
1775
840
|
}
|
|
1776
841
|
async function getSessionUserId(sessionId) {
|
|
1777
842
|
const db = await getDb();
|
|
1778
|
-
const row = await db.select().from(sessions).where(
|
|
843
|
+
const row = await db.select().from(sessions).where(eq2(sessions.id, sessionId)).get();
|
|
1779
844
|
if (!row) return void 0;
|
|
1780
845
|
if (Date.now() - row.createdAt > SESSION_MAX_AGE) {
|
|
1781
|
-
await db.delete(sessions).where(
|
|
846
|
+
await db.delete(sessions).where(eq2(sessions.id, sessionId));
|
|
1782
847
|
return void 0;
|
|
1783
848
|
}
|
|
1784
849
|
return row.userId;
|
|
@@ -1786,7 +851,7 @@ async function getSessionUserId(sessionId) {
|
|
|
1786
851
|
async function cleanExpiredSessions() {
|
|
1787
852
|
const db = await getDb();
|
|
1788
853
|
const cutoff = Date.now() - SESSION_MAX_AGE;
|
|
1789
|
-
await db.delete(sessions).where(
|
|
854
|
+
await db.delete(sessions).where(lt2(sessions.createdAt, cutoff));
|
|
1790
855
|
}
|
|
1791
856
|
var requireAdmin = createMiddleware(async (c, next) => {
|
|
1792
857
|
const user = c.get("user");
|
|
@@ -1878,7 +943,7 @@ var requireSelf = (paramName = "name") => createMiddleware(async (c, next) => {
|
|
|
1878
943
|
await next();
|
|
1879
944
|
});
|
|
1880
945
|
|
|
1881
|
-
// src/web/api/system.ts
|
|
946
|
+
// packages/daemon/src/web/api/system.ts
|
|
1882
947
|
var DEFAULT_API_URL = "https://volute.systems";
|
|
1883
948
|
var igLog = logger_default.child("imagegen");
|
|
1884
949
|
var app = new Hono().post("/restart", requireAdmin, (c) => {
|
|
@@ -1898,10 +963,10 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
|
|
|
1898
963
|
stream.writeSSE({ data: JSON.stringify(entry) }).catch(() => {
|
|
1899
964
|
});
|
|
1900
965
|
});
|
|
1901
|
-
await new Promise((
|
|
966
|
+
await new Promise((resolve18) => {
|
|
1902
967
|
stream.onAbort(() => {
|
|
1903
968
|
unsubscribe();
|
|
1904
|
-
|
|
969
|
+
resolve18();
|
|
1905
970
|
});
|
|
1906
971
|
});
|
|
1907
972
|
});
|
|
@@ -2089,7 +1154,7 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
|
|
|
2089
1154
|
const { model, prompt } = c.req.valid("json");
|
|
2090
1155
|
try {
|
|
2091
1156
|
const buf = await generateImage(model, prompt);
|
|
2092
|
-
return new Response(buf, {
|
|
1157
|
+
return new Response(new Uint8Array(buf), {
|
|
2093
1158
|
headers: {
|
|
2094
1159
|
"Content-Type": "image/png",
|
|
2095
1160
|
"Content-Length": String(buf.length)
|
|
@@ -2116,13 +1181,15 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
|
|
|
2116
1181
|
let authMethod = null;
|
|
2117
1182
|
if (providerConfig?.oauth) authMethod = "oauth";
|
|
2118
1183
|
else if (providerConfig?.apiKey) authMethod = "api_key";
|
|
1184
|
+
const health = authMethod === "oauth" ? oauthHealth.get(id) : void 0;
|
|
2119
1185
|
return {
|
|
2120
1186
|
id,
|
|
2121
1187
|
oauth: !!oauth,
|
|
2122
1188
|
oauthName: oauth?.name,
|
|
2123
1189
|
usesCallbackServer: !!oauth?.usesCallbackServer,
|
|
2124
1190
|
configured,
|
|
2125
|
-
authMethod
|
|
1191
|
+
authMethod,
|
|
1192
|
+
...health && { oauthHealthy: health.healthy, oauthError: health.error }
|
|
2126
1193
|
};
|
|
2127
1194
|
});
|
|
2128
1195
|
return c.json(result);
|
|
@@ -2210,8 +1277,8 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
|
|
|
2210
1277
|
createdAt: Date.now()
|
|
2211
1278
|
};
|
|
2212
1279
|
oauthFlows.set(flowId, flow);
|
|
2213
|
-
const promptPromise = needsManualCode ? new Promise((
|
|
2214
|
-
flow.resolveCode =
|
|
1280
|
+
const promptPromise = needsManualCode ? new Promise((resolve18) => {
|
|
1281
|
+
flow.resolveCode = resolve18;
|
|
2215
1282
|
}) : void 0;
|
|
2216
1283
|
oauthProvider.login({
|
|
2217
1284
|
onAuth: (info) => {
|
|
@@ -2228,10 +1295,11 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
|
|
|
2228
1295
|
return "";
|
|
2229
1296
|
},
|
|
2230
1297
|
onManualCodeInput: needsManualCode ? () => promptPromise : void 0
|
|
2231
|
-
}).then((credentials) => {
|
|
1298
|
+
}).then(async (credentials) => {
|
|
2232
1299
|
saveProviderConfig(provider, { oauth: credentials });
|
|
2233
1300
|
const existing = oauthFlows.get(flowId);
|
|
2234
1301
|
if (existing) existing.status = "complete";
|
|
1302
|
+
await restartMindsForProvider(provider);
|
|
2235
1303
|
}).catch((err) => {
|
|
2236
1304
|
const existing = oauthFlows.get(flowId);
|
|
2237
1305
|
if (existing) {
|
|
@@ -2239,7 +1307,7 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
|
|
|
2239
1307
|
existing.error = err instanceof Error ? err.message : String(err);
|
|
2240
1308
|
}
|
|
2241
1309
|
});
|
|
2242
|
-
await new Promise((
|
|
1310
|
+
await new Promise((resolve18) => setTimeout(resolve18, 2e3));
|
|
2243
1311
|
const state = oauthFlows.get(flowId);
|
|
2244
1312
|
return c.json({
|
|
2245
1313
|
flowId,
|
|
@@ -2343,8 +1411,29 @@ function cleanupOAuthFlows() {
|
|
|
2343
1411
|
}
|
|
2344
1412
|
}
|
|
2345
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
|
+
}
|
|
2346
1434
|
var apiKeyCache = /* @__PURE__ */ new Map();
|
|
2347
|
-
var
|
|
1435
|
+
var API_KEY_CACHE_TTL_MS = 4 * 60 * 1e3;
|
|
1436
|
+
var REFRESH_CHECK_INTERVAL_MS = 6e4;
|
|
2348
1437
|
function getCachedApiKey(provider) {
|
|
2349
1438
|
const cached = apiKeyCache.get(provider);
|
|
2350
1439
|
if (cached && Date.now() < cached.expiresAt) return cached.key;
|
|
@@ -2352,15 +1441,41 @@ function getCachedApiKey(provider) {
|
|
|
2352
1441
|
}
|
|
2353
1442
|
var keyRefreshTimer = null;
|
|
2354
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
|
+
}
|
|
2355
1450
|
async function refreshApiKeyCache() {
|
|
2356
|
-
|
|
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
|
+
}
|
|
2357
1463
|
try {
|
|
2358
1464
|
const key = await resolveApiKey(provider);
|
|
2359
1465
|
if (key) {
|
|
2360
|
-
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
|
+
});
|
|
2361
1474
|
}
|
|
2362
1475
|
} catch (err) {
|
|
1476
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2363
1477
|
slog.warn(`API key refresh failed for ${provider}`, logger_default.errorData(err));
|
|
1478
|
+
oauthHealth.set(provider, { healthy: false, error: message, since: Date.now() });
|
|
2364
1479
|
}
|
|
2365
1480
|
}
|
|
2366
1481
|
}
|
|
@@ -2373,7 +1488,7 @@ function startApiKeyRefresh() {
|
|
|
2373
1488
|
refreshApiKeyCache().catch((err) => {
|
|
2374
1489
|
slog.warn("periodic API key cache refresh failed", logger_default.errorData(err));
|
|
2375
1490
|
});
|
|
2376
|
-
},
|
|
1491
|
+
}, REFRESH_CHECK_INTERVAL_MS);
|
|
2377
1492
|
}
|
|
2378
1493
|
function stopApiKeyRefresh() {
|
|
2379
1494
|
if (keyRefreshTimer) {
|
|
@@ -2383,14 +1498,15 @@ function stopApiKeyRefresh() {
|
|
|
2383
1498
|
}
|
|
2384
1499
|
var system_default = app;
|
|
2385
1500
|
|
|
2386
|
-
// src/web/app.ts
|
|
2387
|
-
import { Hono as
|
|
1501
|
+
// packages/daemon/src/web/app.ts
|
|
1502
|
+
import { Hono as Hono28 } from "hono";
|
|
2388
1503
|
import { bodyLimit } from "hono/body-limit";
|
|
1504
|
+
import { cors } from "hono/cors";
|
|
2389
1505
|
import { csrf } from "hono/csrf";
|
|
2390
1506
|
import { HTTPException } from "hono/http-exception";
|
|
2391
1507
|
|
|
2392
|
-
// src/web/api/activity.ts
|
|
2393
|
-
import { desc
|
|
1508
|
+
// packages/daemon/src/web/api/activity.ts
|
|
1509
|
+
import { desc } from "drizzle-orm";
|
|
2394
1510
|
import { Hono as Hono2 } from "hono";
|
|
2395
1511
|
import { streamSSE as streamSSE2 } from "hono/streaming";
|
|
2396
1512
|
var app2 = new Hono2().get("/events", async (c) => {
|
|
@@ -2401,7 +1517,7 @@ var app2 = new Hono2().get("/events", async (c) => {
|
|
|
2401
1517
|
let recentActivity = [];
|
|
2402
1518
|
try {
|
|
2403
1519
|
const db = await getDb();
|
|
2404
|
-
recentActivity = await db.select().from(activity).orderBy(
|
|
1520
|
+
recentActivity = await db.select().from(activity).orderBy(desc(activity.created_at)).limit(50);
|
|
2405
1521
|
recentActivity = recentActivity.map((row) => ({
|
|
2406
1522
|
...row,
|
|
2407
1523
|
metadata: row.metadata ? JSON.parse(row.metadata) : null
|
|
@@ -2447,8 +1563,8 @@ var app2 = new Hono2().get("/events", async (c) => {
|
|
|
2447
1563
|
});
|
|
2448
1564
|
}, 15e3);
|
|
2449
1565
|
cleanups.push(() => clearInterval(keepAlive));
|
|
2450
|
-
await new Promise((
|
|
2451
|
-
stream.onAbort(() =>
|
|
1566
|
+
await new Promise((resolve18) => {
|
|
1567
|
+
stream.onAbort(() => resolve18());
|
|
2452
1568
|
});
|
|
2453
1569
|
} finally {
|
|
2454
1570
|
for (const cleanup of cleanups) {
|
|
@@ -2462,9 +1578,9 @@ var app2 = new Hono2().get("/events", async (c) => {
|
|
|
2462
1578
|
});
|
|
2463
1579
|
var activity_default = app2;
|
|
2464
1580
|
|
|
2465
|
-
// src/web/api/auth.ts
|
|
2466
|
-
import { existsSync as
|
|
2467
|
-
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";
|
|
2468
1584
|
import { zValidator as zValidator2 } from "@hono/zod-validator";
|
|
2469
1585
|
import { Hono as Hono3 } from "hono";
|
|
2470
1586
|
import { deleteCookie, getCookie as getCookie2, setCookie } from "hono/cookie";
|
|
@@ -2501,7 +1617,7 @@ var AVATAR_MIME = {
|
|
|
2501
1617
|
};
|
|
2502
1618
|
var MAX_AVATAR_SIZE = 2 * 1024 * 1024;
|
|
2503
1619
|
function avatarsDir() {
|
|
2504
|
-
return
|
|
1620
|
+
return resolve3(voluteHome(), "avatars");
|
|
2505
1621
|
}
|
|
2506
1622
|
var authenticated = new Hono3().use(authMiddleware).post("/change-password", zValidator2("json", changePasswordSchema), async (c) => {
|
|
2507
1623
|
const user = c.get("user");
|
|
@@ -2536,13 +1652,13 @@ var authenticated = new Hono3().use(authMiddleware).post("/change-password", zVa
|
|
|
2536
1652
|
return c.json({ error: "Invalid file type (png, jpg, gif, webp only)" }, 400);
|
|
2537
1653
|
}
|
|
2538
1654
|
const dir = avatarsDir();
|
|
2539
|
-
|
|
1655
|
+
mkdirSync2(dir, { recursive: true });
|
|
2540
1656
|
const filename = `avatar-${user.id}${ext}`;
|
|
2541
1657
|
const buffer2 = Buffer.from(await file.arrayBuffer());
|
|
2542
|
-
|
|
1658
|
+
writeFileSync2(resolve3(dir, filename), buffer2);
|
|
2543
1659
|
if (user.avatar && user.avatar !== filename) {
|
|
2544
|
-
const oldPath =
|
|
2545
|
-
|
|
1660
|
+
const oldPath = resolve3(dir, user.avatar);
|
|
1661
|
+
rmSync(oldPath, { force: true });
|
|
2546
1662
|
}
|
|
2547
1663
|
await updateUserProfile(user.id, { avatar: filename });
|
|
2548
1664
|
const sessionId = getCookie2(c, "volute_session");
|
|
@@ -2556,8 +1672,8 @@ var authenticated = new Hono3().use(authMiddleware).post("/change-password", zVa
|
|
|
2556
1672
|
}).delete("/avatar", async (c) => {
|
|
2557
1673
|
const user = c.get("user");
|
|
2558
1674
|
if (user.avatar) {
|
|
2559
|
-
const path =
|
|
2560
|
-
|
|
1675
|
+
const path = resolve3(avatarsDir(), user.avatar);
|
|
1676
|
+
rmSync(path, { force: true });
|
|
2561
1677
|
}
|
|
2562
1678
|
await updateUserProfile(user.id, { avatar: null });
|
|
2563
1679
|
const sessionId = getCookie2(c, "volute_session");
|
|
@@ -2686,7 +1802,13 @@ var app3 = new Hono3().post("/register", zValidator2("json", credentialsSchema),
|
|
|
2686
1802
|
}
|
|
2687
1803
|
return c.json({ ok: true });
|
|
2688
1804
|
}).get("/me", async (c) => {
|
|
2689
|
-
|
|
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
|
+
}
|
|
2690
1812
|
if (!sessionId) return c.json({ error: "Not logged in" }, 401);
|
|
2691
1813
|
const userId = await getSessionUserId(sessionId);
|
|
2692
1814
|
if (userId == null) return c.json({ error: "Not logged in" }, 401);
|
|
@@ -2706,13 +1828,13 @@ var app3 = new Hono3().post("/register", zValidator2("json", credentialsSchema),
|
|
|
2706
1828
|
return c.json({ error: "Invalid filename" }, 400);
|
|
2707
1829
|
}
|
|
2708
1830
|
const dir = avatarsDir();
|
|
2709
|
-
const filePath =
|
|
1831
|
+
const filePath = resolve3(dir, filename);
|
|
2710
1832
|
if (!filePath.startsWith(`${dir}/`)) return c.json({ error: "Invalid path" }, 400);
|
|
2711
|
-
if (!
|
|
1833
|
+
if (!existsSync3(filePath)) return c.json({ error: "Not found" }, 404);
|
|
2712
1834
|
const ext = extname(filename).toLowerCase();
|
|
2713
1835
|
const mime = AVATAR_MIME[ext];
|
|
2714
1836
|
if (!mime) return c.json({ error: "Invalid file type" }, 400);
|
|
2715
|
-
const data =
|
|
1837
|
+
const data = readFileSync3(filePath);
|
|
2716
1838
|
return c.body(data, 200, {
|
|
2717
1839
|
"Content-Type": mime,
|
|
2718
1840
|
"Cache-Control": "public, max-age=3600",
|
|
@@ -2721,13 +1843,13 @@ var app3 = new Hono3().post("/register", zValidator2("json", credentialsSchema),
|
|
|
2721
1843
|
}).route("/", admin).route("/", authenticated);
|
|
2722
1844
|
var auth_default = app3;
|
|
2723
1845
|
|
|
2724
|
-
// src/web/api/bridges.ts
|
|
1846
|
+
// packages/daemon/src/web/api/bridges.ts
|
|
2725
1847
|
import { zValidator as zValidator3 } from "@hono/zod-validator";
|
|
2726
1848
|
import { Hono as Hono4 } from "hono";
|
|
2727
1849
|
import { z as z3 } from "zod";
|
|
2728
1850
|
|
|
2729
|
-
// src/lib/puppets.ts
|
|
2730
|
-
import { and as
|
|
1851
|
+
// packages/daemon/src/lib/puppets.ts
|
|
1852
|
+
import { and as and2, eq as eq3 } from "drizzle-orm";
|
|
2731
1853
|
async function findOrCreatePuppet(platform, platformId, displayName) {
|
|
2732
1854
|
const username = `${platform}:${platformId}`;
|
|
2733
1855
|
const db = await getDb();
|
|
@@ -2736,10 +1858,10 @@ async function findOrCreatePuppet(platform, platformId, displayName) {
|
|
|
2736
1858
|
username: users.username,
|
|
2737
1859
|
display_name: users.display_name,
|
|
2738
1860
|
avatar: users.avatar
|
|
2739
|
-
}).from(users).where(
|
|
1861
|
+
}).from(users).where(and2(eq3(users.username, username), eq3(users.user_type, "puppet"))).get();
|
|
2740
1862
|
if (existing) {
|
|
2741
1863
|
if (existing.display_name !== displayName) {
|
|
2742
|
-
await db.update(users).set({ display_name: displayName }).where(
|
|
1864
|
+
await db.update(users).set({ display_name: displayName }).where(eq3(users.id, existing.id));
|
|
2743
1865
|
existing.display_name = displayName;
|
|
2744
1866
|
}
|
|
2745
1867
|
return existing;
|
|
@@ -2765,14 +1887,14 @@ async function findOrCreatePuppet(platform, platformId, displayName) {
|
|
|
2765
1887
|
username: users.username,
|
|
2766
1888
|
display_name: users.display_name,
|
|
2767
1889
|
avatar: users.avatar
|
|
2768
|
-
}).from(users).where(
|
|
1890
|
+
}).from(users).where(and2(eq3(users.username, username), eq3(users.user_type, "puppet"))).get();
|
|
2769
1891
|
if (retried) return retried;
|
|
2770
1892
|
}
|
|
2771
1893
|
throw err;
|
|
2772
1894
|
}
|
|
2773
1895
|
}
|
|
2774
1896
|
|
|
2775
|
-
// src/web/api/bridges.ts
|
|
1897
|
+
// packages/daemon/src/web/api/bridges.ts
|
|
2776
1898
|
var inboundSchema = z3.object({
|
|
2777
1899
|
content: z3.array(
|
|
2778
1900
|
z3.union([
|
|
@@ -2847,7 +1969,7 @@ var app4 = new Hono4().post("/:platform/inbound", zValidator3("json", inboundSch
|
|
|
2847
1969
|
}
|
|
2848
1970
|
const participants = await getParticipants(channel.id);
|
|
2849
1971
|
if (!participants.some((p) => p.userId === puppet.id)) {
|
|
2850
|
-
const { addParticipant } = await import("./conversations-
|
|
1972
|
+
const { addParticipant } = await import("./conversations-2PW57WO2.js");
|
|
2851
1973
|
await addParticipant(channel.id, puppet.id);
|
|
2852
1974
|
}
|
|
2853
1975
|
const contentBlocks = body.content;
|
|
@@ -2940,8 +2062,8 @@ async function fanOutToBridgedMinds(opts) {
|
|
|
2940
2062
|
const participants = await getParticipants(opts.conversationId);
|
|
2941
2063
|
const mindParticipants = participants.filter((p) => p.userType === "mind");
|
|
2942
2064
|
const participantNames = participants.map((p) => p.username);
|
|
2943
|
-
const { getMindManager: getMindManager2 } = await import("./mind-manager-
|
|
2944
|
-
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");
|
|
2945
2067
|
const manager = getMindManager2();
|
|
2946
2068
|
const sm = getSleepManagerIfReady2();
|
|
2947
2069
|
const targetMinds = mindParticipants.filter((ap) => {
|
|
@@ -2969,10 +2091,10 @@ async function fanOutToBridgedMinds(opts) {
|
|
|
2969
2091
|
}
|
|
2970
2092
|
var bridges_default = app4;
|
|
2971
2093
|
|
|
2972
|
-
// src/web/api/channels.ts
|
|
2094
|
+
// packages/daemon/src/web/api/channels.ts
|
|
2973
2095
|
import { Hono as Hono5 } from "hono";
|
|
2974
2096
|
|
|
2975
|
-
// src/lib/channels/discord.ts
|
|
2097
|
+
// packages/daemon/src/lib/channels/discord.ts
|
|
2976
2098
|
var discord_exports = {};
|
|
2977
2099
|
__export(discord_exports, {
|
|
2978
2100
|
createConversation: () => createConversation2,
|
|
@@ -2982,7 +2104,7 @@ __export(discord_exports, {
|
|
|
2982
2104
|
send: () => send
|
|
2983
2105
|
});
|
|
2984
2106
|
|
|
2985
|
-
// src/connectors/sdk.ts
|
|
2107
|
+
// packages/daemon/src/connectors/sdk.ts
|
|
2986
2108
|
function splitMessage(text, maxLength) {
|
|
2987
2109
|
const chunks = [];
|
|
2988
2110
|
while (text.length > maxLength) {
|
|
@@ -2995,7 +2117,7 @@ function splitMessage(text, maxLength) {
|
|
|
2995
2117
|
return chunks;
|
|
2996
2118
|
}
|
|
2997
2119
|
|
|
2998
|
-
// src/lib/channels/discord.ts
|
|
2120
|
+
// packages/daemon/src/lib/channels/discord.ts
|
|
2999
2121
|
var DISCORD_MAX_LENGTH = 2e3;
|
|
3000
2122
|
var API_BASE = "https://discord.com/api/v10";
|
|
3001
2123
|
function requireToken(env) {
|
|
@@ -3021,8 +2143,8 @@ async function read(env, channelSlug, limit) {
|
|
|
3021
2143
|
if (!res.ok) {
|
|
3022
2144
|
throw new Error(`Discord API error: ${res.status} ${res.statusText}`);
|
|
3023
2145
|
}
|
|
3024
|
-
const
|
|
3025
|
-
return
|
|
2146
|
+
const messages = await res.json();
|
|
2147
|
+
return messages.reverse().map((m) => `${m.author.username}: ${m.content}`).join("\n");
|
|
3026
2148
|
}
|
|
3027
2149
|
async function send(env, channelSlug, message, images) {
|
|
3028
2150
|
const token = requireToken(env);
|
|
@@ -3142,7 +2264,7 @@ async function createConversation2(env, participants, _name) {
|
|
|
3142
2264
|
return `discord:${dmChannel.id}`;
|
|
3143
2265
|
}
|
|
3144
2266
|
|
|
3145
|
-
// src/lib/channels/slack.ts
|
|
2267
|
+
// packages/daemon/src/lib/channels/slack.ts
|
|
3146
2268
|
var slack_exports = {};
|
|
3147
2269
|
__export(slack_exports, {
|
|
3148
2270
|
createConversation: () => createConversation3,
|
|
@@ -3313,7 +2435,7 @@ async function createConversation3(env, participants, name) {
|
|
|
3313
2435
|
return `slack:${openData.channel.id}`;
|
|
3314
2436
|
}
|
|
3315
2437
|
|
|
3316
|
-
// src/lib/channels/telegram.ts
|
|
2438
|
+
// packages/daemon/src/lib/channels/telegram.ts
|
|
3317
2439
|
var telegram_exports = {};
|
|
3318
2440
|
__export(telegram_exports, {
|
|
3319
2441
|
createConversation: () => createConversation4,
|
|
@@ -3409,7 +2531,7 @@ async function createConversation4() {
|
|
|
3409
2531
|
);
|
|
3410
2532
|
}
|
|
3411
2533
|
|
|
3412
|
-
// src/lib/channels/volute.ts
|
|
2534
|
+
// packages/daemon/src/lib/channels/volute.ts
|
|
3413
2535
|
var volute_exports = {};
|
|
3414
2536
|
__export(volute_exports, {
|
|
3415
2537
|
createConversation: () => createConversation5,
|
|
@@ -3418,16 +2540,28 @@ __export(volute_exports, {
|
|
|
3418
2540
|
read: () => read4,
|
|
3419
2541
|
send: () => send4
|
|
3420
2542
|
});
|
|
3421
|
-
import { existsSync as
|
|
3422
|
-
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
|
+
}
|
|
3423
2557
|
function getDaemonConfig() {
|
|
3424
|
-
const configPath =
|
|
3425
|
-
if (!
|
|
2558
|
+
const configPath = resolve4(voluteSystemDir(), "daemon.json");
|
|
2559
|
+
if (!existsSync4(configPath)) {
|
|
3426
2560
|
throw new Error("Volute daemon is not running");
|
|
3427
2561
|
}
|
|
3428
2562
|
let config2;
|
|
3429
2563
|
try {
|
|
3430
|
-
config2 = JSON.parse(
|
|
2564
|
+
config2 = JSON.parse(readFileSync4(configPath, "utf-8"));
|
|
3431
2565
|
} catch (err) {
|
|
3432
2566
|
throw new Error(`Failed to parse ${configPath}: ${err}`);
|
|
3433
2567
|
}
|
|
@@ -3575,7 +2709,7 @@ async function createConversation5(env, participants, name) {
|
|
|
3575
2709
|
return conv.id;
|
|
3576
2710
|
}
|
|
3577
2711
|
|
|
3578
|
-
// src/lib/channels.ts
|
|
2712
|
+
// packages/daemon/src/lib/channels.ts
|
|
3579
2713
|
var CHANNELS = {
|
|
3580
2714
|
volute: {
|
|
3581
2715
|
name: "volute",
|
|
@@ -3609,7 +2743,7 @@ function resolveChannelId(slug) {
|
|
|
3609
2743
|
return colonIdx !== -1 ? slug.slice(colonIdx + 1) : slug;
|
|
3610
2744
|
}
|
|
3611
2745
|
|
|
3612
|
-
// src/web/api/channels.ts
|
|
2746
|
+
// packages/daemon/src/web/api/channels.ts
|
|
3613
2747
|
function buildEnv(name) {
|
|
3614
2748
|
return { ...loadMergedEnv(name), VOLUTE_MIND: name, VOLUTE_MIND_DIR: mindDir(name) };
|
|
3615
2749
|
}
|
|
@@ -3637,7 +2771,7 @@ var app5 = new Hono5().post("/:name/channels/create", requireSelf(), async (c) =
|
|
|
3637
2771
|
});
|
|
3638
2772
|
var channels_default = app5;
|
|
3639
2773
|
|
|
3640
|
-
// src/web/api/config.ts
|
|
2774
|
+
// packages/daemon/src/web/api/config.ts
|
|
3641
2775
|
import { Hono as Hono6 } from "hono";
|
|
3642
2776
|
var config = new Hono6();
|
|
3643
2777
|
config.get("/models", (c) => {
|
|
@@ -3665,7 +2799,7 @@ config.get("/status", (c) => {
|
|
|
3665
2799
|
});
|
|
3666
2800
|
var config_default = config;
|
|
3667
2801
|
|
|
3668
|
-
// src/web/api/env.ts
|
|
2802
|
+
// packages/daemon/src/web/api/env.ts
|
|
3669
2803
|
import { Hono as Hono7 } from "hono";
|
|
3670
2804
|
var app6 = new Hono7().get("/:name/env", async (c) => {
|
|
3671
2805
|
const name = c.req.param("name");
|
|
@@ -3739,7 +2873,7 @@ var sharedEnvApp = new Hono7().get("/", (c) => {
|
|
|
3739
2873
|
});
|
|
3740
2874
|
var env_default = app6;
|
|
3741
2875
|
|
|
3742
|
-
// src/web/api/extensions.ts
|
|
2876
|
+
// packages/daemon/src/web/api/extensions.ts
|
|
3743
2877
|
import { Hono as Hono8 } from "hono";
|
|
3744
2878
|
var app7 = new Hono8().get("/", (c) => {
|
|
3745
2879
|
return c.json(getLoadedExtensions());
|
|
@@ -3784,15 +2918,15 @@ var app7 = new Hono8().get("/", (c) => {
|
|
|
3784
2918
|
});
|
|
3785
2919
|
var extensions_default = app7;
|
|
3786
2920
|
|
|
3787
|
-
// src/web/api/file-sharing.ts
|
|
3788
|
-
import { readFileSync as
|
|
3789
|
-
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";
|
|
3790
2924
|
import { Hono as Hono9 } from "hono";
|
|
3791
2925
|
async function notifyMind(mindName, message) {
|
|
3792
2926
|
const entry = await findMind(mindName);
|
|
3793
2927
|
if (!entry) return;
|
|
3794
2928
|
try {
|
|
3795
|
-
const { sendSystemMessage } = await import("./system-chat-
|
|
2929
|
+
const { sendSystemMessage } = await import("./system-chat-IDPHYHY4.js");
|
|
3796
2930
|
await sendSystemMessage(mindName, message);
|
|
3797
2931
|
} catch (err) {
|
|
3798
2932
|
logger_default.warn(`[file-sharing] notify mind ${mindName} failed`, logger_default.errorData(err));
|
|
@@ -3811,21 +2945,21 @@ var app8 = new Hono9().post("/:name/files/send", requireSelf(), async (c) => {
|
|
|
3811
2945
|
const pathErr = validateFilePath(body.filePath);
|
|
3812
2946
|
if (pathErr) return c.json({ error: pathErr }, 400);
|
|
3813
2947
|
const senderDir = mindDir(senderName);
|
|
3814
|
-
const filePath =
|
|
2948
|
+
const filePath = resolve5(senderDir, "home", body.filePath);
|
|
3815
2949
|
const MAX_FILE_SIZE3 = 50 * 1024 * 1024;
|
|
3816
|
-
const
|
|
3817
|
-
if (!
|
|
3818
|
-
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) {
|
|
3819
2953
|
return c.json(
|
|
3820
2954
|
{
|
|
3821
|
-
error: `File too large (${formatFileSize(
|
|
2955
|
+
error: `File too large (${formatFileSize(stat3.size)}, max ${formatFileSize(MAX_FILE_SIZE3)})`
|
|
3822
2956
|
},
|
|
3823
2957
|
413
|
|
3824
2958
|
);
|
|
3825
2959
|
}
|
|
3826
2960
|
let content;
|
|
3827
2961
|
try {
|
|
3828
|
-
content =
|
|
2962
|
+
content = readFileSync5(filePath);
|
|
3829
2963
|
} catch (err) {
|
|
3830
2964
|
const code = err.code;
|
|
3831
2965
|
if (code === "ENOENT") {
|
|
@@ -3914,10 +3048,10 @@ var app8 = new Hono9().post("/:name/files/send", requireSelf(), async (c) => {
|
|
|
3914
3048
|
});
|
|
3915
3049
|
var file_sharing_default = app8;
|
|
3916
3050
|
|
|
3917
|
-
// src/web/api/files.ts
|
|
3918
|
-
import { mkdirSync as
|
|
3919
|
-
import { readFile, realpath, stat } from "fs/promises";
|
|
3920
|
-
import { extname as extname2, resolve as
|
|
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";
|
|
3921
3055
|
import { Hono as Hono10 } from "hono";
|
|
3922
3056
|
var AVATAR_MIME2 = {
|
|
3923
3057
|
".png": "image/png",
|
|
@@ -3927,6 +3061,24 @@ var AVATAR_MIME2 = {
|
|
|
3927
3061
|
".webp": "image/webp"
|
|
3928
3062
|
};
|
|
3929
3063
|
var MAX_AVATAR_SIZE2 = 2 * 1024 * 1024;
|
|
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
|
+
};
|
|
3930
3082
|
var app9 = new Hono10().post("/:name/avatar", requireSelf(), async (c) => {
|
|
3931
3083
|
const name = c.req.param("name");
|
|
3932
3084
|
const entry = await findMind(name);
|
|
@@ -3944,17 +3096,17 @@ var app9 = new Hono10().post("/:name/avatar", requireSelf(), async (c) => {
|
|
|
3944
3096
|
return c.json({ error: "Invalid file type (png, jpg, gif, webp only)" }, 400);
|
|
3945
3097
|
}
|
|
3946
3098
|
const dir = entry.dir ?? mindDir(name);
|
|
3947
|
-
const homeDir =
|
|
3099
|
+
const homeDir = resolve6(dir, "home");
|
|
3948
3100
|
const filename = `avatar${ext}`;
|
|
3949
|
-
const avatarPath =
|
|
3101
|
+
const avatarPath = resolve6(homeDir, filename);
|
|
3950
3102
|
const config2 = readVoluteConfig(dir) ?? {};
|
|
3951
3103
|
const oldAvatar = config2.profile?.avatar;
|
|
3952
3104
|
if (oldAvatar && oldAvatar !== filename) {
|
|
3953
|
-
|
|
3105
|
+
rmSync2(resolve6(homeDir, oldAvatar), { force: true });
|
|
3954
3106
|
}
|
|
3955
3107
|
const buffer2 = Buffer.from(await file.arrayBuffer());
|
|
3956
|
-
|
|
3957
|
-
|
|
3108
|
+
mkdirSync3(homeDir, { recursive: true });
|
|
3109
|
+
writeFileSync3(avatarPath, buffer2);
|
|
3958
3110
|
const profile = config2.profile ?? {};
|
|
3959
3111
|
profile.avatar = filename;
|
|
3960
3112
|
config2.profile = profile;
|
|
@@ -3972,8 +3124,8 @@ var app9 = new Hono10().post("/:name/avatar", requireSelf(), async (c) => {
|
|
|
3972
3124
|
const ext = extname2(config2.profile.avatar).toLowerCase();
|
|
3973
3125
|
const mime = AVATAR_MIME2[ext];
|
|
3974
3126
|
if (!mime) return c.json({ error: "Invalid avatar extension" }, 400);
|
|
3975
|
-
const homeDir =
|
|
3976
|
-
const avatarPath =
|
|
3127
|
+
const homeDir = resolve6(dir, "home");
|
|
3128
|
+
const avatarPath = resolve6(homeDir, config2.profile.avatar);
|
|
3977
3129
|
if (!avatarPath.startsWith(`${homeDir}/`)) return c.json({ error: "Invalid avatar path" }, 400);
|
|
3978
3130
|
let realAvatarPath;
|
|
3979
3131
|
try {
|
|
@@ -3997,11 +3149,67 @@ var app9 = new Hono10().post("/:name/avatar", requireSelf(), async (c) => {
|
|
|
3997
3149
|
} catch {
|
|
3998
3150
|
return c.json({ error: "Failed to read avatar file" }, 500);
|
|
3999
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 });
|
|
4000
3208
|
});
|
|
4001
3209
|
var files_default = app9;
|
|
4002
3210
|
|
|
4003
|
-
// src/web/api/history.ts
|
|
4004
|
-
import { and as
|
|
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";
|
|
4005
3213
|
import { Hono as Hono11 } from "hono";
|
|
4006
3214
|
var history = new Hono11().get("/turns", async (c) => {
|
|
4007
3215
|
const mindFilter = c.req.query("mind");
|
|
@@ -4011,16 +3219,16 @@ var history = new Hono11().get("/turns", async (c) => {
|
|
|
4011
3219
|
const offset = Math.max(parseInt(c.req.query("offset") ?? "0", 10) || 0, 0);
|
|
4012
3220
|
const db = await getDb();
|
|
4013
3221
|
const conditions = [];
|
|
4014
|
-
if (mindFilter) conditions.push(
|
|
4015
|
-
if (turnIdFilter) conditions.push(
|
|
3222
|
+
if (mindFilter) conditions.push(eq4(turns.mind, mindFilter));
|
|
3223
|
+
if (turnIdFilter) conditions.push(eq4(turns.id, turnIdFilter));
|
|
4016
3224
|
if (turnIdsFilter) {
|
|
4017
3225
|
const ids = turnIdsFilter.split(",").filter(Boolean);
|
|
4018
3226
|
if (ids.length > 0) conditions.push(inArray(turns.id, ids));
|
|
4019
3227
|
}
|
|
4020
|
-
const turnRows = await db.select().from(turns).where(conditions.length > 0 ?
|
|
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);
|
|
4021
3229
|
if (turnRows.length === 0) return c.json([]);
|
|
4022
3230
|
const turnIds = turnRows.map((t) => t.id);
|
|
4023
|
-
const summaryRows = await db.select().from(summaries).where(
|
|
3231
|
+
const summaryRows = await db.select().from(summaries).where(and3(eq4(summaries.period, "turn"), inArray(summaries.period_key, turnIds)));
|
|
4024
3232
|
const summaryByTurn = /* @__PURE__ */ new Map();
|
|
4025
3233
|
for (const s of summaryRows) {
|
|
4026
3234
|
summaryByTurn.set(s.period_key, { content: s.content, metadata: s.metadata });
|
|
@@ -4035,9 +3243,9 @@ var history = new Hono11().get("/turns", async (c) => {
|
|
|
4035
3243
|
turn_id: mindHistory.turn_id,
|
|
4036
3244
|
created_at: mindHistory.created_at
|
|
4037
3245
|
}).from(mindHistory).where(
|
|
4038
|
-
|
|
3246
|
+
and3(
|
|
4039
3247
|
inArray(mindHistory.turn_id, turnIds),
|
|
4040
|
-
|
|
3248
|
+
sql`${mindHistory.type} IN ('inbound', 'outbound')`
|
|
4041
3249
|
)
|
|
4042
3250
|
).orderBy(mindHistory.created_at);
|
|
4043
3251
|
const msgsByTurnChannel = /* @__PURE__ */ new Map();
|
|
@@ -4127,8 +3335,8 @@ var history = new Hono11().get("/turns", async (c) => {
|
|
|
4127
3335
|
});
|
|
4128
3336
|
if (channelNames.length > 0) {
|
|
4129
3337
|
const channelRows = await db.select({ id: conversations.id, name: conversations.name }).from(conversations).where(
|
|
4130
|
-
|
|
4131
|
-
|
|
3338
|
+
and3(
|
|
3339
|
+
eq4(conversations.type, "channel"),
|
|
4132
3340
|
inArray(
|
|
4133
3341
|
conversations.name,
|
|
4134
3342
|
channelNames.map((c2) => c2.name)
|
|
@@ -4148,22 +3356,22 @@ var history = new Hono11().get("/turns", async (c) => {
|
|
|
4148
3356
|
db.select({
|
|
4149
3357
|
conversation_id: conversationParticipants.conversation_id,
|
|
4150
3358
|
username: users.username
|
|
4151
|
-
}).from(conversationParticipants).innerJoin(users,
|
|
3359
|
+
}).from(conversationParticipants).innerJoin(users, eq4(conversationParticipants.user_id, users.id))
|
|
4152
3360
|
);
|
|
4153
3361
|
const dmRows = await db.with(cp2).select({
|
|
4154
3362
|
id: conversations.id,
|
|
4155
3363
|
targetUsername: users.username
|
|
4156
3364
|
}).from(conversations).innerJoin(
|
|
4157
3365
|
conversationParticipants,
|
|
4158
|
-
|
|
4159
|
-
).innerJoin(users,
|
|
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)));
|
|
4160
3368
|
for (const row of dmRows) {
|
|
4161
3369
|
const slug = `@${row.targetUsername}`;
|
|
4162
3370
|
const mindNames = dmSlugMinds.get(slug);
|
|
4163
3371
|
if (mindNames && !channelIdMap.has(slug)) {
|
|
4164
|
-
const mindCheck = await db.select({ id: conversationParticipants.user_id }).from(conversationParticipants).innerJoin(users,
|
|
4165
|
-
|
|
4166
|
-
|
|
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),
|
|
4167
3375
|
inArray(users.username, [...mindNames])
|
|
4168
3376
|
)
|
|
4169
3377
|
).get();
|
|
@@ -4318,9 +3526,9 @@ var history = new Hono11().get("/turns", async (c) => {
|
|
|
4318
3526
|
return c.json({ error: "period is required (turn, hour, day, week, month)" }, 400);
|
|
4319
3527
|
}
|
|
4320
3528
|
const db = await getDb();
|
|
4321
|
-
const conditions = [
|
|
4322
|
-
if (from) conditions.push(
|
|
4323
|
-
if (to) conditions.push(
|
|
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}`);
|
|
4324
3532
|
const rows = await db.select({
|
|
4325
3533
|
id: summaries.id,
|
|
4326
3534
|
mind: summaries.mind,
|
|
@@ -4329,7 +3537,7 @@ var history = new Hono11().get("/turns", async (c) => {
|
|
|
4329
3537
|
content: summaries.content,
|
|
4330
3538
|
metadata: summaries.metadata,
|
|
4331
3539
|
created_at: summaries.created_at
|
|
4332
|
-
}).from(summaries).where(
|
|
3540
|
+
}).from(summaries).where(and3(...conditions)).orderBy(desc2(summaries.period_key)).limit(limit);
|
|
4333
3541
|
const result = rows.map((r) => {
|
|
4334
3542
|
let metadata = null;
|
|
4335
3543
|
if (r.metadata) {
|
|
@@ -4345,24 +3553,24 @@ var history = new Hono11().get("/turns", async (c) => {
|
|
|
4345
3553
|
});
|
|
4346
3554
|
var history_default = history;
|
|
4347
3555
|
|
|
4348
|
-
// src/web/api/keys.ts
|
|
3556
|
+
// packages/daemon/src/web/api/keys.ts
|
|
4349
3557
|
import { Hono as Hono12 } from "hono";
|
|
4350
3558
|
|
|
4351
|
-
// src/lib/identity.ts
|
|
3559
|
+
// packages/daemon/src/lib/identity.ts
|
|
4352
3560
|
import { createHash, generateKeyPairSync, sign, verify } from "crypto";
|
|
4353
|
-
import { existsSync as
|
|
4354
|
-
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";
|
|
4355
3563
|
function generateIdentity(mindDir2) {
|
|
4356
|
-
const identityDir =
|
|
4357
|
-
|
|
3564
|
+
const identityDir = resolve7(mindDir2, ".mind/identity");
|
|
3565
|
+
mkdirSync4(identityDir, { recursive: true });
|
|
4358
3566
|
const { publicKey, privateKey } = generateKeyPairSync("ed25519", {
|
|
4359
3567
|
publicKeyEncoding: { type: "spki", format: "pem" },
|
|
4360
3568
|
privateKeyEncoding: { type: "pkcs8", format: "pem" }
|
|
4361
3569
|
});
|
|
4362
|
-
const privatePath =
|
|
4363
|
-
const publicPath =
|
|
4364
|
-
|
|
4365
|
-
|
|
3570
|
+
const privatePath = resolve7(identityDir, "private.pem");
|
|
3571
|
+
const publicPath = resolve7(identityDir, "public.pem");
|
|
3572
|
+
writeFileSync4(privatePath, privateKey, { mode: 384 });
|
|
3573
|
+
writeFileSync4(publicPath, publicKey, { mode: 420 });
|
|
4366
3574
|
const config2 = readVoluteConfig(mindDir2) ?? {};
|
|
4367
3575
|
config2.identity = {
|
|
4368
3576
|
privateKey: ".mind/identity/private.pem",
|
|
@@ -4375,9 +3583,9 @@ function getPublicKey(mindDir2) {
|
|
|
4375
3583
|
const config2 = readVoluteConfig(mindDir2);
|
|
4376
3584
|
const relPath = config2?.identity?.publicKey;
|
|
4377
3585
|
if (!relPath) return null;
|
|
4378
|
-
const fullPath =
|
|
4379
|
-
if (!
|
|
4380
|
-
return
|
|
3586
|
+
const fullPath = resolve7(mindDir2, relPath);
|
|
3587
|
+
if (!existsSync5(fullPath)) return null;
|
|
3588
|
+
return readFileSync6(fullPath, "utf-8");
|
|
4381
3589
|
}
|
|
4382
3590
|
function getFingerprint(publicKeyPem) {
|
|
4383
3591
|
return createHash("sha256").update(publicKeyPem).digest("hex");
|
|
@@ -4405,7 +3613,7 @@ async function publishPublicKey(mindName, publicKeyPem) {
|
|
|
4405
3613
|
}
|
|
4406
3614
|
}
|
|
4407
3615
|
|
|
4408
|
-
// src/web/api/keys.ts
|
|
3616
|
+
// packages/daemon/src/web/api/keys.ts
|
|
4409
3617
|
var app10 = new Hono12().get("/:fingerprint", async (c) => {
|
|
4410
3618
|
const fingerprint = c.req.param("fingerprint");
|
|
4411
3619
|
for (const entry of await readRegistry()) {
|
|
@@ -4422,18 +3630,18 @@ var app10 = new Hono12().get("/:fingerprint", async (c) => {
|
|
|
4422
3630
|
});
|
|
4423
3631
|
var keys_default = app10;
|
|
4424
3632
|
|
|
4425
|
-
// src/web/api/logs.ts
|
|
3633
|
+
// packages/daemon/src/web/api/logs.ts
|
|
4426
3634
|
import { spawn as spawn2 } from "child_process";
|
|
4427
|
-
import { existsSync as
|
|
4428
|
-
import { resolve as
|
|
3635
|
+
import { existsSync as existsSync6 } from "fs";
|
|
3636
|
+
import { resolve as resolve8 } from "path";
|
|
4429
3637
|
import { Hono as Hono13 } from "hono";
|
|
4430
3638
|
import { streamSSE as streamSSE3 } from "hono/streaming";
|
|
4431
3639
|
var app11 = new Hono13().get("/:name/logs", async (c) => {
|
|
4432
3640
|
const name = c.req.param("name");
|
|
4433
3641
|
const entry = await findMind(name);
|
|
4434
3642
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
4435
|
-
const logFile =
|
|
4436
|
-
if (!
|
|
3643
|
+
const logFile = resolve8(stateDir(name), "logs", "mind.log");
|
|
3644
|
+
if (!existsSync6(logFile)) {
|
|
4437
3645
|
return c.json({ error: "No log file found" }, 404);
|
|
4438
3646
|
}
|
|
4439
3647
|
return streamSSE3(c, async (stream) => {
|
|
@@ -4451,17 +3659,17 @@ var app11 = new Hono13().get("/:name/logs", async (c) => {
|
|
|
4451
3659
|
stream.onAbort(() => {
|
|
4452
3660
|
tail.kill();
|
|
4453
3661
|
});
|
|
4454
|
-
await new Promise((
|
|
4455
|
-
tail.on("exit",
|
|
4456
|
-
stream.onAbort(
|
|
3662
|
+
await new Promise((resolve18) => {
|
|
3663
|
+
tail.on("exit", resolve18);
|
|
3664
|
+
stream.onAbort(resolve18);
|
|
4457
3665
|
});
|
|
4458
3666
|
});
|
|
4459
3667
|
}).get("/:name/logs/tail", async (c) => {
|
|
4460
3668
|
const name = c.req.param("name");
|
|
4461
3669
|
const entry = await findMind(name);
|
|
4462
3670
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
4463
|
-
const logFile =
|
|
4464
|
-
if (!
|
|
3671
|
+
const logFile = resolve8(stateDir(name), "logs", "mind.log");
|
|
3672
|
+
if (!existsSync6(logFile)) {
|
|
4465
3673
|
return c.json({ error: "No log file found" }, 404);
|
|
4466
3674
|
}
|
|
4467
3675
|
const nParam = parseInt(c.req.query("n") ?? "50", 10);
|
|
@@ -4471,14 +3679,14 @@ var app11 = new Hono13().get("/:name/logs", async (c) => {
|
|
|
4471
3679
|
tail.stdout.on("data", (data) => {
|
|
4472
3680
|
output += data.toString();
|
|
4473
3681
|
});
|
|
4474
|
-
await new Promise((
|
|
4475
|
-
tail.on("exit",
|
|
3682
|
+
await new Promise((resolve18) => {
|
|
3683
|
+
tail.on("exit", resolve18);
|
|
4476
3684
|
});
|
|
4477
3685
|
return c.text(output);
|
|
4478
3686
|
});
|
|
4479
3687
|
var logs_default = app11;
|
|
4480
3688
|
|
|
4481
|
-
// src/web/api/mind-skills.ts
|
|
3689
|
+
// packages/daemon/src/web/api/mind-skills.ts
|
|
4482
3690
|
import { zValidator as zValidator4 } from "@hono/zod-validator";
|
|
4483
3691
|
import { Hono as Hono14 } from "hono";
|
|
4484
3692
|
import { z as z4 } from "zod";
|
|
@@ -4559,36 +3767,36 @@ var app12 = new Hono14().get("/:name/skills", async (c) => {
|
|
|
4559
3767
|
});
|
|
4560
3768
|
var mind_skills_default = app12;
|
|
4561
3769
|
|
|
4562
|
-
// src/web/api/minds.ts
|
|
3770
|
+
// packages/daemon/src/web/api/minds.ts
|
|
4563
3771
|
import {
|
|
4564
3772
|
cpSync,
|
|
4565
|
-
existsSync as
|
|
4566
|
-
mkdirSync as
|
|
3773
|
+
existsSync as existsSync8,
|
|
3774
|
+
mkdirSync as mkdirSync6,
|
|
4567
3775
|
readdirSync as readdirSync2,
|
|
4568
|
-
readFileSync as
|
|
4569
|
-
rmSync as
|
|
4570
|
-
writeFileSync as
|
|
3776
|
+
readFileSync as readFileSync9,
|
|
3777
|
+
rmSync as rmSync4,
|
|
3778
|
+
writeFileSync as writeFileSync7
|
|
4571
3779
|
} from "fs";
|
|
4572
|
-
import { resolve as
|
|
3780
|
+
import { resolve as resolve11 } from "path";
|
|
4573
3781
|
import { zValidator as zValidator5 } from "@hono/zod-validator";
|
|
4574
|
-
import { and as
|
|
3782
|
+
import { and as and4, desc as desc3, eq as eq5, sql as sql2 } from "drizzle-orm";
|
|
4575
3783
|
import { Hono as Hono15 } from "hono";
|
|
4576
3784
|
import { z as z5 } from "zod";
|
|
4577
3785
|
|
|
4578
|
-
// src/lib/consolidate.ts
|
|
4579
|
-
import { readdirSync, readFileSync as
|
|
4580
|
-
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";
|
|
4581
3789
|
async function consolidateMemory(mindDir2) {
|
|
4582
|
-
const soulPath =
|
|
4583
|
-
const memoryPath =
|
|
4584
|
-
const memoryDir =
|
|
4585
|
-
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");
|
|
4586
3794
|
const logs = [];
|
|
4587
3795
|
try {
|
|
4588
3796
|
const files = readdirSync(memoryDir).filter((f) => /^\d{4}-\d{2}-\d{2}\.md$/.test(f)).sort();
|
|
4589
3797
|
for (const filename of files) {
|
|
4590
3798
|
const date = filename.replace(".md", "");
|
|
4591
|
-
const content2 =
|
|
3799
|
+
const content2 = readFileSync7(resolve9(memoryDir, filename), "utf-8").trim();
|
|
4592
3800
|
if (content2) {
|
|
4593
3801
|
logs.push(`### ${date}
|
|
4594
3802
|
|
|
@@ -4638,7 +3846,7 @@ ${content2}`);
|
|
|
4638
3846
|
const data = await res.json();
|
|
4639
3847
|
const content = data.content.filter((b) => b.type === "text" && b.text).map((b) => b.text).join("").trim();
|
|
4640
3848
|
if (content) {
|
|
4641
|
-
|
|
3849
|
+
writeFileSync5(memoryPath, `${content}
|
|
4642
3850
|
`);
|
|
4643
3851
|
console.log("MEMORY.md created successfully.");
|
|
4644
3852
|
} else {
|
|
@@ -4646,26 +3854,26 @@ ${content2}`);
|
|
|
4646
3854
|
}
|
|
4647
3855
|
}
|
|
4648
3856
|
|
|
4649
|
-
// src/lib/convert-session.ts
|
|
3857
|
+
// packages/daemon/src/lib/convert-session.ts
|
|
4650
3858
|
import { randomUUID } from "crypto";
|
|
4651
|
-
import { mkdirSync as
|
|
3859
|
+
import { mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "fs";
|
|
4652
3860
|
import { homedir } from "os";
|
|
4653
|
-
import { resolve as
|
|
3861
|
+
import { resolve as resolve10 } from "path";
|
|
4654
3862
|
function convertSession(opts) {
|
|
4655
|
-
const lines =
|
|
3863
|
+
const lines = readFileSync8(opts.sessionPath, "utf-8").trim().split("\n");
|
|
4656
3864
|
const sessionId = randomUUID();
|
|
4657
3865
|
const idMap = /* @__PURE__ */ new Map();
|
|
4658
|
-
const
|
|
3866
|
+
const messages = [];
|
|
4659
3867
|
for (const line of lines) {
|
|
4660
3868
|
const event = JSON.parse(line);
|
|
4661
3869
|
if (event.type === "message" && event.message) {
|
|
4662
|
-
|
|
3870
|
+
messages.push(event);
|
|
4663
3871
|
}
|
|
4664
3872
|
}
|
|
4665
3873
|
const sdkEvents = [];
|
|
4666
3874
|
let lastSdkUuid = null;
|
|
4667
|
-
for (let i = 0; i <
|
|
4668
|
-
const event =
|
|
3875
|
+
for (let i = 0; i < messages.length; i++) {
|
|
3876
|
+
const event = messages[i];
|
|
4669
3877
|
const msg = event.message;
|
|
4670
3878
|
if (msg.role === "user") {
|
|
4671
3879
|
const uuid = randomUUID();
|
|
@@ -4726,8 +3934,8 @@ function convertSession(opts) {
|
|
|
4726
3934
|
let lastToolResultId = event.id;
|
|
4727
3935
|
let lastTimestamp = event.timestamp;
|
|
4728
3936
|
let j = i;
|
|
4729
|
-
while (j <
|
|
4730
|
-
const tr =
|
|
3937
|
+
while (j < messages.length && messages[j].message.role === "toolResult") {
|
|
3938
|
+
const tr = messages[j];
|
|
4731
3939
|
const trMsg = tr.message;
|
|
4732
3940
|
lastToolResultId = tr.id;
|
|
4733
3941
|
lastTimestamp = tr.timestamp;
|
|
@@ -4766,10 +3974,10 @@ function convertSession(opts) {
|
|
|
4766
3974
|
}
|
|
4767
3975
|
}
|
|
4768
3976
|
const projectId = opts.projectDir.replace(/\//g, "-");
|
|
4769
|
-
const sdkDir =
|
|
4770
|
-
|
|
4771
|
-
const sdkPath =
|
|
4772
|
-
|
|
3977
|
+
const sdkDir = resolve10(homedir(), ".claude", "projects", projectId);
|
|
3978
|
+
mkdirSync5(sdkDir, { recursive: true });
|
|
3979
|
+
const sdkPath = resolve10(sdkDir, `${sessionId}.jsonl`);
|
|
3980
|
+
writeFileSync6(sdkPath, `${sdkEvents.join("\n")}
|
|
4773
3981
|
`);
|
|
4774
3982
|
console.log(`Converted ${sdkEvents.length} messages \u2192 ${sdkPath}`);
|
|
4775
3983
|
return sessionId;
|
|
@@ -4820,7 +4028,7 @@ function convertAssistantContent(content) {
|
|
|
4820
4028
|
return result;
|
|
4821
4029
|
}
|
|
4822
4030
|
|
|
4823
|
-
// src/lib/health.ts
|
|
4031
|
+
// packages/daemon/src/lib/health.ts
|
|
4824
4032
|
async function checkHealth(port) {
|
|
4825
4033
|
try {
|
|
4826
4034
|
const res = await fetch(`http://127.0.0.1:${port}/health`, {
|
|
@@ -4834,8 +4042,8 @@ async function checkHealth(port) {
|
|
|
4834
4042
|
}
|
|
4835
4043
|
}
|
|
4836
4044
|
|
|
4837
|
-
// src/lib/variant-cleanup.ts
|
|
4838
|
-
import { existsSync as
|
|
4045
|
+
// packages/daemon/src/lib/variant-cleanup.ts
|
|
4046
|
+
import { existsSync as existsSync7, rmSync as rmSync3 } from "fs";
|
|
4839
4047
|
async function cleanupVariant(variantName, projectRoot, variantPath, opts) {
|
|
4840
4048
|
if (opts?.stop) {
|
|
4841
4049
|
try {
|
|
@@ -4844,14 +4052,14 @@ async function cleanupVariant(variantName, projectRoot, variantPath, opts) {
|
|
|
4844
4052
|
logger_default.warn(`failed to stop variant ${variantName}`, logger_default.errorData(err));
|
|
4845
4053
|
}
|
|
4846
4054
|
}
|
|
4847
|
-
const { findMind: findMind2 } = await import("./registry-
|
|
4055
|
+
const { findMind: findMind2 } = await import("./registry-GBSNW3HG.js");
|
|
4848
4056
|
const variantEntry = await findMind2(variantName);
|
|
4849
4057
|
const branchName = variantEntry?.branch ?? variantName;
|
|
4850
|
-
if (
|
|
4058
|
+
if (existsSync7(variantPath)) {
|
|
4851
4059
|
try {
|
|
4852
4060
|
await gitExec(["worktree", "remove", "--force", variantPath], { cwd: projectRoot });
|
|
4853
4061
|
} catch {
|
|
4854
|
-
|
|
4062
|
+
rmSync3(variantPath, { recursive: true, force: true });
|
|
4855
4063
|
try {
|
|
4856
4064
|
await gitExec(["worktree", "prune"], { cwd: projectRoot });
|
|
4857
4065
|
} catch (err) {
|
|
@@ -4880,7 +4088,7 @@ async function cleanupVariant(variantName, projectRoot, variantPath, opts) {
|
|
|
4880
4088
|
}
|
|
4881
4089
|
}
|
|
4882
4090
|
|
|
4883
|
-
// src/lib/variants.ts
|
|
4091
|
+
// packages/daemon/src/lib/variants.ts
|
|
4884
4092
|
var SAFE_BRANCH_RE = /^[a-zA-Z0-9._\-/]+$/;
|
|
4885
4093
|
function validateBranchName(branch) {
|
|
4886
4094
|
if (!SAFE_BRANCH_RE.test(branch)) {
|
|
@@ -4892,13 +4100,13 @@ function validateBranchName(branch) {
|
|
|
4892
4100
|
return null;
|
|
4893
4101
|
}
|
|
4894
4102
|
|
|
4895
|
-
// src/web/api/minds.ts
|
|
4103
|
+
// packages/daemon/src/web/api/minds.ts
|
|
4896
4104
|
var SUBSTANTIVE_TYPES = /* @__PURE__ */ new Set(["thinking", "text", "tool_use", "tool_result", "outbound"]);
|
|
4897
4105
|
async function getMindStatus(name, port) {
|
|
4898
4106
|
const manager = getMindManager();
|
|
4899
4107
|
let status = "stopped";
|
|
4900
4108
|
try {
|
|
4901
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
4109
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-7KFK3USC.js");
|
|
4902
4110
|
if (getSleepManagerIfReady2()?.isSleeping(name)) {
|
|
4903
4111
|
status = "sleeping";
|
|
4904
4112
|
}
|
|
@@ -4944,7 +4152,7 @@ async function initTemplateBranch(projectRoot, composedDir, manifest, mindName,
|
|
|
4944
4152
|
await gitExec(["commit", "-m", "initial commit"], opts);
|
|
4945
4153
|
}
|
|
4946
4154
|
async function updateTemplateBranch(projectRoot, template, mindName) {
|
|
4947
|
-
const tempWorktree =
|
|
4155
|
+
const tempWorktree = resolve11(projectRoot, ".variants", "_template_update");
|
|
4948
4156
|
let branchExists = false;
|
|
4949
4157
|
try {
|
|
4950
4158
|
await gitExec(["rev-parse", "--verify", TEMPLATE_BRANCH], { cwd: projectRoot });
|
|
@@ -4955,8 +4163,8 @@ async function updateTemplateBranch(projectRoot, template, mindName) {
|
|
|
4955
4163
|
await gitExec(["worktree", "remove", "--force", tempWorktree], { cwd: projectRoot });
|
|
4956
4164
|
} catch {
|
|
4957
4165
|
}
|
|
4958
|
-
if (
|
|
4959
|
-
|
|
4166
|
+
if (existsSync8(tempWorktree)) {
|
|
4167
|
+
rmSync4(tempWorktree, { recursive: true, force: true });
|
|
4960
4168
|
}
|
|
4961
4169
|
const templatesRoot = findTemplatesRoot();
|
|
4962
4170
|
const { composedDir, manifest } = composeTemplate(templatesRoot, template);
|
|
@@ -4976,15 +4184,15 @@ async function updateTemplateBranch(projectRoot, template, mindName) {
|
|
|
4976
4184
|
});
|
|
4977
4185
|
}
|
|
4978
4186
|
copyTemplateToDir(composedDir, tempWorktree, mindName, manifest);
|
|
4979
|
-
const initDir =
|
|
4980
|
-
if (
|
|
4981
|
-
|
|
4187
|
+
const initDir = resolve11(tempWorktree, ".init");
|
|
4188
|
+
if (existsSync8(initDir)) {
|
|
4189
|
+
rmSync4(initDir, { recursive: true, force: true });
|
|
4982
4190
|
}
|
|
4983
|
-
const homeDir =
|
|
4984
|
-
if (
|
|
4191
|
+
const homeDir = resolve11(tempWorktree, "home");
|
|
4192
|
+
if (existsSync8(homeDir)) {
|
|
4985
4193
|
for (const entry of readdirSync2(homeDir)) {
|
|
4986
4194
|
if (entry !== "VOLUTE.md") {
|
|
4987
|
-
|
|
4195
|
+
rmSync4(resolve11(homeDir, entry), { recursive: true, force: true });
|
|
4988
4196
|
}
|
|
4989
4197
|
}
|
|
4990
4198
|
}
|
|
@@ -4999,10 +4207,10 @@ async function updateTemplateBranch(projectRoot, template, mindName) {
|
|
|
4999
4207
|
await gitExec(["worktree", "remove", "--force", tempWorktree], { cwd: projectRoot });
|
|
5000
4208
|
} catch {
|
|
5001
4209
|
}
|
|
5002
|
-
if (
|
|
5003
|
-
|
|
4210
|
+
if (existsSync8(tempWorktree)) {
|
|
4211
|
+
rmSync4(tempWorktree, { recursive: true, force: true });
|
|
5004
4212
|
}
|
|
5005
|
-
|
|
4213
|
+
rmSync4(composedDir, { recursive: true, force: true });
|
|
5006
4214
|
}
|
|
5007
4215
|
}
|
|
5008
4216
|
async function mergeTemplateBranch(worktreeDir) {
|
|
@@ -5025,7 +4233,7 @@ async function mergeTemplateBranch(worktreeDir) {
|
|
|
5025
4233
|
async function npmInstallAsMind(cwd, mindName) {
|
|
5026
4234
|
if (isIsolationEnabled()) {
|
|
5027
4235
|
const [cmd, args] = await wrapForIsolation("npm", ["install"], mindName);
|
|
5028
|
-
await exec(cmd, args, { cwd, env: { ...process.env, HOME:
|
|
4236
|
+
await exec(cmd, args, { cwd, env: { ...process.env, HOME: resolve11(cwd, "home") } });
|
|
5029
4237
|
} else {
|
|
5030
4238
|
await exec("npm", ["install"], { cwd });
|
|
5031
4239
|
}
|
|
@@ -5076,8 +4284,8 @@ async function mergeUpgradeAndRestart(mindName, dir, worktreeDir, upgradeVariant
|
|
|
5076
4284
|
return { ok: true };
|
|
5077
4285
|
}
|
|
5078
4286
|
async function importFromArchive(c, tempDir, nameOverride, manifest) {
|
|
5079
|
-
const extractedMindDir =
|
|
5080
|
-
if (!
|
|
4287
|
+
const extractedMindDir = resolve11(tempDir, "mind");
|
|
4288
|
+
if (!existsSync8(extractedMindDir)) {
|
|
5081
4289
|
return c.json({ error: "Invalid archive: missing mind/ directory" }, 400);
|
|
5082
4290
|
}
|
|
5083
4291
|
if (!manifest?.includes || !manifest.name || !manifest.template) {
|
|
@@ -5095,17 +4303,17 @@ async function importFromFullArchive(c, tempDir, extractedMindDir, nameOverride,
|
|
|
5095
4303
|
if (await findMind(name)) return c.json({ error: `Mind already exists: ${name}` }, 409);
|
|
5096
4304
|
ensureVoluteHome();
|
|
5097
4305
|
const dest = mindDir(name);
|
|
5098
|
-
if (
|
|
4306
|
+
if (existsSync8(dest)) return c.json({ error: "Mind directory already exists" }, 409);
|
|
5099
4307
|
try {
|
|
5100
4308
|
cpSync(extractedMindDir, dest, { recursive: true });
|
|
5101
4309
|
if (!manifest.includes.identity) {
|
|
5102
4310
|
generateIdentity(dest);
|
|
5103
4311
|
}
|
|
5104
4312
|
const state = stateDir(name);
|
|
5105
|
-
|
|
5106
|
-
const envJson =
|
|
5107
|
-
if (
|
|
5108
|
-
cpSync(envJson,
|
|
4313
|
+
mkdirSync6(state, { recursive: true });
|
|
4314
|
+
const envJson = resolve11(tempDir, "state/env.json");
|
|
4315
|
+
if (existsSync8(envJson)) {
|
|
4316
|
+
cpSync(envJson, resolve11(state, "env.json"));
|
|
5109
4317
|
}
|
|
5110
4318
|
const port = await nextPort();
|
|
5111
4319
|
await addMind(name, port, manifest.stage, manifest.template);
|
|
@@ -5114,36 +4322,36 @@ async function importFromFullArchive(c, tempDir, extractedMindDir, nameOverride,
|
|
|
5114
4322
|
} catch (err) {
|
|
5115
4323
|
logger_default.warn(`failed to set template hash for ${name}`, logger_default.errorData(err));
|
|
5116
4324
|
}
|
|
5117
|
-
const homeDir =
|
|
4325
|
+
const homeDir = resolve11(dest, "home");
|
|
5118
4326
|
ensureVoluteGroup();
|
|
5119
4327
|
createMindUser(name, homeDir);
|
|
5120
4328
|
chownMindDir(dest, name);
|
|
5121
4329
|
await npmInstallAsMind(dest, name);
|
|
5122
4330
|
await importHistoryFromArchive(name, tempDir);
|
|
5123
4331
|
importSessionsFromArchive(dest, tempDir);
|
|
5124
|
-
if (!
|
|
4332
|
+
if (!existsSync8(resolve11(dest, ".git"))) {
|
|
5125
4333
|
try {
|
|
5126
|
-
const env = isIsolationEnabled() ? { ...process.env, HOME:
|
|
4334
|
+
const env = isIsolationEnabled() ? { ...process.env, HOME: resolve11(dest, "home") } : void 0;
|
|
5127
4335
|
await gitExec(["init"], { cwd: dest, mindName: name, env });
|
|
5128
4336
|
await configureGitIdentity(name, { cwd: dest, mindName: name, env });
|
|
5129
4337
|
await gitExec(["add", "-A"], { cwd: dest, mindName: name, env });
|
|
5130
4338
|
await gitExec(["commit", "-m", "import from archive"], { cwd: dest, mindName: name, env });
|
|
5131
4339
|
} catch (err) {
|
|
5132
4340
|
logger_default.error(`git setup failed for imported mind ${name}`, logger_default.errorData(err));
|
|
5133
|
-
|
|
4341
|
+
rmSync4(resolve11(dest, ".git"), { recursive: true, force: true });
|
|
5134
4342
|
}
|
|
5135
4343
|
}
|
|
5136
4344
|
chownMindDir(dest, name);
|
|
5137
|
-
|
|
4345
|
+
rmSync4(tempDir, { recursive: true, force: true });
|
|
5138
4346
|
return c.json({ ok: true, name, port, message: `Imported mind: ${name} (port ${port})` });
|
|
5139
4347
|
} catch (err) {
|
|
5140
|
-
if (
|
|
4348
|
+
if (existsSync8(dest)) rmSync4(dest, { recursive: true, force: true });
|
|
5141
4349
|
try {
|
|
5142
4350
|
await removeMind(name);
|
|
5143
4351
|
} catch (cleanupErr) {
|
|
5144
4352
|
logger_default.error(`Failed to clean up registry for ${name}`, logger_default.errorData(cleanupErr));
|
|
5145
4353
|
}
|
|
5146
|
-
|
|
4354
|
+
rmSync4(tempDir, { recursive: true, force: true });
|
|
5147
4355
|
return c.json({ error: err instanceof Error ? err.message : "Failed to import mind" }, 500);
|
|
5148
4356
|
}
|
|
5149
4357
|
}
|
|
@@ -5154,7 +4362,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
|
|
|
5154
4362
|
if (await findMind(name)) return c.json({ error: `Mind already exists: ${name}` }, 409);
|
|
5155
4363
|
ensureVoluteHome();
|
|
5156
4364
|
const dest = mindDir(name);
|
|
5157
|
-
if (
|
|
4365
|
+
if (existsSync8(dest)) return c.json({ error: "Mind directory already exists" }, 409);
|
|
5158
4366
|
const templatesRoot = findTemplatesRoot();
|
|
5159
4367
|
const { composedDir, manifest: templateManifest } = composeTemplate(
|
|
5160
4368
|
templatesRoot,
|
|
@@ -5163,36 +4371,36 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
|
|
|
5163
4371
|
try {
|
|
5164
4372
|
copyTemplateToDir(composedDir, dest, name, templateManifest);
|
|
5165
4373
|
applyInitFiles(dest);
|
|
5166
|
-
const extractedHome =
|
|
5167
|
-
if (
|
|
5168
|
-
cpSync(extractedHome,
|
|
4374
|
+
const extractedHome = resolve11(extractedMindDir, "home");
|
|
4375
|
+
if (existsSync8(extractedHome)) {
|
|
4376
|
+
cpSync(extractedHome, resolve11(dest, "home"), { recursive: true });
|
|
5169
4377
|
}
|
|
5170
|
-
const extractedMindInternal =
|
|
5171
|
-
if (
|
|
5172
|
-
cpSync(extractedMindInternal,
|
|
4378
|
+
const extractedMindInternal = resolve11(extractedMindDir, ".mind");
|
|
4379
|
+
if (existsSync8(extractedMindInternal)) {
|
|
4380
|
+
cpSync(extractedMindInternal, resolve11(dest, ".mind"), { recursive: true });
|
|
5173
4381
|
}
|
|
5174
|
-
const identityDir =
|
|
4382
|
+
const identityDir = resolve11(dest, ".mind/identity");
|
|
5175
4383
|
let publicKeyPem;
|
|
5176
|
-
if (!manifest.includes.identity || !
|
|
4384
|
+
if (!manifest.includes.identity || !existsSync8(resolve11(identityDir, "private.pem"))) {
|
|
5177
4385
|
({ publicKeyPem } = generateIdentity(dest));
|
|
5178
4386
|
} else {
|
|
5179
|
-
publicKeyPem =
|
|
4387
|
+
publicKeyPem = readFileSync9(resolve11(identityDir, "public.pem"), "utf-8");
|
|
5180
4388
|
}
|
|
5181
|
-
const promptsPath =
|
|
5182
|
-
if (!
|
|
4389
|
+
const promptsPath = resolve11(dest, "home/.config/prompts.json");
|
|
4390
|
+
if (!existsSync8(promptsPath)) {
|
|
5183
4391
|
const mindPrompts = await getMindPromptDefaults();
|
|
5184
|
-
|
|
4392
|
+
writeFileSync7(promptsPath, `${JSON.stringify(mindPrompts, null, 2)}
|
|
5185
4393
|
`);
|
|
5186
4394
|
}
|
|
5187
4395
|
const state = stateDir(name);
|
|
5188
|
-
|
|
5189
|
-
const envJson =
|
|
5190
|
-
if (
|
|
5191
|
-
cpSync(envJson,
|
|
4396
|
+
mkdirSync6(state, { recursive: true });
|
|
4397
|
+
const envJson = resolve11(tempDir, "state/env.json");
|
|
4398
|
+
if (existsSync8(envJson)) {
|
|
4399
|
+
cpSync(envJson, resolve11(state, "env.json"));
|
|
5192
4400
|
}
|
|
5193
4401
|
const port = await nextPort();
|
|
5194
4402
|
await addMind(name, port, manifest.stage, manifest.template);
|
|
5195
|
-
const homeDir =
|
|
4403
|
+
const homeDir = resolve11(dest, "home");
|
|
5196
4404
|
ensureVoluteGroup();
|
|
5197
4405
|
createMindUser(name, homeDir);
|
|
5198
4406
|
chownMindDir(dest, name);
|
|
@@ -5205,14 +4413,9 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
|
|
|
5205
4413
|
await initTemplateBranch(dest, composedDir, templateManifest, name, env);
|
|
5206
4414
|
} catch (err) {
|
|
5207
4415
|
logger_default.error(`git setup failed for imported mind ${name}`, logger_default.errorData(err));
|
|
5208
|
-
|
|
4416
|
+
rmSync4(resolve11(dest, ".git"), { recursive: true, force: true });
|
|
5209
4417
|
gitWarning = "Git setup failed \u2014 variants and upgrades won't be available until git is initialized.";
|
|
5210
4418
|
}
|
|
5211
|
-
try {
|
|
5212
|
-
await addSharedWorktree(name, dest);
|
|
5213
|
-
} catch (err) {
|
|
5214
|
-
logger_default.warn(`failed to add shared worktree for ${name}`, logger_default.errorData(err));
|
|
5215
|
-
}
|
|
5216
4419
|
const skillSet = manifest.stage === "seed" ? SEED_SKILLS : getStandardSkillsWithExtensions();
|
|
5217
4420
|
const skillWarnings = [];
|
|
5218
4421
|
for (const skillId of skillSet) {
|
|
@@ -5229,7 +4432,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
|
|
|
5229
4432
|
publishPublicKey(name, publicKeyPem).catch(
|
|
5230
4433
|
(err) => logger_default.warn(`failed to publish key for ${name}`, { error: err.message })
|
|
5231
4434
|
);
|
|
5232
|
-
|
|
4435
|
+
rmSync4(tempDir, { recursive: true, force: true });
|
|
5233
4436
|
return c.json({
|
|
5234
4437
|
ok: true,
|
|
5235
4438
|
name,
|
|
@@ -5240,24 +4443,24 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
|
|
|
5240
4443
|
...skillWarnings.length > 0 && { skillWarnings }
|
|
5241
4444
|
});
|
|
5242
4445
|
} catch (err) {
|
|
5243
|
-
if (
|
|
4446
|
+
if (existsSync8(dest)) rmSync4(dest, { recursive: true, force: true });
|
|
5244
4447
|
try {
|
|
5245
4448
|
await removeMind(name);
|
|
5246
4449
|
} catch (cleanupErr) {
|
|
5247
4450
|
logger_default.error(`Failed to clean up registry for ${name}`, logger_default.errorData(cleanupErr));
|
|
5248
4451
|
}
|
|
5249
|
-
|
|
4452
|
+
rmSync4(tempDir, { recursive: true, force: true });
|
|
5250
4453
|
return c.json({ error: err instanceof Error ? err.message : "Failed to import mind" }, 500);
|
|
5251
4454
|
} finally {
|
|
5252
|
-
|
|
4455
|
+
rmSync4(composedDir, { recursive: true, force: true });
|
|
5253
4456
|
}
|
|
5254
4457
|
}
|
|
5255
4458
|
async function importHistoryFromArchive(name, tempDir) {
|
|
5256
|
-
const historyJsonl =
|
|
5257
|
-
if (!
|
|
4459
|
+
const historyJsonl = resolve11(tempDir, "history.jsonl");
|
|
4460
|
+
if (!existsSync8(historyJsonl)) return;
|
|
5258
4461
|
try {
|
|
5259
4462
|
const db = await getDb();
|
|
5260
|
-
const lines =
|
|
4463
|
+
const lines = readFileSync9(historyJsonl, "utf-8").trim().split("\n");
|
|
5261
4464
|
let imported = 0;
|
|
5262
4465
|
let failed = 0;
|
|
5263
4466
|
for (const line of lines) {
|
|
@@ -5293,13 +4496,13 @@ async function importHistoryFromArchive(name, tempDir) {
|
|
|
5293
4496
|
}
|
|
5294
4497
|
}
|
|
5295
4498
|
function importSessionsFromArchive(dest, tempDir) {
|
|
5296
|
-
const sessionsDir =
|
|
5297
|
-
if (!
|
|
4499
|
+
const sessionsDir = resolve11(tempDir, "sessions");
|
|
4500
|
+
if (!existsSync8(sessionsDir)) return;
|
|
5298
4501
|
try {
|
|
5299
|
-
const destSessions =
|
|
5300
|
-
|
|
4502
|
+
const destSessions = resolve11(dest, ".mind/sessions");
|
|
4503
|
+
mkdirSync6(destSessions, { recursive: true });
|
|
5301
4504
|
for (const file of readdirSync2(sessionsDir)) {
|
|
5302
|
-
cpSync(
|
|
4505
|
+
cpSync(resolve11(sessionsDir, file), resolve11(destSessions, file));
|
|
5303
4506
|
}
|
|
5304
4507
|
} catch (err) {
|
|
5305
4508
|
logger_default.error("Failed to import sessions from archive", logger_default.errorData(err));
|
|
@@ -5334,7 +4537,7 @@ var app13 = new Hono15().post("/", requireAdminOrSystem, zValidator5("json", cre
|
|
|
5334
4537
|
if (await findMind(name)) return c.json({ error: `Mind already exists: ${name}` }, 409);
|
|
5335
4538
|
ensureVoluteHome();
|
|
5336
4539
|
const dest = mindDir(name);
|
|
5337
|
-
if (
|
|
4540
|
+
if (existsSync8(dest)) return c.json({ error: "Mind directory already exists" }, 409);
|
|
5338
4541
|
const templatesRoot = findTemplatesRoot();
|
|
5339
4542
|
const { composedDir, manifest } = composeTemplate(templatesRoot, template);
|
|
5340
4543
|
try {
|
|
@@ -5342,7 +4545,7 @@ var app13 = new Hono15().post("/", requireAdminOrSystem, zValidator5("json", cre
|
|
|
5342
4545
|
applyInitFiles(dest);
|
|
5343
4546
|
const { publicKeyPem } = generateIdentity(dest);
|
|
5344
4547
|
{
|
|
5345
|
-
const { readGlobalConfig: readGlobal } = await import("./setup-
|
|
4548
|
+
const { readGlobalConfig: readGlobal } = await import("./setup-RHJRFURI.js");
|
|
5346
4549
|
const mindDefaults = readGlobal().mindDefaults;
|
|
5347
4550
|
const config2 = readVoluteConfig(dest);
|
|
5348
4551
|
if (!config2) throw new Error("Failed to read volute.json after identity generation");
|
|
@@ -5379,22 +4582,22 @@ var app13 = new Hono15().post("/", requireAdminOrSystem, zValidator5("json", cre
|
|
|
5379
4582
|
}
|
|
5380
4583
|
writeVoluteConfig(dest, config2);
|
|
5381
4584
|
const modelId = body.model ?? cog?.model;
|
|
5382
|
-
const sdkConfigPath =
|
|
4585
|
+
const sdkConfigPath = resolve11(dest, "home/.config/config.json");
|
|
5383
4586
|
if (modelId || cog?.compaction) {
|
|
5384
|
-
const existing =
|
|
4587
|
+
const existing = existsSync8(sdkConfigPath) ? JSON.parse(readFileSync9(sdkConfigPath, "utf-8")) : {};
|
|
5385
4588
|
if (modelId) {
|
|
5386
4589
|
existing.model = template === "pi" ? qualifyModelId(modelId) : unqualifyModelId(modelId);
|
|
5387
4590
|
}
|
|
5388
4591
|
if (cog?.compaction && !existing.compaction) {
|
|
5389
4592
|
existing.compaction = cog.compaction;
|
|
5390
4593
|
}
|
|
5391
|
-
|
|
4594
|
+
writeFileSync7(sdkConfigPath, `${JSON.stringify(existing, null, 2)}
|
|
5392
4595
|
`);
|
|
5393
4596
|
}
|
|
5394
4597
|
}
|
|
5395
4598
|
const mindPrompts = await getMindPromptDefaults();
|
|
5396
|
-
|
|
5397
|
-
|
|
4599
|
+
writeFileSync7(
|
|
4600
|
+
resolve11(dest, "home/.config/prompts.json"),
|
|
5398
4601
|
`${JSON.stringify(mindPrompts, null, 2)}
|
|
5399
4602
|
`
|
|
5400
4603
|
);
|
|
@@ -5406,7 +4609,7 @@ var app13 = new Hono15().post("/", requireAdminOrSystem, zValidator5("json", cre
|
|
|
5406
4609
|
} catch (err) {
|
|
5407
4610
|
logger_default.warn(`failed to set template hash for ${name}`, logger_default.errorData(err));
|
|
5408
4611
|
}
|
|
5409
|
-
const homeDir =
|
|
4612
|
+
const homeDir = resolve11(dest, "home");
|
|
5410
4613
|
ensureVoluteGroup();
|
|
5411
4614
|
createMindUser(name, homeDir);
|
|
5412
4615
|
chownMindDir(dest, name);
|
|
@@ -5419,14 +4622,9 @@ var app13 = new Hono15().post("/", requireAdminOrSystem, zValidator5("json", cre
|
|
|
5419
4622
|
await initTemplateBranch(dest, composedDir, manifest, name, env);
|
|
5420
4623
|
} catch (err) {
|
|
5421
4624
|
logger_default.error(`git setup failed for ${name}`, logger_default.errorData(err));
|
|
5422
|
-
|
|
4625
|
+
rmSync4(resolve11(dest, ".git"), { recursive: true, force: true });
|
|
5423
4626
|
gitWarning = "Git setup failed \u2014 variants and upgrades won't be available until git is initialized.";
|
|
5424
4627
|
}
|
|
5425
|
-
try {
|
|
5426
|
-
await addSharedWorktree(name, dest);
|
|
5427
|
-
} catch (err) {
|
|
5428
|
-
logger_default.warn(`failed to add shared worktree for ${name}`, logger_default.errorData(err));
|
|
5429
|
-
}
|
|
5430
4628
|
chownMindDir(dest, name);
|
|
5431
4629
|
if (body.stage === "seed") {
|
|
5432
4630
|
const descLine = body.description ? `
|
|
@@ -5434,11 +4632,11 @@ The human who planted you described you as: "${body.description}"
|
|
|
5434
4632
|
` : "";
|
|
5435
4633
|
const seedSoulRaw = body.seedSoul ?? await getPrompt("seed_soul", { name, description: descLine });
|
|
5436
4634
|
const seedSoul = body.seedSoul ? substitute(seedSoulRaw, { name, description: descLine }) : seedSoulRaw;
|
|
5437
|
-
|
|
4635
|
+
writeFileSync7(resolve11(dest, "home/SOUL.md"), seedSoul);
|
|
5438
4636
|
}
|
|
5439
4637
|
let skillSet = body.skills ?? (body.stage === "seed" ? SEED_SKILLS : getStandardSkillsWithExtensions());
|
|
5440
4638
|
if (body.stage === "seed" && !body.skills) {
|
|
5441
|
-
const { isImagegenEnabled } = await import("./setup-
|
|
4639
|
+
const { isImagegenEnabled } = await import("./setup-RHJRFURI.js");
|
|
5442
4640
|
if (isImagegenEnabled()) {
|
|
5443
4641
|
skillSet = [...skillSet, "imagegen"];
|
|
5444
4642
|
}
|
|
@@ -5456,7 +4654,7 @@ The human who planted you described you as: "${body.description}"
|
|
|
5456
4654
|
try {
|
|
5457
4655
|
const spiritEntry = await findMind("volute");
|
|
5458
4656
|
if (spiritEntry) {
|
|
5459
|
-
const { spiritDir } = await import("./spirit-
|
|
4657
|
+
const { spiritDir } = await import("./spirit-ZFRDXMG7.js");
|
|
5460
4658
|
const sDir = spiritEntry.dir ?? spiritDir();
|
|
5461
4659
|
const spiritConfig = readVoluteConfig(sDir) ?? {};
|
|
5462
4660
|
const schedules = spiritConfig.schedules ?? [];
|
|
@@ -5471,7 +4669,7 @@ The human who planted you described you as: "${body.description}"
|
|
|
5471
4669
|
});
|
|
5472
4670
|
spiritConfig.schedules = schedules;
|
|
5473
4671
|
writeVoluteConfig(sDir, spiritConfig);
|
|
5474
|
-
const { getScheduler: getScheduler2 } = await import("./scheduler-
|
|
4672
|
+
const { getScheduler: getScheduler2 } = await import("./scheduler-Y7O4CJXL.js");
|
|
5475
4673
|
getScheduler2().loadSchedules("volute", sDir);
|
|
5476
4674
|
}
|
|
5477
4675
|
}
|
|
@@ -5482,11 +4680,11 @@ The human who planted you described you as: "${body.description}"
|
|
|
5482
4680
|
if (body.stage !== "seed") {
|
|
5483
4681
|
const customSoul = await getPromptIfCustom("default_soul");
|
|
5484
4682
|
if (customSoul) {
|
|
5485
|
-
|
|
4683
|
+
writeFileSync7(resolve11(dest, "home/SOUL.md"), customSoul.replace(/\{\{name\}\}/g, name));
|
|
5486
4684
|
}
|
|
5487
4685
|
const customMemory = await getPromptIfCustom("default_memory");
|
|
5488
4686
|
if (customMemory) {
|
|
5489
|
-
|
|
4687
|
+
writeFileSync7(resolve11(dest, "home/MEMORY.md"), customMemory);
|
|
5490
4688
|
}
|
|
5491
4689
|
}
|
|
5492
4690
|
publishPublicKey(name, publicKeyPem).catch(
|
|
@@ -5515,14 +4713,14 @@ The human who planted you described you as: "${body.description}"
|
|
|
5515
4713
|
...skillWarnings.length > 0 && { skillWarnings }
|
|
5516
4714
|
});
|
|
5517
4715
|
} catch (err) {
|
|
5518
|
-
if (
|
|
4716
|
+
if (existsSync8(dest)) rmSync4(dest, { recursive: true, force: true });
|
|
5519
4717
|
try {
|
|
5520
4718
|
await removeMind(name);
|
|
5521
4719
|
} catch {
|
|
5522
4720
|
}
|
|
5523
4721
|
return c.json({ error: err instanceof Error ? err.message : "Failed to create mind" }, 500);
|
|
5524
4722
|
} finally {
|
|
5525
|
-
|
|
4723
|
+
rmSync4(composedDir, { recursive: true, force: true });
|
|
5526
4724
|
}
|
|
5527
4725
|
}).post("/import", requireAdmin, async (c) => {
|
|
5528
4726
|
let body;
|
|
@@ -5535,13 +4733,13 @@ The human who planted you described you as: "${body.description}"
|
|
|
5535
4733
|
return importFromArchive(c, body.archivePath, body.name, body.manifest);
|
|
5536
4734
|
}
|
|
5537
4735
|
const wsDir = body.workspacePath;
|
|
5538
|
-
if (!wsDir || !
|
|
4736
|
+
if (!wsDir || !existsSync8(resolve11(wsDir, "SOUL.md")) || !existsSync8(resolve11(wsDir, "IDENTITY.md"))) {
|
|
5539
4737
|
return c.json({ error: "Invalid workspace: missing SOUL.md or IDENTITY.md" }, 400);
|
|
5540
4738
|
}
|
|
5541
|
-
const soul =
|
|
5542
|
-
const identity =
|
|
5543
|
-
const userPath =
|
|
5544
|
-
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") : "";
|
|
5545
4743
|
const name = body.name ?? parseNameFromIdentity(identity) ?? "imported-mind";
|
|
5546
4744
|
const template = body.template ?? "claude";
|
|
5547
4745
|
const nameErr = validateMindName(name);
|
|
@@ -5561,33 +4759,33 @@ ${user.trimEnd()}
|
|
|
5561
4759
|
` : "";
|
|
5562
4760
|
ensureVoluteHome();
|
|
5563
4761
|
const dest = mindDir(name);
|
|
5564
|
-
if (
|
|
4762
|
+
if (existsSync8(dest)) return c.json({ error: "Mind directory already exists" }, 409);
|
|
5565
4763
|
const templatesRoot = findTemplatesRoot();
|
|
5566
4764
|
const { composedDir, manifest } = composeTemplate(templatesRoot, template);
|
|
5567
4765
|
try {
|
|
5568
4766
|
copyTemplateToDir(composedDir, dest, name, manifest);
|
|
5569
4767
|
applyInitFiles(dest);
|
|
5570
4768
|
const { publicKeyPem: importPublicKey } = generateIdentity(dest);
|
|
5571
|
-
|
|
5572
|
-
const wsMemoryPath =
|
|
5573
|
-
const hasMemory =
|
|
4769
|
+
writeFileSync7(resolve11(dest, "home/SOUL.md"), mergedSoul);
|
|
4770
|
+
const wsMemoryPath = resolve11(wsDir, "MEMORY.md");
|
|
4771
|
+
const hasMemory = existsSync8(wsMemoryPath);
|
|
5574
4772
|
if (hasMemory) {
|
|
5575
|
-
const existingMemory =
|
|
5576
|
-
|
|
5577
|
-
|
|
4773
|
+
const existingMemory = readFileSync9(wsMemoryPath, "utf-8");
|
|
4774
|
+
writeFileSync7(
|
|
4775
|
+
resolve11(dest, "home/MEMORY.md"),
|
|
5578
4776
|
`${existingMemory.trimEnd()}${mergedMemoryExtra}`
|
|
5579
4777
|
);
|
|
5580
4778
|
} else if (user) {
|
|
5581
|
-
|
|
4779
|
+
writeFileSync7(resolve11(dest, "home/MEMORY.md"), `${user.trimEnd()}
|
|
5582
4780
|
`);
|
|
5583
4781
|
}
|
|
5584
|
-
const wsMemoryDir =
|
|
4782
|
+
const wsMemoryDir = resolve11(wsDir, "memory");
|
|
5585
4783
|
let dailyLogCount = 0;
|
|
5586
|
-
if (
|
|
5587
|
-
const destMemoryDir =
|
|
4784
|
+
if (existsSync8(wsMemoryDir)) {
|
|
4785
|
+
const destMemoryDir = resolve11(dest, "home/memory");
|
|
5588
4786
|
const files = readdirSync2(wsMemoryDir).filter((f) => f.endsWith(".md"));
|
|
5589
4787
|
for (const file of files) {
|
|
5590
|
-
cpSync(
|
|
4788
|
+
cpSync(resolve11(wsMemoryDir, file), resolve11(destMemoryDir, file));
|
|
5591
4789
|
}
|
|
5592
4790
|
dailyLogCount = files.length;
|
|
5593
4791
|
}
|
|
@@ -5598,7 +4796,7 @@ ${user.trimEnd()}
|
|
|
5598
4796
|
} catch (err) {
|
|
5599
4797
|
logger_default.warn(`failed to set template hash for ${name}`, logger_default.errorData(err));
|
|
5600
4798
|
}
|
|
5601
|
-
const homeDir =
|
|
4799
|
+
const homeDir = resolve11(dest, "home");
|
|
5602
4800
|
ensureVoluteGroup();
|
|
5603
4801
|
createMindUser(name, homeDir);
|
|
5604
4802
|
chownMindDir(dest, name);
|
|
@@ -5606,42 +4804,37 @@ ${user.trimEnd()}
|
|
|
5606
4804
|
if (!hasMemory && dailyLogCount > 0) {
|
|
5607
4805
|
await consolidateMemory(dest);
|
|
5608
4806
|
}
|
|
5609
|
-
const env = isIsolationEnabled() ? { ...process.env, HOME:
|
|
4807
|
+
const env = isIsolationEnabled() ? { ...process.env, HOME: resolve11(dest, "home") } : void 0;
|
|
5610
4808
|
await gitExec(["init"], { cwd: dest, mindName: name, env });
|
|
5611
4809
|
await configureGitIdentity(name, { cwd: dest, mindName: name, env });
|
|
5612
4810
|
await gitExec(["add", "-A"], { cwd: dest, mindName: name, env });
|
|
5613
4811
|
await gitExec(["commit", "-m", "import from OpenClaw"], { cwd: dest, mindName: name, env });
|
|
5614
|
-
const sessionFile = body.sessionPath ?
|
|
5615
|
-
if (sessionFile &&
|
|
4812
|
+
const sessionFile = body.sessionPath ? resolve11(body.sessionPath) : findOpenClawSession(wsDir);
|
|
4813
|
+
if (sessionFile && existsSync8(sessionFile)) {
|
|
5616
4814
|
if (template === "pi") {
|
|
5617
4815
|
importPiSession(sessionFile, dest);
|
|
5618
4816
|
} else if (template === "claude") {
|
|
5619
4817
|
const sessionId = convertSession({ sessionPath: sessionFile, projectDir: dest });
|
|
5620
|
-
const mindRuntimeDir =
|
|
5621
|
-
|
|
5622
|
-
|
|
4818
|
+
const mindRuntimeDir = resolve11(dest, ".mind");
|
|
4819
|
+
mkdirSync6(mindRuntimeDir, { recursive: true });
|
|
4820
|
+
writeFileSync7(resolve11(mindRuntimeDir, "session.json"), JSON.stringify({ sessionId }));
|
|
5623
4821
|
}
|
|
5624
4822
|
}
|
|
5625
4823
|
importOpenClawConnectors(name, dest);
|
|
5626
|
-
try {
|
|
5627
|
-
await addSharedWorktree(name, dest);
|
|
5628
|
-
} catch (err) {
|
|
5629
|
-
logger_default.warn(`failed to add shared worktree for ${name}`, logger_default.errorData(err));
|
|
5630
|
-
}
|
|
5631
4824
|
chownMindDir(dest, name);
|
|
5632
4825
|
publishPublicKey(name, importPublicKey).catch(
|
|
5633
4826
|
(err) => logger_default.warn(`failed to publish key for ${name}`, { error: err.message })
|
|
5634
4827
|
);
|
|
5635
4828
|
return c.json({ ok: true, name, port, message: `Imported mind: ${name} (port ${port})` });
|
|
5636
4829
|
} catch (err) {
|
|
5637
|
-
if (
|
|
4830
|
+
if (existsSync8(dest)) rmSync4(dest, { recursive: true, force: true });
|
|
5638
4831
|
try {
|
|
5639
4832
|
await removeMind(name);
|
|
5640
4833
|
} catch {
|
|
5641
4834
|
}
|
|
5642
4835
|
return c.json({ error: err instanceof Error ? err.message : "Failed to import mind" }, 500);
|
|
5643
4836
|
} finally {
|
|
5644
|
-
|
|
4837
|
+
rmSync4(composedDir, { recursive: true, force: true });
|
|
5645
4838
|
}
|
|
5646
4839
|
}).get("/", async (c) => {
|
|
5647
4840
|
const entries = await readRegistry();
|
|
@@ -5650,7 +4843,7 @@ ${user.trimEnd()}
|
|
|
5650
4843
|
const db = await getDb();
|
|
5651
4844
|
const lastActiveRows = await db.select({
|
|
5652
4845
|
mind: mindHistory.mind,
|
|
5653
|
-
lastActiveAt:
|
|
4846
|
+
lastActiveAt: sql2`MAX(${mindHistory.created_at})`
|
|
5654
4847
|
}).from(mindHistory).groupBy(mindHistory.mind);
|
|
5655
4848
|
lastActiveMap = new Map(lastActiveRows.map((r) => [r.mind, r.lastActiveAt]));
|
|
5656
4849
|
} catch {
|
|
@@ -5658,7 +4851,7 @@ ${user.trimEnd()}
|
|
|
5658
4851
|
const minds = await Promise.all(
|
|
5659
4852
|
entries.map(async (entry) => {
|
|
5660
4853
|
const mindStatus = await getMindStatus(entry.name, entry.port);
|
|
5661
|
-
const hasPages =
|
|
4854
|
+
const hasPages = existsSync8(resolve11(mindDir(entry.name), "home", "pages"));
|
|
5662
4855
|
return {
|
|
5663
4856
|
...entry,
|
|
5664
4857
|
...mindStatus,
|
|
@@ -5673,7 +4866,7 @@ ${user.trimEnd()}
|
|
|
5673
4866
|
const entry = await findMind(name);
|
|
5674
4867
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
5675
4868
|
const dir = entry.dir ?? mindDir(entry.parent ?? name);
|
|
5676
|
-
if (!
|
|
4869
|
+
if (!existsSync8(dir)) return c.json({ error: "Mind directory missing" }, 404);
|
|
5677
4870
|
const mindStatus = await getMindStatus(name, entry.port);
|
|
5678
4871
|
const variants = await findVariants(name);
|
|
5679
4872
|
const manager = getMindManager();
|
|
@@ -5687,7 +4880,7 @@ ${user.trimEnd()}
|
|
|
5687
4880
|
return { name: s.name, port: s.port, status: variantStatus };
|
|
5688
4881
|
})
|
|
5689
4882
|
);
|
|
5690
|
-
const hasPages =
|
|
4883
|
+
const hasPages = existsSync8(resolve11(mindDir(name), "home", "pages"));
|
|
5691
4884
|
return c.json({ ...entry, ...mindStatus, variants: variantStatuses, hasPages });
|
|
5692
4885
|
}).get("/:name/context", async (c) => {
|
|
5693
4886
|
const name = c.req.param("name");
|
|
@@ -5724,7 +4917,7 @@ ${user.trimEnd()}
|
|
|
5724
4917
|
if (!entry.dir) return c.json({ error: `Variant ${name} has no directory` }, 404);
|
|
5725
4918
|
} else {
|
|
5726
4919
|
const dir = mindDir(name);
|
|
5727
|
-
if (!
|
|
4920
|
+
if (!existsSync8(dir)) return c.json({ error: "Mind directory missing" }, 404);
|
|
5728
4921
|
}
|
|
5729
4922
|
if (getMindManager().isRunning(name)) {
|
|
5730
4923
|
return c.json({ error: "Mind already running" }, 409);
|
|
@@ -5745,7 +4938,7 @@ ${user.trimEnd()}
|
|
|
5745
4938
|
if (!entry.dir) return c.json({ error: `Variant ${name} has no directory` }, 404);
|
|
5746
4939
|
} else {
|
|
5747
4940
|
const dir = mindDir(name);
|
|
5748
|
-
if (!
|
|
4941
|
+
if (!existsSync8(dir)) return c.json({ error: "Mind directory missing" }, 404);
|
|
5749
4942
|
}
|
|
5750
4943
|
let context;
|
|
5751
4944
|
const contentType = c.req.header("content-type");
|
|
@@ -5760,7 +4953,7 @@ ${user.trimEnd()}
|
|
|
5760
4953
|
const manager = getMindManager();
|
|
5761
4954
|
try {
|
|
5762
4955
|
if (context?.type === "reload") {
|
|
5763
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
4956
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-7KFK3USC.js");
|
|
5764
4957
|
const sleepState = getSleepManagerIfReady2()?.getState(name);
|
|
5765
4958
|
if (sleepState?.sleeping) {
|
|
5766
4959
|
logger_default.info(`skipping reload for ${name} during sleep \u2014 will apply on next wake`);
|
|
@@ -5780,7 +4973,7 @@ ${user.trimEnd()}
|
|
|
5780
4973
|
const variantEntry = await findMind(mergeVariantName);
|
|
5781
4974
|
if (variantEntry && variantEntry.parent === baseName && variantEntry.dir && variantEntry.branch) {
|
|
5782
4975
|
const projectRoot = mindDir(baseName);
|
|
5783
|
-
if (
|
|
4976
|
+
if (existsSync8(variantEntry.dir)) {
|
|
5784
4977
|
const status = (await gitExec(["status", "--porcelain"], { cwd: variantEntry.dir })).trim();
|
|
5785
4978
|
if (status) {
|
|
5786
4979
|
try {
|
|
@@ -5822,7 +5015,7 @@ ${user.trimEnd()}
|
|
|
5822
5015
|
if (context?.type === "sprouted" && !entry.parent) {
|
|
5823
5016
|
try {
|
|
5824
5017
|
const db = await getDb();
|
|
5825
|
-
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();
|
|
5826
5019
|
for (const conv of activeConvs) {
|
|
5827
5020
|
await recordInbound(baseName, conv.channel, "system", "[seed has sprouted]");
|
|
5828
5021
|
await addMessage(conv.id, "assistant", "system", [
|
|
@@ -5856,7 +5049,7 @@ ${user.trimEnd()}
|
|
|
5856
5049
|
const name = c.req.param("name");
|
|
5857
5050
|
const entry = await findMind(name);
|
|
5858
5051
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
5859
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
5052
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-7KFK3USC.js");
|
|
5860
5053
|
const sm = getSleepManagerIfReady2();
|
|
5861
5054
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
5862
5055
|
return c.json(sm.getState(name));
|
|
@@ -5864,7 +5057,7 @@ ${user.trimEnd()}
|
|
|
5864
5057
|
const name = c.req.param("name");
|
|
5865
5058
|
const entry = await findMind(name);
|
|
5866
5059
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
5867
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
5060
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-7KFK3USC.js");
|
|
5868
5061
|
const sm = getSleepManagerIfReady2();
|
|
5869
5062
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
5870
5063
|
if (sm.isSleeping(name)) return c.json({ error: "Mind is already sleeping" }, 409);
|
|
@@ -5884,7 +5077,7 @@ ${user.trimEnd()}
|
|
|
5884
5077
|
const name = c.req.param("name");
|
|
5885
5078
|
const entry = await findMind(name);
|
|
5886
5079
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
5887
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
5080
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-7KFK3USC.js");
|
|
5888
5081
|
const sm = getSleepManagerIfReady2();
|
|
5889
5082
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
5890
5083
|
const sleepState = sm.getState(name);
|
|
@@ -5899,7 +5092,7 @@ ${user.trimEnd()}
|
|
|
5899
5092
|
const name = c.req.param("name");
|
|
5900
5093
|
const entry = await findMind(name);
|
|
5901
5094
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
5902
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
5095
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-7KFK3USC.js");
|
|
5903
5096
|
const sm = getSleepManagerIfReady2();
|
|
5904
5097
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
5905
5098
|
const flushed = await sm.flushQueuedMessages(name);
|
|
@@ -5917,7 +5110,7 @@ ${user.trimEnd()}
|
|
|
5917
5110
|
if (body.avatar !== void 0) profile.avatar = body.avatar;
|
|
5918
5111
|
config2.profile = profile;
|
|
5919
5112
|
writeVoluteConfig(dir, config2);
|
|
5920
|
-
const { syncMindProfile: syncMindProfile2 } = await import("./auth-
|
|
5113
|
+
const { syncMindProfile: syncMindProfile2 } = await import("./auth-WX4TESEI.js");
|
|
5921
5114
|
await syncMindProfile2(name, profile);
|
|
5922
5115
|
broadcast({ type: "profile_updated", mind: name, summary: `${name} profile updated` });
|
|
5923
5116
|
return c.json({ ok: true });
|
|
@@ -5933,21 +5126,21 @@ ${user.trimEnd()}
|
|
|
5933
5126
|
const rawSpirit = Number(process.env.VOLUTE_NURTURE_SPIRIT_MINUTES);
|
|
5934
5127
|
const spiritThreshold = Number.isNaN(rawSpirit) ? 15 : rawSpirit;
|
|
5935
5128
|
const lastCreatorMsg = await db.select({ created_at: mindHistory.created_at }).from(mindHistory).where(
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
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`
|
|
5942
5135
|
)
|
|
5943
|
-
).orderBy(
|
|
5136
|
+
).orderBy(desc3(mindHistory.created_at)).limit(1);
|
|
5944
5137
|
const lastSpiritMsg = await db.select({ created_at: mindHistory.created_at }).from(mindHistory).where(
|
|
5945
|
-
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
|
|
5138
|
+
and4(
|
|
5139
|
+
eq5(mindHistory.mind, name),
|
|
5140
|
+
eq5(mindHistory.type, "inbound"),
|
|
5141
|
+
eq5(mindHistory.sender, "volute")
|
|
5949
5142
|
)
|
|
5950
|
-
).orderBy(
|
|
5143
|
+
).orderBy(desc3(mindHistory.created_at)).limit(1);
|
|
5951
5144
|
const now = Date.now();
|
|
5952
5145
|
const creatorTime = lastCreatorMsg[0] ? new Date(lastCreatorMsg[0].created_at).getTime() : 0;
|
|
5953
5146
|
const spiritTime = lastSpiritMsg[0] ? new Date(lastSpiritMsg[0].created_at).getTime() : 0;
|
|
@@ -5957,14 +5150,14 @@ ${user.trimEnd()}
|
|
|
5957
5150
|
return c.json({ output: "" });
|
|
5958
5151
|
}
|
|
5959
5152
|
const dir = entry.dir ?? mindDir(name);
|
|
5960
|
-
const soulPath =
|
|
5961
|
-
const memoryPath =
|
|
5962
|
-
const soulCustom =
|
|
5963
|
-
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;
|
|
5964
5157
|
const config2 = readVoluteConfig(dir);
|
|
5965
5158
|
const displayNameSet = !!config2?.profile?.displayName;
|
|
5966
5159
|
const avatarSet = !!config2?.profile?.avatar;
|
|
5967
|
-
const { isImagegenEnabled } = await import("./setup-
|
|
5160
|
+
const { isImagegenEnabled } = await import("./setup-RHJRFURI.js");
|
|
5968
5161
|
const imagegenEnabled = isImagegenEnabled();
|
|
5969
5162
|
const done = [];
|
|
5970
5163
|
const remaining = [];
|
|
@@ -6002,7 +5195,7 @@ ${user.trimEnd()}
|
|
|
6002
5195
|
try {
|
|
6003
5196
|
const spiritEntry = await findMind("volute");
|
|
6004
5197
|
if (spiritEntry) {
|
|
6005
|
-
const { spiritDir } = await import("./spirit-
|
|
5198
|
+
const { spiritDir } = await import("./spirit-ZFRDXMG7.js");
|
|
6006
5199
|
const sDir = spiritEntry.dir ?? spiritDir();
|
|
6007
5200
|
const spiritConfig = readVoluteConfig(sDir);
|
|
6008
5201
|
if (spiritConfig?.schedules) {
|
|
@@ -6010,7 +5203,7 @@ ${user.trimEnd()}
|
|
|
6010
5203
|
spiritConfig.schedules = spiritConfig.schedules.filter((s) => s.id !== nurtureId);
|
|
6011
5204
|
if (spiritConfig.schedules.length === 0) spiritConfig.schedules = void 0;
|
|
6012
5205
|
writeVoluteConfig(sDir, spiritConfig);
|
|
6013
|
-
const { getScheduler: getScheduler2 } = await import("./scheduler-
|
|
5206
|
+
const { getScheduler: getScheduler2 } = await import("./scheduler-Y7O4CJXL.js");
|
|
6014
5207
|
getScheduler2().loadSchedules("volute", sDir);
|
|
6015
5208
|
}
|
|
6016
5209
|
}
|
|
@@ -6034,19 +5227,14 @@ ${user.trimEnd()}
|
|
|
6034
5227
|
await cleanupVariant(s.name, dir, s.dir, { stop: true });
|
|
6035
5228
|
}
|
|
6036
5229
|
}
|
|
6037
|
-
try {
|
|
6038
|
-
await removeSharedWorktree(name, dir);
|
|
6039
|
-
} catch (err) {
|
|
6040
|
-
logger_default.warn(`failed to clean up shared worktree for ${name}`, logger_default.errorData(err));
|
|
6041
|
-
}
|
|
6042
5230
|
await removeMind(name);
|
|
6043
5231
|
await deleteMindUser2(name);
|
|
6044
5232
|
const state = stateDir(name);
|
|
6045
|
-
if (
|
|
6046
|
-
|
|
5233
|
+
if (existsSync8(state)) {
|
|
5234
|
+
rmSync4(state, { recursive: true, force: true });
|
|
6047
5235
|
}
|
|
6048
|
-
if (force &&
|
|
6049
|
-
|
|
5236
|
+
if (force && existsSync8(dir)) {
|
|
5237
|
+
rmSync4(dir, { recursive: true, force: true });
|
|
6050
5238
|
deleteMindUser(name);
|
|
6051
5239
|
}
|
|
6052
5240
|
fireWebhook({
|
|
@@ -6060,7 +5248,7 @@ ${user.trimEnd()}
|
|
|
6060
5248
|
const entry = await findMind(mindName);
|
|
6061
5249
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
6062
5250
|
const dir = mindDir(mindName);
|
|
6063
|
-
if (!
|
|
5251
|
+
if (!existsSync8(dir)) return c.json({ error: "Mind directory missing" }, 404);
|
|
6064
5252
|
let body = {};
|
|
6065
5253
|
try {
|
|
6066
5254
|
body = await c.req.json();
|
|
@@ -6069,16 +5257,16 @@ ${user.trimEnd()}
|
|
|
6069
5257
|
const template = body.template ?? entry.template ?? "claude";
|
|
6070
5258
|
const UPGRADE_BRANCH = "upgrade";
|
|
6071
5259
|
const upgradeVariantName = `${mindName}-upgrade`;
|
|
6072
|
-
const worktreeDir =
|
|
5260
|
+
const worktreeDir = resolve11(dir, ".variants", UPGRADE_BRANCH);
|
|
6073
5261
|
if (body.abort) {
|
|
6074
|
-
if (!
|
|
5262
|
+
if (!existsSync8(worktreeDir)) {
|
|
6075
5263
|
return c.json({ error: "No upgrade in progress" }, 400);
|
|
6076
5264
|
}
|
|
6077
5265
|
try {
|
|
6078
5266
|
try {
|
|
6079
|
-
const gitDirContent =
|
|
5267
|
+
const gitDirContent = readFileSync9(resolve11(worktreeDir, ".git"), "utf-8").trim();
|
|
6080
5268
|
const gitDir = gitDirContent.replace("gitdir: ", "");
|
|
6081
|
-
if (
|
|
5269
|
+
if (existsSync8(resolve11(gitDir, "MERGE_HEAD"))) {
|
|
6082
5270
|
await gitExec(["merge", "--abort"], { cwd: worktreeDir });
|
|
6083
5271
|
}
|
|
6084
5272
|
} catch {
|
|
@@ -6097,7 +5285,7 @@ ${user.trimEnd()}
|
|
|
6097
5285
|
}
|
|
6098
5286
|
}
|
|
6099
5287
|
if (body.continue) {
|
|
6100
|
-
if (!
|
|
5288
|
+
if (!existsSync8(worktreeDir)) {
|
|
6101
5289
|
return c.json({ error: "No upgrade in progress" }, 400);
|
|
6102
5290
|
}
|
|
6103
5291
|
const status = await gitExec(["status", "--porcelain"], { cwd: worktreeDir });
|
|
@@ -6146,7 +5334,7 @@ ${user.trimEnd()}
|
|
|
6146
5334
|
}
|
|
6147
5335
|
}
|
|
6148
5336
|
if (body.accept) {
|
|
6149
|
-
if (
|
|
5337
|
+
if (existsSync8(worktreeDir)) {
|
|
6150
5338
|
try {
|
|
6151
5339
|
await cleanupVariant(upgradeVariantName, dir, worktreeDir, { stop: true });
|
|
6152
5340
|
} catch (err) {
|
|
@@ -6161,7 +5349,7 @@ ${user.trimEnd()}
|
|
|
6161
5349
|
}
|
|
6162
5350
|
if (body.diff) {
|
|
6163
5351
|
try {
|
|
6164
|
-
if (!
|
|
5352
|
+
if (!existsSync8(resolve11(dir, ".git"))) {
|
|
6165
5353
|
return c.json({ error: "Mind has no git history \u2014 nothing to diff against" }, 400);
|
|
6166
5354
|
}
|
|
6167
5355
|
await updateTemplateBranch(dir, template, mindName);
|
|
@@ -6179,22 +5367,22 @@ ${user.trimEnd()}
|
|
|
6179
5367
|
);
|
|
6180
5368
|
}
|
|
6181
5369
|
}
|
|
6182
|
-
if (
|
|
5370
|
+
if (existsSync8(worktreeDir)) {
|
|
6183
5371
|
return c.json(
|
|
6184
5372
|
{ error: "Upgrade variant already exists. Use continue or delete it first." },
|
|
6185
5373
|
409
|
|
6186
5374
|
);
|
|
6187
5375
|
}
|
|
6188
|
-
if (!
|
|
5376
|
+
if (!existsSync8(resolve11(dir, ".git"))) {
|
|
6189
5377
|
try {
|
|
6190
|
-
const env = isIsolationEnabled() ? { ...process.env, HOME:
|
|
5378
|
+
const env = isIsolationEnabled() ? { ...process.env, HOME: resolve11(dir, "home") } : void 0;
|
|
6191
5379
|
await gitExec(["init"], { cwd: dir, mindName, env });
|
|
6192
5380
|
await configureGitIdentity(mindName, { cwd: dir, mindName, env });
|
|
6193
5381
|
await gitExec(["add", "-A"], { cwd: dir, mindName, env });
|
|
6194
5382
|
await gitExec(["commit", "-m", "initial commit"], { cwd: dir, mindName, env });
|
|
6195
5383
|
chownMindDir(dir, mindName);
|
|
6196
5384
|
} catch (err) {
|
|
6197
|
-
|
|
5385
|
+
rmSync4(resolve11(dir, ".git"), { recursive: true, force: true });
|
|
6198
5386
|
return c.json(
|
|
6199
5387
|
{
|
|
6200
5388
|
error: `Git initialization failed: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -6208,20 +5396,10 @@ ${user.trimEnd()}
|
|
|
6208
5396
|
await gitExec(["branch", "-D", UPGRADE_BRANCH], { cwd: dir });
|
|
6209
5397
|
} catch {
|
|
6210
5398
|
}
|
|
6211
|
-
if (!existsSync9(resolve12(dir, "home", "shared"))) {
|
|
6212
|
-
try {
|
|
6213
|
-
await addSharedWorktree(mindName, dir);
|
|
6214
|
-
} catch (err) {
|
|
6215
|
-
logger_default.warn(
|
|
6216
|
-
`failed to add shared worktree during upgrade for ${mindName}`,
|
|
6217
|
-
logger_default.errorData(err)
|
|
6218
|
-
);
|
|
6219
|
-
}
|
|
6220
|
-
}
|
|
6221
5399
|
await updateTemplateBranch(dir, template, mindName);
|
|
6222
|
-
const parentDir =
|
|
6223
|
-
if (!
|
|
6224
|
-
|
|
5400
|
+
const parentDir = resolve11(dir, ".variants");
|
|
5401
|
+
if (!existsSync8(parentDir)) {
|
|
5402
|
+
mkdirSync6(parentDir, { recursive: true });
|
|
6225
5403
|
}
|
|
6226
5404
|
await gitExec(["worktree", "add", "-b", UPGRADE_BRANCH, worktreeDir], { cwd: dir });
|
|
6227
5405
|
await gitExec(["rm", "-r", "--cached", "--ignore-unmatch", "home/"], {
|
|
@@ -6349,22 +5527,22 @@ ${user.trimEnd()}
|
|
|
6349
5527
|
const entry = await findMind(name);
|
|
6350
5528
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
6351
5529
|
const dir = entry.dir ?? mindDir(name);
|
|
6352
|
-
if (!
|
|
5530
|
+
if (!existsSync8(dir)) return c.json({ error: "Mind directory missing" }, 404);
|
|
6353
5531
|
let config2 = readVoluteConfig(dir);
|
|
6354
5532
|
if (!config2 && entry.template === "pi") {
|
|
6355
|
-
const piConfigPath =
|
|
6356
|
-
if (
|
|
5533
|
+
const piConfigPath = resolve11(dir, "home/.config/config.json");
|
|
5534
|
+
if (existsSync8(piConfigPath)) {
|
|
6357
5535
|
try {
|
|
6358
|
-
config2 = JSON.parse(
|
|
5536
|
+
config2 = JSON.parse(readFileSync9(piConfigPath, "utf-8"));
|
|
6359
5537
|
} catch {
|
|
6360
5538
|
}
|
|
6361
5539
|
}
|
|
6362
5540
|
}
|
|
6363
5541
|
let templateConfig = {};
|
|
6364
|
-
const configJsonPath =
|
|
6365
|
-
if (
|
|
5542
|
+
const configJsonPath = resolve11(dir, "home/.config/config.json");
|
|
5543
|
+
if (existsSync8(configJsonPath)) {
|
|
6366
5544
|
try {
|
|
6367
|
-
templateConfig = JSON.parse(
|
|
5545
|
+
templateConfig = JSON.parse(readFileSync9(configJsonPath, "utf-8"));
|
|
6368
5546
|
} catch {
|
|
6369
5547
|
}
|
|
6370
5548
|
}
|
|
@@ -6418,7 +5596,7 @@ ${user.trimEnd()}
|
|
|
6418
5596
|
const entry = await findMind(name);
|
|
6419
5597
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
6420
5598
|
const dir = entry.dir ?? mindDir(name);
|
|
6421
|
-
if (!
|
|
5599
|
+
if (!existsSync8(dir)) return c.json({ error: "Mind directory missing" }, 404);
|
|
6422
5600
|
const body = c.req.valid("json");
|
|
6423
5601
|
const existing = readVoluteConfig(dir) ?? {};
|
|
6424
5602
|
if (body.model !== void 0) existing.model = body.model;
|
|
@@ -6449,11 +5627,11 @@ ${user.trimEnd()}
|
|
|
6449
5627
|
writeVoluteConfig(dir, existing);
|
|
6450
5628
|
const needsConfigJson = body.model !== void 0 || body.thinkingLevel !== void 0 || body.maxThinkingTokens !== void 0 || body.compaction !== void 0;
|
|
6451
5629
|
if (needsConfigJson) {
|
|
6452
|
-
const configJsonPath =
|
|
5630
|
+
const configJsonPath = resolve11(dir, "home/.config/config.json");
|
|
6453
5631
|
let templateConfig = {};
|
|
6454
|
-
if (
|
|
5632
|
+
if (existsSync8(configJsonPath)) {
|
|
6455
5633
|
try {
|
|
6456
|
-
templateConfig = JSON.parse(
|
|
5634
|
+
templateConfig = JSON.parse(readFileSync9(configJsonPath, "utf-8"));
|
|
6457
5635
|
} catch {
|
|
6458
5636
|
}
|
|
6459
5637
|
}
|
|
@@ -6516,12 +5694,12 @@ ${user.trimEnd()}
|
|
|
6516
5694
|
templateConfig.compaction = comp;
|
|
6517
5695
|
}
|
|
6518
5696
|
}
|
|
6519
|
-
|
|
5697
|
+
writeFileSync7(configJsonPath, `${JSON.stringify(templateConfig, null, 2)}
|
|
6520
5698
|
`);
|
|
6521
5699
|
}
|
|
6522
5700
|
if (entry.mindType === "spirit" && body.model !== void 0) {
|
|
6523
5701
|
try {
|
|
6524
|
-
const { readGlobalConfig: readGlobalConfig2, writeGlobalConfig: writeGlobalConfig2 } = await import("./setup-
|
|
5702
|
+
const { readGlobalConfig: readGlobalConfig2, writeGlobalConfig: writeGlobalConfig2 } = await import("./setup-RHJRFURI.js");
|
|
6525
5703
|
const globalConfig = readGlobalConfig2();
|
|
6526
5704
|
globalConfig.spiritModel = body.model;
|
|
6527
5705
|
writeGlobalConfig2(globalConfig);
|
|
@@ -6549,7 +5727,7 @@ ${user.trimEnd()}
|
|
|
6549
5727
|
if (!body.systemPrompt || !body.message) {
|
|
6550
5728
|
return c.json({ error: "systemPrompt and message required" }, 400);
|
|
6551
5729
|
}
|
|
6552
|
-
const { aiComplete: aiCompleteFn, isAiConfigured } = await import("./ai-service-
|
|
5730
|
+
const { aiComplete: aiCompleteFn, isAiConfigured } = await import("./ai-service-LURBEDDB.js");
|
|
6553
5731
|
if (!isAiConfigured()) {
|
|
6554
5732
|
return c.json({ error: "AI service not configured" }, 503);
|
|
6555
5733
|
}
|
|
@@ -6783,32 +5961,32 @@ ${user.trimEnd()}
|
|
|
6783
5961
|
const db = await getDb();
|
|
6784
5962
|
const rows = await db.select({
|
|
6785
5963
|
session: mindHistory.session,
|
|
6786
|
-
started_at:
|
|
6787
|
-
event_count:
|
|
6788
|
-
message_count:
|
|
6789
|
-
tool_count:
|
|
6790
|
-
}).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`);
|
|
6791
5969
|
return c.json(rows);
|
|
6792
5970
|
}).get("/:name/history/channels", async (c) => {
|
|
6793
5971
|
const name = c.req.param("name");
|
|
6794
5972
|
const db = await getDb();
|
|
6795
|
-
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));
|
|
6796
5974
|
return c.json(rows.map((r) => r.channel));
|
|
6797
5975
|
}).get("/:name/history/export", async (c) => {
|
|
6798
5976
|
const name = c.req.param("name");
|
|
6799
5977
|
if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
|
|
6800
5978
|
const db = await getDb();
|
|
6801
|
-
const rows = await db.select().from(mindHistory).where(
|
|
5979
|
+
const rows = await db.select().from(mindHistory).where(eq5(mindHistory.mind, name));
|
|
6802
5980
|
return c.json(rows);
|
|
6803
5981
|
}).get("/:name/history/turn", async (c) => {
|
|
6804
5982
|
const name = c.req.param("name");
|
|
6805
5983
|
const turnId = c.req.query("turn_id");
|
|
6806
5984
|
const detail = c.req.query("detail") === "1";
|
|
6807
5985
|
const db = await getDb();
|
|
6808
|
-
const typeFilter = detail ? void 0 :
|
|
5986
|
+
const typeFilter = detail ? void 0 : sql2`${mindHistory.type} IN ('inbound','outbound','tool_use','tool_result','text','thinking','activity')`;
|
|
6809
5987
|
let rows;
|
|
6810
5988
|
if (turnId) {
|
|
6811
|
-
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);
|
|
6812
5990
|
} else {
|
|
6813
5991
|
const session = c.req.query("session");
|
|
6814
5992
|
const fromId = parseInt(c.req.query("from_id") ?? "", 10);
|
|
@@ -6817,11 +5995,11 @@ ${user.trimEnd()}
|
|
|
6817
5995
|
return c.json({ error: "turn_id, or session with from_id and to_id, required" }, 400);
|
|
6818
5996
|
}
|
|
6819
5997
|
rows = await db.select().from(mindHistory).where(
|
|
6820
|
-
|
|
6821
|
-
|
|
6822
|
-
|
|
6823
|
-
|
|
6824
|
-
|
|
5998
|
+
and4(
|
|
5999
|
+
eq5(mindHistory.mind, name),
|
|
6000
|
+
eq5(mindHistory.session, session),
|
|
6001
|
+
sql2`${mindHistory.id} >= ${fromId}`,
|
|
6002
|
+
sql2`${mindHistory.id} <= ${toId}`,
|
|
6825
6003
|
typeFilter
|
|
6826
6004
|
)
|
|
6827
6005
|
).orderBy(mindHistory.id);
|
|
@@ -6834,14 +6012,14 @@ ${user.trimEnd()}
|
|
|
6834
6012
|
let sinceTimestamp = null;
|
|
6835
6013
|
if (currentSession) {
|
|
6836
6014
|
const lastTurn = await db.select({ turn_id: mindHistory.turn_id }).from(mindHistory).where(
|
|
6837
|
-
|
|
6838
|
-
|
|
6839
|
-
|
|
6840
|
-
|
|
6015
|
+
and4(
|
|
6016
|
+
eq5(mindHistory.mind, name),
|
|
6017
|
+
eq5(mindHistory.session, currentSession),
|
|
6018
|
+
sql2`${mindHistory.turn_id} IS NOT NULL`
|
|
6841
6019
|
)
|
|
6842
|
-
).orderBy(
|
|
6020
|
+
).orderBy(desc3(mindHistory.created_at)).limit(1);
|
|
6843
6021
|
if (lastTurn.length > 0 && lastTurn[0].turn_id) {
|
|
6844
|
-
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);
|
|
6845
6023
|
if (firstEvent.length > 0) {
|
|
6846
6024
|
sinceTimestamp = firstEvent[0].created_at;
|
|
6847
6025
|
}
|
|
@@ -6851,18 +6029,18 @@ ${user.trimEnd()}
|
|
|
6851
6029
|
sinceTimestamp = new Date(Date.now() - 36e5).toISOString().replace("T", " ").slice(0, 19);
|
|
6852
6030
|
}
|
|
6853
6031
|
const conditions = [
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
|
|
6032
|
+
eq5(summaries.mind, name),
|
|
6033
|
+
eq5(summaries.period, "turn"),
|
|
6034
|
+
sql2`${summaries.created_at} > ${sinceTimestamp}`
|
|
6857
6035
|
];
|
|
6858
6036
|
if (currentSession) {
|
|
6859
|
-
conditions.push(
|
|
6037
|
+
conditions.push(sql2`${turns.session} != ${currentSession}`);
|
|
6860
6038
|
}
|
|
6861
6039
|
const rows = await db.select({
|
|
6862
6040
|
session: turns.session,
|
|
6863
6041
|
content: summaries.content,
|
|
6864
6042
|
created_at: summaries.created_at
|
|
6865
|
-
}).from(summaries).innerJoin(turns,
|
|
6043
|
+
}).from(summaries).innerJoin(turns, eq5(turns.id, summaries.period_key)).where(and4(...conditions)).orderBy(desc3(summaries.created_at)).limit(50);
|
|
6866
6044
|
if (rows.length === 0) {
|
|
6867
6045
|
return c.json({ context: null });
|
|
6868
6046
|
}
|
|
@@ -6882,18 +6060,18 @@ ${lines.join("\n")}` });
|
|
|
6882
6060
|
const limit = Math.min(Math.max(parseInt(c.req.query("limit") ?? "50", 10) || 50, 1), 200);
|
|
6883
6061
|
const offset = Math.max(parseInt(c.req.query("offset") ?? "0", 10) || 0, 0);
|
|
6884
6062
|
const db = await getDb();
|
|
6885
|
-
const conditions = [
|
|
6063
|
+
const conditions = [eq5(mindHistory.mind, name)];
|
|
6886
6064
|
if (channel) {
|
|
6887
|
-
conditions.push(
|
|
6065
|
+
conditions.push(eq5(mindHistory.channel, channel));
|
|
6888
6066
|
}
|
|
6889
6067
|
if (session) {
|
|
6890
|
-
conditions.push(
|
|
6068
|
+
conditions.push(eq5(mindHistory.session, session));
|
|
6891
6069
|
}
|
|
6892
6070
|
const effectivePreset = full ? "all" : preset;
|
|
6893
6071
|
if (!effectivePreset || effectivePreset === "summary") {
|
|
6894
|
-
const sumConditions = [
|
|
6072
|
+
const sumConditions = [eq5(summaries.mind, name), eq5(summaries.period, "turn")];
|
|
6895
6073
|
if (session) {
|
|
6896
|
-
sumConditions.push(
|
|
6074
|
+
sumConditions.push(eq5(turns.session, session));
|
|
6897
6075
|
const sumRows2 = await db.select({
|
|
6898
6076
|
id: summaries.id,
|
|
6899
6077
|
mind: summaries.mind,
|
|
@@ -6903,7 +6081,7 @@ ${lines.join("\n")}` });
|
|
|
6903
6081
|
metadata: summaries.metadata,
|
|
6904
6082
|
created_at: summaries.created_at,
|
|
6905
6083
|
session: turns.session
|
|
6906
|
-
}).from(summaries).innerJoin(turns,
|
|
6084
|
+
}).from(summaries).innerJoin(turns, eq5(turns.id, summaries.period_key)).where(and4(...sumConditions)).orderBy(desc3(summaries.created_at)).limit(limit).offset(offset);
|
|
6907
6085
|
return c.json(
|
|
6908
6086
|
sumRows2.map((r) => ({
|
|
6909
6087
|
id: r.id,
|
|
@@ -6920,7 +6098,7 @@ ${lines.join("\n")}` });
|
|
|
6920
6098
|
}))
|
|
6921
6099
|
);
|
|
6922
6100
|
}
|
|
6923
|
-
const sumRows = await db.select().from(summaries).where(
|
|
6101
|
+
const sumRows = await db.select().from(summaries).where(and4(...sumConditions)).orderBy(desc3(summaries.created_at)).limit(limit).offset(offset);
|
|
6924
6102
|
return c.json(
|
|
6925
6103
|
sumRows.map((r) => ({
|
|
6926
6104
|
id: r.id,
|
|
@@ -6941,22 +6119,22 @@ ${lines.join("\n")}` });
|
|
|
6941
6119
|
case "all":
|
|
6942
6120
|
break;
|
|
6943
6121
|
case "conversation":
|
|
6944
|
-
conditions.push(
|
|
6122
|
+
conditions.push(sql2`${mindHistory.type} IN ('inbound','outbound','tool_use')`);
|
|
6945
6123
|
break;
|
|
6946
6124
|
case "detailed":
|
|
6947
6125
|
conditions.push(
|
|
6948
|
-
|
|
6126
|
+
sql2`${mindHistory.type} IN ('inbound','outbound','tool_use','tool_result','text','thinking')`
|
|
6949
6127
|
);
|
|
6950
6128
|
break;
|
|
6951
6129
|
}
|
|
6952
|
-
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);
|
|
6953
6131
|
return c.json(rows);
|
|
6954
6132
|
});
|
|
6955
6133
|
var minds_default = app13;
|
|
6956
6134
|
|
|
6957
|
-
// src/web/api/prompts.ts
|
|
6135
|
+
// packages/daemon/src/web/api/prompts.ts
|
|
6958
6136
|
import { zValidator as zValidator6 } from "@hono/zod-validator";
|
|
6959
|
-
import { eq as
|
|
6137
|
+
import { eq as eq6, sql as sql3 } from "drizzle-orm";
|
|
6960
6138
|
import { Hono as Hono16 } from "hono";
|
|
6961
6139
|
import { z as z6 } from "zod";
|
|
6962
6140
|
var app14 = new Hono16().get("/", async (c) => {
|
|
@@ -6989,9 +6167,9 @@ var app14 = new Hono16().get("/", async (c) => {
|
|
|
6989
6167
|
}
|
|
6990
6168
|
const { content } = c.req.valid("json");
|
|
6991
6169
|
const db = await getDb();
|
|
6992
|
-
await db.insert(systemPrompts).values({ key, content, updated_at:
|
|
6170
|
+
await db.insert(systemPrompts).values({ key, content, updated_at: sql3`(datetime('now'))` }).onConflictDoUpdate({
|
|
6993
6171
|
target: systemPrompts.key,
|
|
6994
|
-
set: { content, updated_at:
|
|
6172
|
+
set: { content, updated_at: sql3`(datetime('now'))` }
|
|
6995
6173
|
});
|
|
6996
6174
|
return c.json({ ok: true });
|
|
6997
6175
|
}).delete("/:key", requireAdmin, async (c) => {
|
|
@@ -7000,103 +6178,14 @@ var app14 = new Hono16().get("/", async (c) => {
|
|
|
7000
6178
|
return c.json({ error: "Unknown prompt key" }, 404);
|
|
7001
6179
|
}
|
|
7002
6180
|
const db = await getDb();
|
|
7003
|
-
await db.delete(systemPrompts).where(
|
|
6181
|
+
await db.delete(systemPrompts).where(eq6(systemPrompts.key, key));
|
|
7004
6182
|
return c.json({ ok: true });
|
|
7005
6183
|
});
|
|
7006
6184
|
var prompts_default = app14;
|
|
7007
6185
|
|
|
7008
|
-
// src/web/api/
|
|
7009
|
-
import { readdir, readFile as readFile2, stat as stat2 } from "fs/promises";
|
|
7010
|
-
import { extname as extname3, resolve as resolve13 } from "path";
|
|
7011
|
-
import { Hono as Hono17 } from "hono";
|
|
7012
|
-
var MAX_FILE_SIZE = 50 * 1024 * 1024;
|
|
7013
|
-
async function resolvePublicRoot(name) {
|
|
7014
|
-
if (name === "_system") return resolve13(voluteHome(), "shared");
|
|
7015
|
-
if (!await findMind(name)) return null;
|
|
7016
|
-
return resolve13(mindDir(name), "home", "public");
|
|
7017
|
-
}
|
|
7018
|
-
function hasDotSegment(relativePath) {
|
|
7019
|
-
return relativePath.split("/").some((seg) => seg.startsWith("."));
|
|
7020
|
-
}
|
|
7021
|
-
var MIME_TYPES = {
|
|
7022
|
-
".html": "text/html",
|
|
7023
|
-
".js": "application/javascript",
|
|
7024
|
-
".css": "text/css",
|
|
7025
|
-
".json": "application/json",
|
|
7026
|
-
".svg": "image/svg+xml",
|
|
7027
|
-
".png": "image/png",
|
|
7028
|
-
".jpg": "image/jpeg",
|
|
7029
|
-
".jpeg": "image/jpeg",
|
|
7030
|
-
".gif": "image/gif",
|
|
7031
|
-
".ico": "image/x-icon",
|
|
7032
|
-
".woff": "font/woff",
|
|
7033
|
-
".woff2": "font/woff2",
|
|
7034
|
-
".txt": "text/plain",
|
|
7035
|
-
".xml": "application/xml",
|
|
7036
|
-
".md": "text/markdown",
|
|
7037
|
-
".webp": "image/webp"
|
|
7038
|
-
};
|
|
7039
|
-
async function listDir(dirPath) {
|
|
7040
|
-
let entries;
|
|
7041
|
-
try {
|
|
7042
|
-
entries = await readdir(dirPath, { withFileTypes: true });
|
|
7043
|
-
} catch (err) {
|
|
7044
|
-
if (err?.code === "ENOENT") return [];
|
|
7045
|
-
throw err;
|
|
7046
|
-
}
|
|
7047
|
-
return entries.filter((e) => !e.name.startsWith(".")).map((e) => ({
|
|
7048
|
-
name: e.name,
|
|
7049
|
-
type: e.isDirectory() ? "directory" : "file"
|
|
7050
|
-
}));
|
|
7051
|
-
}
|
|
7052
|
-
var app15 = new Hono17().get("/:name/", async (c) => {
|
|
7053
|
-
const name = c.req.param("name");
|
|
7054
|
-
const publicRoot = await resolvePublicRoot(name);
|
|
7055
|
-
if (!publicRoot) return c.json({ error: "Not found" }, 404);
|
|
7056
|
-
return c.json(await listDir(publicRoot));
|
|
7057
|
-
}).get("/:name/*", async (c) => {
|
|
7058
|
-
const name = c.req.param("name");
|
|
7059
|
-
const publicRoot = await resolvePublicRoot(name);
|
|
7060
|
-
if (!publicRoot) return c.text("Not found", 404);
|
|
7061
|
-
const wildcard = c.req.path.replace(`/public/${name}`, "") || "/";
|
|
7062
|
-
const relativePath = wildcard.slice(1);
|
|
7063
|
-
const requestedPath = resolve13(publicRoot, relativePath);
|
|
7064
|
-
if (!requestedPath.startsWith(publicRoot)) return c.text("Forbidden", 403);
|
|
7065
|
-
if (hasDotSegment(relativePath)) return c.text("Forbidden", 403);
|
|
7066
|
-
let fileStat;
|
|
7067
|
-
try {
|
|
7068
|
-
fileStat = await stat2(requestedPath);
|
|
7069
|
-
} catch (err) {
|
|
7070
|
-
if (err?.code === "ENOENT") return c.text("Not found", 404);
|
|
7071
|
-
if (err?.code === "EACCES") return c.text("Forbidden", 403);
|
|
7072
|
-
return c.text("Internal server error", 500);
|
|
7073
|
-
}
|
|
7074
|
-
if (fileStat.isDirectory()) {
|
|
7075
|
-
if (wildcard.endsWith("/")) {
|
|
7076
|
-
return c.json(await listDir(requestedPath));
|
|
7077
|
-
}
|
|
7078
|
-
return c.text("Not found", 404);
|
|
7079
|
-
}
|
|
7080
|
-
if (fileStat.isFile()) {
|
|
7081
|
-
if (fileStat.size > MAX_FILE_SIZE) return c.text("File too large", 413);
|
|
7082
|
-
const ext = extname3(requestedPath);
|
|
7083
|
-
const mime = MIME_TYPES[ext] || "application/octet-stream";
|
|
7084
|
-
try {
|
|
7085
|
-
const body = await readFile2(requestedPath);
|
|
7086
|
-
return c.body(body, 200, { "Content-Type": mime });
|
|
7087
|
-
} catch (err) {
|
|
7088
|
-
if (err?.code === "ENOENT") return c.text("Not found", 404);
|
|
7089
|
-
if (err?.code === "EACCES") return c.text("Forbidden", 403);
|
|
7090
|
-
return c.text("Failed to read file", 500);
|
|
7091
|
-
}
|
|
7092
|
-
}
|
|
7093
|
-
return c.text("Not found", 404);
|
|
7094
|
-
});
|
|
7095
|
-
var public_files_default = app15;
|
|
7096
|
-
|
|
7097
|
-
// src/web/api/schedules.ts
|
|
6186
|
+
// packages/daemon/src/web/api/schedules.ts
|
|
7098
6187
|
import { CronExpressionParser } from "cron-parser";
|
|
7099
|
-
import { Hono as
|
|
6188
|
+
import { Hono as Hono17 } from "hono";
|
|
7100
6189
|
var slog2 = logger_default.child("schedules");
|
|
7101
6190
|
function readSchedules(name) {
|
|
7102
6191
|
return readVoluteConfig(mindDir(name))?.schedules ?? [];
|
|
@@ -7114,7 +6203,7 @@ function writeSchedules(name, schedules) {
|
|
|
7114
6203
|
data: { schedules }
|
|
7115
6204
|
});
|
|
7116
6205
|
}
|
|
7117
|
-
var
|
|
6206
|
+
var app15 = new Hono17().get("/:name/clock/status", async (c) => {
|
|
7118
6207
|
const name = c.req.param("name");
|
|
7119
6208
|
if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
|
|
7120
6209
|
const sleepManager = getSleepManagerIfReady();
|
|
@@ -7329,7 +6418,7 @@ var app16 = new Hono18().get("/:name/clock/status", async (c) => {
|
|
|
7329
6418
|
const body = await c.req.text();
|
|
7330
6419
|
const message = `[webhook: ${event}] ${body}`;
|
|
7331
6420
|
try {
|
|
7332
|
-
const { sendSystemMessage } = await import("./system-chat-
|
|
6421
|
+
const { sendSystemMessage } = await import("./system-chat-IDPHYHY4.js");
|
|
7333
6422
|
await sendSystemMessage(name, message);
|
|
7334
6423
|
return c.json({ ok: true });
|
|
7335
6424
|
} catch (err) {
|
|
@@ -7337,21 +6426,21 @@ var app16 = new Hono18().get("/:name/clock/status", async (c) => {
|
|
|
7337
6426
|
return c.json({ error: "Failed to reach mind" }, 502);
|
|
7338
6427
|
}
|
|
7339
6428
|
});
|
|
7340
|
-
var schedules_default =
|
|
6429
|
+
var schedules_default = app15;
|
|
7341
6430
|
|
|
7342
|
-
// src/web/api/setup.ts
|
|
7343
|
-
import { mkdirSync as
|
|
6431
|
+
// packages/daemon/src/web/api/setup.ts
|
|
6432
|
+
import { mkdirSync as mkdirSync7 } from "fs";
|
|
7344
6433
|
import { homedir as homedir2 } from "os";
|
|
7345
|
-
import { resolve as
|
|
7346
|
-
import { Hono as
|
|
6434
|
+
import { resolve as resolve12 } from "path";
|
|
6435
|
+
import { Hono as Hono18 } from "hono";
|
|
7347
6436
|
import { setCookie as setCookie2 } from "hono/cookie";
|
|
7348
6437
|
var DEFAULT_API_URL2 = "https://volute.systems";
|
|
7349
|
-
var setup = new
|
|
6438
|
+
var setup = new Hono18();
|
|
7350
6439
|
function writeSetupConfig(systemName, description) {
|
|
7351
|
-
const configHome = process.env.VOLUTE_HOME ??
|
|
7352
|
-
const mindsDir =
|
|
7353
|
-
|
|
7354
|
-
|
|
6440
|
+
const configHome = process.env.VOLUTE_HOME ?? resolve12(homedir2(), ".volute");
|
|
6441
|
+
const mindsDir = resolve12(configHome, "minds");
|
|
6442
|
+
mkdirSync7(configHome, { recursive: true });
|
|
6443
|
+
mkdirSync7(mindsDir, { recursive: true });
|
|
7355
6444
|
const existingConfig = readGlobalConfig();
|
|
7356
6445
|
const setupConfig = {
|
|
7357
6446
|
type: "local",
|
|
@@ -7382,7 +6471,7 @@ setup.get("/status", async (c) => {
|
|
|
7382
6471
|
let hasAccount = false;
|
|
7383
6472
|
if (hasSystem) {
|
|
7384
6473
|
try {
|
|
7385
|
-
const { listUsersByType: listUsersByType2 } = await import("./auth-
|
|
6474
|
+
const { listUsersByType: listUsersByType2 } = await import("./auth-WX4TESEI.js");
|
|
7386
6475
|
const brains = await listUsersByType2("brain");
|
|
7387
6476
|
hasAccount = brains.length > 0;
|
|
7388
6477
|
} catch (err) {
|
|
@@ -7431,7 +6520,11 @@ setup.post("/system", async (c) => {
|
|
|
7431
6520
|
return c.json({ error: "System name is required" }, 400);
|
|
7432
6521
|
}
|
|
7433
6522
|
try {
|
|
7434
|
-
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
|
+
}
|
|
7435
6528
|
return c.json({ ok: true });
|
|
7436
6529
|
} catch (err) {
|
|
7437
6530
|
return c.json({ error: `Failed to write configuration: ${err.message}` }, 500);
|
|
@@ -7567,7 +6660,7 @@ setup.post("/account", async (c) => {
|
|
|
7567
6660
|
}
|
|
7568
6661
|
}
|
|
7569
6662
|
try {
|
|
7570
|
-
const { createUser: createUser2, updateUserProfile: updateUserProfile2 } = await import("./auth-
|
|
6663
|
+
const { createUser: createUser2, updateUserProfile: updateUserProfile2 } = await import("./auth-WX4TESEI.js");
|
|
7571
6664
|
const user = await createUser2(body.username.trim(), body.password);
|
|
7572
6665
|
if (body.displayName?.trim()) {
|
|
7573
6666
|
await updateUserProfile2(user.id, { display_name: body.displayName.trim() });
|
|
@@ -7613,7 +6706,7 @@ setup.post("/models", async (c) => {
|
|
|
7613
6706
|
return c.json({ error: "Spirit model is required" }, 400);
|
|
7614
6707
|
}
|
|
7615
6708
|
try {
|
|
7616
|
-
const { setEnabledModels: setEnabledModels3, setUtilityModel: setUtilityModel2 } = await import("./ai-service-
|
|
6709
|
+
const { setEnabledModels: setEnabledModels3, setUtilityModel: setUtilityModel2 } = await import("./ai-service-LURBEDDB.js");
|
|
7617
6710
|
setEnabledModels3(body.models);
|
|
7618
6711
|
const config2 = readGlobalConfig();
|
|
7619
6712
|
config2.spiritModel = body.spiritModel.trim();
|
|
@@ -7631,8 +6724,8 @@ setup.post("/complete", async (c) => {
|
|
|
7631
6724
|
return c.json({ error: "Setup already complete" }, 400);
|
|
7632
6725
|
}
|
|
7633
6726
|
try {
|
|
7634
|
-
const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-
|
|
7635
|
-
const { startSpiritFull } = await import("./mind-service-
|
|
6727
|
+
const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-ZFRDXMG7.js");
|
|
6728
|
+
const { startSpiritFull } = await import("./mind-service-X2CAA6W6.js");
|
|
7636
6729
|
await ensureSpiritProject();
|
|
7637
6730
|
await syncSpiritTemplate();
|
|
7638
6731
|
const warnings = [];
|
|
@@ -7648,8 +6741,8 @@ setup.post("/complete", async (c) => {
|
|
|
7648
6741
|
}
|
|
7649
6742
|
let spiritConversationId;
|
|
7650
6743
|
try {
|
|
7651
|
-
const { getOrCreateMindUser: getOrCreateMindUser2, listUsersByType: listUsersByType2 } = await import("./auth-
|
|
7652
|
-
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");
|
|
7653
6746
|
const spiritUser = await getOrCreateMindUser2("volute");
|
|
7654
6747
|
const brains = await listUsersByType2("brain");
|
|
7655
6748
|
const admin2 = brains.find((u) => u.role === "admin");
|
|
@@ -7672,8 +6765,8 @@ setup.post("/complete", async (c) => {
|
|
|
7672
6765
|
logger_default.info("setup complete state", { spiritConversationId, spiritStarted });
|
|
7673
6766
|
if (spiritConversationId && spiritStarted) {
|
|
7674
6767
|
try {
|
|
7675
|
-
const { deliverMessage: deliverMessage2 } = await import("./message-delivery-
|
|
7676
|
-
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");
|
|
7677
6770
|
const admins = await listUsers6("brain");
|
|
7678
6771
|
const admin2 = admins.find((u) => u.role === "admin");
|
|
7679
6772
|
const adminName = admin2?.display_name || admin2?.username || "the admin";
|
|
@@ -7709,35 +6802,13 @@ setup.post("/complete", async (c) => {
|
|
|
7709
6802
|
});
|
|
7710
6803
|
var setup_default = setup;
|
|
7711
6804
|
|
|
7712
|
-
// src/web/api/
|
|
7713
|
-
import {
|
|
7714
|
-
var app17 = new Hono20().post("/:name/shared/merge", requireAdmin, async (c) => {
|
|
7715
|
-
const name = c.req.param("name");
|
|
7716
|
-
const entry = await findMind(name);
|
|
7717
|
-
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
7718
|
-
let body;
|
|
7719
|
-
try {
|
|
7720
|
-
body = await c.req.json();
|
|
7721
|
-
} catch {
|
|
7722
|
-
return c.json({ error: "Invalid JSON in request body" }, 400);
|
|
7723
|
-
}
|
|
7724
|
-
const message = body.message || `shared: merge from ${name}`;
|
|
7725
|
-
try {
|
|
7726
|
-
const result = await sharedMerge(name, mindDir(name), message);
|
|
7727
|
-
return c.json(result);
|
|
7728
|
-
} catch (err) {
|
|
7729
|
-
return c.json({ error: err instanceof Error ? err.message : "Merge failed" }, 500);
|
|
7730
|
-
}
|
|
7731
|
-
});
|
|
7732
|
-
var shared_default = app17;
|
|
7733
|
-
|
|
7734
|
-
// src/web/api/skills.ts
|
|
7735
|
-
import { existsSync as existsSync10, mkdtempSync, readdirSync as readdirSync3, rmSync as rmSync6 } from "fs";
|
|
6805
|
+
// packages/daemon/src/web/api/skills.ts
|
|
6806
|
+
import { existsSync as existsSync9, mkdtempSync, readdirSync as readdirSync3, rmSync as rmSync5 } from "fs";
|
|
7736
6807
|
import { tmpdir } from "os";
|
|
7737
|
-
import { join, resolve as
|
|
6808
|
+
import { join, resolve as resolve13 } from "path";
|
|
7738
6809
|
import AdmZip from "adm-zip";
|
|
7739
|
-
import { Hono as
|
|
7740
|
-
var
|
|
6810
|
+
import { Hono as Hono19 } from "hono";
|
|
6811
|
+
var app16 = new Hono19().get("/", async (c) => {
|
|
7741
6812
|
const skills = await listSharedSkills();
|
|
7742
6813
|
return c.json(skills);
|
|
7743
6814
|
}).get("/defaults/list", async (c) => {
|
|
@@ -7810,19 +6881,19 @@ var app18 = new Hono21().get("/", async (c) => {
|
|
|
7810
6881
|
try {
|
|
7811
6882
|
const zip = new AdmZip(buffer2);
|
|
7812
6883
|
for (const entry of zip.getEntries()) {
|
|
7813
|
-
const target =
|
|
6884
|
+
const target = resolve13(tmpDir, entry.entryName);
|
|
7814
6885
|
if (!target.startsWith(tmpDir)) {
|
|
7815
6886
|
return c.json({ error: "Invalid zip: paths must not escape archive" }, 400);
|
|
7816
6887
|
}
|
|
7817
6888
|
}
|
|
7818
6889
|
zip.extractAllTo(tmpDir, true);
|
|
7819
6890
|
let skillDir = null;
|
|
7820
|
-
if (
|
|
6891
|
+
if (existsSync9(join(tmpDir, "SKILL.md"))) {
|
|
7821
6892
|
skillDir = tmpDir;
|
|
7822
6893
|
} else {
|
|
7823
6894
|
const entries = readdirSync3(tmpDir, { withFileTypes: true }).filter((e) => e.isDirectory());
|
|
7824
6895
|
for (const entry of entries) {
|
|
7825
|
-
if (
|
|
6896
|
+
if (existsSync9(join(tmpDir, entry.name, "SKILL.md"))) {
|
|
7826
6897
|
skillDir = join(tmpDir, entry.name);
|
|
7827
6898
|
break;
|
|
7828
6899
|
}
|
|
@@ -7839,7 +6910,7 @@ var app18 = new Hono21().get("/", async (c) => {
|
|
|
7839
6910
|
}
|
|
7840
6911
|
throw e;
|
|
7841
6912
|
} finally {
|
|
7842
|
-
|
|
6913
|
+
rmSync5(tmpDir, { recursive: true, force: true });
|
|
7843
6914
|
}
|
|
7844
6915
|
}).get("/:id", async (c) => {
|
|
7845
6916
|
const id = c.req.param("id");
|
|
@@ -7858,18 +6929,18 @@ var app18 = new Hono21().get("/", async (c) => {
|
|
|
7858
6929
|
}
|
|
7859
6930
|
return c.json({ ok: true });
|
|
7860
6931
|
});
|
|
7861
|
-
var skills_default =
|
|
6932
|
+
var skills_default = app16;
|
|
7862
6933
|
|
|
7863
|
-
// src/web/api/typing.ts
|
|
6934
|
+
// packages/daemon/src/web/api/typing.ts
|
|
7864
6935
|
import { zValidator as zValidator7 } from "@hono/zod-validator";
|
|
7865
|
-
import { Hono as
|
|
6936
|
+
import { Hono as Hono20 } from "hono";
|
|
7866
6937
|
import { z as z7 } from "zod";
|
|
7867
6938
|
var typingSchema = z7.object({
|
|
7868
6939
|
channel: z7.string().min(1),
|
|
7869
6940
|
sender: z7.string().min(1),
|
|
7870
6941
|
active: z7.boolean()
|
|
7871
6942
|
});
|
|
7872
|
-
var
|
|
6943
|
+
var app17 = new Hono20().post("/:name/typing", zValidator7("json", typingSchema), (c) => {
|
|
7873
6944
|
const { channel, sender, active } = c.req.valid("json");
|
|
7874
6945
|
const map = getTypingMap();
|
|
7875
6946
|
if (active) {
|
|
@@ -7889,13 +6960,13 @@ var app19 = new Hono22().post("/:name/typing", zValidator7("json", typingSchema)
|
|
|
7889
6960
|
const map = getTypingMap();
|
|
7890
6961
|
return c.json({ typing: map.get(channel) });
|
|
7891
6962
|
});
|
|
7892
|
-
var typing_default =
|
|
6963
|
+
var typing_default = app17;
|
|
7893
6964
|
|
|
7894
|
-
// src/web/api/update.ts
|
|
6965
|
+
// packages/daemon/src/web/api/update.ts
|
|
7895
6966
|
import { spawn as spawn3 } from "child_process";
|
|
7896
|
-
import { Hono as
|
|
6967
|
+
import { Hono as Hono21 } from "hono";
|
|
7897
6968
|
var bin;
|
|
7898
|
-
var
|
|
6969
|
+
var app18 = new Hono21().get("/update", async (c) => {
|
|
7899
6970
|
const result = await checkForUpdate();
|
|
7900
6971
|
return c.json(result);
|
|
7901
6972
|
}).post("/update", requireAdmin, async (c) => {
|
|
@@ -7910,17 +6981,17 @@ var app20 = new Hono23().get("/update", async (c) => {
|
|
|
7910
6981
|
child.unref();
|
|
7911
6982
|
return c.json({ ok: true, message: "Updating..." });
|
|
7912
6983
|
});
|
|
7913
|
-
var update_default =
|
|
6984
|
+
var update_default = app18;
|
|
7914
6985
|
|
|
7915
|
-
// src/web/api/v1/conversations.ts
|
|
6986
|
+
// packages/daemon/src/web/api/v1/conversations.ts
|
|
7916
6987
|
import { zValidator as zValidator8 } from "@hono/zod-validator";
|
|
7917
|
-
import { Hono as
|
|
6988
|
+
import { Hono as Hono22 } from "hono";
|
|
7918
6989
|
import { z as z8 } from "zod";
|
|
7919
6990
|
var createSchema = z8.object({
|
|
7920
6991
|
title: z8.string().optional(),
|
|
7921
6992
|
participantNames: z8.array(z8.string()).min(1)
|
|
7922
6993
|
});
|
|
7923
|
-
var
|
|
6994
|
+
var app19 = new Hono22().use("*", authMiddleware).get("/", async (c) => {
|
|
7924
6995
|
const user = c.get("user");
|
|
7925
6996
|
const convs = await listConversationsWithParticipants(user.id);
|
|
7926
6997
|
return c.json(convs);
|
|
@@ -8005,14 +7076,14 @@ var app21 = new Hono24().use("*", authMiddleware).get("/", async (c) => {
|
|
|
8005
7076
|
if (!deleted) return c.json({ error: "Conversation not found" }, 404);
|
|
8006
7077
|
return c.json({ ok: true });
|
|
8007
7078
|
});
|
|
8008
|
-
var conversations_default =
|
|
7079
|
+
var conversations_default = app19;
|
|
8009
7080
|
|
|
8010
|
-
// src/web/api/v1/events.ts
|
|
8011
|
-
import { desc as
|
|
8012
|
-
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";
|
|
8013
7084
|
import { streamSSE as streamSSE4 } from "hono/streaming";
|
|
8014
7085
|
|
|
8015
|
-
// src/lib/events/brain-presence.ts
|
|
7086
|
+
// packages/daemon/src/lib/events/brain-presence.ts
|
|
8016
7087
|
var connections = /* @__PURE__ */ new Map();
|
|
8017
7088
|
function addConnection(username) {
|
|
8018
7089
|
const count = connections.get(username) ?? 0;
|
|
@@ -8035,7 +7106,7 @@ function getOnlineBrains() {
|
|
|
8035
7106
|
return [...connections.keys()];
|
|
8036
7107
|
}
|
|
8037
7108
|
|
|
8038
|
-
// src/lib/events/event-sequencer.ts
|
|
7109
|
+
// packages/daemon/src/lib/events/event-sequencer.ts
|
|
8039
7110
|
var BUFFER_SIZE = 1e3;
|
|
8040
7111
|
var MAX_AGE_MS = 5 * 60 * 1e3;
|
|
8041
7112
|
var nextId = 1;
|
|
@@ -8055,8 +7126,8 @@ function getEventsSince(sinceId) {
|
|
|
8055
7126
|
return buffer.slice(startIdx).filter((e) => now - e.timestamp < MAX_AGE_MS);
|
|
8056
7127
|
}
|
|
8057
7128
|
|
|
8058
|
-
// src/web/api/v1/events.ts
|
|
8059
|
-
var
|
|
7129
|
+
// packages/daemon/src/web/api/v1/events.ts
|
|
7130
|
+
var app20 = new Hono23().use("*", authMiddleware).get("/", async (c) => {
|
|
8060
7131
|
const user = c.get("user");
|
|
8061
7132
|
const since = c.req.query("since");
|
|
8062
7133
|
const sinceId = since ? Number(since) : 0;
|
|
@@ -8079,7 +7150,7 @@ var app22 = new Hono25().use("*", authMiddleware).get("/", async (c) => {
|
|
|
8079
7150
|
let recentActivity = [];
|
|
8080
7151
|
try {
|
|
8081
7152
|
const db = await getDb();
|
|
8082
|
-
recentActivity = await db.select().from(activity).orderBy(
|
|
7153
|
+
recentActivity = await db.select().from(activity).orderBy(desc4(activity.created_at)).limit(50);
|
|
8083
7154
|
recentActivity = recentActivity.map((row) => ({
|
|
8084
7155
|
...row,
|
|
8085
7156
|
metadata: row.metadata ? JSON.parse(row.metadata) : null
|
|
@@ -8146,8 +7217,8 @@ var app22 = new Hono25().use("*", authMiddleware).get("/", async (c) => {
|
|
|
8146
7217
|
});
|
|
8147
7218
|
}, 15e3);
|
|
8148
7219
|
cleanups.push(() => clearInterval(keepAlive));
|
|
8149
|
-
await new Promise((
|
|
8150
|
-
stream.onAbort(() =>
|
|
7220
|
+
await new Promise((resolve18) => {
|
|
7221
|
+
stream.onAbort(() => resolve18());
|
|
8151
7222
|
});
|
|
8152
7223
|
} finally {
|
|
8153
7224
|
for (const cleanup of cleanups) {
|
|
@@ -8159,19 +7230,19 @@ var app22 = new Hono25().use("*", authMiddleware).get("/", async (c) => {
|
|
|
8159
7230
|
}
|
|
8160
7231
|
});
|
|
8161
7232
|
});
|
|
8162
|
-
var events_default =
|
|
7233
|
+
var events_default = app20;
|
|
8163
7234
|
|
|
8164
|
-
// src/web/api/variants.ts
|
|
8165
|
-
import { existsSync as
|
|
8166
|
-
import { resolve as
|
|
8167
|
-
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";
|
|
8168
7239
|
|
|
8169
|
-
// src/lib/spawn-server.ts
|
|
7240
|
+
// packages/daemon/src/lib/spawn-server.ts
|
|
8170
7241
|
import { spawn as spawn4 } from "child_process";
|
|
8171
|
-
import { closeSync, mkdirSync as
|
|
8172
|
-
import { resolve as
|
|
7242
|
+
import { closeSync, mkdirSync as mkdirSync8, openSync, readFileSync as readFileSync10 } from "fs";
|
|
7243
|
+
import { resolve as resolve14 } from "path";
|
|
8173
7244
|
function tsxBin(cwd) {
|
|
8174
|
-
return
|
|
7245
|
+
return resolve14(cwd, "node_modules", ".bin", "tsx");
|
|
8175
7246
|
}
|
|
8176
7247
|
function spawnServer(cwd, port, options) {
|
|
8177
7248
|
if (options?.detached) {
|
|
@@ -8184,31 +7255,31 @@ function spawnAttached(cwd, port) {
|
|
|
8184
7255
|
cwd,
|
|
8185
7256
|
stdio: ["ignore", "pipe", "pipe"]
|
|
8186
7257
|
});
|
|
8187
|
-
return new Promise((
|
|
8188
|
-
const timeout = setTimeout(() =>
|
|
7258
|
+
return new Promise((resolve18) => {
|
|
7259
|
+
const timeout = setTimeout(() => resolve18(null), 3e4);
|
|
8189
7260
|
function checkOutput(data) {
|
|
8190
7261
|
const match = data.toString().match(/listening on :(\d+)/);
|
|
8191
7262
|
if (match) {
|
|
8192
7263
|
clearTimeout(timeout);
|
|
8193
|
-
|
|
7264
|
+
resolve18({ child, actualPort: parseInt(match[1], 10) });
|
|
8194
7265
|
}
|
|
8195
7266
|
}
|
|
8196
7267
|
child.stdout?.on("data", checkOutput);
|
|
8197
7268
|
child.stderr?.on("data", checkOutput);
|
|
8198
7269
|
child.on("error", () => {
|
|
8199
7270
|
clearTimeout(timeout);
|
|
8200
|
-
|
|
7271
|
+
resolve18(null);
|
|
8201
7272
|
});
|
|
8202
7273
|
child.on("exit", () => {
|
|
8203
7274
|
clearTimeout(timeout);
|
|
8204
|
-
|
|
7275
|
+
resolve18(null);
|
|
8205
7276
|
});
|
|
8206
7277
|
});
|
|
8207
7278
|
}
|
|
8208
7279
|
function spawnDetached(cwd, port, logDir) {
|
|
8209
|
-
const logsDir = logDir ??
|
|
8210
|
-
|
|
8211
|
-
const logPath =
|
|
7280
|
+
const logsDir = logDir ?? resolve14(cwd, ".mind", "logs");
|
|
7281
|
+
mkdirSync8(logsDir, { recursive: true });
|
|
7282
|
+
const logPath = resolve14(logsDir, "mind.log");
|
|
8212
7283
|
const logFd = openSync(logPath, "a");
|
|
8213
7284
|
const child = spawn4(tsxBin(cwd), ["src/server.ts", "--port", String(port)], {
|
|
8214
7285
|
cwd,
|
|
@@ -8228,7 +7299,7 @@ function spawnDetached(cwd, port, logDir) {
|
|
|
8228
7299
|
}
|
|
8229
7300
|
const interval = setInterval(() => {
|
|
8230
7301
|
try {
|
|
8231
|
-
const content =
|
|
7302
|
+
const content = readFileSync10(logPath, "utf-8");
|
|
8232
7303
|
const match = content.match(/listening on :(\d+)/);
|
|
8233
7304
|
if (match) {
|
|
8234
7305
|
finish({ child, actualPort: parseInt(match[1], 10) });
|
|
@@ -8242,7 +7313,7 @@ function spawnDetached(cwd, port, logDir) {
|
|
|
8242
7313
|
});
|
|
8243
7314
|
}
|
|
8244
7315
|
|
|
8245
|
-
// src/lib/verify.ts
|
|
7316
|
+
// packages/daemon/src/lib/verify.ts
|
|
8246
7317
|
async function verify2(port) {
|
|
8247
7318
|
const health = await checkHealth(port);
|
|
8248
7319
|
if (!health.ok) {
|
|
@@ -8279,8 +7350,8 @@ async function verify2(port) {
|
|
|
8279
7350
|
}
|
|
8280
7351
|
}
|
|
8281
7352
|
|
|
8282
|
-
// src/web/api/variants.ts
|
|
8283
|
-
var
|
|
7353
|
+
// packages/daemon/src/web/api/variants.ts
|
|
7354
|
+
var app21 = new Hono24().get("/:name/variants", async (c) => {
|
|
8284
7355
|
const name = c.req.param("name");
|
|
8285
7356
|
const entry = await findMind(name);
|
|
8286
7357
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
@@ -8324,11 +7395,11 @@ var app23 = new Hono26().get("/:name/variants", async (c) => {
|
|
|
8324
7395
|
return c.json({ error: `Name already in use: ${variantName}` }, 409);
|
|
8325
7396
|
}
|
|
8326
7397
|
const projectRoot = mindDir(mindName);
|
|
8327
|
-
const variantDir =
|
|
8328
|
-
if (
|
|
7398
|
+
const variantDir = resolve15(projectRoot, ".variants", variantName);
|
|
7399
|
+
if (existsSync10(variantDir)) {
|
|
8329
7400
|
return c.json({ error: `Variant directory already exists: ${variantDir}` }, 409);
|
|
8330
7401
|
}
|
|
8331
|
-
|
|
7402
|
+
mkdirSync9(resolve15(projectRoot, ".variants"), { recursive: true });
|
|
8332
7403
|
try {
|
|
8333
7404
|
await gitExec(["worktree", "add", "-b", variantName, variantDir], { cwd: projectRoot });
|
|
8334
7405
|
} catch (e) {
|
|
@@ -8341,7 +7412,7 @@ var app23 = new Hono26().get("/:name/variants", async (c) => {
|
|
|
8341
7412
|
const [cmd, args] = await wrapForIsolation("npm", ["install"], mindName);
|
|
8342
7413
|
await exec(cmd, args, {
|
|
8343
7414
|
cwd: variantDir,
|
|
8344
|
-
env: { ...process.env, HOME:
|
|
7415
|
+
env: { ...process.env, HOME: resolve15(variantDir, "home") }
|
|
8345
7416
|
});
|
|
8346
7417
|
} else {
|
|
8347
7418
|
await exec("npm", ["install"], { cwd: variantDir });
|
|
@@ -8351,7 +7422,7 @@ var app23 = new Hono26().get("/:name/variants", async (c) => {
|
|
|
8351
7422
|
return c.json({ error: `npm install failed: ${msg}` }, 500);
|
|
8352
7423
|
}
|
|
8353
7424
|
if (body.soul) {
|
|
8354
|
-
|
|
7425
|
+
writeFileSync8(resolve15(variantDir, "home/SOUL.md"), body.soul);
|
|
8355
7426
|
}
|
|
8356
7427
|
const variantPort = body.port ?? await nextPort();
|
|
8357
7428
|
await addVariant(variantName, mindName, variantPort, variantDir, variantName);
|
|
@@ -8386,7 +7457,7 @@ var app23 = new Hono26().get("/:name/variants", async (c) => {
|
|
|
8386
7457
|
} catch {
|
|
8387
7458
|
}
|
|
8388
7459
|
const projectRoot = mindDir(mindName);
|
|
8389
|
-
if (
|
|
7460
|
+
if (existsSync10(variantEntry.dir)) {
|
|
8390
7461
|
const status = (await gitExec(["status", "--porcelain"], { cwd: variantEntry.dir })).trim();
|
|
8391
7462
|
if (status) {
|
|
8392
7463
|
try {
|
|
@@ -8446,8 +7517,8 @@ var app23 = new Hono26().get("/:name/variants", async (c) => {
|
|
|
8446
7517
|
await cleanupVariant(variantName, projectRoot, variantEntry.dir);
|
|
8447
7518
|
if (variantName.endsWith("-upgrade") || variantName === "upgrade") {
|
|
8448
7519
|
try {
|
|
8449
|
-
const { computeTemplateHash: computeTemplateHash2 } = await import("./template-hash-
|
|
8450
|
-
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");
|
|
8451
7522
|
const tmpl = parentEntry.template ?? "claude";
|
|
8452
7523
|
await setMindTemplateHash2(mindName, computeTemplateHash2(tmpl));
|
|
8453
7524
|
} catch (err) {
|
|
@@ -8460,7 +7531,7 @@ var app23 = new Hono26().get("/:name/variants", async (c) => {
|
|
|
8460
7531
|
const [cmd, args] = await wrapForIsolation("npm", ["install"], mindName);
|
|
8461
7532
|
await exec(cmd, args, {
|
|
8462
7533
|
cwd: projectRoot,
|
|
8463
|
-
env: { ...process.env, HOME:
|
|
7534
|
+
env: { ...process.env, HOME: resolve15(projectRoot, "home") }
|
|
8464
7535
|
});
|
|
8465
7536
|
} else {
|
|
8466
7537
|
await exec("npm", ["install"], { cwd: projectRoot });
|
|
@@ -8502,11 +7573,11 @@ var app23 = new Hono26().get("/:name/variants", async (c) => {
|
|
|
8502
7573
|
await cleanupVariant(variantName, projectRoot, variantEntry.dir, { stop: true });
|
|
8503
7574
|
return c.json({ ok: true });
|
|
8504
7575
|
});
|
|
8505
|
-
var variants_default =
|
|
7576
|
+
var variants_default = app21;
|
|
8506
7577
|
|
|
8507
|
-
// src/web/api/volute/channels.ts
|
|
7578
|
+
// packages/daemon/src/web/api/volute/channels.ts
|
|
8508
7579
|
import { zValidator as zValidator9 } from "@hono/zod-validator";
|
|
8509
|
-
import { Hono as
|
|
7580
|
+
import { Hono as Hono25 } from "hono";
|
|
8510
7581
|
import { z as z9 } from "zod";
|
|
8511
7582
|
var createSchema2 = z9.object({
|
|
8512
7583
|
name: z9.string().min(1).max(50).regex(/^[a-z0-9][a-z0-9-]*$/, "Channel names must be lowercase alphanumeric with hyphens")
|
|
@@ -8514,7 +7585,7 @@ var createSchema2 = z9.object({
|
|
|
8514
7585
|
var inviteSchema = z9.object({
|
|
8515
7586
|
username: z9.string().min(1)
|
|
8516
7587
|
});
|
|
8517
|
-
var
|
|
7588
|
+
var app22 = new Hono25().get("/", async (c) => {
|
|
8518
7589
|
const user = c.get("user");
|
|
8519
7590
|
const channels = await listChannels();
|
|
8520
7591
|
const results = await Promise.all(
|
|
@@ -8584,15 +7655,15 @@ var app24 = new Hono27().get("/", async (c) => {
|
|
|
8584
7655
|
]);
|
|
8585
7656
|
return c.json({ ok: true });
|
|
8586
7657
|
});
|
|
8587
|
-
var channels_default2 =
|
|
7658
|
+
var channels_default2 = app22;
|
|
8588
7659
|
|
|
8589
|
-
// src/web/api/volute/chat.ts
|
|
7660
|
+
// packages/daemon/src/web/api/volute/chat.ts
|
|
8590
7661
|
import { zValidator as zValidator10 } from "@hono/zod-validator";
|
|
8591
|
-
import { Hono as
|
|
7662
|
+
import { Hono as Hono26 } from "hono";
|
|
8592
7663
|
import { streamSSE as streamSSE5 } from "hono/streaming";
|
|
8593
7664
|
import { z as z10 } from "zod";
|
|
8594
7665
|
|
|
8595
|
-
// src/lib/bridge-outbound.ts
|
|
7666
|
+
// packages/daemon/src/lib/bridge-outbound.ts
|
|
8596
7667
|
function extractContent(contentBlocks) {
|
|
8597
7668
|
const text = contentBlocks.filter((b) => b.type === "text").map((b) => b.text).join("\n");
|
|
8598
7669
|
const images = contentBlocks.filter((b) => b.type === "image").map((b) => ({ media_type: b.media_type, data: b.data }));
|
|
@@ -8663,7 +7734,7 @@ async function routeDMOutbound(conversationId, senderName, contentBlocks) {
|
|
|
8663
7734
|
}
|
|
8664
7735
|
}
|
|
8665
7736
|
|
|
8666
|
-
// src/web/api/volute/chat.ts
|
|
7737
|
+
// packages/daemon/src/web/api/volute/chat.ts
|
|
8667
7738
|
async function fanOutToMinds(opts) {
|
|
8668
7739
|
const participants = await getParticipants(opts.conversationId);
|
|
8669
7740
|
const mindParticipants = participants.filter(
|
|
@@ -8671,8 +7742,8 @@ async function fanOutToMinds(opts) {
|
|
|
8671
7742
|
);
|
|
8672
7743
|
const participantNames = participants.map((p) => p.username);
|
|
8673
7744
|
const isDM = opts.isDM ?? participants.length === 2;
|
|
8674
|
-
const { getMindManager: getMindManager2 } = await import("./mind-manager-
|
|
8675
|
-
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");
|
|
8676
7747
|
const manager = getMindManager2();
|
|
8677
7748
|
const sm = getSleepManagerIfReady2();
|
|
8678
7749
|
const targetMinds = mindParticipants.map((ap) => {
|
|
@@ -8752,7 +7823,7 @@ function stageFilesForMinds(files, targetMinds, senderName) {
|
|
|
8752
7823
|
}
|
|
8753
7824
|
return { notifications };
|
|
8754
7825
|
}
|
|
8755
|
-
var unifiedChatApp = new
|
|
7826
|
+
var unifiedChatApp = new Hono26().post(
|
|
8756
7827
|
"/chat",
|
|
8757
7828
|
zValidator10("json", chatSchema),
|
|
8758
7829
|
async (c) => {
|
|
@@ -8890,7 +7961,7 @@ var unifiedChatApp = new Hono28().post(
|
|
|
8890
7961
|
return c.json({ ok: true, conversationId, outboundId });
|
|
8891
7962
|
}
|
|
8892
7963
|
);
|
|
8893
|
-
var
|
|
7964
|
+
var app23 = new Hono26().get("/:name/conversations/:id/events", async (c) => {
|
|
8894
7965
|
const conversationId = c.req.param("id");
|
|
8895
7966
|
const user = c.get("user");
|
|
8896
7967
|
if (user.id !== 0 && !await isParticipantOrOwner(conversationId, user.id)) {
|
|
@@ -8907,27 +7978,27 @@ var app25 = new Hono28().get("/:name/conversations/:id/events", async (c) => {
|
|
|
8907
7978
|
if (!stream.aborted) console.error("[chat] SSE ping error:", err);
|
|
8908
7979
|
});
|
|
8909
7980
|
}, 15e3);
|
|
8910
|
-
await new Promise((
|
|
7981
|
+
await new Promise((resolve18) => {
|
|
8911
7982
|
stream.onAbort(() => {
|
|
8912
7983
|
unsubscribe();
|
|
8913
7984
|
clearInterval(keepAlive);
|
|
8914
|
-
|
|
7985
|
+
resolve18();
|
|
8915
7986
|
});
|
|
8916
7987
|
});
|
|
8917
7988
|
});
|
|
8918
7989
|
});
|
|
8919
|
-
var chat_default =
|
|
7990
|
+
var chat_default = app23;
|
|
8920
7991
|
|
|
8921
|
-
// src/web/api/volute/conversations.ts
|
|
7992
|
+
// packages/daemon/src/web/api/volute/conversations.ts
|
|
8922
7993
|
import { zValidator as zValidator11 } from "@hono/zod-validator";
|
|
8923
|
-
import { Hono as
|
|
7994
|
+
import { Hono as Hono27 } from "hono";
|
|
8924
7995
|
import { z as z11 } from "zod";
|
|
8925
7996
|
var createConvSchema = z11.object({
|
|
8926
7997
|
title: z11.string().optional(),
|
|
8927
7998
|
participantIds: z11.array(z11.number()).optional(),
|
|
8928
7999
|
participantNames: z11.array(z11.string()).optional()
|
|
8929
8000
|
});
|
|
8930
|
-
var
|
|
8001
|
+
var app24 = new Hono27().get("/:name/conversations", async (c) => {
|
|
8931
8002
|
const name = c.req.param("name");
|
|
8932
8003
|
const user = c.get("user");
|
|
8933
8004
|
let lookupId = user.id;
|
|
@@ -9035,12 +8106,12 @@ var app26 = new Hono29().get("/:name/conversations", async (c) => {
|
|
|
9035
8106
|
if (!deleted) return c.json({ error: "Conversation not found" }, 404);
|
|
9036
8107
|
return c.json({ ok: true });
|
|
9037
8108
|
});
|
|
9038
|
-
var conversations_default2 =
|
|
8109
|
+
var conversations_default2 = app24;
|
|
9039
8110
|
|
|
9040
|
-
// src/web/app.ts
|
|
8111
|
+
// packages/daemon/src/web/app.ts
|
|
9041
8112
|
var httpLog = logger_default.child("http");
|
|
9042
|
-
var
|
|
9043
|
-
|
|
8113
|
+
var app25 = new Hono28();
|
|
8114
|
+
app25.onError((err, c) => {
|
|
9044
8115
|
if (err instanceof HTTPException) {
|
|
9045
8116
|
return err.getResponse();
|
|
9046
8117
|
}
|
|
@@ -9051,10 +8122,10 @@ app27.onError((err, c) => {
|
|
|
9051
8122
|
});
|
|
9052
8123
|
return c.json({ error: "Internal server error" }, 500);
|
|
9053
8124
|
});
|
|
9054
|
-
|
|
8125
|
+
app25.notFound((c) => {
|
|
9055
8126
|
return c.json({ error: "Not found" }, 404);
|
|
9056
8127
|
});
|
|
9057
|
-
|
|
8128
|
+
app25.use("*", async (c, next) => {
|
|
9058
8129
|
const start = Date.now();
|
|
9059
8130
|
await next();
|
|
9060
8131
|
const duration = Date.now() - start;
|
|
@@ -9065,7 +8136,22 @@ app27.use("*", async (c, next) => {
|
|
|
9065
8136
|
httpLog.debug("request", data);
|
|
9066
8137
|
}
|
|
9067
8138
|
});
|
|
9068
|
-
|
|
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) => {
|
|
9069
8155
|
let version = "unknown";
|
|
9070
8156
|
let cached = null;
|
|
9071
8157
|
try {
|
|
@@ -9080,45 +8166,42 @@ app27.get("/api/health", (c) => {
|
|
|
9080
8166
|
...cached?.updateAvailable ? { updateAvailable: true, latest: cached.latest } : {}
|
|
9081
8167
|
});
|
|
9082
8168
|
});
|
|
9083
|
-
|
|
9084
|
-
|
|
9085
|
-
|
|
9086
|
-
|
|
9087
|
-
|
|
9088
|
-
|
|
9089
|
-
|
|
9090
|
-
|
|
9091
|
-
|
|
9092
|
-
|
|
9093
|
-
|
|
9094
|
-
|
|
9095
|
-
|
|
9096
|
-
|
|
9097
|
-
|
|
9098
|
-
|
|
9099
|
-
|
|
9100
|
-
|
|
9101
|
-
|
|
9102
|
-
|
|
9103
|
-
|
|
9104
|
-
|
|
9105
|
-
|
|
9106
|
-
|
|
9107
|
-
|
|
9108
|
-
|
|
9109
|
-
|
|
9110
|
-
|
|
9111
|
-
|
|
9112
|
-
|
|
9113
|
-
app27.route("/api/v1/env", sharedEnvApp);
|
|
9114
|
-
app27.route("/api/conversations", conversations_default);
|
|
9115
|
-
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;
|
|
9116
8199
|
|
|
9117
|
-
// src/web/server.ts
|
|
9118
|
-
import { existsSync as
|
|
9119
|
-
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";
|
|
9120
8203
|
import { createServer as createHttpsServer } from "https";
|
|
9121
|
-
import { dirname as dirname2, extname as
|
|
8204
|
+
import { dirname as dirname2, extname as extname3, resolve as resolve16 } from "path";
|
|
9122
8205
|
import { serve } from "@hono/node-server";
|
|
9123
8206
|
var MIME_TYPES2 = {
|
|
9124
8207
|
".html": "text/html",
|
|
@@ -9137,8 +8220,8 @@ async function startServer({
|
|
|
9137
8220
|
let assetsDir = "";
|
|
9138
8221
|
let searchDir = dirname2(new URL(import.meta.url).pathname);
|
|
9139
8222
|
for (let i = 0; i < 5; i++) {
|
|
9140
|
-
const candidate =
|
|
9141
|
-
if (
|
|
8223
|
+
const candidate = resolve16(searchDir, "dist", "web-assets");
|
|
8224
|
+
if (existsSync11(candidate)) {
|
|
9142
8225
|
assetsDir = candidate;
|
|
9143
8226
|
break;
|
|
9144
8227
|
}
|
|
@@ -9148,19 +8231,19 @@ async function startServer({
|
|
|
9148
8231
|
app_default.get("*", async (c) => {
|
|
9149
8232
|
const urlPath = new URL(c.req.url).pathname;
|
|
9150
8233
|
if (urlPath.startsWith("/api/") || urlPath.startsWith("/ext/")) return c.notFound();
|
|
9151
|
-
const filePath =
|
|
8234
|
+
const filePath = resolve16(assetsDir, urlPath.slice(1));
|
|
9152
8235
|
if (!filePath.startsWith(assetsDir)) return c.text("Forbidden", 403);
|
|
9153
|
-
const s = await
|
|
8236
|
+
const s = await stat2(filePath).catch(() => null);
|
|
9154
8237
|
if (s?.isFile()) {
|
|
9155
|
-
const ext =
|
|
8238
|
+
const ext = extname3(filePath);
|
|
9156
8239
|
const mime = MIME_TYPES2[ext] || "application/octet-stream";
|
|
9157
|
-
const body = await
|
|
8240
|
+
const body = await readFile2(filePath);
|
|
9158
8241
|
return c.body(body, 200, { "Content-Type": mime });
|
|
9159
8242
|
}
|
|
9160
|
-
const indexPath =
|
|
9161
|
-
const indexStat = await
|
|
8243
|
+
const indexPath = resolve16(assetsDir, "index.html");
|
|
8244
|
+
const indexStat = await stat2(indexPath).catch(() => null);
|
|
9162
8245
|
if (indexStat?.isFile()) {
|
|
9163
|
-
const body = await
|
|
8246
|
+
const body = await readFile2(indexPath, "utf-8");
|
|
9164
8247
|
return c.html(body);
|
|
9165
8248
|
}
|
|
9166
8249
|
return c.text("Not found", 404);
|
|
@@ -9174,10 +8257,10 @@ async function startServer({
|
|
|
9174
8257
|
createServer: createHttpsServer,
|
|
9175
8258
|
serverOptions: { key: tls.key, cert: tls.cert }
|
|
9176
8259
|
});
|
|
9177
|
-
await new Promise((
|
|
8260
|
+
await new Promise((resolve18, reject) => {
|
|
9178
8261
|
server2.on("listening", () => {
|
|
9179
8262
|
logger_default.info("Volute UI running (https)", { hostname, port });
|
|
9180
|
-
|
|
8263
|
+
resolve18();
|
|
9181
8264
|
});
|
|
9182
8265
|
server2.on("error", (err) => {
|
|
9183
8266
|
reject(err);
|
|
@@ -9185,13 +8268,13 @@ async function startServer({
|
|
|
9185
8268
|
});
|
|
9186
8269
|
const internalPort = port + 1;
|
|
9187
8270
|
const internalServer = serve({ fetch: app_default.fetch, port: internalPort, hostname: "127.0.0.1" });
|
|
9188
|
-
await new Promise((
|
|
8271
|
+
await new Promise((resolve18, reject) => {
|
|
9189
8272
|
internalServer.on("listening", () => {
|
|
9190
8273
|
logger_default.info("Volute API running (http, internal)", {
|
|
9191
8274
|
hostname: "127.0.0.1",
|
|
9192
8275
|
port: internalPort
|
|
9193
8276
|
});
|
|
9194
|
-
|
|
8277
|
+
resolve18();
|
|
9195
8278
|
});
|
|
9196
8279
|
internalServer.on("error", (err) => {
|
|
9197
8280
|
reject(err);
|
|
@@ -9200,10 +8283,10 @@ async function startServer({
|
|
|
9200
8283
|
return { server: server2, internalPort };
|
|
9201
8284
|
}
|
|
9202
8285
|
const server = serve({ fetch: app_default.fetch, port, hostname });
|
|
9203
|
-
await new Promise((
|
|
8286
|
+
await new Promise((resolve18, reject) => {
|
|
9204
8287
|
server.on("listening", () => {
|
|
9205
8288
|
logger_default.info("Volute API running (http)", { hostname, port });
|
|
9206
|
-
|
|
8289
|
+
resolve18();
|
|
9207
8290
|
});
|
|
9208
8291
|
server.on("error", (err) => {
|
|
9209
8292
|
reject(err);
|
|
@@ -9212,9 +8295,9 @@ async function startServer({
|
|
|
9212
8295
|
return { server };
|
|
9213
8296
|
}
|
|
9214
8297
|
|
|
9215
|
-
// src/daemon.ts
|
|
8298
|
+
// packages/daemon/src/daemon.ts
|
|
9216
8299
|
if (!process.env.VOLUTE_HOME) {
|
|
9217
|
-
process.env.VOLUTE_HOME =
|
|
8300
|
+
process.env.VOLUTE_HOME = resolve17(homedir3(), ".volute");
|
|
9218
8301
|
}
|
|
9219
8302
|
if (process.env.VOLUTE_TIMEZONE && !process.env.TZ) {
|
|
9220
8303
|
process.env.TZ = process.env.VOLUTE_TIMEZONE;
|
|
@@ -9225,7 +8308,7 @@ async function startDaemon(opts) {
|
|
|
9225
8308
|
const home = voluteHome();
|
|
9226
8309
|
const systemDir = voluteSystemDir();
|
|
9227
8310
|
if (!opts.foreground) {
|
|
9228
|
-
const rotatingLog = new RotatingLog(
|
|
8311
|
+
const rotatingLog = new RotatingLog(resolve17(systemDir, "daemon.log"));
|
|
9229
8312
|
logger_default.setOutput((line) => rotatingLog.write(`${line}
|
|
9230
8313
|
`));
|
|
9231
8314
|
const write = (...args) => rotatingLog.write(`${format(...args)}
|
|
@@ -9235,27 +8318,22 @@ async function startDaemon(opts) {
|
|
|
9235
8318
|
console.warn = write;
|
|
9236
8319
|
console.info = write;
|
|
9237
8320
|
}
|
|
9238
|
-
const DAEMON_PID_PATH =
|
|
9239
|
-
const DAEMON_JSON_PATH =
|
|
9240
|
-
|
|
8321
|
+
const DAEMON_PID_PATH = resolve17(systemDir, "daemon.pid");
|
|
8322
|
+
const DAEMON_JSON_PATH = resolve17(systemDir, "daemon.json");
|
|
8323
|
+
mkdirSync10(home, { recursive: true });
|
|
9241
8324
|
ensureSystemDir();
|
|
9242
|
-
|
|
9243
|
-
await ensureSharedRepo();
|
|
9244
|
-
} catch (err) {
|
|
9245
|
-
logger_default.warn("failed to initialize shared repo", logger_default.errorData(err));
|
|
9246
|
-
}
|
|
9247
|
-
const { migrateSetupCompleted } = await import("./setup-GGMKENLN.js");
|
|
8325
|
+
const { migrateSetupCompleted } = await import("./setup-RHJRFURI.js");
|
|
9248
8326
|
migrateSetupCompleted();
|
|
9249
|
-
await (await import("./db-
|
|
8327
|
+
await (await import("./db-BVBJ57TU.js")).getDb();
|
|
9250
8328
|
try {
|
|
9251
|
-
const { eq:
|
|
9252
|
-
const { users: users2 } = await import("./schema-
|
|
9253
|
-
const db = await (await import("./db-
|
|
9254
|
-
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")));
|
|
9255
8333
|
} catch (err) {
|
|
9256
8334
|
logger_default.warn("failed to migrate system user role", logger_default.errorData(err));
|
|
9257
8335
|
}
|
|
9258
|
-
const { initSandbox } = await import("./sandbox-
|
|
8336
|
+
const { initSandbox } = await import("./sandbox-R37VIU36.js");
|
|
9259
8337
|
await initSandbox();
|
|
9260
8338
|
try {
|
|
9261
8339
|
await syncBuiltinSkills();
|
|
@@ -9282,7 +8360,7 @@ async function startDaemon(opts) {
|
|
|
9282
8360
|
logger_default.warn("failed to ensure #system channel", logger_default.errorData(err));
|
|
9283
8361
|
}
|
|
9284
8362
|
try {
|
|
9285
|
-
const { getOrCreateSystemUser: getOrCreateSystemUser2 } = await import("./auth-
|
|
8363
|
+
const { getOrCreateSystemUser: getOrCreateSystemUser2 } = await import("./auth-WX4TESEI.js");
|
|
9286
8364
|
await getOrCreateSystemUser2();
|
|
9287
8365
|
} catch (err) {
|
|
9288
8366
|
logger_default.warn(
|
|
@@ -9293,7 +8371,7 @@ async function startDaemon(opts) {
|
|
|
9293
8371
|
const token = process.env.VOLUTE_DAEMON_TOKEN || randomBytes(32).toString("hex");
|
|
9294
8372
|
let tls;
|
|
9295
8373
|
if (opts.tailscale) {
|
|
9296
|
-
const { getTailscaleTls } = await import("./tailscale-
|
|
8374
|
+
const { getTailscaleTls } = await import("./tailscale-ZIZ2HWJ5.js");
|
|
9297
8375
|
const tlsConfig = await getTailscaleTls();
|
|
9298
8376
|
tls = { key: tlsConfig.key, cert: tlsConfig.cert };
|
|
9299
8377
|
logger_default.info("Tailscale HTTPS enabled", { hostname: tlsConfig.hostname });
|
|
@@ -9314,11 +8392,11 @@ async function startDaemon(opts) {
|
|
|
9314
8392
|
process.env.VOLUTE_DAEMON_TOKEN = token;
|
|
9315
8393
|
process.env.VOLUTE_DAEMON_PORT = String(daemonPort);
|
|
9316
8394
|
process.env.VOLUTE_DAEMON_HOSTNAME = hostname;
|
|
9317
|
-
|
|
8395
|
+
writeFileSync9(DAEMON_PID_PATH, myPid, { mode: 420 });
|
|
9318
8396
|
const daemonConfig = { port, hostname, token };
|
|
9319
8397
|
if (internalPort) daemonConfig.internalPort = internalPort;
|
|
9320
8398
|
if (tls) daemonConfig.tls = true;
|
|
9321
|
-
|
|
8399
|
+
writeFileSync9(DAEMON_JSON_PATH, `${JSON.stringify(daemonConfig, null, 2)}
|
|
9322
8400
|
`, { mode: 420 });
|
|
9323
8401
|
const delivery = initDeliveryManager();
|
|
9324
8402
|
const manager = initMindManager();
|
|
@@ -9335,7 +8413,8 @@ async function startDaemon(opts) {
|
|
|
9335
8413
|
const summarizer = initSummarizer();
|
|
9336
8414
|
summarizer.start();
|
|
9337
8415
|
const unsubscribeWebhook = initWebhook();
|
|
9338
|
-
await completeOrphanedTurns();
|
|
8416
|
+
const orphanedTurns = await completeOrphanedTurns();
|
|
8417
|
+
summarizeOrphanedTurns(orphanedTurns);
|
|
9339
8418
|
const allMinds = await readAllMinds();
|
|
9340
8419
|
const runningEntries = allMinds.filter((e) => e.running && e.mindType !== "spirit");
|
|
9341
8420
|
{
|
|
@@ -9365,10 +8444,10 @@ async function startDaemon(opts) {
|
|
|
9365
8444
|
await Promise.all(workers);
|
|
9366
8445
|
}
|
|
9367
8446
|
try {
|
|
9368
|
-
const { isSetupComplete: isSetupComplete2 } = await import("./setup-
|
|
8447
|
+
const { isSetupComplete: isSetupComplete2 } = await import("./setup-RHJRFURI.js");
|
|
9369
8448
|
if (isSetupComplete2()) {
|
|
9370
|
-
const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-
|
|
9371
|
-
const { startSpiritFull } = await import("./mind-service-
|
|
8449
|
+
const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-ZFRDXMG7.js");
|
|
8450
|
+
const { startSpiritFull } = await import("./mind-service-X2CAA6W6.js");
|
|
9372
8451
|
await ensureSpiritProject();
|
|
9373
8452
|
await syncSpiritTemplate();
|
|
9374
8453
|
const spiritEntry = await findMind("volute");
|
|
@@ -9385,7 +8464,7 @@ async function startDaemon(opts) {
|
|
|
9385
8464
|
bridgeManager.startBridges(daemonPort).catch((err) => {
|
|
9386
8465
|
logger_default.warn("failed to start bridges", logger_default.errorData(err));
|
|
9387
8466
|
});
|
|
9388
|
-
import("./cloud-sync-
|
|
8467
|
+
import("./cloud-sync-6JL4C24T.js").then(
|
|
9389
8468
|
({ consumeQueuedMessages }) => consumeQueuedMessages().catch((err) => {
|
|
9390
8469
|
logger_default.warn("failed to consume queued cloud messages", logger_default.errorData(err));
|
|
9391
8470
|
})
|
|
@@ -9393,7 +8472,7 @@ async function startDaemon(opts) {
|
|
|
9393
8472
|
logger_default.warn("failed to load cloud-sync module", logger_default.errorData(err));
|
|
9394
8473
|
});
|
|
9395
8474
|
try {
|
|
9396
|
-
const { backfillTemplateHashes, notifyVersionUpdate } = await import("./version-notify-
|
|
8475
|
+
const { backfillTemplateHashes, notifyVersionUpdate } = await import("./version-notify-FXSEMXWW.js");
|
|
9397
8476
|
backfillTemplateHashes();
|
|
9398
8477
|
notifyVersionUpdate().catch((err) => {
|
|
9399
8478
|
logger_default.warn("failed to send version update notifications", logger_default.errorData(err));
|
|
@@ -9414,13 +8493,13 @@ async function startDaemon(opts) {
|
|
|
9414
8493
|
logger_default.info(`running on ${hostname}:${port}, pid ${myPid}`);
|
|
9415
8494
|
function cleanup() {
|
|
9416
8495
|
try {
|
|
9417
|
-
if (
|
|
8496
|
+
if (readFileSync11(DAEMON_PID_PATH, "utf-8").trim() === myPid) {
|
|
9418
8497
|
unlinkSync2(DAEMON_PID_PATH);
|
|
9419
8498
|
}
|
|
9420
8499
|
} catch {
|
|
9421
8500
|
}
|
|
9422
8501
|
try {
|
|
9423
|
-
const data = JSON.parse(
|
|
8502
|
+
const data = JSON.parse(readFileSync11(DAEMON_JSON_PATH, "utf-8"));
|
|
9424
8503
|
if (data.token === token) {
|
|
9425
8504
|
unlinkSync2(DAEMON_JSON_PATH);
|
|
9426
8505
|
}
|