volute 0.28.0 → 0.30.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 +127 -18
- package/dist/{accept-666DIZX2.js → accept-E3PAH3QJ.js} +2 -2
- package/dist/{activity-events-BBIEA2F4.js → activity-events-BKBPPUBP.js} +2 -2
- package/dist/ai-service-VAJT5UBS.js +29 -0
- package/dist/api.d.ts +586 -529
- package/dist/{archive-UA4BDFXQ.js → archive-WWDBWYN2.js} +2 -2
- package/dist/{bridge-FQHZL3MC.js → bridge-RO37CUFM.js} +2 -2
- package/dist/{chat-M4SX42JD.js → chat-TCUNPFGO.js} +8 -8
- package/dist/{chunk-IAYBDWVG.js → chunk-2C2VXEBB.js} +147 -2
- package/dist/chunk-2NDZC3S7.js +1330 -0
- package/dist/{chunk-IKRVFPWU.js → chunk-7D47T4RB.js} +3 -2
- package/dist/chunk-A6TUJJ3L.js +19 -0
- package/dist/{chunk-AW7PFDVN.js → chunk-CVH6Y2YG.js} +1 -1
- package/dist/{chunk-XBLSAVJF.js → chunk-DTC6EH5I.js} +1 -1
- package/dist/chunk-EFP3PE6C.js +232 -0
- package/dist/{chunk-JGFVMROS.js → chunk-EFVHR7KH.js} +1 -1
- package/dist/{chunk-K5NAC55T.js → chunk-FSM45XD5.js} +2 -2
- package/dist/{chunk-LAC664WU.js → chunk-FXHXHI2A.js} +42 -24
- package/dist/{chunk-RKQEHRBB.js → chunk-G3GBKZGG.js} +1 -1
- package/dist/{chunk-H7OZRFJB.js → chunk-HHTXM4JT.js} +0 -49
- package/dist/{chunk-J4IBNXGJ.js → chunk-IKHDUZRH.js} +4 -3
- package/dist/{chunk-MD4C26II.js → chunk-JGFRDMR6.js} +1 -1
- package/dist/{chunk-POSXWWTA.js → chunk-LIRWLNAK.js} +26 -12
- package/dist/{chunk-NI5FFCCS.js → chunk-MDPCSXZ4.js} +35 -11
- package/dist/chunk-NSBFETWP.js +188 -0
- package/dist/{chunk-VIVMW2H2.js → chunk-P27RV5WM.js} +1 -1
- package/dist/{chunk-EHYDTZTF.js → chunk-P7VFDSSG.js} +2 -2
- package/dist/{chunk-AAPXKR5V.js → chunk-QVAQ5454.js} +181 -544
- package/dist/{chunk-HDN7MNGD.js → chunk-S5LR3XYJ.js} +1 -1
- package/dist/{chunk-2YP2TVDT.js → chunk-UPA6COHU.js} +5 -5
- package/dist/{chunk-AKPFNL7L.js → chunk-VGWJSNHS.js} +1 -1
- package/dist/{chunk-SGVNFZHW.js → chunk-W5OOPLNP.js} +3 -3
- package/dist/{chunk-2WPW7OT6.js → chunk-ZWKTUQEL.js} +1 -1
- package/dist/cli.js +25 -26
- package/dist/clock-G3ALCMLJ.js +263 -0
- package/dist/{cloud-sync-HDL6PHZI.js → cloud-sync-JV4LJOK3.js} +14 -12
- package/dist/connectors/discord-bridge.js +1 -1
- package/dist/connectors/slack-bridge.js +1 -1
- package/dist/connectors/telegram-bridge.js +1 -1
- package/dist/{conversations-M2K4253F.js → conversations-7KVQV7EZ.js} +9 -3
- package/dist/create-JTLS7GX3.js +70 -0
- package/dist/{create-QWV73WXD.js → create-VQSQHJQW.js} +1 -1
- package/dist/{daemon-client-I42FK2BF.js → daemon-client-BCTFGVCZ.js} +2 -2
- package/dist/{daemon-restart-G4B2OYAB.js → daemon-restart-4JGBHEJ4.js} +7 -7
- package/dist/daemon.js +1474 -1124
- package/dist/{db-IC4J52XQ.js → db-HMFPIRO2.js} +1 -1
- package/dist/{delete-4JYGD4VN.js → delete-JESHKE7F.js} +1 -1
- package/dist/down-NGBMGORS.js +14 -0
- package/dist/{env-YJMUMFIY.js → env-CLXXT7M2.js} +2 -2
- package/dist/{export-BOJQWBMA.js → export-EGA5M5PB.js} +3 -3
- package/dist/extension-WZ4SUPJB.js +174 -0
- package/dist/extensions-ECO4RPFQ.js +27 -0
- package/dist/{files-M546TKVN.js → files-4VEJDASH.js} +3 -3
- package/dist/{history-ALPTNB3I.js → history-EJMMLXDO.js} +17 -2
- package/dist/{import-SRTQXBGH.js → import-YCGPMBSI.js} +3 -3
- package/dist/{join-J4QU42DL.js → join-2GBJKZEN.js} +1 -1
- package/dist/{list-R73GENNL.js → list-Q6O7FGAN.js} +2 -2
- package/dist/{login-3QZNR2DF.js → login-RET5WESK.js} +2 -2
- package/dist/{login-BKP3AFWN.js → login-RL6AU2SM.js} +3 -3
- package/dist/{logout-T53VKCPU.js → logout-CGAGJN3L.js} +2 -2
- package/dist/{logout-IQK7FNEK.js → logout-JRPBEMMR.js} +3 -3
- package/dist/message-delivery-6YMVNOEC.js +28 -0
- package/dist/{migrate-registry-to-db-XC7T5B7P.js → migrate-registry-to-db-FK35IPEH.js} +1 -1
- package/dist/{mind-S5V6CK5W.js → mind-LUWRQUQ5.js} +17 -17
- package/dist/{mind-activity-tracker-EN6XNXPF.js → mind-activity-tracker-VYN2ZZ2M.js} +3 -3
- package/dist/{mind-list-UPJ75GPI.js → mind-list-V5WW5DUA.js} +2 -2
- package/dist/{mind-manager-S6ILZVX3.js → mind-manager-YFCOIAAX.js} +6 -6
- package/dist/{mind-sleep-BTSWQNAC.js → mind-sleep-R6PTNNW4.js} +2 -2
- package/dist/{mind-status-TK5AETEM.js → mind-status-I4ISFJ6I.js} +2 -2
- package/dist/{mind-wake-SBAKIDVP.js → mind-wake-67ZQEWAV.js} +2 -2
- package/dist/{package-CG4RWUGP.js → package-S2OAA5ZA.js} +11 -5
- package/dist/pages-watcher-Z3PKNROC.js +21 -0
- package/dist/{read-36UFXN3G.js → read-WQMPTSN2.js} +2 -2
- package/dist/{register-CHREOMJ3.js → register-NZDSTLP3.js} +3 -3
- package/dist/{registry-NDNOOYG4.js → registry-ODSALQQL.js} +1 -1
- package/dist/{reject-LXIZFJ4Q.js → reject-2HZOJEIJ.js} +2 -2
- package/dist/{restart-6ESL3NBO.js → restart-QHS3NT64.js} +2 -2
- package/dist/{sandbox-5BW5HPXM.js → sandbox-O5FUSF43.js} +3 -3
- package/dist/{seed-SSUCYYDF.js → seed-WUQMPLDM.js} +1 -1
- package/dist/{send-TAOEZ4NH.js → send-OAN3RYYY.js} +20 -6
- package/dist/{setup-JHL5ZEST.js → setup-QMDK5RZX.js} +2 -2
- package/dist/{setup-RXYVGGT7.js → setup-XJH3E7YM.js} +45 -14
- package/dist/{skill-AUAQTSP5.js → skill-FZIN4W4Q.js} +65 -3
- package/dist/skills/dreaming/references/INSTALL.md +3 -17
- package/dist/skills/volute-mind/SKILL.md +45 -27
- package/dist/sleep-manager-O7YQFCV5.js +30 -0
- package/dist/{split-TKJ5OT3P.js → split-EXYGGGQN.js} +1 -1
- package/dist/{sprout-UNT7LKKE.js → sprout-AXQ6H5DB.js} +8 -7
- package/dist/{start-EUJSS5R4.js → start-MTOVL6SY.js} +2 -2
- package/dist/{status-NQJYR4BG.js → status-ZRO37MWR.js} +5 -5
- package/dist/{stop-3XAITBBF.js → stop-OK5WEPVC.js} +2 -2
- package/dist/{systems-SMEFSHTA.js → systems-W3BBMSOZ.js} +5 -5
- package/dist/{tailscale-NY5MUMY3.js → tailscale-BM72RXCJ.js} +1 -1
- package/dist/{template-hash-BIMA4ILT.js → template-hash-3HOR4UAJ.js} +1 -1
- package/dist/up-BXUAIDXB.js +17 -0
- package/dist/{update-PTSH22AZ.js → update-PLPHMMZ2.js} +5 -5
- package/dist/{update-check-64FWC4Y2.js → update-check-CVCN7MF6.js} +2 -2
- package/dist/{upgrade-HA47CS4C.js → upgrade-I6NPCYUU.js} +1 -1
- package/dist/{version-notify-JDUF4HQJ.js → version-notify-2NTWVEHL.js} +18 -16
- package/dist/web-assets/assets/index--kREqKl9.js +72 -0
- package/dist/web-assets/assets/index-BXYTG0nJ.css +1 -0
- package/dist/web-assets/ext-theme.css +111 -0
- package/dist/web-assets/index.html +2 -2
- package/package.json +11 -5
- package/packages/extensions/notes/dist/ui/assets/index-DgawVO5g.css +1 -0
- package/packages/extensions/notes/dist/ui/assets/index-qUWoeC4c.js +2 -0
- package/packages/extensions/notes/dist/ui/index.html +14 -0
- package/packages/extensions/notes/skills/notes/SKILL.md +62 -0
- package/packages/extensions/notes/skills/notes/scripts/notes.mjs +185 -0
- package/packages/extensions/pages/dist/ui/assets/index-D0HyS-xQ.css +1 -0
- package/packages/extensions/pages/dist/ui/assets/index-tLTROSk5.js +2 -0
- package/packages/extensions/pages/dist/ui/index.html +14 -0
- package/packages/extensions/pages/skills/pages/SKILL.md +58 -0
- package/templates/_base/home/VOLUTE.md +1 -1
- package/templates/_base/src/lib/logger.ts +10 -49
- package/templates/_base/src/lib/router.ts +1 -9
- package/templates/claude/src/lib/stream-consumer.ts +1 -4
- package/templates/pi/src/lib/event-handler.ts +1 -14
- package/dist/chunk-P72MVS4R.js +0 -188
- package/dist/chunk-T6HKBWXZ.js +0 -23
- package/dist/chunk-ZYGKG6VC.js +0 -22
- package/dist/create-D7J73A6H.js +0 -45
- package/dist/down-LVBXEULC.js +0 -14
- package/dist/message-delivery-HV3S6HZV.js +0 -24
- package/dist/notes-XCER3I7M.js +0 -220
- package/dist/pages-KJDJX4TA.js +0 -36
- package/dist/publish-ZZB33WP4.js +0 -86
- package/dist/schedule-QTJMFATP.js +0 -154
- package/dist/skills/notes/SKILL.md +0 -34
- package/dist/sleep-manager-WMVG2VCL.js +0 -28
- package/dist/status-S7UUPNRW.js +0 -38
- package/dist/up-GM2JOH2Y.js +0 -17
- package/dist/web-assets/assets/index-BZGvToHi.css +0 -1
- package/dist/web-assets/assets/index-Cz4TrpzB.js +0 -75
package/dist/daemon.js
CHANGED
|
@@ -1,72 +1,51 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
addSharedWorktree,
|
|
4
|
-
ensureSharedRepo,
|
|
5
|
-
removeSharedWorktree,
|
|
6
|
-
sharedMerge
|
|
7
|
-
} from "./chunk-P72MVS4R.js";
|
|
8
|
-
import {
|
|
9
|
-
checkForUpdate,
|
|
10
|
-
checkForUpdateCached,
|
|
11
|
-
getCurrentVersion
|
|
12
|
-
} from "./chunk-HDN7MNGD.js";
|
|
13
|
-
import {
|
|
14
|
-
applyInitFiles,
|
|
15
|
-
composeTemplate,
|
|
16
|
-
computeTemplateHash,
|
|
17
|
-
copyTemplateToDir,
|
|
18
|
-
findTemplatesRoot,
|
|
19
|
-
listFiles
|
|
20
|
-
} from "./chunk-AKPFNL7L.js";
|
|
21
2
|
import {
|
|
22
3
|
announceToSystem,
|
|
23
|
-
approveUser,
|
|
24
|
-
changePassword,
|
|
25
|
-
countAdmins,
|
|
26
|
-
createUser,
|
|
27
|
-
deleteMindUser as deleteMindUser2,
|
|
28
|
-
deleteSystemsConfig,
|
|
29
|
-
deleteUser,
|
|
30
4
|
deliverMessage,
|
|
31
5
|
ensureSystemChannel,
|
|
32
6
|
extractTextContent,
|
|
33
|
-
getCachedRecentPages,
|
|
34
|
-
getCachedSites,
|
|
35
7
|
getDeliveryManager,
|
|
36
|
-
getOrCreateMindUser,
|
|
37
8
|
getScheduler,
|
|
9
|
+
getSleepManagerIfReady,
|
|
38
10
|
getTokenBudget,
|
|
39
11
|
getTypingMap,
|
|
40
|
-
getUser,
|
|
41
|
-
getUserByUsername,
|
|
42
12
|
initDeliveryManager,
|
|
43
13
|
initMailPoller,
|
|
44
14
|
initScheduler,
|
|
45
15
|
initSleepManager,
|
|
46
16
|
initTokenBudget,
|
|
47
17
|
joinSystemChannel,
|
|
48
|
-
listPendingUsers,
|
|
49
|
-
listUsers,
|
|
50
|
-
listUsersByType,
|
|
51
|
-
migrateMindRoles,
|
|
52
18
|
publish as publish2,
|
|
53
19
|
publishTypingForChannels,
|
|
54
|
-
readSystemsConfig,
|
|
55
20
|
readVoluteConfig,
|
|
56
21
|
recordInbound,
|
|
57
22
|
resolveChannelId,
|
|
58
|
-
setUserRole,
|
|
59
23
|
splitMessage,
|
|
60
24
|
startMindFull,
|
|
61
|
-
stopAllWatchers,
|
|
62
25
|
stopMindFull,
|
|
63
26
|
subscribe as subscribe3,
|
|
64
|
-
updateUserProfile,
|
|
65
|
-
verifyUser,
|
|
66
27
|
writeChannelEntry,
|
|
67
|
-
writeSystemsConfig,
|
|
68
28
|
writeVoluteConfig
|
|
69
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-QVAQ5454.js";
|
|
30
|
+
import {
|
|
31
|
+
getActiveMinds,
|
|
32
|
+
onMindEvent,
|
|
33
|
+
stopAll
|
|
34
|
+
} from "./chunk-FSM45XD5.js";
|
|
35
|
+
import {
|
|
36
|
+
PROMPT_DEFAULTS,
|
|
37
|
+
PROMPT_KEYS,
|
|
38
|
+
RestartTracker,
|
|
39
|
+
RotatingLog,
|
|
40
|
+
getMindManager,
|
|
41
|
+
getMindPromptDefaults,
|
|
42
|
+
getPrompt,
|
|
43
|
+
getPromptIfCustom,
|
|
44
|
+
initMindManager,
|
|
45
|
+
resolveMindToken,
|
|
46
|
+
substitute
|
|
47
|
+
} from "./chunk-LIRWLNAK.js";
|
|
48
|
+
import "./chunk-IKHDUZRH.js";
|
|
70
49
|
import {
|
|
71
50
|
addMessage,
|
|
72
51
|
createChannel,
|
|
@@ -81,45 +60,81 @@ import {
|
|
|
81
60
|
getParticipants,
|
|
82
61
|
getUnreadCounts,
|
|
83
62
|
initWebhook,
|
|
63
|
+
isConversationForMind,
|
|
84
64
|
isParticipant,
|
|
85
65
|
isParticipantOrOwner,
|
|
86
66
|
joinChannel,
|
|
87
67
|
leaveChannel,
|
|
88
68
|
listChannels,
|
|
69
|
+
listConversationsForMind,
|
|
89
70
|
listConversationsForUser,
|
|
90
71
|
listConversationsWithParticipants,
|
|
91
72
|
markConversationRead,
|
|
92
73
|
publish,
|
|
93
74
|
subscribe as subscribe2
|
|
94
|
-
} from "./chunk-
|
|
75
|
+
} from "./chunk-2C2VXEBB.js";
|
|
95
76
|
import {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
} from "./chunk-
|
|
77
|
+
checkForUpdate,
|
|
78
|
+
checkForUpdateCached,
|
|
79
|
+
getCurrentVersion
|
|
80
|
+
} from "./chunk-S5LR3XYJ.js";
|
|
81
|
+
import {
|
|
82
|
+
applyInitFiles,
|
|
83
|
+
composeTemplate,
|
|
84
|
+
computeTemplateHash,
|
|
85
|
+
copyTemplateToDir,
|
|
86
|
+
findTemplatesRoot,
|
|
87
|
+
listFiles
|
|
88
|
+
} from "./chunk-VGWJSNHS.js";
|
|
89
|
+
import {
|
|
90
|
+
acceptPending,
|
|
91
|
+
formatFileSize,
|
|
92
|
+
listPending,
|
|
93
|
+
rejectPending,
|
|
94
|
+
stageFile,
|
|
95
|
+
validateFilePath
|
|
96
|
+
} from "./chunk-JGFRDMR6.js";
|
|
97
|
+
import {
|
|
98
|
+
getCachedRecentPages,
|
|
99
|
+
getCachedSites
|
|
100
|
+
} from "./chunk-EFP3PE6C.js";
|
|
101
|
+
import {
|
|
102
|
+
approveUser,
|
|
103
|
+
changePassword,
|
|
104
|
+
countAdmins,
|
|
105
|
+
createUser,
|
|
106
|
+
deleteMindUser as deleteMindUser2,
|
|
107
|
+
deleteSystemsConfig,
|
|
108
|
+
deleteUser,
|
|
109
|
+
getExtensionStandardSkills,
|
|
110
|
+
getLoadedExtensions,
|
|
111
|
+
getOrCreateMindUser,
|
|
112
|
+
getUser,
|
|
113
|
+
getUserByUsername,
|
|
114
|
+
listPendingUsers,
|
|
115
|
+
listUsers,
|
|
116
|
+
listUsersByType,
|
|
117
|
+
loadAllExtensions,
|
|
118
|
+
migrateMindRoles,
|
|
119
|
+
notifyExtensionsDaemonStart,
|
|
120
|
+
notifyExtensionsDaemonStop,
|
|
121
|
+
readSystemsConfig,
|
|
122
|
+
setUserRole,
|
|
123
|
+
updateUserProfile,
|
|
124
|
+
verifyUser,
|
|
125
|
+
writeSystemsConfig
|
|
126
|
+
} from "./chunk-2NDZC3S7.js";
|
|
100
127
|
import {
|
|
101
128
|
broadcast,
|
|
102
129
|
subscribe
|
|
103
|
-
} from "./chunk-
|
|
104
|
-
import {
|
|
105
|
-
PROMPT_DEFAULTS,
|
|
106
|
-
PROMPT_KEYS,
|
|
107
|
-
RestartTracker,
|
|
108
|
-
RotatingLog,
|
|
109
|
-
getMindManager,
|
|
110
|
-
getMindPromptDefaults,
|
|
111
|
-
getPrompt,
|
|
112
|
-
getPromptIfCustom,
|
|
113
|
-
initMindManager,
|
|
114
|
-
resolveMindToken,
|
|
115
|
-
substitute
|
|
116
|
-
} from "./chunk-POSXWWTA.js";
|
|
117
|
-
import "./chunk-J4IBNXGJ.js";
|
|
130
|
+
} from "./chunk-P27RV5WM.js";
|
|
118
131
|
import {
|
|
119
132
|
SEED_SKILLS,
|
|
120
133
|
STANDARD_SKILLS,
|
|
121
134
|
getSharedSkill,
|
|
135
|
+
getStandardSkillsWithExtensions,
|
|
122
136
|
importSkillFromDir,
|
|
137
|
+
initDefaultSkills,
|
|
123
138
|
installSkill,
|
|
124
139
|
listFilesRecursive,
|
|
125
140
|
listMindSkills,
|
|
@@ -130,15 +145,10 @@ import {
|
|
|
130
145
|
syncBuiltinSkills,
|
|
131
146
|
uninstallSkill,
|
|
132
147
|
updateSkill
|
|
133
|
-
} from "./chunk-
|
|
148
|
+
} from "./chunk-MDPCSXZ4.js";
|
|
134
149
|
import {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
listPending,
|
|
138
|
-
rejectPending,
|
|
139
|
-
stageFile,
|
|
140
|
-
validateFilePath
|
|
141
|
-
} from "./chunk-MD4C26II.js";
|
|
150
|
+
isHomeOnlyArchive
|
|
151
|
+
} from "./chunk-DTC6EH5I.js";
|
|
142
152
|
import {
|
|
143
153
|
findBridgeForChannel,
|
|
144
154
|
findOpenClawSession,
|
|
@@ -152,40 +162,51 @@ import {
|
|
|
152
162
|
resolveChannelMapping,
|
|
153
163
|
setBridgeConfig,
|
|
154
164
|
setChannelMapping
|
|
155
|
-
} from "./chunk-
|
|
165
|
+
} from "./chunk-UPA6COHU.js";
|
|
156
166
|
import {
|
|
157
167
|
loadMergedEnv,
|
|
158
168
|
mindEnvPath,
|
|
159
169
|
readEnv,
|
|
160
170
|
sharedEnvPath,
|
|
161
171
|
writeEnv
|
|
162
|
-
} from "./chunk-
|
|
172
|
+
} from "./chunk-ZWKTUQEL.js";
|
|
173
|
+
import {
|
|
174
|
+
aiComplete,
|
|
175
|
+
getAiConfig,
|
|
176
|
+
getAvailableModels,
|
|
177
|
+
getEnabledModels,
|
|
178
|
+
removeAiConfig,
|
|
179
|
+
removeProviderConfig,
|
|
180
|
+
saveProviderConfig,
|
|
181
|
+
setEnabledModels
|
|
182
|
+
} from "./chunk-NSBFETWP.js";
|
|
163
183
|
import {
|
|
164
184
|
logBuffer,
|
|
165
185
|
logger_default
|
|
166
186
|
} from "./chunk-YUIHSKR6.js";
|
|
167
|
-
import {
|
|
168
|
-
isHomeOnlyArchive
|
|
169
|
-
} from "./chunk-XBLSAVJF.js";
|
|
170
187
|
import {
|
|
171
188
|
exec,
|
|
172
189
|
gitExec,
|
|
173
190
|
resolveVoluteBin
|
|
174
|
-
} from "./chunk-
|
|
191
|
+
} from "./chunk-CVH6Y2YG.js";
|
|
175
192
|
import {
|
|
176
193
|
chownMindDir,
|
|
177
194
|
createMindUser,
|
|
178
195
|
deleteMindUser,
|
|
179
196
|
ensureVoluteGroup,
|
|
180
197
|
isIsolationEnabled,
|
|
198
|
+
mindUserName,
|
|
181
199
|
wrapForIsolation
|
|
182
|
-
} from "./chunk-
|
|
183
|
-
import
|
|
200
|
+
} from "./chunk-G3GBKZGG.js";
|
|
201
|
+
import {
|
|
202
|
+
readGlobalConfig,
|
|
203
|
+
writeGlobalConfig
|
|
204
|
+
} from "./chunk-7D47T4RB.js";
|
|
184
205
|
import "./chunk-D424ZQGI.js";
|
|
185
206
|
import {
|
|
186
207
|
buildVoluteSlug,
|
|
187
208
|
slugify
|
|
188
|
-
} from "./chunk-
|
|
209
|
+
} from "./chunk-A6TUJJ3L.js";
|
|
189
210
|
import {
|
|
190
211
|
activity,
|
|
191
212
|
addMind,
|
|
@@ -201,9 +222,6 @@ import {
|
|
|
201
222
|
mindDir,
|
|
202
223
|
mindHistory,
|
|
203
224
|
nextPort,
|
|
204
|
-
noteComments,
|
|
205
|
-
noteReactions,
|
|
206
|
-
notes,
|
|
207
225
|
readAllMinds,
|
|
208
226
|
readRegistry,
|
|
209
227
|
removeMind,
|
|
@@ -217,14 +235,14 @@ import {
|
|
|
217
235
|
validateMindName,
|
|
218
236
|
voluteHome,
|
|
219
237
|
voluteSystemDir
|
|
220
|
-
} from "./chunk-
|
|
238
|
+
} from "./chunk-HHTXM4JT.js";
|
|
221
239
|
import {
|
|
222
240
|
__export
|
|
223
241
|
} from "./chunk-K3NQKI34.js";
|
|
224
242
|
|
|
225
243
|
// src/daemon.ts
|
|
226
244
|
import { randomBytes } from "crypto";
|
|
227
|
-
import { mkdirSync as
|
|
245
|
+
import { mkdirSync as mkdirSync12, readFileSync as readFileSync14, unlinkSync as unlinkSync2, writeFileSync as writeFileSync11 } from "fs";
|
|
228
246
|
import { homedir as homedir2 } from "os";
|
|
229
247
|
import { resolve as resolve22 } from "path";
|
|
230
248
|
import { format } from "util";
|
|
@@ -813,136 +831,174 @@ function migrateToSystemDir() {
|
|
|
813
831
|
}
|
|
814
832
|
}
|
|
815
833
|
|
|
816
|
-
// src/
|
|
817
|
-
import {
|
|
818
|
-
import {
|
|
819
|
-
import {
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
sessionCache.delete(sessionId);
|
|
831
|
-
}
|
|
832
|
-
async function createSession(userId) {
|
|
833
|
-
const db = await getDb();
|
|
834
|
-
const sessionId = crypto.randomUUID();
|
|
835
|
-
await db.insert(sessions).values({ id: sessionId, userId, createdAt: Date.now() });
|
|
836
|
-
return sessionId;
|
|
834
|
+
// src/lib/shared.ts
|
|
835
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
836
|
+
import { chmodSync, existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync4, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
837
|
+
import { resolve as resolve6 } from "path";
|
|
838
|
+
function readWorktreeGitDir(worktreePath) {
|
|
839
|
+
const dotGit = resolve6(worktreePath, ".git");
|
|
840
|
+
if (!existsSync6(dotGit)) return null;
|
|
841
|
+
try {
|
|
842
|
+
const content = readFileSync4(dotGit, "utf-8").trim();
|
|
843
|
+
const match = content.match(/^gitdir:\s*(.+)$/);
|
|
844
|
+
return match ? match[1] : null;
|
|
845
|
+
} catch {
|
|
846
|
+
return null;
|
|
847
|
+
}
|
|
837
848
|
}
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
const db = await getDb();
|
|
841
|
-
await db.delete(sessions).where(eq2(sessions.id, sessionId));
|
|
849
|
+
function sharedDir() {
|
|
850
|
+
return resolve6(voluteHome(), "shared");
|
|
842
851
|
}
|
|
843
|
-
async function
|
|
844
|
-
const
|
|
845
|
-
|
|
846
|
-
if (
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
852
|
+
async function ensureSharedRepo() {
|
|
853
|
+
const dir = sharedDir();
|
|
854
|
+
mkdirSync4(dir, { recursive: true });
|
|
855
|
+
if (existsSync6(resolve6(dir, ".git"))) {
|
|
856
|
+
try {
|
|
857
|
+
await gitExec(["rev-parse", "HEAD"], { cwd: dir });
|
|
858
|
+
return;
|
|
859
|
+
} catch (err) {
|
|
860
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
861
|
+
if (msg.includes("unknown revision") || msg.includes("bad default revision")) {
|
|
862
|
+
logger_default.warn("shared repo has no commits, re-initializing");
|
|
863
|
+
rmSync2(resolve6(dir, ".git"), { recursive: true, force: true });
|
|
864
|
+
} else {
|
|
865
|
+
throw err;
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
const initArgs = isIsolationEnabled() ? ["init", "--shared=group"] : ["init"];
|
|
870
|
+
await gitExec(initArgs, { cwd: dir });
|
|
871
|
+
await gitExec(["checkout", "-b", "main"], { cwd: dir });
|
|
872
|
+
const pagesDir = resolve6(dir, "pages");
|
|
873
|
+
mkdirSync4(pagesDir, { recursive: true });
|
|
874
|
+
writeFileSync3(resolve6(pagesDir, ".gitkeep"), "");
|
|
875
|
+
await gitExec(["add", "-A"], { cwd: dir });
|
|
876
|
+
await gitExec(["commit", "-m", "init shared repo"], { cwd: dir });
|
|
877
|
+
if (isIsolationEnabled()) {
|
|
878
|
+
try {
|
|
879
|
+
execFileSync2("chgrp", ["-R", "volute", dir], { stdio: "ignore" });
|
|
880
|
+
} catch (err) {
|
|
881
|
+
logger_default.warn("failed to chgrp shared repo to volute group", logger_default.errorData(err));
|
|
882
|
+
}
|
|
883
|
+
chmodSync(dir, 1533);
|
|
850
884
|
}
|
|
851
|
-
return row.userId;
|
|
852
885
|
}
|
|
853
|
-
async function
|
|
854
|
-
const
|
|
855
|
-
|
|
856
|
-
|
|
886
|
+
async function addSharedWorktree(mindName, mindDir2) {
|
|
887
|
+
const dir = sharedDir();
|
|
888
|
+
if (!existsSync6(resolve6(dir, ".git"))) return;
|
|
889
|
+
const worktreePath = resolve6(mindDir2, "home", "shared");
|
|
890
|
+
if (existsSync6(worktreePath)) return;
|
|
891
|
+
let branchExists = false;
|
|
892
|
+
try {
|
|
893
|
+
await gitExec(["rev-parse", "--verify", mindName], { cwd: dir });
|
|
894
|
+
branchExists = true;
|
|
895
|
+
} catch {
|
|
896
|
+
}
|
|
897
|
+
if (branchExists) {
|
|
898
|
+
await gitExec(["worktree", "add", worktreePath, mindName], { cwd: dir });
|
|
899
|
+
} else {
|
|
900
|
+
await gitExec(["worktree", "add", "-b", mindName, worktreePath], { cwd: dir });
|
|
901
|
+
}
|
|
902
|
+
if (isIsolationEnabled()) {
|
|
903
|
+
const worktreeGitDir = readWorktreeGitDir(worktreePath);
|
|
904
|
+
if (worktreeGitDir) {
|
|
905
|
+
try {
|
|
906
|
+
const user = mindUserName(mindName);
|
|
907
|
+
execFileSync2("chown", ["-R", `${user}:volute`, worktreeGitDir], { stdio: "ignore" });
|
|
908
|
+
} catch (err) {
|
|
909
|
+
logger_default.warn(`failed to chown worktree git dir for ${mindName}`, logger_default.errorData(err));
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
}
|
|
857
913
|
}
|
|
858
|
-
|
|
859
|
-
const
|
|
860
|
-
if (
|
|
861
|
-
|
|
914
|
+
async function removeSharedWorktree(mindName, mindDir2) {
|
|
915
|
+
const dir = sharedDir();
|
|
916
|
+
if (!existsSync6(resolve6(dir, ".git"))) return;
|
|
917
|
+
const worktreePath = resolve6(mindDir2, "home", "shared");
|
|
918
|
+
if (existsSync6(worktreePath)) {
|
|
919
|
+
try {
|
|
920
|
+
await gitExec(["worktree", "remove", "--force", worktreePath], { cwd: dir });
|
|
921
|
+
} catch (err) {
|
|
922
|
+
logger_default.debug(`worktree remove failed for ${mindName}`, logger_default.errorData(err));
|
|
923
|
+
}
|
|
862
924
|
}
|
|
863
|
-
|
|
864
|
-
});
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
if (cached && cached.expires > Date.now()) {
|
|
868
|
-
return cached.user;
|
|
925
|
+
try {
|
|
926
|
+
await gitExec(["worktree", "prune"], { cwd: dir });
|
|
927
|
+
} catch (err) {
|
|
928
|
+
logger_default.debug(`worktree prune failed for ${mindName}`, logger_default.errorData(err));
|
|
869
929
|
}
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
return null;
|
|
930
|
+
try {
|
|
931
|
+
await gitExec(["branch", "-D", mindName], { cwd: dir });
|
|
932
|
+
} catch {
|
|
874
933
|
}
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
934
|
+
}
|
|
935
|
+
var sharedLock = Promise.resolve();
|
|
936
|
+
function rechownWorktree(worktreePath, mindName) {
|
|
937
|
+
if (!isIsolationEnabled()) return;
|
|
938
|
+
try {
|
|
939
|
+
const user = mindUserName(mindName);
|
|
940
|
+
execFileSync2("chown", ["-R", `${user}:volute`, worktreePath], { stdio: "ignore" });
|
|
941
|
+
} catch (err) {
|
|
942
|
+
logger_default.warn(`failed to rechown worktree for ${mindName}`, logger_default.errorData(err));
|
|
879
943
|
}
|
|
880
|
-
sessionCache.set(sessionId, { userId, user, expires: Date.now() + SESSION_CACHE_TTL });
|
|
881
|
-
return user;
|
|
882
944
|
}
|
|
883
|
-
|
|
884
|
-
const
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
945
|
+
async function withSharedLock(fn) {
|
|
946
|
+
const prev = sharedLock;
|
|
947
|
+
let resolve_;
|
|
948
|
+
sharedLock = new Promise((r) => {
|
|
949
|
+
resolve_ = r;
|
|
950
|
+
});
|
|
951
|
+
await prev;
|
|
952
|
+
try {
|
|
953
|
+
return await fn();
|
|
954
|
+
} finally {
|
|
955
|
+
resolve_();
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
async function sharedMerge(mindName, mindDir2, message) {
|
|
959
|
+
return withSharedLock(async () => {
|
|
960
|
+
const dir = sharedDir();
|
|
961
|
+
const worktreePath = resolve6(mindDir2, "home", "shared");
|
|
962
|
+
const status = (await gitExec(["status", "--porcelain"], { cwd: worktreePath })).trim();
|
|
963
|
+
if (status) {
|
|
964
|
+
await gitExec(["add", "-A"], { cwd: worktreePath });
|
|
965
|
+
await gitExec(
|
|
966
|
+
["commit", "--author", `${mindName} <${mindName}@volute>`, "-m", `wip: ${mindName}`],
|
|
967
|
+
{ cwd: worktreePath }
|
|
968
|
+
);
|
|
899
969
|
}
|
|
900
|
-
const
|
|
901
|
-
if (
|
|
902
|
-
|
|
903
|
-
c.set("user", mindUser);
|
|
904
|
-
await next();
|
|
905
|
-
return;
|
|
970
|
+
const diff = (await gitExec(["diff", `main...${mindName}`, "--stat"], { cwd: dir })).trim();
|
|
971
|
+
if (!diff) {
|
|
972
|
+
return { ok: true, message: "Nothing to merge" };
|
|
906
973
|
}
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
974
|
+
try {
|
|
975
|
+
await gitExec(["merge", "--squash", mindName], { cwd: dir });
|
|
976
|
+
} catch {
|
|
977
|
+
try {
|
|
978
|
+
await gitExec(["reset", "--hard", "HEAD"], { cwd: dir });
|
|
979
|
+
} catch (resetErr) {
|
|
980
|
+
logger_default.error("reset after squash conflict failed in shared repo", logger_default.errorData(resetErr));
|
|
914
981
|
}
|
|
982
|
+
return { ok: false, conflicts: true, message: "Merge conflicts detected" };
|
|
915
983
|
}
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
const user = c.get("user");
|
|
927
|
-
if (user.role !== "admin") {
|
|
928
|
-
const target = c.req.param(paramName) ?? "";
|
|
929
|
-
const baseName = await getBaseName(target);
|
|
930
|
-
if (user.username !== baseName) {
|
|
931
|
-
return c.json({ error: "Forbidden" }, 403);
|
|
984
|
+
await gitExec(["commit", "--author", `${mindName} <${mindName}@volute>`, "-m", message], {
|
|
985
|
+
cwd: dir
|
|
986
|
+
});
|
|
987
|
+
try {
|
|
988
|
+
await gitExec(["reset", "--hard", "main"], { cwd: worktreePath });
|
|
989
|
+
} catch {
|
|
990
|
+
return {
|
|
991
|
+
ok: true,
|
|
992
|
+
message: "Merged to main, but branch reset failed \u2014 run 'volute shared pull' to sync"
|
|
993
|
+
};
|
|
932
994
|
}
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
});
|
|
936
|
-
|
|
937
|
-
// src/web/server.ts
|
|
938
|
-
import { existsSync as existsSync16 } from "fs";
|
|
939
|
-
import { readFile as readFile4, stat as stat4 } from "fs/promises";
|
|
940
|
-
import { createServer as createHttpsServer } from "https";
|
|
941
|
-
import { dirname as dirname2, extname as extname5, resolve as resolve21 } from "path";
|
|
942
|
-
import { serve } from "@hono/node-server";
|
|
995
|
+
rechownWorktree(worktreePath, mindName);
|
|
996
|
+
return { ok: true };
|
|
997
|
+
});
|
|
998
|
+
}
|
|
943
999
|
|
|
944
1000
|
// src/web/app.ts
|
|
945
|
-
import { Hono as
|
|
1001
|
+
import { Hono as Hono29 } from "hono";
|
|
946
1002
|
import { bodyLimit } from "hono/body-limit";
|
|
947
1003
|
import { csrf } from "hono/csrf";
|
|
948
1004
|
import { HTTPException } from "hono/http-exception";
|
|
@@ -973,15 +1029,11 @@ var app = new Hono().get("/events", async (c) => {
|
|
|
973
1029
|
} catch (err) {
|
|
974
1030
|
logger_default.error("[activity-sse] failed to fetch conversations", logger_default.errorData(err));
|
|
975
1031
|
}
|
|
976
|
-
const sites = getCachedSites();
|
|
977
|
-
const recentPages = getCachedRecentPages();
|
|
978
1032
|
await stream.writeSSE({
|
|
979
1033
|
data: JSON.stringify({
|
|
980
1034
|
event: "snapshot",
|
|
981
1035
|
activity: recentActivity,
|
|
982
1036
|
conversations: conversations2,
|
|
983
|
-
sites,
|
|
984
|
-
recentPages,
|
|
985
1037
|
activeMinds: getActiveMinds()
|
|
986
1038
|
})
|
|
987
1039
|
});
|
|
@@ -1012,25 +1064,148 @@ var app = new Hono().get("/events", async (c) => {
|
|
|
1012
1064
|
await new Promise((resolve23) => {
|
|
1013
1065
|
stream.onAbort(() => resolve23());
|
|
1014
1066
|
});
|
|
1015
|
-
} finally {
|
|
1016
|
-
for (const cleanup of cleanups) {
|
|
1017
|
-
try {
|
|
1018
|
-
cleanup();
|
|
1019
|
-
} catch {
|
|
1020
|
-
}
|
|
1067
|
+
} finally {
|
|
1068
|
+
for (const cleanup of cleanups) {
|
|
1069
|
+
try {
|
|
1070
|
+
cleanup();
|
|
1071
|
+
} catch {
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
});
|
|
1076
|
+
});
|
|
1077
|
+
var activity_default = app;
|
|
1078
|
+
|
|
1079
|
+
// src/web/api/auth.ts
|
|
1080
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync5, rmSync as rmSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
1081
|
+
import { extname, resolve as resolve7 } from "path";
|
|
1082
|
+
import { zValidator } from "@hono/zod-validator";
|
|
1083
|
+
import { Hono as Hono2 } from "hono";
|
|
1084
|
+
import { deleteCookie, getCookie as getCookie2, setCookie } from "hono/cookie";
|
|
1085
|
+
import { z } from "zod";
|
|
1086
|
+
|
|
1087
|
+
// src/web/middleware/auth.ts
|
|
1088
|
+
import { timingSafeEqual } from "crypto";
|
|
1089
|
+
import { eq as eq2, lt as lt2 } from "drizzle-orm";
|
|
1090
|
+
import { getCookie } from "hono/cookie";
|
|
1091
|
+
import { createMiddleware } from "hono/factory";
|
|
1092
|
+
function isValidDaemonToken(token) {
|
|
1093
|
+
const expected = process.env.VOLUTE_DAEMON_TOKEN;
|
|
1094
|
+
if (!expected || token.length !== expected.length) return false;
|
|
1095
|
+
return timingSafeEqual(Buffer.from(token), Buffer.from(expected));
|
|
1096
|
+
}
|
|
1097
|
+
var SESSION_MAX_AGE = 365 * 24 * 60 * 60 * 1e3;
|
|
1098
|
+
var SESSION_CACHE_TTL = 5 * 60 * 1e3;
|
|
1099
|
+
var sessionCache = /* @__PURE__ */ new Map();
|
|
1100
|
+
function invalidateSessionCache(sessionId) {
|
|
1101
|
+
sessionCache.delete(sessionId);
|
|
1102
|
+
}
|
|
1103
|
+
async function createSession(userId) {
|
|
1104
|
+
const db = await getDb();
|
|
1105
|
+
const sessionId = crypto.randomUUID();
|
|
1106
|
+
await db.insert(sessions).values({ id: sessionId, userId, createdAt: Date.now() });
|
|
1107
|
+
return sessionId;
|
|
1108
|
+
}
|
|
1109
|
+
async function deleteSession(sessionId) {
|
|
1110
|
+
sessionCache.delete(sessionId);
|
|
1111
|
+
const db = await getDb();
|
|
1112
|
+
await db.delete(sessions).where(eq2(sessions.id, sessionId));
|
|
1113
|
+
}
|
|
1114
|
+
async function getSessionUserId(sessionId) {
|
|
1115
|
+
const db = await getDb();
|
|
1116
|
+
const row = await db.select().from(sessions).where(eq2(sessions.id, sessionId)).get();
|
|
1117
|
+
if (!row) return void 0;
|
|
1118
|
+
if (Date.now() - row.createdAt > SESSION_MAX_AGE) {
|
|
1119
|
+
await db.delete(sessions).where(eq2(sessions.id, sessionId));
|
|
1120
|
+
return void 0;
|
|
1121
|
+
}
|
|
1122
|
+
return row.userId;
|
|
1123
|
+
}
|
|
1124
|
+
async function cleanExpiredSessions() {
|
|
1125
|
+
const db = await getDb();
|
|
1126
|
+
const cutoff = Date.now() - SESSION_MAX_AGE;
|
|
1127
|
+
await db.delete(sessions).where(lt2(sessions.createdAt, cutoff));
|
|
1128
|
+
}
|
|
1129
|
+
var requireAdmin = createMiddleware(async (c, next) => {
|
|
1130
|
+
const user = c.get("user");
|
|
1131
|
+
if (user.role !== "admin") {
|
|
1132
|
+
return c.json({ error: "Forbidden" }, 403);
|
|
1133
|
+
}
|
|
1134
|
+
await next();
|
|
1135
|
+
});
|
|
1136
|
+
async function resolveSession(sessionId) {
|
|
1137
|
+
const cached = sessionCache.get(sessionId);
|
|
1138
|
+
if (cached && cached.expires > Date.now()) {
|
|
1139
|
+
return cached.user;
|
|
1140
|
+
}
|
|
1141
|
+
const userId = await getSessionUserId(sessionId);
|
|
1142
|
+
if (userId == null) {
|
|
1143
|
+
sessionCache.delete(sessionId);
|
|
1144
|
+
return null;
|
|
1145
|
+
}
|
|
1146
|
+
const user = await getUser(userId);
|
|
1147
|
+
if (!user) {
|
|
1148
|
+
sessionCache.delete(sessionId);
|
|
1149
|
+
return null;
|
|
1150
|
+
}
|
|
1151
|
+
sessionCache.set(sessionId, { userId, user, expires: Date.now() + SESSION_CACHE_TTL });
|
|
1152
|
+
return user;
|
|
1153
|
+
}
|
|
1154
|
+
var authMiddleware = createMiddleware(async (c, next) => {
|
|
1155
|
+
const authHeader = c.req.header("Authorization");
|
|
1156
|
+
if (authHeader?.startsWith("Bearer ")) {
|
|
1157
|
+
const token = authHeader.slice(7);
|
|
1158
|
+
if (token && isValidDaemonToken(token)) {
|
|
1159
|
+
c.set("user", {
|
|
1160
|
+
id: 0,
|
|
1161
|
+
username: "daemon",
|
|
1162
|
+
role: "admin",
|
|
1163
|
+
user_type: "brain",
|
|
1164
|
+
display_name: null,
|
|
1165
|
+
description: null,
|
|
1166
|
+
avatar: null
|
|
1167
|
+
});
|
|
1168
|
+
await next();
|
|
1169
|
+
return;
|
|
1170
|
+
}
|
|
1171
|
+
const mindName = resolveMindToken(token);
|
|
1172
|
+
if (mindName) {
|
|
1173
|
+
const mindUser = await getOrCreateMindUser(mindName);
|
|
1174
|
+
c.set("user", mindUser);
|
|
1175
|
+
await next();
|
|
1176
|
+
return;
|
|
1177
|
+
}
|
|
1178
|
+
if (token) {
|
|
1179
|
+
const user2 = await resolveSession(token);
|
|
1180
|
+
if (user2) {
|
|
1181
|
+
if (user2.role === "pending") return c.json({ error: "Account pending approval" }, 403);
|
|
1182
|
+
c.set("user", user2);
|
|
1183
|
+
await next();
|
|
1184
|
+
return;
|
|
1021
1185
|
}
|
|
1022
1186
|
}
|
|
1023
|
-
}
|
|
1187
|
+
}
|
|
1188
|
+
const sessionId = getCookie(c, "volute_session");
|
|
1189
|
+
if (!sessionId) return c.json({ error: "Unauthorized" }, 401);
|
|
1190
|
+
const user = await resolveSession(sessionId);
|
|
1191
|
+
if (!user) return c.json({ error: "Unauthorized" }, 401);
|
|
1192
|
+
if (user.role === "pending") return c.json({ error: "Account pending approval" }, 403);
|
|
1193
|
+
c.set("user", user);
|
|
1194
|
+
await next();
|
|
1195
|
+
});
|
|
1196
|
+
var requireSelf = (paramName = "name") => createMiddleware(async (c, next) => {
|
|
1197
|
+
const user = c.get("user");
|
|
1198
|
+
if (user.role !== "admin") {
|
|
1199
|
+
const target = c.req.param(paramName) ?? "";
|
|
1200
|
+
const baseName = await getBaseName(target);
|
|
1201
|
+
if (user.username !== baseName) {
|
|
1202
|
+
return c.json({ error: "Forbidden" }, 403);
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
await next();
|
|
1024
1206
|
});
|
|
1025
|
-
var activity_default = app;
|
|
1026
1207
|
|
|
1027
1208
|
// src/web/api/auth.ts
|
|
1028
|
-
import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync4, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
1029
|
-
import { extname, resolve as resolve6 } from "path";
|
|
1030
|
-
import { zValidator } from "@hono/zod-validator";
|
|
1031
|
-
import { Hono as Hono2 } from "hono";
|
|
1032
|
-
import { deleteCookie, getCookie as getCookie2, setCookie } from "hono/cookie";
|
|
1033
|
-
import { z } from "zod";
|
|
1034
1209
|
function tryJoinSystem(userId) {
|
|
1035
1210
|
if (!process.env.VOLUTE_DAEMON_TOKEN) return;
|
|
1036
1211
|
joinSystemChannel(userId).catch(() => {
|
|
@@ -1063,7 +1238,7 @@ var AVATAR_MIME = {
|
|
|
1063
1238
|
};
|
|
1064
1239
|
var MAX_AVATAR_SIZE = 2 * 1024 * 1024;
|
|
1065
1240
|
function avatarsDir() {
|
|
1066
|
-
return
|
|
1241
|
+
return resolve7(voluteHome(), "avatars");
|
|
1067
1242
|
}
|
|
1068
1243
|
var authenticated = new Hono2().use(authMiddleware).post("/change-password", zValidator("json", changePasswordSchema), async (c) => {
|
|
1069
1244
|
const user = c.get("user");
|
|
@@ -1098,13 +1273,13 @@ var authenticated = new Hono2().use(authMiddleware).post("/change-password", zVa
|
|
|
1098
1273
|
return c.json({ error: "Invalid file type (png, jpg, gif, webp only)" }, 400);
|
|
1099
1274
|
}
|
|
1100
1275
|
const dir = avatarsDir();
|
|
1101
|
-
|
|
1276
|
+
mkdirSync5(dir, { recursive: true });
|
|
1102
1277
|
const filename = `avatar-${user.id}${ext}`;
|
|
1103
1278
|
const buffer2 = Buffer.from(await file.arrayBuffer());
|
|
1104
|
-
|
|
1279
|
+
writeFileSync4(resolve7(dir, filename), buffer2);
|
|
1105
1280
|
if (user.avatar && user.avatar !== filename) {
|
|
1106
|
-
const oldPath =
|
|
1107
|
-
|
|
1281
|
+
const oldPath = resolve7(dir, user.avatar);
|
|
1282
|
+
rmSync3(oldPath, { force: true });
|
|
1108
1283
|
}
|
|
1109
1284
|
await updateUserProfile(user.id, { avatar: filename });
|
|
1110
1285
|
const sessionId = getCookie2(c, "volute_session");
|
|
@@ -1118,8 +1293,8 @@ var authenticated = new Hono2().use(authMiddleware).post("/change-password", zVa
|
|
|
1118
1293
|
}).delete("/avatar", async (c) => {
|
|
1119
1294
|
const user = c.get("user");
|
|
1120
1295
|
if (user.avatar) {
|
|
1121
|
-
const path =
|
|
1122
|
-
|
|
1296
|
+
const path = resolve7(avatarsDir(), user.avatar);
|
|
1297
|
+
rmSync3(path, { force: true });
|
|
1123
1298
|
}
|
|
1124
1299
|
await updateUserProfile(user.id, { avatar: null });
|
|
1125
1300
|
const sessionId = getCookie2(c, "volute_session");
|
|
@@ -1268,13 +1443,13 @@ var app2 = new Hono2().post("/register", zValidator("json", credentialsSchema),
|
|
|
1268
1443
|
return c.json({ error: "Invalid filename" }, 400);
|
|
1269
1444
|
}
|
|
1270
1445
|
const dir = avatarsDir();
|
|
1271
|
-
const filePath =
|
|
1446
|
+
const filePath = resolve7(dir, filename);
|
|
1272
1447
|
if (!filePath.startsWith(`${dir}/`)) return c.json({ error: "Invalid path" }, 400);
|
|
1273
|
-
if (!
|
|
1448
|
+
if (!existsSync7(filePath)) return c.json({ error: "Not found" }, 404);
|
|
1274
1449
|
const ext = extname(filename).toLowerCase();
|
|
1275
1450
|
const mime = AVATAR_MIME[ext];
|
|
1276
1451
|
if (!mime) return c.json({ error: "Invalid file type" }, 400);
|
|
1277
|
-
const data =
|
|
1452
|
+
const data = readFileSync5(filePath);
|
|
1278
1453
|
return c.body(data, 200, {
|
|
1279
1454
|
"Content-Type": mime,
|
|
1280
1455
|
"Cache-Control": "public, max-age=3600",
|
|
@@ -1409,7 +1584,7 @@ var app3 = new Hono3().post("/:platform/inbound", zValidator2("json", inboundSch
|
|
|
1409
1584
|
}
|
|
1410
1585
|
const participants = await getParticipants(channel.id);
|
|
1411
1586
|
if (!participants.some((p) => p.userId === puppet.id)) {
|
|
1412
|
-
const { addParticipant } = await import("./conversations-
|
|
1587
|
+
const { addParticipant } = await import("./conversations-7KVQV7EZ.js");
|
|
1413
1588
|
await addParticipant(channel.id, puppet.id);
|
|
1414
1589
|
}
|
|
1415
1590
|
const contentBlocks = body.content;
|
|
@@ -1464,7 +1639,7 @@ var app3 = new Hono3().post("/:platform/inbound", zValidator2("json", inboundSch
|
|
|
1464
1639
|
});
|
|
1465
1640
|
try {
|
|
1466
1641
|
const daemonPort = parseInt(process.env.VOLUTE_DAEMON_PORT ?? "", 10);
|
|
1467
|
-
if (isNaN(daemonPort)) {
|
|
1642
|
+
if (Number.isNaN(daemonPort)) {
|
|
1468
1643
|
return c.json({ error: "VOLUTE_DAEMON_PORT not available" }, 500);
|
|
1469
1644
|
}
|
|
1470
1645
|
await manager.startBridge(platform, daemonPort);
|
|
@@ -1502,10 +1677,10 @@ async function fanOutToBridgedMinds(opts) {
|
|
|
1502
1677
|
const participants = await getParticipants(opts.conversationId);
|
|
1503
1678
|
const mindParticipants = participants.filter((p) => p.userType === "mind");
|
|
1504
1679
|
const participantNames = participants.map((p) => p.username);
|
|
1505
|
-
const { getMindManager: getMindManager2 } = await import("./mind-manager-
|
|
1506
|
-
const { getSleepManagerIfReady } = await import("./sleep-manager-
|
|
1680
|
+
const { getMindManager: getMindManager2 } = await import("./mind-manager-YFCOIAAX.js");
|
|
1681
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-O7YQFCV5.js");
|
|
1507
1682
|
const manager = getMindManager2();
|
|
1508
|
-
const sm =
|
|
1683
|
+
const sm = getSleepManagerIfReady2();
|
|
1509
1684
|
const targetMinds = mindParticipants.filter((ap) => {
|
|
1510
1685
|
return (manager.isRunning(ap.username) || sm?.isSleeping(ap.username)) && ap.username !== opts.senderName;
|
|
1511
1686
|
}).map((ap) => ap.username);
|
|
@@ -1520,7 +1695,7 @@ async function fanOutToBridgedMinds(opts) {
|
|
|
1520
1695
|
writeChannelEntry(mindName, channel, {
|
|
1521
1696
|
platformId: opts.conversationId,
|
|
1522
1697
|
platform: "volute",
|
|
1523
|
-
type: opts.isDM ? "dm" : "
|
|
1698
|
+
type: opts.isDM ? "dm" : "channel"
|
|
1524
1699
|
});
|
|
1525
1700
|
} catch (err) {
|
|
1526
1701
|
logger_default.warn(`failed to write channel entry for ${mindName}`, logger_default.errorData(err));
|
|
@@ -1648,7 +1823,7 @@ async function listConversations(env) {
|
|
|
1648
1823
|
id: slug,
|
|
1649
1824
|
platformId: dm.id,
|
|
1650
1825
|
name: recipients.join(", ") || "DM",
|
|
1651
|
-
type: dm.type === 1 ? "dm" : "
|
|
1826
|
+
type: dm.type === 1 ? "dm" : "channel"
|
|
1652
1827
|
});
|
|
1653
1828
|
}
|
|
1654
1829
|
return results;
|
|
@@ -1818,7 +1993,6 @@ async function listConversations2(env) {
|
|
|
1818
1993
|
return data.channels.map((ch) => {
|
|
1819
1994
|
let type = "channel";
|
|
1820
1995
|
if (ch.is_im) type = "dm";
|
|
1821
|
-
else if (ch.is_mpim) type = "group";
|
|
1822
1996
|
let slug;
|
|
1823
1997
|
let name;
|
|
1824
1998
|
if (ch.is_im && ch.user) {
|
|
@@ -1895,7 +2069,7 @@ async function createConversation3(env, participants, name) {
|
|
|
1895
2069
|
platformId,
|
|
1896
2070
|
platform: "slack",
|
|
1897
2071
|
name: participants.join(", "),
|
|
1898
|
-
type: participants.length === 1 ? "dm" : "
|
|
2072
|
+
type: participants.length === 1 ? "dm" : "channel"
|
|
1899
2073
|
});
|
|
1900
2074
|
}
|
|
1901
2075
|
return slug;
|
|
@@ -2006,18 +2180,18 @@ __export(volute_exports, {
|
|
|
2006
2180
|
read: () => read4,
|
|
2007
2181
|
send: () => send4
|
|
2008
2182
|
});
|
|
2009
|
-
import { existsSync as
|
|
2010
|
-
import { resolve as
|
|
2183
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
|
|
2184
|
+
import { resolve as resolve8 } from "path";
|
|
2011
2185
|
function getDaemonConfig() {
|
|
2012
|
-
const newPath =
|
|
2013
|
-
const legacyPath =
|
|
2014
|
-
const configPath =
|
|
2015
|
-
if (!
|
|
2186
|
+
const newPath = resolve8(voluteSystemDir(), "daemon.json");
|
|
2187
|
+
const legacyPath = resolve8(voluteHome(), "daemon.json");
|
|
2188
|
+
const configPath = existsSync8(newPath) ? newPath : legacyPath;
|
|
2189
|
+
if (!existsSync8(configPath)) {
|
|
2016
2190
|
throw new Error("Volute daemon is not running");
|
|
2017
2191
|
}
|
|
2018
2192
|
let config;
|
|
2019
2193
|
try {
|
|
2020
|
-
config = JSON.parse(
|
|
2194
|
+
config = JSON.parse(readFileSync6(configPath, "utf-8"));
|
|
2021
2195
|
} catch (err) {
|
|
2022
2196
|
throw new Error(`Failed to parse ${configPath}: ${err}`);
|
|
2023
2197
|
}
|
|
@@ -2111,7 +2285,7 @@ async function listConversations4(env) {
|
|
|
2111
2285
|
convType: conv.type,
|
|
2112
2286
|
convName: conv.name
|
|
2113
2287
|
});
|
|
2114
|
-
const convType = conv.type === "channel" ? "channel" :
|
|
2288
|
+
const convType = conv.type === "channel" ? "channel" : "dm";
|
|
2115
2289
|
results.push({
|
|
2116
2290
|
id: slug,
|
|
2117
2291
|
platformId: conv.id,
|
|
@@ -2374,10 +2548,17 @@ var sharedEnvApp = new Hono5().get("/", (c) => {
|
|
|
2374
2548
|
});
|
|
2375
2549
|
var env_default = app5;
|
|
2376
2550
|
|
|
2377
|
-
// src/web/api/
|
|
2378
|
-
import { readFileSync as readFileSync6, statSync } from "fs";
|
|
2379
|
-
import { resolve as resolve8 } from "path";
|
|
2551
|
+
// src/web/api/extensions.ts
|
|
2380
2552
|
import { Hono as Hono6 } from "hono";
|
|
2553
|
+
var app6 = new Hono6().get("/", (c) => {
|
|
2554
|
+
return c.json(getLoadedExtensions());
|
|
2555
|
+
});
|
|
2556
|
+
var extensions_default = app6;
|
|
2557
|
+
|
|
2558
|
+
// src/web/api/file-sharing.ts
|
|
2559
|
+
import { readFileSync as readFileSync7, statSync } from "fs";
|
|
2560
|
+
import { resolve as resolve9 } from "path";
|
|
2561
|
+
import { Hono as Hono7 } from "hono";
|
|
2381
2562
|
async function notifyMind(port, message, channel, sender) {
|
|
2382
2563
|
try {
|
|
2383
2564
|
const res = await fetch(`http://127.0.0.1:${port}/message`, {
|
|
@@ -2396,7 +2577,7 @@ async function notifyMind(port, message, channel, sender) {
|
|
|
2396
2577
|
console.warn(`[file-sharing] notify mind on port ${port} failed:`, err);
|
|
2397
2578
|
}
|
|
2398
2579
|
}
|
|
2399
|
-
var
|
|
2580
|
+
var app7 = new Hono7().post("/:name/files/send", requireSelf(), async (c) => {
|
|
2400
2581
|
const senderName = c.req.param("name");
|
|
2401
2582
|
const senderEntry = await findMind(senderName);
|
|
2402
2583
|
if (!senderEntry) return c.json({ error: "Sender mind not found" }, 404);
|
|
@@ -2409,21 +2590,21 @@ var app6 = new Hono6().post("/:name/files/send", requireSelf(), async (c) => {
|
|
|
2409
2590
|
const pathErr = validateFilePath(body.filePath);
|
|
2410
2591
|
if (pathErr) return c.json({ error: pathErr }, 400);
|
|
2411
2592
|
const senderDir = mindDir(senderName);
|
|
2412
|
-
const filePath =
|
|
2593
|
+
const filePath = resolve9(senderDir, "home", body.filePath);
|
|
2413
2594
|
const MAX_FILE_SIZE2 = 50 * 1024 * 1024;
|
|
2414
|
-
const
|
|
2415
|
-
if (!
|
|
2416
|
-
if (
|
|
2595
|
+
const stat4 = statSync(filePath, { throwIfNoEntry: false });
|
|
2596
|
+
if (!stat4) return c.json({ error: `File not found: ${body.filePath}` }, 404);
|
|
2597
|
+
if (stat4.size > MAX_FILE_SIZE2) {
|
|
2417
2598
|
return c.json(
|
|
2418
2599
|
{
|
|
2419
|
-
error: `File too large (${formatFileSize(
|
|
2600
|
+
error: `File too large (${formatFileSize(stat4.size)}, max ${formatFileSize(MAX_FILE_SIZE2)})`
|
|
2420
2601
|
},
|
|
2421
2602
|
413
|
|
2422
2603
|
);
|
|
2423
2604
|
}
|
|
2424
2605
|
let content;
|
|
2425
2606
|
try {
|
|
2426
|
-
content =
|
|
2607
|
+
content = readFileSync7(filePath);
|
|
2427
2608
|
} catch (err) {
|
|
2428
2609
|
const code = err.code;
|
|
2429
2610
|
if (code === "ENOENT") {
|
|
@@ -2534,13 +2715,13 @@ var app6 = new Hono6().post("/:name/files/send", requireSelf(), async (c) => {
|
|
|
2534
2715
|
}
|
|
2535
2716
|
return c.json({ status: "pending", id }, 200);
|
|
2536
2717
|
});
|
|
2537
|
-
var file_sharing_default =
|
|
2718
|
+
var file_sharing_default = app7;
|
|
2538
2719
|
|
|
2539
2720
|
// src/web/api/files.ts
|
|
2540
|
-
import { existsSync as
|
|
2721
|
+
import { existsSync as existsSync9 } from "fs";
|
|
2541
2722
|
import { readdir, readFile, realpath, stat } from "fs/promises";
|
|
2542
|
-
import { extname as extname2, resolve as
|
|
2543
|
-
import { Hono as
|
|
2723
|
+
import { extname as extname2, resolve as resolve10 } from "path";
|
|
2724
|
+
import { Hono as Hono8 } from "hono";
|
|
2544
2725
|
var ALLOWED_FILES = /* @__PURE__ */ new Set(["SOUL.md", "MEMORY.md", "CLAUDE.md", "VOLUTE.md"]);
|
|
2545
2726
|
var AVATAR_MIME2 = {
|
|
2546
2727
|
".png": "image/png",
|
|
@@ -2550,7 +2731,7 @@ var AVATAR_MIME2 = {
|
|
|
2550
2731
|
".webp": "image/webp"
|
|
2551
2732
|
};
|
|
2552
2733
|
var MAX_AVATAR_SIZE2 = 2 * 1024 * 1024;
|
|
2553
|
-
var
|
|
2734
|
+
var app8 = new Hono8().get("/:name/avatar", async (c) => {
|
|
2554
2735
|
const name = c.req.param("name");
|
|
2555
2736
|
const entry = await findMind(name);
|
|
2556
2737
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
@@ -2560,8 +2741,8 @@ var app7 = new Hono7().get("/:name/avatar", async (c) => {
|
|
|
2560
2741
|
const ext = extname2(config.profile.avatar).toLowerCase();
|
|
2561
2742
|
const mime = AVATAR_MIME2[ext];
|
|
2562
2743
|
if (!mime) return c.json({ error: "Invalid avatar extension" }, 400);
|
|
2563
|
-
const homeDir =
|
|
2564
|
-
const avatarPath =
|
|
2744
|
+
const homeDir = resolve10(dir, "home");
|
|
2745
|
+
const avatarPath = resolve10(homeDir, config.profile.avatar);
|
|
2565
2746
|
if (!avatarPath.startsWith(`${homeDir}/`)) return c.json({ error: "Invalid avatar path" }, 400);
|
|
2566
2747
|
let realAvatarPath;
|
|
2567
2748
|
try {
|
|
@@ -2590,8 +2771,8 @@ var app7 = new Hono7().get("/:name/avatar", async (c) => {
|
|
|
2590
2771
|
const entry = await findMind(name);
|
|
2591
2772
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
2592
2773
|
const dir = mindDir(name);
|
|
2593
|
-
const homeDir =
|
|
2594
|
-
if (!
|
|
2774
|
+
const homeDir = resolve10(dir, "home");
|
|
2775
|
+
if (!existsSync9(homeDir)) return c.json({ error: "Home directory missing" }, 404);
|
|
2595
2776
|
const allFiles = await readdir(homeDir);
|
|
2596
2777
|
const files = allFiles.filter((f) => f.endsWith(".md") && ALLOWED_FILES.has(f));
|
|
2597
2778
|
return c.json(files);
|
|
@@ -2604,33 +2785,33 @@ var app7 = new Hono7().get("/:name/avatar", async (c) => {
|
|
|
2604
2785
|
const entry = await findMind(name);
|
|
2605
2786
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
2606
2787
|
const dir = mindDir(name);
|
|
2607
|
-
const filePath =
|
|
2608
|
-
if (!
|
|
2788
|
+
const filePath = resolve10(dir, "home", filename);
|
|
2789
|
+
if (!existsSync9(filePath)) {
|
|
2609
2790
|
return c.json({ error: "File not found" }, 404);
|
|
2610
2791
|
}
|
|
2611
2792
|
const content = await readFile(filePath, "utf-8");
|
|
2612
2793
|
return c.json({ filename, content });
|
|
2613
2794
|
});
|
|
2614
|
-
var files_default =
|
|
2795
|
+
var files_default = app8;
|
|
2615
2796
|
|
|
2616
2797
|
// src/web/api/keys.ts
|
|
2617
|
-
import { Hono as
|
|
2798
|
+
import { Hono as Hono9 } from "hono";
|
|
2618
2799
|
|
|
2619
2800
|
// src/lib/identity.ts
|
|
2620
2801
|
import { createHash, generateKeyPairSync, sign, verify } from "crypto";
|
|
2621
|
-
import { existsSync as
|
|
2622
|
-
import { resolve as
|
|
2802
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync6, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
|
|
2803
|
+
import { resolve as resolve11 } from "path";
|
|
2623
2804
|
function generateIdentity(mindDir2) {
|
|
2624
|
-
const identityDir =
|
|
2625
|
-
|
|
2805
|
+
const identityDir = resolve11(mindDir2, ".mind/identity");
|
|
2806
|
+
mkdirSync6(identityDir, { recursive: true });
|
|
2626
2807
|
const { publicKey, privateKey } = generateKeyPairSync("ed25519", {
|
|
2627
2808
|
publicKeyEncoding: { type: "spki", format: "pem" },
|
|
2628
2809
|
privateKeyEncoding: { type: "pkcs8", format: "pem" }
|
|
2629
2810
|
});
|
|
2630
|
-
const privatePath =
|
|
2631
|
-
const publicPath =
|
|
2632
|
-
|
|
2633
|
-
|
|
2811
|
+
const privatePath = resolve11(identityDir, "private.pem");
|
|
2812
|
+
const publicPath = resolve11(identityDir, "public.pem");
|
|
2813
|
+
writeFileSync5(privatePath, privateKey, { mode: 384 });
|
|
2814
|
+
writeFileSync5(publicPath, publicKey, { mode: 420 });
|
|
2634
2815
|
const config = readVoluteConfig(mindDir2) ?? {};
|
|
2635
2816
|
config.identity = {
|
|
2636
2817
|
privateKey: ".mind/identity/private.pem",
|
|
@@ -2643,17 +2824,17 @@ function getPrivateKey(mindDir2) {
|
|
|
2643
2824
|
const config = readVoluteConfig(mindDir2);
|
|
2644
2825
|
const relPath = config?.identity?.privateKey;
|
|
2645
2826
|
if (!relPath) return null;
|
|
2646
|
-
const fullPath =
|
|
2647
|
-
if (!
|
|
2648
|
-
return
|
|
2827
|
+
const fullPath = resolve11(mindDir2, relPath);
|
|
2828
|
+
if (!existsSync10(fullPath)) return null;
|
|
2829
|
+
return readFileSync8(fullPath, "utf-8");
|
|
2649
2830
|
}
|
|
2650
2831
|
function getPublicKey(mindDir2) {
|
|
2651
2832
|
const config = readVoluteConfig(mindDir2);
|
|
2652
2833
|
const relPath = config?.identity?.publicKey;
|
|
2653
2834
|
if (!relPath) return null;
|
|
2654
|
-
const fullPath =
|
|
2655
|
-
if (!
|
|
2656
|
-
return
|
|
2835
|
+
const fullPath = resolve11(mindDir2, relPath);
|
|
2836
|
+
if (!existsSync10(fullPath)) return null;
|
|
2837
|
+
return readFileSync8(fullPath, "utf-8");
|
|
2657
2838
|
}
|
|
2658
2839
|
function getFingerprint(publicKeyPem) {
|
|
2659
2840
|
return createHash("sha256").update(publicKeyPem).digest("hex");
|
|
@@ -2688,7 +2869,7 @@ async function publishPublicKey(mindName, publicKeyPem) {
|
|
|
2688
2869
|
}
|
|
2689
2870
|
|
|
2690
2871
|
// src/web/api/keys.ts
|
|
2691
|
-
var
|
|
2872
|
+
var app9 = new Hono9().get("/:fingerprint", async (c) => {
|
|
2692
2873
|
const fingerprint = c.req.param("fingerprint");
|
|
2693
2874
|
for (const entry of await readRegistry()) {
|
|
2694
2875
|
try {
|
|
@@ -2702,20 +2883,20 @@ var app8 = new Hono8().get("/:fingerprint", async (c) => {
|
|
|
2702
2883
|
}
|
|
2703
2884
|
return c.json({ error: "Key not found" }, 404);
|
|
2704
2885
|
});
|
|
2705
|
-
var keys_default =
|
|
2886
|
+
var keys_default = app9;
|
|
2706
2887
|
|
|
2707
2888
|
// src/web/api/logs.ts
|
|
2708
2889
|
import { spawn as spawn2 } from "child_process";
|
|
2709
|
-
import { existsSync as
|
|
2710
|
-
import { resolve as
|
|
2711
|
-
import { Hono as
|
|
2890
|
+
import { existsSync as existsSync11 } from "fs";
|
|
2891
|
+
import { resolve as resolve12 } from "path";
|
|
2892
|
+
import { Hono as Hono10 } from "hono";
|
|
2712
2893
|
import { streamSSE as streamSSE2 } from "hono/streaming";
|
|
2713
|
-
var
|
|
2894
|
+
var app10 = new Hono10().get("/:name/logs", async (c) => {
|
|
2714
2895
|
const name = c.req.param("name");
|
|
2715
2896
|
const entry = await findMind(name);
|
|
2716
2897
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
2717
|
-
const logFile =
|
|
2718
|
-
if (!
|
|
2898
|
+
const logFile = resolve12(stateDir(name), "logs", "mind.log");
|
|
2899
|
+
if (!existsSync11(logFile)) {
|
|
2719
2900
|
return c.json({ error: "No log file found" }, 404);
|
|
2720
2901
|
}
|
|
2721
2902
|
return streamSSE2(c, async (stream) => {
|
|
@@ -2742,8 +2923,8 @@ var app9 = new Hono9().get("/:name/logs", async (c) => {
|
|
|
2742
2923
|
const name = c.req.param("name");
|
|
2743
2924
|
const entry = await findMind(name);
|
|
2744
2925
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
2745
|
-
const logFile =
|
|
2746
|
-
if (!
|
|
2926
|
+
const logFile = resolve12(stateDir(name), "logs", "mind.log");
|
|
2927
|
+
if (!existsSync11(logFile)) {
|
|
2747
2928
|
return c.json({ error: "No log file found" }, 404);
|
|
2748
2929
|
}
|
|
2749
2930
|
const nParam = parseInt(c.req.query("n") ?? "50", 10);
|
|
@@ -2758,13 +2939,13 @@ var app9 = new Hono9().get("/:name/logs", async (c) => {
|
|
|
2758
2939
|
});
|
|
2759
2940
|
return c.text(output);
|
|
2760
2941
|
});
|
|
2761
|
-
var logs_default =
|
|
2942
|
+
var logs_default = app10;
|
|
2762
2943
|
|
|
2763
2944
|
// src/web/api/mind-skills.ts
|
|
2764
2945
|
import { zValidator as zValidator3 } from "@hono/zod-validator";
|
|
2765
|
-
import { Hono as
|
|
2946
|
+
import { Hono as Hono11 } from "hono";
|
|
2766
2947
|
import { z as z3 } from "zod";
|
|
2767
|
-
var
|
|
2948
|
+
var app11 = new Hono11().get("/:name/skills", async (c) => {
|
|
2768
2949
|
const name = c.req.param("name");
|
|
2769
2950
|
const entry = await findMind(name);
|
|
2770
2951
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
@@ -2839,38 +3020,38 @@ var app10 = new Hono10().get("/:name/skills", async (c) => {
|
|
|
2839
3020
|
}
|
|
2840
3021
|
return c.json({ ok: true });
|
|
2841
3022
|
});
|
|
2842
|
-
var mind_skills_default =
|
|
3023
|
+
var mind_skills_default = app11;
|
|
2843
3024
|
|
|
2844
3025
|
// src/web/api/minds.ts
|
|
2845
3026
|
import {
|
|
2846
3027
|
cpSync,
|
|
2847
|
-
existsSync as
|
|
2848
|
-
mkdirSync as
|
|
3028
|
+
existsSync as existsSync14,
|
|
3029
|
+
mkdirSync as mkdirSync9,
|
|
2849
3030
|
readdirSync as readdirSync3,
|
|
2850
|
-
readFileSync as
|
|
2851
|
-
rmSync as
|
|
2852
|
-
writeFileSync as
|
|
3031
|
+
readFileSync as readFileSync12,
|
|
3032
|
+
rmSync as rmSync5,
|
|
3033
|
+
writeFileSync as writeFileSync9
|
|
2853
3034
|
} from "fs";
|
|
2854
|
-
import { resolve as
|
|
3035
|
+
import { resolve as resolve16 } from "path";
|
|
2855
3036
|
import { zValidator as zValidator4 } from "@hono/zod-validator";
|
|
2856
|
-
import { and as
|
|
2857
|
-
import { Hono as
|
|
3037
|
+
import { and as and4, desc as desc3, eq as eq5, sql as sql2 } from "drizzle-orm";
|
|
3038
|
+
import { Hono as Hono12 } from "hono";
|
|
2858
3039
|
import { z as z4 } from "zod";
|
|
2859
3040
|
|
|
2860
3041
|
// src/lib/consolidate.ts
|
|
2861
|
-
import { readdirSync as readdirSync2, readFileSync as
|
|
2862
|
-
import { resolve as
|
|
3042
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync9, writeFileSync as writeFileSync6 } from "fs";
|
|
3043
|
+
import { resolve as resolve13 } from "path";
|
|
2863
3044
|
async function consolidateMemory(mindDir2) {
|
|
2864
|
-
const soulPath =
|
|
2865
|
-
const memoryPath =
|
|
2866
|
-
const memoryDir =
|
|
2867
|
-
const soul =
|
|
3045
|
+
const soulPath = resolve13(mindDir2, "home/SOUL.md");
|
|
3046
|
+
const memoryPath = resolve13(mindDir2, "home/MEMORY.md");
|
|
3047
|
+
const memoryDir = resolve13(mindDir2, "home/memory");
|
|
3048
|
+
const soul = readFileSync9(soulPath, "utf-8");
|
|
2868
3049
|
const logs = [];
|
|
2869
3050
|
try {
|
|
2870
3051
|
const files = readdirSync2(memoryDir).filter((f) => /^\d{4}-\d{2}-\d{2}\.md$/.test(f)).sort();
|
|
2871
3052
|
for (const filename of files) {
|
|
2872
3053
|
const date = filename.replace(".md", "");
|
|
2873
|
-
const content2 =
|
|
3054
|
+
const content2 = readFileSync9(resolve13(memoryDir, filename), "utf-8").trim();
|
|
2874
3055
|
if (content2) {
|
|
2875
3056
|
logs.push(`### ${date}
|
|
2876
3057
|
|
|
@@ -2920,7 +3101,7 @@ ${content2}`);
|
|
|
2920
3101
|
const data = await res.json();
|
|
2921
3102
|
const content = data.content.filter((b) => b.type === "text" && b.text).map((b) => b.text).join("").trim();
|
|
2922
3103
|
if (content) {
|
|
2923
|
-
|
|
3104
|
+
writeFileSync6(memoryPath, `${content}
|
|
2924
3105
|
`);
|
|
2925
3106
|
console.log("MEMORY.md created successfully.");
|
|
2926
3107
|
} else {
|
|
@@ -2930,11 +3111,11 @@ ${content2}`);
|
|
|
2930
3111
|
|
|
2931
3112
|
// src/lib/convert-session.ts
|
|
2932
3113
|
import { randomUUID } from "crypto";
|
|
2933
|
-
import { mkdirSync as
|
|
3114
|
+
import { mkdirSync as mkdirSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync7 } from "fs";
|
|
2934
3115
|
import { homedir } from "os";
|
|
2935
|
-
import { resolve as
|
|
3116
|
+
import { resolve as resolve14 } from "path";
|
|
2936
3117
|
function convertSession(opts) {
|
|
2937
|
-
const lines =
|
|
3118
|
+
const lines = readFileSync10(opts.sessionPath, "utf-8").trim().split("\n");
|
|
2938
3119
|
const sessionId = randomUUID();
|
|
2939
3120
|
const idMap = /* @__PURE__ */ new Map();
|
|
2940
3121
|
const messages = [];
|
|
@@ -3048,10 +3229,10 @@ function convertSession(opts) {
|
|
|
3048
3229
|
}
|
|
3049
3230
|
}
|
|
3050
3231
|
const projectId = opts.projectDir.replace(/\//g, "-");
|
|
3051
|
-
const sdkDir =
|
|
3052
|
-
|
|
3053
|
-
const sdkPath =
|
|
3054
|
-
|
|
3232
|
+
const sdkDir = resolve14(homedir(), ".claude", "projects", projectId);
|
|
3233
|
+
mkdirSync7(sdkDir, { recursive: true });
|
|
3234
|
+
const sdkPath = resolve14(sdkDir, `${sessionId}.jsonl`);
|
|
3235
|
+
writeFileSync7(sdkPath, `${sdkEvents.join("\n")}
|
|
3055
3236
|
`);
|
|
3056
3237
|
console.log(`Converted ${sdkEvents.length} messages \u2192 ${sdkPath}`);
|
|
3057
3238
|
return sessionId;
|
|
@@ -3102,6 +3283,194 @@ function convertAssistantContent(content) {
|
|
|
3102
3283
|
return result;
|
|
3103
3284
|
}
|
|
3104
3285
|
|
|
3286
|
+
// src/lib/daemon/turn-summarizer.ts
|
|
3287
|
+
import { and as and3, desc as desc2, eq as eq4, gt, lt as lt3, sql } from "drizzle-orm";
|
|
3288
|
+
|
|
3289
|
+
// src/lib/format-tool.ts
|
|
3290
|
+
function summarizeTool(name, input) {
|
|
3291
|
+
if (input && typeof input === "object") {
|
|
3292
|
+
const args = input;
|
|
3293
|
+
const val = args.path ?? args.command ?? args.query ?? args.url;
|
|
3294
|
+
if (typeof val === "string") {
|
|
3295
|
+
const brief = val.length > 60 ? `${val.slice(0, 57)}...` : val;
|
|
3296
|
+
return `[${name} ${brief}]`;
|
|
3297
|
+
}
|
|
3298
|
+
}
|
|
3299
|
+
return `[${name}]`;
|
|
3300
|
+
}
|
|
3301
|
+
|
|
3302
|
+
// src/lib/daemon/turn-summarizer.ts
|
|
3303
|
+
var sLog = logger_default.child("turn-summarizer");
|
|
3304
|
+
async function gatherTurnEvents(mind, session, doneId) {
|
|
3305
|
+
const db = await getDb();
|
|
3306
|
+
const conditions = [
|
|
3307
|
+
eq4(mindHistory.mind, mind),
|
|
3308
|
+
eq4(mindHistory.type, "done"),
|
|
3309
|
+
lt3(mindHistory.id, doneId)
|
|
3310
|
+
];
|
|
3311
|
+
if (session) {
|
|
3312
|
+
conditions.push(eq4(mindHistory.session, session));
|
|
3313
|
+
}
|
|
3314
|
+
const prevDone = await db.select({ id: mindHistory.id }).from(mindHistory).where(and3(...conditions)).orderBy(desc2(mindHistory.id)).limit(1);
|
|
3315
|
+
const prevDoneId = prevDone.length > 0 ? prevDone[0].id : 0;
|
|
3316
|
+
const turnConditions = [
|
|
3317
|
+
eq4(mindHistory.mind, mind),
|
|
3318
|
+
gt(mindHistory.id, prevDoneId),
|
|
3319
|
+
sql`${mindHistory.id} <= ${doneId}`
|
|
3320
|
+
];
|
|
3321
|
+
if (session) {
|
|
3322
|
+
turnConditions.push(eq4(mindHistory.session, session));
|
|
3323
|
+
}
|
|
3324
|
+
const events = await db.select({
|
|
3325
|
+
id: mindHistory.id,
|
|
3326
|
+
type: mindHistory.type,
|
|
3327
|
+
channel: mindHistory.channel,
|
|
3328
|
+
session: mindHistory.session,
|
|
3329
|
+
content: mindHistory.content,
|
|
3330
|
+
metadata: mindHistory.metadata,
|
|
3331
|
+
created_at: mindHistory.created_at
|
|
3332
|
+
}).from(mindHistory).where(and3(...turnConditions)).orderBy(mindHistory.id);
|
|
3333
|
+
return {
|
|
3334
|
+
events,
|
|
3335
|
+
fromId: events.length > 0 ? events[0].id : doneId,
|
|
3336
|
+
toId: doneId
|
|
3337
|
+
};
|
|
3338
|
+
}
|
|
3339
|
+
function buildDeterministicSummary(events) {
|
|
3340
|
+
const channels = /* @__PURE__ */ new Set();
|
|
3341
|
+
const tools = [];
|
|
3342
|
+
let hasInbound = false;
|
|
3343
|
+
let hasOutbound = false;
|
|
3344
|
+
for (const ev of events) {
|
|
3345
|
+
if (ev.type === "inbound") {
|
|
3346
|
+
hasInbound = true;
|
|
3347
|
+
if (ev.channel) channels.add(ev.channel);
|
|
3348
|
+
}
|
|
3349
|
+
if (ev.type === "outbound" || ev.type === "text") {
|
|
3350
|
+
hasOutbound = true;
|
|
3351
|
+
}
|
|
3352
|
+
if (ev.type === "tool_use" && ev.metadata) {
|
|
3353
|
+
try {
|
|
3354
|
+
const meta = JSON.parse(ev.metadata);
|
|
3355
|
+
if (meta.name) tools.push(meta.name);
|
|
3356
|
+
} catch (err) {
|
|
3357
|
+
sLog.debug(`failed to parse tool_use metadata for event ${ev.id}`, logger_default.errorData(err));
|
|
3358
|
+
}
|
|
3359
|
+
}
|
|
3360
|
+
}
|
|
3361
|
+
const parts = [];
|
|
3362
|
+
if (hasInbound) {
|
|
3363
|
+
const channelList = [...channels];
|
|
3364
|
+
parts.push(
|
|
3365
|
+
channelList.length > 0 ? `Received message on ${channelList.join(", ")}` : "Received message"
|
|
3366
|
+
);
|
|
3367
|
+
}
|
|
3368
|
+
if (tools.length > 0) {
|
|
3369
|
+
const unique = [...new Set(tools)];
|
|
3370
|
+
parts.push(`Used ${unique.join(", ")}`);
|
|
3371
|
+
}
|
|
3372
|
+
if (hasOutbound) {
|
|
3373
|
+
parts.push("Sent response");
|
|
3374
|
+
}
|
|
3375
|
+
return parts.length > 0 ? `${parts.join(". ")}.` : "Turn completed.";
|
|
3376
|
+
}
|
|
3377
|
+
function buildTranscript(events) {
|
|
3378
|
+
const lines = [];
|
|
3379
|
+
for (const ev of events) {
|
|
3380
|
+
switch (ev.type) {
|
|
3381
|
+
case "inbound":
|
|
3382
|
+
lines.push(`[inbound${ev.channel ? ` ${ev.channel}` : ""}] ${ev.content ?? ""}`);
|
|
3383
|
+
break;
|
|
3384
|
+
case "outbound":
|
|
3385
|
+
case "text":
|
|
3386
|
+
lines.push(`[response] ${(ev.content ?? "").slice(0, 500)}`);
|
|
3387
|
+
break;
|
|
3388
|
+
case "tool_use": {
|
|
3389
|
+
let toolInfo = "tool";
|
|
3390
|
+
if (ev.metadata) {
|
|
3391
|
+
try {
|
|
3392
|
+
const meta = JSON.parse(ev.metadata);
|
|
3393
|
+
toolInfo = summarizeTool(meta.name ?? "tool", meta.input ?? {});
|
|
3394
|
+
} catch (err) {
|
|
3395
|
+
sLog.debug(`failed to parse tool_use metadata for event ${ev.id}`, logger_default.errorData(err));
|
|
3396
|
+
}
|
|
3397
|
+
}
|
|
3398
|
+
lines.push(toolInfo);
|
|
3399
|
+
break;
|
|
3400
|
+
}
|
|
3401
|
+
}
|
|
3402
|
+
}
|
|
3403
|
+
return lines.join("\n");
|
|
3404
|
+
}
|
|
3405
|
+
async function summarizeTurn(mind, session, channel, doneId) {
|
|
3406
|
+
const { events, fromId, toId } = await gatherTurnEvents(mind, session, doneId);
|
|
3407
|
+
if (events.length === 0) return;
|
|
3408
|
+
const tools = [];
|
|
3409
|
+
for (const ev of events) {
|
|
3410
|
+
if (ev.type === "tool_use" && ev.metadata) {
|
|
3411
|
+
try {
|
|
3412
|
+
const meta = JSON.parse(ev.metadata);
|
|
3413
|
+
if (meta.name) tools.push(meta.name);
|
|
3414
|
+
} catch (err) {
|
|
3415
|
+
sLog.debug(`failed to parse tool_use metadata for event ${ev.id}`, logger_default.errorData(err));
|
|
3416
|
+
}
|
|
3417
|
+
}
|
|
3418
|
+
}
|
|
3419
|
+
const fromTime = events[0].created_at;
|
|
3420
|
+
const toTime = events[events.length - 1].created_at;
|
|
3421
|
+
let summaryText;
|
|
3422
|
+
let deterministic;
|
|
3423
|
+
const transcript = buildTranscript(events);
|
|
3424
|
+
if (transcript.trim()) {
|
|
3425
|
+
const summaryPrompt = await getPrompt("turn_summary");
|
|
3426
|
+
const aiResult = await aiComplete(summaryPrompt, transcript);
|
|
3427
|
+
if (aiResult) {
|
|
3428
|
+
summaryText = aiResult;
|
|
3429
|
+
deterministic = false;
|
|
3430
|
+
} else {
|
|
3431
|
+
summaryText = buildDeterministicSummary(events);
|
|
3432
|
+
deterministic = true;
|
|
3433
|
+
}
|
|
3434
|
+
} else {
|
|
3435
|
+
summaryText = buildDeterministicSummary(events);
|
|
3436
|
+
deterministic = true;
|
|
3437
|
+
}
|
|
3438
|
+
const metadata = {
|
|
3439
|
+
deterministic,
|
|
3440
|
+
tool_count: tools.length,
|
|
3441
|
+
tools: [...new Set(tools)],
|
|
3442
|
+
from_id: fromId,
|
|
3443
|
+
to_id: toId,
|
|
3444
|
+
from_time: fromTime,
|
|
3445
|
+
to_time: toTime
|
|
3446
|
+
};
|
|
3447
|
+
const db = await getDb();
|
|
3448
|
+
try {
|
|
3449
|
+
await db.insert(mindHistory).values({
|
|
3450
|
+
mind,
|
|
3451
|
+
type: "summary",
|
|
3452
|
+
session: session ?? null,
|
|
3453
|
+
channel: channel ?? null,
|
|
3454
|
+
content: summaryText,
|
|
3455
|
+
metadata: JSON.stringify(metadata)
|
|
3456
|
+
});
|
|
3457
|
+
} catch (err) {
|
|
3458
|
+
sLog.error(
|
|
3459
|
+
`failed to persist summary for ${mind} (events ${fromId}-${toId})`,
|
|
3460
|
+
logger_default.errorData(err)
|
|
3461
|
+
);
|
|
3462
|
+
return;
|
|
3463
|
+
}
|
|
3464
|
+
publish2(mind, {
|
|
3465
|
+
mind,
|
|
3466
|
+
type: "summary",
|
|
3467
|
+
session,
|
|
3468
|
+
channel,
|
|
3469
|
+
content: summaryText,
|
|
3470
|
+
metadata
|
|
3471
|
+
});
|
|
3472
|
+
}
|
|
3473
|
+
|
|
3105
3474
|
// src/lib/health.ts
|
|
3106
3475
|
async function checkHealth(port) {
|
|
3107
3476
|
try {
|
|
@@ -3117,7 +3486,7 @@ async function checkHealth(port) {
|
|
|
3117
3486
|
}
|
|
3118
3487
|
|
|
3119
3488
|
// src/lib/variant-cleanup.ts
|
|
3120
|
-
import { existsSync as
|
|
3489
|
+
import { existsSync as existsSync12, rmSync as rmSync4 } from "fs";
|
|
3121
3490
|
async function cleanupVariant(variantName, projectRoot, variantPath, opts) {
|
|
3122
3491
|
if (opts?.stop) {
|
|
3123
3492
|
try {
|
|
@@ -3126,14 +3495,14 @@ async function cleanupVariant(variantName, projectRoot, variantPath, opts) {
|
|
|
3126
3495
|
logger_default.warn(`failed to stop variant ${variantName}`, logger_default.errorData(err));
|
|
3127
3496
|
}
|
|
3128
3497
|
}
|
|
3129
|
-
const { findMind: findMind2 } = await import("./registry-
|
|
3498
|
+
const { findMind: findMind2 } = await import("./registry-ODSALQQL.js");
|
|
3130
3499
|
const variantEntry = await findMind2(variantName);
|
|
3131
3500
|
const branchName = variantEntry?.branch ?? variantName;
|
|
3132
|
-
if (
|
|
3501
|
+
if (existsSync12(variantPath)) {
|
|
3133
3502
|
try {
|
|
3134
3503
|
await gitExec(["worktree", "remove", "--force", variantPath], { cwd: projectRoot });
|
|
3135
3504
|
} catch {
|
|
3136
|
-
|
|
3505
|
+
rmSync4(variantPath, { recursive: true, force: true });
|
|
3137
3506
|
try {
|
|
3138
3507
|
await gitExec(["worktree", "prune"], { cwd: projectRoot });
|
|
3139
3508
|
} catch (err) {
|
|
@@ -3163,8 +3532,8 @@ async function cleanupVariant(variantName, projectRoot, variantPath, opts) {
|
|
|
3163
3532
|
}
|
|
3164
3533
|
|
|
3165
3534
|
// src/lib/variants.ts
|
|
3166
|
-
import { existsSync as
|
|
3167
|
-
import { resolve as
|
|
3535
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync8, readFileSync as readFileSync11, writeFileSync as writeFileSync8 } from "fs";
|
|
3536
|
+
import { resolve as resolve15 } from "path";
|
|
3168
3537
|
async function checkHealth2(port) {
|
|
3169
3538
|
try {
|
|
3170
3539
|
const res = await fetch(`http://127.0.0.1:${port}/health`, {
|
|
@@ -3193,8 +3562,8 @@ async function getMindStatus(name, port) {
|
|
|
3193
3562
|
const manager = getMindManager();
|
|
3194
3563
|
let status = "stopped";
|
|
3195
3564
|
try {
|
|
3196
|
-
const { getSleepManagerIfReady } = await import("./sleep-manager-
|
|
3197
|
-
if (
|
|
3565
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-O7YQFCV5.js");
|
|
3566
|
+
if (getSleepManagerIfReady2()?.isSleeping(name)) {
|
|
3198
3567
|
status = "sleeping";
|
|
3199
3568
|
}
|
|
3200
3569
|
} catch {
|
|
@@ -3241,7 +3610,7 @@ async function initTemplateBranch(projectRoot, composedDir, manifest, mindName,
|
|
|
3241
3610
|
await gitExec(["commit", "-m", "initial commit"], opts);
|
|
3242
3611
|
}
|
|
3243
3612
|
async function updateTemplateBranch(projectRoot, template, mindName) {
|
|
3244
|
-
const tempWorktree =
|
|
3613
|
+
const tempWorktree = resolve16(projectRoot, ".variants", "_template_update");
|
|
3245
3614
|
let branchExists = false;
|
|
3246
3615
|
try {
|
|
3247
3616
|
await gitExec(["rev-parse", "--verify", TEMPLATE_BRANCH], { cwd: projectRoot });
|
|
@@ -3252,8 +3621,8 @@ async function updateTemplateBranch(projectRoot, template, mindName) {
|
|
|
3252
3621
|
await gitExec(["worktree", "remove", "--force", tempWorktree], { cwd: projectRoot });
|
|
3253
3622
|
} catch {
|
|
3254
3623
|
}
|
|
3255
|
-
if (
|
|
3256
|
-
|
|
3624
|
+
if (existsSync14(tempWorktree)) {
|
|
3625
|
+
rmSync5(tempWorktree, { recursive: true, force: true });
|
|
3257
3626
|
}
|
|
3258
3627
|
const templatesRoot = findTemplatesRoot();
|
|
3259
3628
|
const { composedDir, manifest } = composeTemplate(templatesRoot, template);
|
|
@@ -3273,9 +3642,9 @@ async function updateTemplateBranch(projectRoot, template, mindName) {
|
|
|
3273
3642
|
});
|
|
3274
3643
|
}
|
|
3275
3644
|
copyTemplateToDir(composedDir, tempWorktree, mindName, manifest);
|
|
3276
|
-
const initDir =
|
|
3277
|
-
if (
|
|
3278
|
-
|
|
3645
|
+
const initDir = resolve16(tempWorktree, ".init");
|
|
3646
|
+
if (existsSync14(initDir)) {
|
|
3647
|
+
rmSync5(initDir, { recursive: true, force: true });
|
|
3279
3648
|
}
|
|
3280
3649
|
await gitExec(["add", "-A"], { cwd: tempWorktree });
|
|
3281
3650
|
try {
|
|
@@ -3288,10 +3657,10 @@ async function updateTemplateBranch(projectRoot, template, mindName) {
|
|
|
3288
3657
|
await gitExec(["worktree", "remove", "--force", tempWorktree], { cwd: projectRoot });
|
|
3289
3658
|
} catch {
|
|
3290
3659
|
}
|
|
3291
|
-
if (
|
|
3292
|
-
|
|
3660
|
+
if (existsSync14(tempWorktree)) {
|
|
3661
|
+
rmSync5(tempWorktree, { recursive: true, force: true });
|
|
3293
3662
|
}
|
|
3294
|
-
|
|
3663
|
+
rmSync5(composedDir, { recursive: true, force: true });
|
|
3295
3664
|
}
|
|
3296
3665
|
}
|
|
3297
3666
|
async function mergeTemplateBranch(worktreeDir) {
|
|
@@ -3314,14 +3683,14 @@ async function mergeTemplateBranch(worktreeDir) {
|
|
|
3314
3683
|
async function npmInstallAsMind(cwd, mindName) {
|
|
3315
3684
|
if (isIsolationEnabled()) {
|
|
3316
3685
|
const [cmd, args] = await wrapForIsolation("npm", ["install"], mindName);
|
|
3317
|
-
await exec(cmd, args, { cwd, env: { ...process.env, HOME:
|
|
3686
|
+
await exec(cmd, args, { cwd, env: { ...process.env, HOME: resolve16(cwd, "home") } });
|
|
3318
3687
|
} else {
|
|
3319
3688
|
await exec("npm", ["install"], { cwd });
|
|
3320
3689
|
}
|
|
3321
3690
|
}
|
|
3322
3691
|
async function importFromArchive(c, tempDir, nameOverride, manifest) {
|
|
3323
|
-
const extractedMindDir =
|
|
3324
|
-
if (!
|
|
3692
|
+
const extractedMindDir = resolve16(tempDir, "mind");
|
|
3693
|
+
if (!existsSync14(extractedMindDir)) {
|
|
3325
3694
|
return c.json({ error: "Invalid archive: missing mind/ directory" }, 400);
|
|
3326
3695
|
}
|
|
3327
3696
|
if (!manifest?.includes || !manifest.name || !manifest.template) {
|
|
@@ -3339,21 +3708,21 @@ async function importFromFullArchive(c, tempDir, extractedMindDir, nameOverride,
|
|
|
3339
3708
|
if (await findMind(name)) return c.json({ error: `Mind already exists: ${name}` }, 409);
|
|
3340
3709
|
ensureVoluteHome();
|
|
3341
3710
|
const dest = mindDir(name);
|
|
3342
|
-
if (
|
|
3711
|
+
if (existsSync14(dest)) return c.json({ error: "Mind directory already exists" }, 409);
|
|
3343
3712
|
try {
|
|
3344
3713
|
cpSync(extractedMindDir, dest, { recursive: true });
|
|
3345
3714
|
if (!manifest.includes.identity) {
|
|
3346
3715
|
generateIdentity(dest);
|
|
3347
3716
|
}
|
|
3348
3717
|
const state = stateDir(name);
|
|
3349
|
-
|
|
3350
|
-
const channelsJson =
|
|
3351
|
-
if (
|
|
3352
|
-
cpSync(channelsJson,
|
|
3718
|
+
mkdirSync9(state, { recursive: true });
|
|
3719
|
+
const channelsJson = resolve16(tempDir, "state/channels.json");
|
|
3720
|
+
if (existsSync14(channelsJson)) {
|
|
3721
|
+
cpSync(channelsJson, resolve16(state, "channels.json"));
|
|
3353
3722
|
}
|
|
3354
|
-
const envJson =
|
|
3355
|
-
if (
|
|
3356
|
-
cpSync(envJson,
|
|
3723
|
+
const envJson = resolve16(tempDir, "state/env.json");
|
|
3724
|
+
if (existsSync14(envJson)) {
|
|
3725
|
+
cpSync(envJson, resolve16(state, "env.json"));
|
|
3357
3726
|
}
|
|
3358
3727
|
const port = await nextPort();
|
|
3359
3728
|
await addMind(name, port, manifest.stage, manifest.template);
|
|
@@ -3362,36 +3731,36 @@ async function importFromFullArchive(c, tempDir, extractedMindDir, nameOverride,
|
|
|
3362
3731
|
} catch (err) {
|
|
3363
3732
|
logger_default.warn(`failed to set template hash for ${name}`, logger_default.errorData(err));
|
|
3364
3733
|
}
|
|
3365
|
-
const homeDir =
|
|
3734
|
+
const homeDir = resolve16(dest, "home");
|
|
3366
3735
|
ensureVoluteGroup();
|
|
3367
3736
|
createMindUser(name, homeDir);
|
|
3368
3737
|
chownMindDir(dest, name);
|
|
3369
3738
|
await npmInstallAsMind(dest, name);
|
|
3370
3739
|
await importHistoryFromArchive(name, tempDir);
|
|
3371
3740
|
importSessionsFromArchive(dest, tempDir);
|
|
3372
|
-
if (!
|
|
3741
|
+
if (!existsSync14(resolve16(dest, ".git"))) {
|
|
3373
3742
|
try {
|
|
3374
|
-
const env = isIsolationEnabled() ? { ...process.env, HOME:
|
|
3743
|
+
const env = isIsolationEnabled() ? { ...process.env, HOME: resolve16(dest, "home") } : void 0;
|
|
3375
3744
|
await gitExec(["init"], { cwd: dest, mindName: name, env });
|
|
3376
3745
|
await configureGitIdentity(name, { cwd: dest, mindName: name, env });
|
|
3377
3746
|
await gitExec(["add", "-A"], { cwd: dest, mindName: name, env });
|
|
3378
3747
|
await gitExec(["commit", "-m", "import from archive"], { cwd: dest, mindName: name, env });
|
|
3379
3748
|
} catch (err) {
|
|
3380
3749
|
logger_default.error(`git setup failed for imported mind ${name}`, logger_default.errorData(err));
|
|
3381
|
-
|
|
3750
|
+
rmSync5(resolve16(dest, ".git"), { recursive: true, force: true });
|
|
3382
3751
|
}
|
|
3383
3752
|
}
|
|
3384
3753
|
chownMindDir(dest, name);
|
|
3385
|
-
|
|
3754
|
+
rmSync5(tempDir, { recursive: true, force: true });
|
|
3386
3755
|
return c.json({ ok: true, name, port, message: `Imported mind: ${name} (port ${port})` });
|
|
3387
3756
|
} catch (err) {
|
|
3388
|
-
if (
|
|
3757
|
+
if (existsSync14(dest)) rmSync5(dest, { recursive: true, force: true });
|
|
3389
3758
|
try {
|
|
3390
3759
|
await removeMind(name);
|
|
3391
3760
|
} catch (cleanupErr) {
|
|
3392
3761
|
logger_default.error(`Failed to clean up registry for ${name}`, logger_default.errorData(cleanupErr));
|
|
3393
3762
|
}
|
|
3394
|
-
|
|
3763
|
+
rmSync5(tempDir, { recursive: true, force: true });
|
|
3395
3764
|
return c.json({ error: err instanceof Error ? err.message : "Failed to import mind" }, 500);
|
|
3396
3765
|
}
|
|
3397
3766
|
}
|
|
@@ -3402,7 +3771,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
|
|
|
3402
3771
|
if (await findMind(name)) return c.json({ error: `Mind already exists: ${name}` }, 409);
|
|
3403
3772
|
ensureVoluteHome();
|
|
3404
3773
|
const dest = mindDir(name);
|
|
3405
|
-
if (
|
|
3774
|
+
if (existsSync14(dest)) return c.json({ error: "Mind directory already exists" }, 409);
|
|
3406
3775
|
const templatesRoot = findTemplatesRoot();
|
|
3407
3776
|
const { composedDir, manifest: templateManifest } = composeTemplate(
|
|
3408
3777
|
templatesRoot,
|
|
@@ -3411,40 +3780,40 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
|
|
|
3411
3780
|
try {
|
|
3412
3781
|
copyTemplateToDir(composedDir, dest, name, templateManifest);
|
|
3413
3782
|
applyInitFiles(dest);
|
|
3414
|
-
const extractedHome =
|
|
3415
|
-
if (
|
|
3416
|
-
cpSync(extractedHome,
|
|
3783
|
+
const extractedHome = resolve16(extractedMindDir, "home");
|
|
3784
|
+
if (existsSync14(extractedHome)) {
|
|
3785
|
+
cpSync(extractedHome, resolve16(dest, "home"), { recursive: true });
|
|
3417
3786
|
}
|
|
3418
|
-
const extractedMindInternal =
|
|
3419
|
-
if (
|
|
3420
|
-
cpSync(extractedMindInternal,
|
|
3787
|
+
const extractedMindInternal = resolve16(extractedMindDir, ".mind");
|
|
3788
|
+
if (existsSync14(extractedMindInternal)) {
|
|
3789
|
+
cpSync(extractedMindInternal, resolve16(dest, ".mind"), { recursive: true });
|
|
3421
3790
|
}
|
|
3422
|
-
const identityDir =
|
|
3791
|
+
const identityDir = resolve16(dest, ".mind/identity");
|
|
3423
3792
|
let publicKeyPem;
|
|
3424
|
-
if (!manifest.includes.identity || !
|
|
3793
|
+
if (!manifest.includes.identity || !existsSync14(resolve16(identityDir, "private.pem"))) {
|
|
3425
3794
|
({ publicKeyPem } = generateIdentity(dest));
|
|
3426
3795
|
} else {
|
|
3427
|
-
publicKeyPem =
|
|
3796
|
+
publicKeyPem = readFileSync12(resolve16(identityDir, "public.pem"), "utf-8");
|
|
3428
3797
|
}
|
|
3429
|
-
const promptsPath =
|
|
3430
|
-
if (!
|
|
3798
|
+
const promptsPath = resolve16(dest, "home/.config/prompts.json");
|
|
3799
|
+
if (!existsSync14(promptsPath)) {
|
|
3431
3800
|
const mindPrompts = await getMindPromptDefaults();
|
|
3432
|
-
|
|
3801
|
+
writeFileSync9(promptsPath, `${JSON.stringify(mindPrompts, null, 2)}
|
|
3433
3802
|
`);
|
|
3434
3803
|
}
|
|
3435
3804
|
const state = stateDir(name);
|
|
3436
|
-
|
|
3437
|
-
const channelsJson =
|
|
3438
|
-
if (
|
|
3439
|
-
cpSync(channelsJson,
|
|
3805
|
+
mkdirSync9(state, { recursive: true });
|
|
3806
|
+
const channelsJson = resolve16(tempDir, "state/channels.json");
|
|
3807
|
+
if (existsSync14(channelsJson)) {
|
|
3808
|
+
cpSync(channelsJson, resolve16(state, "channels.json"));
|
|
3440
3809
|
}
|
|
3441
|
-
const envJson =
|
|
3442
|
-
if (
|
|
3443
|
-
cpSync(envJson,
|
|
3810
|
+
const envJson = resolve16(tempDir, "state/env.json");
|
|
3811
|
+
if (existsSync14(envJson)) {
|
|
3812
|
+
cpSync(envJson, resolve16(state, "env.json"));
|
|
3444
3813
|
}
|
|
3445
3814
|
const port = await nextPort();
|
|
3446
3815
|
await addMind(name, port, manifest.stage, manifest.template);
|
|
3447
|
-
const homeDir =
|
|
3816
|
+
const homeDir = resolve16(dest, "home");
|
|
3448
3817
|
ensureVoluteGroup();
|
|
3449
3818
|
createMindUser(name, homeDir);
|
|
3450
3819
|
chownMindDir(dest, name);
|
|
@@ -3457,7 +3826,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
|
|
|
3457
3826
|
await initTemplateBranch(dest, composedDir, templateManifest, name, env);
|
|
3458
3827
|
} catch (err) {
|
|
3459
3828
|
logger_default.error(`git setup failed for imported mind ${name}`, logger_default.errorData(err));
|
|
3460
|
-
|
|
3829
|
+
rmSync5(resolve16(dest, ".git"), { recursive: true, force: true });
|
|
3461
3830
|
gitWarning = "Git setup failed \u2014 variants and upgrades won't be available until git is initialized.";
|
|
3462
3831
|
}
|
|
3463
3832
|
try {
|
|
@@ -3465,7 +3834,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
|
|
|
3465
3834
|
} catch (err) {
|
|
3466
3835
|
logger_default.warn(`failed to add shared worktree for ${name}`, logger_default.errorData(err));
|
|
3467
3836
|
}
|
|
3468
|
-
const skillSet = manifest.stage === "seed" ? SEED_SKILLS :
|
|
3837
|
+
const skillSet = manifest.stage === "seed" ? SEED_SKILLS : getStandardSkillsWithExtensions();
|
|
3469
3838
|
const skillWarnings = [];
|
|
3470
3839
|
for (const skillId of skillSet) {
|
|
3471
3840
|
try {
|
|
@@ -3481,7 +3850,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
|
|
|
3481
3850
|
publishPublicKey(name, publicKeyPem).catch(
|
|
3482
3851
|
(err) => logger_default.warn(`failed to publish key for ${name}`, { error: err.message })
|
|
3483
3852
|
);
|
|
3484
|
-
|
|
3853
|
+
rmSync5(tempDir, { recursive: true, force: true });
|
|
3485
3854
|
return c.json({
|
|
3486
3855
|
ok: true,
|
|
3487
3856
|
name,
|
|
@@ -3492,24 +3861,24 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
|
|
|
3492
3861
|
...skillWarnings.length > 0 && { skillWarnings }
|
|
3493
3862
|
});
|
|
3494
3863
|
} catch (err) {
|
|
3495
|
-
if (
|
|
3864
|
+
if (existsSync14(dest)) rmSync5(dest, { recursive: true, force: true });
|
|
3496
3865
|
try {
|
|
3497
3866
|
await removeMind(name);
|
|
3498
3867
|
} catch (cleanupErr) {
|
|
3499
3868
|
logger_default.error(`Failed to clean up registry for ${name}`, logger_default.errorData(cleanupErr));
|
|
3500
3869
|
}
|
|
3501
|
-
|
|
3870
|
+
rmSync5(tempDir, { recursive: true, force: true });
|
|
3502
3871
|
return c.json({ error: err instanceof Error ? err.message : "Failed to import mind" }, 500);
|
|
3503
3872
|
} finally {
|
|
3504
|
-
|
|
3873
|
+
rmSync5(composedDir, { recursive: true, force: true });
|
|
3505
3874
|
}
|
|
3506
3875
|
}
|
|
3507
3876
|
async function importHistoryFromArchive(name, tempDir) {
|
|
3508
|
-
const historyJsonl =
|
|
3509
|
-
if (!
|
|
3877
|
+
const historyJsonl = resolve16(tempDir, "history.jsonl");
|
|
3878
|
+
if (!existsSync14(historyJsonl)) return;
|
|
3510
3879
|
try {
|
|
3511
3880
|
const db = await getDb();
|
|
3512
|
-
const lines =
|
|
3881
|
+
const lines = readFileSync12(historyJsonl, "utf-8").trim().split("\n");
|
|
3513
3882
|
let imported = 0;
|
|
3514
3883
|
let failed = 0;
|
|
3515
3884
|
for (const line of lines) {
|
|
@@ -3545,13 +3914,13 @@ async function importHistoryFromArchive(name, tempDir) {
|
|
|
3545
3914
|
}
|
|
3546
3915
|
}
|
|
3547
3916
|
function importSessionsFromArchive(dest, tempDir) {
|
|
3548
|
-
const sessionsDir =
|
|
3549
|
-
if (!
|
|
3917
|
+
const sessionsDir = resolve16(tempDir, "sessions");
|
|
3918
|
+
if (!existsSync14(sessionsDir)) return;
|
|
3550
3919
|
try {
|
|
3551
|
-
const destSessions =
|
|
3552
|
-
|
|
3920
|
+
const destSessions = resolve16(dest, ".mind/sessions");
|
|
3921
|
+
mkdirSync9(destSessions, { recursive: true });
|
|
3553
3922
|
for (const file of readdirSync3(sessionsDir)) {
|
|
3554
|
-
cpSync(
|
|
3923
|
+
cpSync(resolve16(sessionsDir, file), resolve16(destSessions, file));
|
|
3555
3924
|
}
|
|
3556
3925
|
} catch (err) {
|
|
3557
3926
|
logger_default.error("Failed to import sessions from archive", logger_default.errorData(err));
|
|
@@ -3566,7 +3935,7 @@ var createMindSchema = z4.object({
|
|
|
3566
3935
|
seedSoul: z4.string().optional(),
|
|
3567
3936
|
skills: z4.array(z4.string()).optional()
|
|
3568
3937
|
});
|
|
3569
|
-
var
|
|
3938
|
+
var app12 = new Hono12().post("/", requireAdmin, zValidator4("json", createMindSchema), async (c) => {
|
|
3570
3939
|
const body = c.req.valid("json");
|
|
3571
3940
|
const { name, template = "claude" } = body;
|
|
3572
3941
|
const nameErr = validateMindName(name);
|
|
@@ -3574,7 +3943,7 @@ var app11 = new Hono11().post("/", requireAdmin, zValidator4("json", createMindS
|
|
|
3574
3943
|
if (await findMind(name)) return c.json({ error: `Mind already exists: ${name}` }, 409);
|
|
3575
3944
|
ensureVoluteHome();
|
|
3576
3945
|
const dest = mindDir(name);
|
|
3577
|
-
if (
|
|
3946
|
+
if (existsSync14(dest)) return c.json({ error: "Mind directory already exists" }, 409);
|
|
3578
3947
|
const templatesRoot = findTemplatesRoot();
|
|
3579
3948
|
const { composedDir, manifest } = composeTemplate(templatesRoot, template);
|
|
3580
3949
|
try {
|
|
@@ -3607,15 +3976,15 @@ var app11 = new Hono11().post("/", requireAdmin, zValidator4("json", createMindS
|
|
|
3607
3976
|
writeVoluteConfig(dest, config);
|
|
3608
3977
|
}
|
|
3609
3978
|
if (body.model) {
|
|
3610
|
-
const configPath =
|
|
3611
|
-
const existing =
|
|
3979
|
+
const configPath = resolve16(dest, "home/.config/config.json");
|
|
3980
|
+
const existing = existsSync14(configPath) ? JSON.parse(readFileSync12(configPath, "utf-8")) : {};
|
|
3612
3981
|
existing.model = body.model;
|
|
3613
|
-
|
|
3982
|
+
writeFileSync9(configPath, `${JSON.stringify(existing, null, 2)}
|
|
3614
3983
|
`);
|
|
3615
3984
|
}
|
|
3616
3985
|
const mindPrompts = await getMindPromptDefaults();
|
|
3617
|
-
|
|
3618
|
-
|
|
3986
|
+
writeFileSync9(
|
|
3987
|
+
resolve16(dest, "home/.config/prompts.json"),
|
|
3619
3988
|
`${JSON.stringify(mindPrompts, null, 2)}
|
|
3620
3989
|
`
|
|
3621
3990
|
);
|
|
@@ -3626,7 +3995,7 @@ var app11 = new Hono11().post("/", requireAdmin, zValidator4("json", createMindS
|
|
|
3626
3995
|
} catch (err) {
|
|
3627
3996
|
logger_default.warn(`failed to set template hash for ${name}`, logger_default.errorData(err));
|
|
3628
3997
|
}
|
|
3629
|
-
const homeDir =
|
|
3998
|
+
const homeDir = resolve16(dest, "home");
|
|
3630
3999
|
ensureVoluteGroup();
|
|
3631
4000
|
createMindUser(name, homeDir);
|
|
3632
4001
|
chownMindDir(dest, name);
|
|
@@ -3639,7 +4008,7 @@ var app11 = new Hono11().post("/", requireAdmin, zValidator4("json", createMindS
|
|
|
3639
4008
|
await initTemplateBranch(dest, composedDir, manifest, name, env);
|
|
3640
4009
|
} catch (err) {
|
|
3641
4010
|
logger_default.error(`git setup failed for ${name}`, logger_default.errorData(err));
|
|
3642
|
-
|
|
4011
|
+
rmSync5(resolve16(dest, ".git"), { recursive: true, force: true });
|
|
3643
4012
|
gitWarning = "Git setup failed \u2014 variants and upgrades won't be available until git is initialized.";
|
|
3644
4013
|
}
|
|
3645
4014
|
try {
|
|
@@ -3654,9 +4023,9 @@ The human who planted you described you as: "${body.description}"
|
|
|
3654
4023
|
` : "";
|
|
3655
4024
|
const seedSoulRaw = body.seedSoul ?? await getPrompt("seed_soul", { name, description: descLine });
|
|
3656
4025
|
const seedSoul = body.seedSoul ? substitute(seedSoulRaw, { name, description: descLine }) : seedSoulRaw;
|
|
3657
|
-
|
|
4026
|
+
writeFileSync9(resolve16(dest, "home/SOUL.md"), seedSoul);
|
|
3658
4027
|
}
|
|
3659
|
-
const skillSet = body.skills ?? (body.stage === "seed" ? SEED_SKILLS :
|
|
4028
|
+
const skillSet = body.skills ?? (body.stage === "seed" ? SEED_SKILLS : getStandardSkillsWithExtensions());
|
|
3660
4029
|
const skillWarnings = [];
|
|
3661
4030
|
for (const skillId of skillSet) {
|
|
3662
4031
|
try {
|
|
@@ -3669,11 +4038,11 @@ The human who planted you described you as: "${body.description}"
|
|
|
3669
4038
|
if (body.stage !== "seed") {
|
|
3670
4039
|
const customSoul = await getPromptIfCustom("default_soul");
|
|
3671
4040
|
if (customSoul) {
|
|
3672
|
-
|
|
4041
|
+
writeFileSync9(resolve16(dest, "home/SOUL.md"), customSoul.replace(/\{\{name\}\}/g, name));
|
|
3673
4042
|
}
|
|
3674
4043
|
const customMemory = await getPromptIfCustom("default_memory");
|
|
3675
4044
|
if (customMemory) {
|
|
3676
|
-
|
|
4045
|
+
writeFileSync9(resolve16(dest, "home/MEMORY.md"), customMemory);
|
|
3677
4046
|
}
|
|
3678
4047
|
}
|
|
3679
4048
|
publishPublicKey(name, publicKeyPem).catch(
|
|
@@ -3702,14 +4071,14 @@ The human who planted you described you as: "${body.description}"
|
|
|
3702
4071
|
...skillWarnings.length > 0 && { skillWarnings }
|
|
3703
4072
|
});
|
|
3704
4073
|
} catch (err) {
|
|
3705
|
-
if (
|
|
4074
|
+
if (existsSync14(dest)) rmSync5(dest, { recursive: true, force: true });
|
|
3706
4075
|
try {
|
|
3707
4076
|
await removeMind(name);
|
|
3708
4077
|
} catch {
|
|
3709
4078
|
}
|
|
3710
4079
|
return c.json({ error: err instanceof Error ? err.message : "Failed to create mind" }, 500);
|
|
3711
4080
|
} finally {
|
|
3712
|
-
|
|
4081
|
+
rmSync5(composedDir, { recursive: true, force: true });
|
|
3713
4082
|
}
|
|
3714
4083
|
}).post("/import", requireAdmin, async (c) => {
|
|
3715
4084
|
let body;
|
|
@@ -3722,13 +4091,13 @@ The human who planted you described you as: "${body.description}"
|
|
|
3722
4091
|
return importFromArchive(c, body.archivePath, body.name, body.manifest);
|
|
3723
4092
|
}
|
|
3724
4093
|
const wsDir = body.workspacePath;
|
|
3725
|
-
if (!wsDir || !
|
|
4094
|
+
if (!wsDir || !existsSync14(resolve16(wsDir, "SOUL.md")) || !existsSync14(resolve16(wsDir, "IDENTITY.md"))) {
|
|
3726
4095
|
return c.json({ error: "Invalid workspace: missing SOUL.md or IDENTITY.md" }, 400);
|
|
3727
4096
|
}
|
|
3728
|
-
const soul =
|
|
3729
|
-
const identity =
|
|
3730
|
-
const userPath =
|
|
3731
|
-
const user =
|
|
4097
|
+
const soul = readFileSync12(resolve16(wsDir, "SOUL.md"), "utf-8");
|
|
4098
|
+
const identity = readFileSync12(resolve16(wsDir, "IDENTITY.md"), "utf-8");
|
|
4099
|
+
const userPath = resolve16(wsDir, "USER.md");
|
|
4100
|
+
const user = existsSync14(userPath) ? readFileSync12(userPath, "utf-8") : "";
|
|
3732
4101
|
const name = body.name ?? parseNameFromIdentity(identity) ?? "imported-mind";
|
|
3733
4102
|
const template = body.template ?? "claude";
|
|
3734
4103
|
const nameErr = validateMindName(name);
|
|
@@ -3748,33 +4117,33 @@ ${user.trimEnd()}
|
|
|
3748
4117
|
` : "";
|
|
3749
4118
|
ensureVoluteHome();
|
|
3750
4119
|
const dest = mindDir(name);
|
|
3751
|
-
if (
|
|
4120
|
+
if (existsSync14(dest)) return c.json({ error: "Mind directory already exists" }, 409);
|
|
3752
4121
|
const templatesRoot = findTemplatesRoot();
|
|
3753
4122
|
const { composedDir, manifest } = composeTemplate(templatesRoot, template);
|
|
3754
4123
|
try {
|
|
3755
4124
|
copyTemplateToDir(composedDir, dest, name, manifest);
|
|
3756
4125
|
applyInitFiles(dest);
|
|
3757
4126
|
const { publicKeyPem: importPublicKey } = generateIdentity(dest);
|
|
3758
|
-
|
|
3759
|
-
const wsMemoryPath =
|
|
3760
|
-
const hasMemory =
|
|
4127
|
+
writeFileSync9(resolve16(dest, "home/SOUL.md"), mergedSoul);
|
|
4128
|
+
const wsMemoryPath = resolve16(wsDir, "MEMORY.md");
|
|
4129
|
+
const hasMemory = existsSync14(wsMemoryPath);
|
|
3761
4130
|
if (hasMemory) {
|
|
3762
|
-
const existingMemory =
|
|
3763
|
-
|
|
3764
|
-
|
|
4131
|
+
const existingMemory = readFileSync12(wsMemoryPath, "utf-8");
|
|
4132
|
+
writeFileSync9(
|
|
4133
|
+
resolve16(dest, "home/MEMORY.md"),
|
|
3765
4134
|
`${existingMemory.trimEnd()}${mergedMemoryExtra}`
|
|
3766
4135
|
);
|
|
3767
4136
|
} else if (user) {
|
|
3768
|
-
|
|
4137
|
+
writeFileSync9(resolve16(dest, "home/MEMORY.md"), `${user.trimEnd()}
|
|
3769
4138
|
`);
|
|
3770
4139
|
}
|
|
3771
|
-
const wsMemoryDir =
|
|
4140
|
+
const wsMemoryDir = resolve16(wsDir, "memory");
|
|
3772
4141
|
let dailyLogCount = 0;
|
|
3773
|
-
if (
|
|
3774
|
-
const destMemoryDir =
|
|
4142
|
+
if (existsSync14(wsMemoryDir)) {
|
|
4143
|
+
const destMemoryDir = resolve16(dest, "home/memory");
|
|
3775
4144
|
const files = readdirSync3(wsMemoryDir).filter((f) => f.endsWith(".md"));
|
|
3776
4145
|
for (const file of files) {
|
|
3777
|
-
cpSync(
|
|
4146
|
+
cpSync(resolve16(wsMemoryDir, file), resolve16(destMemoryDir, file));
|
|
3778
4147
|
}
|
|
3779
4148
|
dailyLogCount = files.length;
|
|
3780
4149
|
}
|
|
@@ -3785,7 +4154,7 @@ ${user.trimEnd()}
|
|
|
3785
4154
|
} catch (err) {
|
|
3786
4155
|
logger_default.warn(`failed to set template hash for ${name}`, logger_default.errorData(err));
|
|
3787
4156
|
}
|
|
3788
|
-
const homeDir =
|
|
4157
|
+
const homeDir = resolve16(dest, "home");
|
|
3789
4158
|
ensureVoluteGroup();
|
|
3790
4159
|
createMindUser(name, homeDir);
|
|
3791
4160
|
chownMindDir(dest, name);
|
|
@@ -3793,20 +4162,20 @@ ${user.trimEnd()}
|
|
|
3793
4162
|
if (!hasMemory && dailyLogCount > 0) {
|
|
3794
4163
|
await consolidateMemory(dest);
|
|
3795
4164
|
}
|
|
3796
|
-
const env = isIsolationEnabled() ? { ...process.env, HOME:
|
|
4165
|
+
const env = isIsolationEnabled() ? { ...process.env, HOME: resolve16(dest, "home") } : void 0;
|
|
3797
4166
|
await gitExec(["init"], { cwd: dest, mindName: name, env });
|
|
3798
4167
|
await configureGitIdentity(name, { cwd: dest, mindName: name, env });
|
|
3799
4168
|
await gitExec(["add", "-A"], { cwd: dest, mindName: name, env });
|
|
3800
4169
|
await gitExec(["commit", "-m", "import from OpenClaw"], { cwd: dest, mindName: name, env });
|
|
3801
|
-
const sessionFile = body.sessionPath ?
|
|
3802
|
-
if (sessionFile &&
|
|
4170
|
+
const sessionFile = body.sessionPath ? resolve16(body.sessionPath) : findOpenClawSession(wsDir);
|
|
4171
|
+
if (sessionFile && existsSync14(sessionFile)) {
|
|
3803
4172
|
if (template === "pi") {
|
|
3804
4173
|
importPiSession(sessionFile, dest);
|
|
3805
4174
|
} else if (template === "claude") {
|
|
3806
4175
|
const sessionId = convertSession({ sessionPath: sessionFile, projectDir: dest });
|
|
3807
|
-
const mindRuntimeDir =
|
|
3808
|
-
|
|
3809
|
-
|
|
4176
|
+
const mindRuntimeDir = resolve16(dest, ".mind");
|
|
4177
|
+
mkdirSync9(mindRuntimeDir, { recursive: true });
|
|
4178
|
+
writeFileSync9(resolve16(mindRuntimeDir, "session.json"), JSON.stringify({ sessionId }));
|
|
3810
4179
|
}
|
|
3811
4180
|
}
|
|
3812
4181
|
importOpenClawConnectors(name, dest);
|
|
@@ -3821,14 +4190,14 @@ ${user.trimEnd()}
|
|
|
3821
4190
|
);
|
|
3822
4191
|
return c.json({ ok: true, name, port, message: `Imported mind: ${name} (port ${port})` });
|
|
3823
4192
|
} catch (err) {
|
|
3824
|
-
if (
|
|
4193
|
+
if (existsSync14(dest)) rmSync5(dest, { recursive: true, force: true });
|
|
3825
4194
|
try {
|
|
3826
4195
|
await removeMind(name);
|
|
3827
4196
|
} catch {
|
|
3828
4197
|
}
|
|
3829
4198
|
return c.json({ error: err instanceof Error ? err.message : "Failed to import mind" }, 500);
|
|
3830
4199
|
} finally {
|
|
3831
|
-
|
|
4200
|
+
rmSync5(composedDir, { recursive: true, force: true });
|
|
3832
4201
|
}
|
|
3833
4202
|
}).get("/", async (c) => {
|
|
3834
4203
|
const entries = await readRegistry();
|
|
@@ -3837,7 +4206,7 @@ ${user.trimEnd()}
|
|
|
3837
4206
|
const db = await getDb();
|
|
3838
4207
|
const lastActiveRows = await db.select({
|
|
3839
4208
|
mind: mindHistory.mind,
|
|
3840
|
-
lastActiveAt:
|
|
4209
|
+
lastActiveAt: sql2`MAX(${mindHistory.created_at})`
|
|
3841
4210
|
}).from(mindHistory).groupBy(mindHistory.mind);
|
|
3842
4211
|
lastActiveMap = new Map(lastActiveRows.map((r) => [r.mind, r.lastActiveAt]));
|
|
3843
4212
|
} catch {
|
|
@@ -3845,7 +4214,7 @@ ${user.trimEnd()}
|
|
|
3845
4214
|
const minds = await Promise.all(
|
|
3846
4215
|
entries.map(async (entry) => {
|
|
3847
4216
|
const mindStatus = await getMindStatus(entry.name, entry.port);
|
|
3848
|
-
const hasPages =
|
|
4217
|
+
const hasPages = existsSync14(resolve16(mindDir(entry.name), "home", "public", "pages"));
|
|
3849
4218
|
return {
|
|
3850
4219
|
...entry,
|
|
3851
4220
|
...mindStatus,
|
|
@@ -3864,7 +4233,7 @@ ${user.trimEnd()}
|
|
|
3864
4233
|
const entry = await findMind(name);
|
|
3865
4234
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
3866
4235
|
const dir = entry.dir ?? mindDir(entry.parent ?? name);
|
|
3867
|
-
if (!
|
|
4236
|
+
if (!existsSync14(dir)) return c.json({ error: "Mind directory missing" }, 404);
|
|
3868
4237
|
const mindStatus = await getMindStatus(name, entry.port);
|
|
3869
4238
|
const variants = await findVariants(name);
|
|
3870
4239
|
const manager = getMindManager();
|
|
@@ -3878,7 +4247,7 @@ ${user.trimEnd()}
|
|
|
3878
4247
|
return { name: s.name, port: s.port, status: variantStatus };
|
|
3879
4248
|
})
|
|
3880
4249
|
);
|
|
3881
|
-
const hasPages =
|
|
4250
|
+
const hasPages = existsSync14(resolve16(mindDir(name), "home", "public", "pages"));
|
|
3882
4251
|
return c.json({ ...entry, ...mindStatus, variants: variantStatuses, hasPages });
|
|
3883
4252
|
}).post("/:name/start", requireSelf(), async (c) => {
|
|
3884
4253
|
const name = c.req.param("name");
|
|
@@ -3889,7 +4258,7 @@ ${user.trimEnd()}
|
|
|
3889
4258
|
if (!entry.dir) return c.json({ error: `Variant ${name} has no directory` }, 404);
|
|
3890
4259
|
} else {
|
|
3891
4260
|
const dir = mindDir(name);
|
|
3892
|
-
if (!
|
|
4261
|
+
if (!existsSync14(dir)) return c.json({ error: "Mind directory missing" }, 404);
|
|
3893
4262
|
}
|
|
3894
4263
|
if (getMindManager().isRunning(name)) {
|
|
3895
4264
|
return c.json({ error: "Mind already running" }, 409);
|
|
@@ -3910,7 +4279,7 @@ ${user.trimEnd()}
|
|
|
3910
4279
|
if (!entry.dir) return c.json({ error: `Variant ${name} has no directory` }, 404);
|
|
3911
4280
|
} else {
|
|
3912
4281
|
const dir = mindDir(name);
|
|
3913
|
-
if (!
|
|
4282
|
+
if (!existsSync14(dir)) return c.json({ error: "Mind directory missing" }, 404);
|
|
3914
4283
|
}
|
|
3915
4284
|
let context;
|
|
3916
4285
|
const contentType = c.req.header("content-type");
|
|
@@ -3925,8 +4294,8 @@ ${user.trimEnd()}
|
|
|
3925
4294
|
const manager = getMindManager();
|
|
3926
4295
|
try {
|
|
3927
4296
|
if (context?.type === "reload") {
|
|
3928
|
-
const { getSleepManagerIfReady } = await import("./sleep-manager-
|
|
3929
|
-
const sleepState =
|
|
4297
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-O7YQFCV5.js");
|
|
4298
|
+
const sleepState = getSleepManagerIfReady2()?.getState(name);
|
|
3930
4299
|
if (sleepState?.sleeping) {
|
|
3931
4300
|
logger_default.info(`skipping reload for ${name} during sleep \u2014 will apply on next wake`);
|
|
3932
4301
|
return c.json({ ok: true, deferred: true, port: targetPort });
|
|
@@ -3945,7 +4314,7 @@ ${user.trimEnd()}
|
|
|
3945
4314
|
const variantEntry = await findMind(mergeVariantName);
|
|
3946
4315
|
if (variantEntry && variantEntry.parent === baseName && variantEntry.dir && variantEntry.branch) {
|
|
3947
4316
|
const projectRoot = mindDir(baseName);
|
|
3948
|
-
if (
|
|
4317
|
+
if (existsSync14(variantEntry.dir)) {
|
|
3949
4318
|
const status = (await gitExec(["status", "--porcelain"], { cwd: variantEntry.dir })).trim();
|
|
3950
4319
|
if (status) {
|
|
3951
4320
|
try {
|
|
@@ -3987,7 +4356,7 @@ ${user.trimEnd()}
|
|
|
3987
4356
|
if (context?.type === "sprouted" && !entry.parent) {
|
|
3988
4357
|
try {
|
|
3989
4358
|
const db = await getDb();
|
|
3990
|
-
const activeConvs = await db.select({ id: conversations.id }).from(conversations).where(
|
|
4359
|
+
const activeConvs = await db.select({ id: conversations.id }).from(conversations).where(eq5(conversations.mind_name, baseName)).all();
|
|
3991
4360
|
for (const conv of activeConvs) {
|
|
3992
4361
|
await addMessage(conv.id, "assistant", "system", [
|
|
3993
4362
|
{ type: "text", text: "[seed has sprouted]" }
|
|
@@ -4020,16 +4389,16 @@ ${user.trimEnd()}
|
|
|
4020
4389
|
const name = c.req.param("name");
|
|
4021
4390
|
const entry = await findMind(name);
|
|
4022
4391
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
4023
|
-
const { getSleepManagerIfReady } = await import("./sleep-manager-
|
|
4024
|
-
const sm =
|
|
4392
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-O7YQFCV5.js");
|
|
4393
|
+
const sm = getSleepManagerIfReady2();
|
|
4025
4394
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
4026
4395
|
return c.json(sm.getState(name));
|
|
4027
4396
|
}).post("/:name/sleep", requireSelf(), async (c) => {
|
|
4028
4397
|
const name = c.req.param("name");
|
|
4029
4398
|
const entry = await findMind(name);
|
|
4030
4399
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
4031
|
-
const { getSleepManagerIfReady } = await import("./sleep-manager-
|
|
4032
|
-
const sm =
|
|
4400
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-O7YQFCV5.js");
|
|
4401
|
+
const sm = getSleepManagerIfReady2();
|
|
4033
4402
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
4034
4403
|
if (sm.isSleeping(name)) return c.json({ error: "Mind is already sleeping" }, 409);
|
|
4035
4404
|
const body = await c.req.json().catch(() => ({}));
|
|
@@ -4048,8 +4417,8 @@ ${user.trimEnd()}
|
|
|
4048
4417
|
const name = c.req.param("name");
|
|
4049
4418
|
const entry = await findMind(name);
|
|
4050
4419
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
4051
|
-
const { getSleepManagerIfReady } = await import("./sleep-manager-
|
|
4052
|
-
const sm =
|
|
4420
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-O7YQFCV5.js");
|
|
4421
|
+
const sm = getSleepManagerIfReady2();
|
|
4053
4422
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
4054
4423
|
const sleepState = sm.getState(name);
|
|
4055
4424
|
if (!sleepState.sleeping) return c.json({ error: "Mind is not sleeping" }, 409);
|
|
@@ -4063,8 +4432,8 @@ ${user.trimEnd()}
|
|
|
4063
4432
|
const name = c.req.param("name");
|
|
4064
4433
|
const entry = await findMind(name);
|
|
4065
4434
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
4066
|
-
const { getSleepManagerIfReady } = await import("./sleep-manager-
|
|
4067
|
-
const sm =
|
|
4435
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-O7YQFCV5.js");
|
|
4436
|
+
const sm = getSleepManagerIfReady2();
|
|
4068
4437
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
4069
4438
|
const flushed = await sm.flushQueuedMessages(name);
|
|
4070
4439
|
return c.json({ ok: true, flushed });
|
|
@@ -4101,11 +4470,11 @@ ${user.trimEnd()}
|
|
|
4101
4470
|
await removeMind(name);
|
|
4102
4471
|
await deleteMindUser2(name);
|
|
4103
4472
|
const state = stateDir(name);
|
|
4104
|
-
if (
|
|
4105
|
-
|
|
4473
|
+
if (existsSync14(state)) {
|
|
4474
|
+
rmSync5(state, { recursive: true, force: true });
|
|
4106
4475
|
}
|
|
4107
|
-
if (force &&
|
|
4108
|
-
|
|
4476
|
+
if (force && existsSync14(dir)) {
|
|
4477
|
+
rmSync5(dir, { recursive: true, force: true });
|
|
4109
4478
|
deleteMindUser(name);
|
|
4110
4479
|
}
|
|
4111
4480
|
fireWebhook({
|
|
@@ -4119,7 +4488,7 @@ ${user.trimEnd()}
|
|
|
4119
4488
|
const entry = await findMind(mindName);
|
|
4120
4489
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
4121
4490
|
const dir = mindDir(mindName);
|
|
4122
|
-
if (!
|
|
4491
|
+
if (!existsSync14(dir)) return c.json({ error: "Mind directory missing" }, 404);
|
|
4123
4492
|
let body = {};
|
|
4124
4493
|
try {
|
|
4125
4494
|
body = await c.req.json();
|
|
@@ -4128,16 +4497,16 @@ ${user.trimEnd()}
|
|
|
4128
4497
|
const template = body.template ?? entry.template ?? "claude";
|
|
4129
4498
|
const UPGRADE_BRANCH = "upgrade";
|
|
4130
4499
|
const upgradeVariantName = `${mindName}-upgrade`;
|
|
4131
|
-
const worktreeDir =
|
|
4500
|
+
const worktreeDir = resolve16(dir, ".variants", UPGRADE_BRANCH);
|
|
4132
4501
|
if (body.abort) {
|
|
4133
|
-
if (!
|
|
4502
|
+
if (!existsSync14(worktreeDir)) {
|
|
4134
4503
|
return c.json({ error: "No upgrade in progress" }, 400);
|
|
4135
4504
|
}
|
|
4136
4505
|
try {
|
|
4137
4506
|
try {
|
|
4138
|
-
const gitDirContent =
|
|
4507
|
+
const gitDirContent = readFileSync12(resolve16(worktreeDir, ".git"), "utf-8").trim();
|
|
4139
4508
|
const gitDir = gitDirContent.replace("gitdir: ", "");
|
|
4140
|
-
if (
|
|
4509
|
+
if (existsSync14(resolve16(gitDir, "MERGE_HEAD"))) {
|
|
4141
4510
|
await gitExec(["merge", "--abort"], { cwd: worktreeDir });
|
|
4142
4511
|
}
|
|
4143
4512
|
} catch {
|
|
@@ -4156,7 +4525,7 @@ ${user.trimEnd()}
|
|
|
4156
4525
|
}
|
|
4157
4526
|
}
|
|
4158
4527
|
if (body.continue) {
|
|
4159
|
-
if (!
|
|
4528
|
+
if (!existsSync14(worktreeDir)) {
|
|
4160
4529
|
return c.json({ error: "No upgrade in progress" }, 400);
|
|
4161
4530
|
}
|
|
4162
4531
|
const status = await gitExec(["status", "--porcelain"], { cwd: worktreeDir });
|
|
@@ -4195,7 +4564,7 @@ ${user.trimEnd()}
|
|
|
4195
4564
|
}
|
|
4196
4565
|
}
|
|
4197
4566
|
if (body.accept) {
|
|
4198
|
-
if (!
|
|
4567
|
+
if (!existsSync14(worktreeDir)) {
|
|
4199
4568
|
return c.json({ error: "No upgrade in progress" }, 400);
|
|
4200
4569
|
}
|
|
4201
4570
|
const variantEntry = await findMind(upgradeVariantName);
|
|
@@ -4218,7 +4587,7 @@ ${user.trimEnd()}
|
|
|
4218
4587
|
try {
|
|
4219
4588
|
await gitExec(["add", "-A"], { cwd: dir });
|
|
4220
4589
|
await gitExec(["commit", "-m", "Auto-commit before upgrade merge"], { cwd: dir });
|
|
4221
|
-
} catch (
|
|
4590
|
+
} catch (_e) {
|
|
4222
4591
|
return c.json({ error: "Failed to auto-commit main changes before merge" }, 500);
|
|
4223
4592
|
}
|
|
4224
4593
|
}
|
|
@@ -4253,22 +4622,22 @@ ${user.trimEnd()}
|
|
|
4253
4622
|
}
|
|
4254
4623
|
return c.json({ ok: true });
|
|
4255
4624
|
}
|
|
4256
|
-
if (
|
|
4625
|
+
if (existsSync14(worktreeDir)) {
|
|
4257
4626
|
return c.json(
|
|
4258
4627
|
{ error: "Upgrade variant already exists. Use continue or delete it first." },
|
|
4259
4628
|
409
|
|
4260
4629
|
);
|
|
4261
4630
|
}
|
|
4262
|
-
if (!
|
|
4631
|
+
if (!existsSync14(resolve16(dir, ".git"))) {
|
|
4263
4632
|
try {
|
|
4264
|
-
const env = isIsolationEnabled() ? { ...process.env, HOME:
|
|
4633
|
+
const env = isIsolationEnabled() ? { ...process.env, HOME: resolve16(dir, "home") } : void 0;
|
|
4265
4634
|
await gitExec(["init"], { cwd: dir, mindName, env });
|
|
4266
4635
|
await configureGitIdentity(mindName, { cwd: dir, mindName, env });
|
|
4267
4636
|
await gitExec(["add", "-A"], { cwd: dir, mindName, env });
|
|
4268
4637
|
await gitExec(["commit", "-m", "initial commit"], { cwd: dir, mindName, env });
|
|
4269
4638
|
chownMindDir(dir, mindName);
|
|
4270
4639
|
} catch (err) {
|
|
4271
|
-
|
|
4640
|
+
rmSync5(resolve16(dir, ".git"), { recursive: true, force: true });
|
|
4272
4641
|
return c.json(
|
|
4273
4642
|
{
|
|
4274
4643
|
error: `Git initialization failed: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -4282,7 +4651,7 @@ ${user.trimEnd()}
|
|
|
4282
4651
|
await gitExec(["branch", "-D", UPGRADE_BRANCH], { cwd: dir });
|
|
4283
4652
|
} catch {
|
|
4284
4653
|
}
|
|
4285
|
-
if (!
|
|
4654
|
+
if (!existsSync14(resolve16(dir, "home", "shared"))) {
|
|
4286
4655
|
try {
|
|
4287
4656
|
await addSharedWorktree(mindName, dir);
|
|
4288
4657
|
} catch (err) {
|
|
@@ -4293,9 +4662,9 @@ ${user.trimEnd()}
|
|
|
4293
4662
|
}
|
|
4294
4663
|
}
|
|
4295
4664
|
await updateTemplateBranch(dir, template, mindName);
|
|
4296
|
-
const parentDir =
|
|
4297
|
-
if (!
|
|
4298
|
-
|
|
4665
|
+
const parentDir = resolve16(dir, ".variants");
|
|
4666
|
+
if (!existsSync14(parentDir)) {
|
|
4667
|
+
mkdirSync9(parentDir, { recursive: true });
|
|
4299
4668
|
}
|
|
4300
4669
|
await gitExec(["worktree", "add", "-b", UPGRADE_BRANCH, worktreeDir], { cwd: dir });
|
|
4301
4670
|
const hasConflicts = await mergeTemplateBranch(worktreeDir);
|
|
@@ -4332,8 +4701,8 @@ ${user.trimEnd()}
|
|
|
4332
4701
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
4333
4702
|
const baseName = entry.parent ?? name;
|
|
4334
4703
|
try {
|
|
4335
|
-
const { getSleepManagerIfReady } = await import("./sleep-manager-
|
|
4336
|
-
const sm =
|
|
4704
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-O7YQFCV5.js");
|
|
4705
|
+
const sm = getSleepManagerIfReady2();
|
|
4337
4706
|
if (sm?.isSleeping(baseName)) {
|
|
4338
4707
|
const body2 = await c.req.text();
|
|
4339
4708
|
let parsed2 = null;
|
|
@@ -4430,7 +4799,7 @@ ${user.trimEnd()}
|
|
|
4430
4799
|
if (seedEntry?.stage === "seed") {
|
|
4431
4800
|
try {
|
|
4432
4801
|
const db = await getDb();
|
|
4433
|
-
const countResult = await db.select({ count:
|
|
4802
|
+
const countResult = await db.select({ count: sql2`count(*)` }).from(mindHistory).where(eq5(mindHistory.mind, baseName));
|
|
4434
4803
|
const msgCount = countResult[0]?.count ?? 0;
|
|
4435
4804
|
if (msgCount >= 10 && msgCount % 10 === 0) {
|
|
4436
4805
|
const nudge = "\n[You've been exploring for a while. Whenever you feel ready, write your SOUL.md and MEMORY.md, then run volute mind sprout.]";
|
|
@@ -4464,6 +4833,34 @@ ${user.trimEnd()}
|
|
|
4464
4833
|
logger_default.error(`delivery failed for ${name}`, logger_default.errorData(err));
|
|
4465
4834
|
});
|
|
4466
4835
|
return c.json({ ok: true });
|
|
4836
|
+
}).get("/:name/conversations", async (c) => {
|
|
4837
|
+
const name = c.req.param("name");
|
|
4838
|
+
const entry = await findMind(name);
|
|
4839
|
+
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
4840
|
+
const convs = await listConversationsForMind(name);
|
|
4841
|
+
return c.json(convs);
|
|
4842
|
+
}).get("/:name/conversations/:convId/messages", async (c) => {
|
|
4843
|
+
const name = c.req.param("name");
|
|
4844
|
+
const convId = c.req.param("convId");
|
|
4845
|
+
const entry = await findMind(name);
|
|
4846
|
+
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
4847
|
+
const belongs = await isConversationForMind(name, convId);
|
|
4848
|
+
if (!belongs) {
|
|
4849
|
+
return c.json({ error: "Conversation not found" }, 404);
|
|
4850
|
+
}
|
|
4851
|
+
const beforeStr = c.req.query("before");
|
|
4852
|
+
const limitStr = c.req.query("limit");
|
|
4853
|
+
if (!beforeStr && !limitStr) {
|
|
4854
|
+
const msgs = await getMessages(convId);
|
|
4855
|
+
return c.json({ items: msgs, hasMore: false });
|
|
4856
|
+
}
|
|
4857
|
+
const before = beforeStr ? parseInt(beforeStr, 10) : void 0;
|
|
4858
|
+
const limit = limitStr ? parseInt(limitStr, 10) : void 0;
|
|
4859
|
+
if (before !== void 0 && Number.isNaN(before) || limit !== void 0 && Number.isNaN(limit)) {
|
|
4860
|
+
return c.json({ error: "Invalid pagination parameters" }, 400);
|
|
4861
|
+
}
|
|
4862
|
+
const result = await getMessagesPaginated(convId, { before, limit });
|
|
4863
|
+
return c.json({ items: result.messages, hasMore: result.hasMore });
|
|
4467
4864
|
}).get("/:name/budget", async (c) => {
|
|
4468
4865
|
const name = c.req.param("name");
|
|
4469
4866
|
const baseName = await getBaseName(name);
|
|
@@ -4475,13 +4872,13 @@ ${user.trimEnd()}
|
|
|
4475
4872
|
const entry = await findMind(name);
|
|
4476
4873
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
4477
4874
|
const dir = mindDir(name);
|
|
4478
|
-
if (!
|
|
4875
|
+
if (!existsSync14(dir)) return c.json({ error: "Mind directory missing" }, 404);
|
|
4479
4876
|
let config = readVoluteConfig(dir);
|
|
4480
4877
|
if (!config && entry.template === "pi") {
|
|
4481
|
-
const piConfigPath =
|
|
4482
|
-
if (
|
|
4878
|
+
const piConfigPath = resolve16(dir, "home/.config/config.json");
|
|
4879
|
+
if (existsSync14(piConfigPath)) {
|
|
4483
4880
|
try {
|
|
4484
|
-
config = JSON.parse(
|
|
4881
|
+
config = JSON.parse(readFileSync12(piConfigPath, "utf-8"));
|
|
4485
4882
|
} catch {
|
|
4486
4883
|
}
|
|
4487
4884
|
}
|
|
@@ -4518,7 +4915,7 @@ ${user.trimEnd()}
|
|
|
4518
4915
|
const entry = await findMind(name);
|
|
4519
4916
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
4520
4917
|
const dir = mindDir(name);
|
|
4521
|
-
if (!
|
|
4918
|
+
if (!existsSync14(dir)) return c.json({ error: "Mind directory missing" }, 404);
|
|
4522
4919
|
const body = c.req.valid("json");
|
|
4523
4920
|
const existing = readVoluteConfig(dir) ?? {};
|
|
4524
4921
|
if (body.model !== void 0) existing.model = body.model;
|
|
@@ -4555,6 +4952,20 @@ ${user.trimEnd()}
|
|
|
4555
4952
|
logger_default.error(`failed to get pending deliveries for ${baseName}`, logger_default.errorData(err));
|
|
4556
4953
|
return c.json({ error: "Failed to retrieve pending messages" }, 500);
|
|
4557
4954
|
}
|
|
4955
|
+
}).post("/:name/ai/complete", requireSelf(), async (c) => {
|
|
4956
|
+
const body = await c.req.json();
|
|
4957
|
+
if (!body.systemPrompt || !body.message) {
|
|
4958
|
+
return c.json({ error: "systemPrompt and message required" }, 400);
|
|
4959
|
+
}
|
|
4960
|
+
const { aiComplete: aiCompleteFn, isAiConfigured } = await import("./ai-service-VAJT5UBS.js");
|
|
4961
|
+
if (!isAiConfigured()) {
|
|
4962
|
+
return c.json({ error: "AI service not configured" }, 503);
|
|
4963
|
+
}
|
|
4964
|
+
const text = await aiCompleteFn(body.systemPrompt, body.message, body.model);
|
|
4965
|
+
if (text == null) {
|
|
4966
|
+
return c.json({ error: "AI completion failed" }, 502);
|
|
4967
|
+
}
|
|
4968
|
+
return c.json({ text });
|
|
4558
4969
|
}).post("/:name/events", requireSelf(), async (c) => {
|
|
4559
4970
|
const name = c.req.param("name");
|
|
4560
4971
|
const baseName = await getBaseName(name);
|
|
@@ -4568,8 +4979,9 @@ ${user.trimEnd()}
|
|
|
4568
4979
|
return c.json({ error: "type required" }, 400);
|
|
4569
4980
|
}
|
|
4570
4981
|
const db = await getDb();
|
|
4982
|
+
let insertedId;
|
|
4571
4983
|
try {
|
|
4572
|
-
await db.insert(mindHistory).values({
|
|
4984
|
+
const result = await db.insert(mindHistory).values({
|
|
4573
4985
|
mind: baseName,
|
|
4574
4986
|
type: body.type,
|
|
4575
4987
|
session: body.session ?? null,
|
|
@@ -4577,7 +4989,8 @@ ${user.trimEnd()}
|
|
|
4577
4989
|
message_id: body.messageId ?? null,
|
|
4578
4990
|
content: body.content ?? null,
|
|
4579
4991
|
metadata: body.metadata ? JSON.stringify(body.metadata) : null
|
|
4580
|
-
});
|
|
4992
|
+
}).returning({ id: mindHistory.id });
|
|
4993
|
+
insertedId = result[0]?.id;
|
|
4581
4994
|
} catch (err) {
|
|
4582
4995
|
logger_default.error(`failed to persist event for ${baseName}`, logger_default.errorData(err));
|
|
4583
4996
|
}
|
|
@@ -4608,6 +5021,11 @@ ${user.trimEnd()}
|
|
|
4608
5021
|
logger_default.error(`delivery manager sessionDone failed for ${baseName}`, logger_default.errorData(err));
|
|
4609
5022
|
}
|
|
4610
5023
|
}
|
|
5024
|
+
if (insertedId != null) {
|
|
5025
|
+
summarizeTurn(baseName, body.session, body.channel, insertedId).catch((err) => {
|
|
5026
|
+
logger_default.error("turn summarization failed", logger_default.errorData(err));
|
|
5027
|
+
});
|
|
5028
|
+
}
|
|
4611
5029
|
}
|
|
4612
5030
|
if (body.type === "usage" && body.metadata) {
|
|
4613
5031
|
const inputTokens = body.metadata.input_tokens ?? 0;
|
|
@@ -4683,432 +5101,68 @@ ${user.trimEnd()}
|
|
|
4683
5101
|
const db = await getDb();
|
|
4684
5102
|
try {
|
|
4685
5103
|
await db.insert(mindHistory).values({
|
|
4686
|
-
mind: baseName,
|
|
4687
|
-
type: "outbound",
|
|
4688
|
-
channel: body.channel,
|
|
4689
|
-
sender: body.sender ?? baseName,
|
|
4690
|
-
content: body.content
|
|
4691
|
-
});
|
|
4692
|
-
} catch (err) {
|
|
4693
|
-
logger_default.error(`failed to persist external send for ${baseName}`, logger_default.errorData(err));
|
|
4694
|
-
return c.json({ error: "Failed to persist" }, 500);
|
|
4695
|
-
}
|
|
4696
|
-
return c.json({ ok: true });
|
|
4697
|
-
}).get("/:name/history/sessions", async (c) => {
|
|
4698
|
-
const name = c.req.param("name");
|
|
4699
|
-
const db = await getDb();
|
|
4700
|
-
const rows = await db.select({
|
|
4701
|
-
session: mindHistory.session,
|
|
4702
|
-
started_at: sql`MIN(${mindHistory.created_at})`,
|
|
4703
|
-
event_count: sql`COUNT(*)`,
|
|
4704
|
-
message_count: sql`SUM(CASE WHEN ${mindHistory.type} IN ('inbound','outbound') THEN 1 ELSE 0 END)`,
|
|
4705
|
-
tool_count: sql`SUM(CASE WHEN ${mindHistory.type}='tool_use' THEN 1 ELSE 0 END)`
|
|
4706
|
-
}).from(mindHistory).where(and3(eq4(mindHistory.mind, name), sql`${mindHistory.session} IS NOT NULL`)).groupBy(mindHistory.session).orderBy(sql`MIN(${mindHistory.created_at}) DESC`);
|
|
4707
|
-
return c.json(rows);
|
|
4708
|
-
}).get("/:name/history/channels", async (c) => {
|
|
4709
|
-
const name = c.req.param("name");
|
|
4710
|
-
const db = await getDb();
|
|
4711
|
-
const rows = await db.selectDistinct({ channel: mindHistory.channel }).from(mindHistory).where(eq4(mindHistory.mind, name));
|
|
4712
|
-
return c.json(rows.map((r) => r.channel));
|
|
4713
|
-
}).get("/:name/history/export", async (c) => {
|
|
4714
|
-
const name = c.req.param("name");
|
|
4715
|
-
if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
|
|
4716
|
-
const db = await getDb();
|
|
4717
|
-
const rows = await db.select().from(mindHistory).where(eq4(mindHistory.mind, name));
|
|
4718
|
-
return c.json(rows);
|
|
4719
|
-
}).get("/:name/history", async (c) => {
|
|
4720
|
-
const name = c.req.param("name");
|
|
4721
|
-
const channel = c.req.query("channel");
|
|
4722
|
-
const session = c.req.query("session");
|
|
4723
|
-
const full = c.req.query("full") === "true";
|
|
4724
|
-
const limit = Math.min(Math.max(parseInt(c.req.query("limit") ?? "50", 10) || 50, 1), 200);
|
|
4725
|
-
const offset = Math.max(parseInt(c.req.query("offset") ?? "0", 10) || 0, 0);
|
|
4726
|
-
const db = await getDb();
|
|
4727
|
-
const conditions = [eq4(mindHistory.mind, name)];
|
|
4728
|
-
if (channel) {
|
|
4729
|
-
conditions.push(eq4(mindHistory.channel, channel));
|
|
4730
|
-
}
|
|
4731
|
-
if (session) {
|
|
4732
|
-
conditions.push(eq4(mindHistory.session, session));
|
|
4733
|
-
}
|
|
4734
|
-
if (!full) {
|
|
4735
|
-
conditions.push(sql`${mindHistory.type} IN ('inbound', 'outbound')`);
|
|
4736
|
-
}
|
|
4737
|
-
const rows = await db.select().from(mindHistory).where(and3(...conditions)).orderBy(desc2(mindHistory.created_at)).limit(limit).offset(offset);
|
|
4738
|
-
return c.json(rows);
|
|
4739
|
-
});
|
|
4740
|
-
var minds_default = app11;
|
|
4741
|
-
|
|
4742
|
-
// src/web/api/notes.ts
|
|
4743
|
-
import { zValidator as zValidator5 } from "@hono/zod-validator";
|
|
4744
|
-
import { Hono as Hono12 } from "hono";
|
|
4745
|
-
import { z as z5 } from "zod";
|
|
4746
|
-
|
|
4747
|
-
// src/lib/notes.ts
|
|
4748
|
-
import { and as and4, count, desc as desc3, eq as eq5, inArray, sql as sql2 } from "drizzle-orm";
|
|
4749
|
-
async function createNote(authorId, title, content, replyToId) {
|
|
4750
|
-
const db = await getDb();
|
|
4751
|
-
let slug = slugify(title) || "untitled";
|
|
4752
|
-
const existing = await db.select({ slug: notes.slug }).from(notes).where(eq5(notes.author_id, authorId)).all();
|
|
4753
|
-
const existingSlugs = new Set(existing.map((r) => r.slug));
|
|
4754
|
-
if (existingSlugs.has(slug)) {
|
|
4755
|
-
let i = 2;
|
|
4756
|
-
while (existingSlugs.has(`${slug}-${i}`)) i++;
|
|
4757
|
-
slug = `${slug}-${i}`;
|
|
4758
|
-
}
|
|
4759
|
-
const [row] = await db.insert(notes).values({ author_id: authorId, title, slug, content, reply_to_id: replyToId ?? null }).returning();
|
|
4760
|
-
const author = await db.select().from(users).where(eq5(users.id, authorId)).get();
|
|
4761
|
-
return {
|
|
4762
|
-
...row,
|
|
4763
|
-
author_username: author?.username ?? "unknown",
|
|
4764
|
-
author_display_name: author?.display_name ?? null,
|
|
4765
|
-
comment_count: 0
|
|
4766
|
-
};
|
|
4767
|
-
}
|
|
4768
|
-
async function getNote(authorUsername, slug) {
|
|
4769
|
-
const db = await getDb();
|
|
4770
|
-
const row = await db.select({
|
|
4771
|
-
id: notes.id,
|
|
4772
|
-
author_id: notes.author_id,
|
|
4773
|
-
title: notes.title,
|
|
4774
|
-
slug: notes.slug,
|
|
4775
|
-
content: notes.content,
|
|
4776
|
-
reply_to_id: notes.reply_to_id,
|
|
4777
|
-
created_at: notes.created_at,
|
|
4778
|
-
updated_at: notes.updated_at,
|
|
4779
|
-
author_username: users.username,
|
|
4780
|
-
author_display_name: users.display_name
|
|
4781
|
-
}).from(notes).innerJoin(users, eq5(notes.author_id, users.id)).where(and4(eq5(users.username, authorUsername), eq5(notes.slug, slug))).get();
|
|
4782
|
-
if (!row) return null;
|
|
4783
|
-
const comments = await getComments(row.id);
|
|
4784
|
-
const reactions = await getReactions(row.id);
|
|
4785
|
-
let reply_to = null;
|
|
4786
|
-
if (row.reply_to_id) {
|
|
4787
|
-
const parent = await db.select({
|
|
4788
|
-
title: notes.title,
|
|
4789
|
-
slug: notes.slug,
|
|
4790
|
-
author_username: users.username
|
|
4791
|
-
}).from(notes).innerJoin(users, eq5(notes.author_id, users.id)).where(eq5(notes.id, row.reply_to_id)).get();
|
|
4792
|
-
if (parent) {
|
|
4793
|
-
reply_to = parent;
|
|
4794
|
-
}
|
|
4795
|
-
}
|
|
4796
|
-
const replies = await db.select({
|
|
4797
|
-
author_username: users.username,
|
|
4798
|
-
slug: notes.slug,
|
|
4799
|
-
title: notes.title,
|
|
4800
|
-
created_at: notes.created_at
|
|
4801
|
-
}).from(notes).innerJoin(users, eq5(notes.author_id, users.id)).where(eq5(notes.reply_to_id, row.id)).orderBy(notes.created_at).all();
|
|
4802
|
-
return { ...row, comment_count: comments.length, comments, reactions, reply_to, replies };
|
|
4803
|
-
}
|
|
4804
|
-
async function listNotes(opts) {
|
|
4805
|
-
const db = await getDb();
|
|
4806
|
-
const limit = opts?.limit ?? 50;
|
|
4807
|
-
const offset = opts?.offset ?? 0;
|
|
4808
|
-
const conditions = [];
|
|
4809
|
-
if (opts?.authorUsername) {
|
|
4810
|
-
conditions.push(eq5(users.username, opts.authorUsername));
|
|
4811
|
-
}
|
|
4812
|
-
const rows = await db.select({
|
|
4813
|
-
id: notes.id,
|
|
4814
|
-
author_id: notes.author_id,
|
|
4815
|
-
title: notes.title,
|
|
4816
|
-
slug: notes.slug,
|
|
4817
|
-
content: notes.content,
|
|
4818
|
-
reply_to_id: notes.reply_to_id,
|
|
4819
|
-
created_at: notes.created_at,
|
|
4820
|
-
updated_at: notes.updated_at,
|
|
4821
|
-
author_username: users.username,
|
|
4822
|
-
author_display_name: users.display_name
|
|
4823
|
-
}).from(notes).innerJoin(users, eq5(notes.author_id, users.id)).where(conditions.length > 0 ? and4(...conditions) : void 0).orderBy(desc3(notes.created_at)).limit(limit).offset(offset).all();
|
|
4824
|
-
const noteIds = rows.map((r) => r.id);
|
|
4825
|
-
if (noteIds.length === 0) return [];
|
|
4826
|
-
const commentCounts = await db.select({
|
|
4827
|
-
note_id: noteComments.note_id,
|
|
4828
|
-
count: count()
|
|
4829
|
-
}).from(noteComments).where(inArray(noteComments.note_id, noteIds)).groupBy(noteComments.note_id).all();
|
|
4830
|
-
const countMap = new Map(commentCounts.map((r) => [r.note_id, r.count]));
|
|
4831
|
-
const allReactions = await db.select({
|
|
4832
|
-
note_id: noteReactions.note_id,
|
|
4833
|
-
emoji: noteReactions.emoji,
|
|
4834
|
-
count: count()
|
|
4835
|
-
}).from(noteReactions).where(inArray(noteReactions.note_id, noteIds)).groupBy(noteReactions.note_id, noteReactions.emoji).all();
|
|
4836
|
-
const reactionMap = /* @__PURE__ */ new Map();
|
|
4837
|
-
for (const r of allReactions) {
|
|
4838
|
-
if (!reactionMap.has(r.note_id)) reactionMap.set(r.note_id, []);
|
|
4839
|
-
reactionMap.get(r.note_id).push({ emoji: r.emoji, count: r.count });
|
|
4840
|
-
}
|
|
4841
|
-
const replyToIds = [...new Set(rows.filter((r) => r.reply_to_id).map((r) => r.reply_to_id))];
|
|
4842
|
-
const replyToMap = /* @__PURE__ */ new Map();
|
|
4843
|
-
if (replyToIds.length > 0) {
|
|
4844
|
-
const parents = await db.select({
|
|
4845
|
-
id: notes.id,
|
|
4846
|
-
title: notes.title,
|
|
4847
|
-
slug: notes.slug,
|
|
4848
|
-
author_username: users.username
|
|
4849
|
-
}).from(notes).innerJoin(users, eq5(notes.author_id, users.id)).where(inArray(notes.id, replyToIds)).all();
|
|
4850
|
-
for (const parent of parents) {
|
|
4851
|
-
replyToMap.set(parent.id, {
|
|
4852
|
-
author_username: parent.author_username,
|
|
4853
|
-
slug: parent.slug,
|
|
4854
|
-
title: parent.title
|
|
4855
|
-
});
|
|
4856
|
-
}
|
|
4857
|
-
}
|
|
4858
|
-
return rows.map((r) => {
|
|
4859
|
-
const reactions = reactionMap.get(r.id);
|
|
4860
|
-
const topReactions = reactions ? reactions.sort((a, b) => b.count - a.count).slice(0, 3).map((rx) => ({ ...rx, usernames: [] })) : void 0;
|
|
4861
|
-
return {
|
|
4862
|
-
...r,
|
|
4863
|
-
comment_count: countMap.get(r.id) ?? 0,
|
|
4864
|
-
reactions: topReactions,
|
|
4865
|
-
reply_to: r.reply_to_id ? replyToMap.get(r.reply_to_id) ?? null : null
|
|
4866
|
-
};
|
|
4867
|
-
});
|
|
4868
|
-
}
|
|
4869
|
-
async function updateNote(authorUsername, slug, updates) {
|
|
4870
|
-
const db = await getDb();
|
|
4871
|
-
const existing = await db.select({ id: notes.id, author_id: notes.author_id }).from(notes).innerJoin(users, eq5(notes.author_id, users.id)).where(and4(eq5(users.username, authorUsername), eq5(notes.slug, slug))).get();
|
|
4872
|
-
if (!existing) return null;
|
|
4873
|
-
const set = { updated_at: sql2`(datetime('now'))` };
|
|
4874
|
-
if (updates.title !== void 0) set.title = updates.title;
|
|
4875
|
-
if (updates.content !== void 0) set.content = updates.content;
|
|
4876
|
-
await db.update(notes).set(set).where(eq5(notes.id, existing.id));
|
|
4877
|
-
return getNote(authorUsername, slug).then((n) => {
|
|
4878
|
-
if (!n) return null;
|
|
4879
|
-
const { comments, replies, ...note } = n;
|
|
4880
|
-
return note;
|
|
4881
|
-
});
|
|
4882
|
-
}
|
|
4883
|
-
async function deleteNote(authorUsername, slug, authorId) {
|
|
4884
|
-
const db = await getDb();
|
|
4885
|
-
const existing = await db.select({ id: notes.id, author_id: notes.author_id }).from(notes).innerJoin(users, eq5(notes.author_id, users.id)).where(and4(eq5(users.username, authorUsername), eq5(notes.slug, slug))).get();
|
|
4886
|
-
if (!existing || existing.author_id !== authorId) return false;
|
|
4887
|
-
await db.delete(notes).where(eq5(notes.id, existing.id));
|
|
4888
|
-
return true;
|
|
4889
|
-
}
|
|
4890
|
-
async function addComment(noteId, authorId, content) {
|
|
4891
|
-
const db = await getDb();
|
|
4892
|
-
const [row] = await db.insert(noteComments).values({ note_id: noteId, author_id: authorId, content }).returning();
|
|
4893
|
-
const author = await db.select().from(users).where(eq5(users.id, authorId)).get();
|
|
4894
|
-
return {
|
|
4895
|
-
...row,
|
|
4896
|
-
author_username: author?.username ?? "unknown",
|
|
4897
|
-
author_display_name: author?.display_name ?? null
|
|
4898
|
-
};
|
|
4899
|
-
}
|
|
4900
|
-
async function getComments(noteId) {
|
|
4901
|
-
const db = await getDb();
|
|
4902
|
-
return db.select({
|
|
4903
|
-
id: noteComments.id,
|
|
4904
|
-
note_id: noteComments.note_id,
|
|
4905
|
-
author_id: noteComments.author_id,
|
|
4906
|
-
content: noteComments.content,
|
|
4907
|
-
created_at: noteComments.created_at,
|
|
4908
|
-
author_username: users.username,
|
|
4909
|
-
author_display_name: users.display_name
|
|
4910
|
-
}).from(noteComments).innerJoin(users, eq5(noteComments.author_id, users.id)).where(eq5(noteComments.note_id, noteId)).orderBy(noteComments.created_at).all();
|
|
4911
|
-
}
|
|
4912
|
-
async function deleteComment(commentId, authorId) {
|
|
4913
|
-
const db = await getDb();
|
|
4914
|
-
const existing = await db.select({ id: noteComments.id, author_id: noteComments.author_id }).from(noteComments).where(eq5(noteComments.id, commentId)).get();
|
|
4915
|
-
if (!existing || existing.author_id !== authorId) return false;
|
|
4916
|
-
await db.delete(noteComments).where(eq5(noteComments.id, commentId));
|
|
4917
|
-
return true;
|
|
4918
|
-
}
|
|
4919
|
-
async function toggleReaction(noteId, userId, emoji) {
|
|
4920
|
-
const db = await getDb();
|
|
4921
|
-
const existing = await db.select({ id: noteReactions.id }).from(noteReactions).where(
|
|
4922
|
-
and4(
|
|
4923
|
-
eq5(noteReactions.note_id, noteId),
|
|
4924
|
-
eq5(noteReactions.user_id, userId),
|
|
4925
|
-
eq5(noteReactions.emoji, emoji)
|
|
4926
|
-
)
|
|
4927
|
-
).get();
|
|
4928
|
-
if (existing) {
|
|
4929
|
-
await db.delete(noteReactions).where(eq5(noteReactions.id, existing.id));
|
|
4930
|
-
return { added: false };
|
|
5104
|
+
mind: baseName,
|
|
5105
|
+
type: "outbound",
|
|
5106
|
+
channel: body.channel,
|
|
5107
|
+
sender: body.sender ?? baseName,
|
|
5108
|
+
content: body.content
|
|
5109
|
+
});
|
|
5110
|
+
} catch (err) {
|
|
5111
|
+
logger_default.error(`failed to persist external send for ${baseName}`, logger_default.errorData(err));
|
|
5112
|
+
return c.json({ error: "Failed to persist" }, 500);
|
|
4931
5113
|
}
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
async function getReactions(noteId) {
|
|
5114
|
+
return c.json({ ok: true });
|
|
5115
|
+
}).get("/:name/history/sessions", async (c) => {
|
|
5116
|
+
const name = c.req.param("name");
|
|
4936
5117
|
const db = await getDb();
|
|
4937
5118
|
const rows = await db.select({
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
emoji,
|
|
4948
|
-
count: usernames.length,
|
|
4949
|
-
usernames
|
|
4950
|
-
}));
|
|
4951
|
-
}
|
|
4952
|
-
async function resolveNoteId(authorSlug) {
|
|
4953
|
-
const [author, slug] = authorSlug.split("/", 2);
|
|
4954
|
-
if (!author || !slug) return null;
|
|
5119
|
+
session: mindHistory.session,
|
|
5120
|
+
started_at: sql2`MIN(${mindHistory.created_at})`,
|
|
5121
|
+
event_count: sql2`COUNT(*)`,
|
|
5122
|
+
message_count: sql2`SUM(CASE WHEN ${mindHistory.type} IN ('inbound','outbound') THEN 1 ELSE 0 END)`,
|
|
5123
|
+
tool_count: sql2`SUM(CASE WHEN ${mindHistory.type}='tool_use' THEN 1 ELSE 0 END)`
|
|
5124
|
+
}).from(mindHistory).where(and4(eq5(mindHistory.mind, name), sql2`${mindHistory.session} IS NOT NULL`)).groupBy(mindHistory.session).orderBy(sql2`MIN(${mindHistory.created_at}) DESC`);
|
|
5125
|
+
return c.json(rows);
|
|
5126
|
+
}).get("/:name/history/channels", async (c) => {
|
|
5127
|
+
const name = c.req.param("name");
|
|
4955
5128
|
const db = await getDb();
|
|
4956
|
-
const
|
|
4957
|
-
return
|
|
4958
|
-
}
|
|
4959
|
-
|
|
4960
|
-
// src/web/api/notes.ts
|
|
4961
|
-
var createSchema = z5.object({
|
|
4962
|
-
title: z5.string().min(1).max(200),
|
|
4963
|
-
content: z5.string().min(1),
|
|
4964
|
-
reply_to: z5.string().optional()
|
|
4965
|
-
});
|
|
4966
|
-
var updateSchema = z5.object({
|
|
4967
|
-
title: z5.string().min(1).max(200).optional(),
|
|
4968
|
-
content: z5.string().min(1).optional()
|
|
4969
|
-
});
|
|
4970
|
-
var commentSchema = z5.object({
|
|
4971
|
-
content: z5.string().min(1)
|
|
4972
|
-
});
|
|
4973
|
-
var reactionSchema = z5.object({
|
|
4974
|
-
emoji: z5.string().min(1).max(32)
|
|
4975
|
-
});
|
|
4976
|
-
function resolveUserId(c) {
|
|
4977
|
-
const user = c.get("user");
|
|
4978
|
-
if (user.id === 0) return null;
|
|
4979
|
-
return { id: user.id, username: user.username };
|
|
4980
|
-
}
|
|
4981
|
-
var app12 = new Hono12().get("/", async (c) => {
|
|
4982
|
-
const author = c.req.query("author");
|
|
4983
|
-
const limit = c.req.query("limit") ? parseInt(c.req.query("limit"), 10) : void 0;
|
|
4984
|
-
const offset = c.req.query("offset") ? parseInt(c.req.query("offset"), 10) : void 0;
|
|
4985
|
-
const result = await listNotes({ authorUsername: author, limit, offset });
|
|
4986
|
-
return c.json(result);
|
|
4987
|
-
}).post("/", zValidator5("json", createSchema), async (c) => {
|
|
4988
|
-
const actor = resolveUserId(c);
|
|
4989
|
-
if (!actor) return c.json({ error: "Unauthorized" }, 401);
|
|
4990
|
-
const { title, content, reply_to } = c.req.valid("json");
|
|
4991
|
-
let replyToId;
|
|
4992
|
-
if (reply_to) {
|
|
4993
|
-
const id = await resolveNoteId(reply_to);
|
|
4994
|
-
if (id === null) return c.json({ error: `Reply target not found: ${reply_to}` }, 404);
|
|
4995
|
-
replyToId = id;
|
|
4996
|
-
}
|
|
4997
|
-
const note = await createNote(actor.id, title, content, replyToId);
|
|
4998
|
-
const replyInfo = reply_to ? ` (in reply to ${reply_to})` : "";
|
|
4999
|
-
announceToSystem(`${actor.username} published a note: ${title}${replyInfo}`).catch((err) => {
|
|
5000
|
-
logger_default.warn("failed to announce note to #system", logger_default.errorData(err));
|
|
5001
|
-
});
|
|
5002
|
-
return c.json(note, 201);
|
|
5003
|
-
}).get("/:author/:slug", async (c) => {
|
|
5004
|
-
const { author, slug } = c.req.param();
|
|
5005
|
-
const note = await getNote(author, slug);
|
|
5006
|
-
if (!note) return c.json({ error: "Note not found" }, 404);
|
|
5007
|
-
return c.json(note);
|
|
5008
|
-
}).put("/:author/:slug", zValidator5("json", updateSchema), async (c) => {
|
|
5009
|
-
const actor = resolveUserId(c);
|
|
5010
|
-
if (!actor) return c.json({ error: "Unauthorized" }, 401);
|
|
5011
|
-
const { author, slug } = c.req.param();
|
|
5012
|
-
if (actor.username !== author) return c.json({ error: "Forbidden" }, 403);
|
|
5013
|
-
const updates = c.req.valid("json");
|
|
5014
|
-
const note = await updateNote(author, slug, updates);
|
|
5015
|
-
if (!note) return c.json({ error: "Note not found" }, 404);
|
|
5016
|
-
return c.json(note);
|
|
5017
|
-
}).delete("/:author/:slug", async (c) => {
|
|
5018
|
-
const actor = resolveUserId(c);
|
|
5019
|
-
if (!actor) return c.json({ error: "Unauthorized" }, 401);
|
|
5020
|
-
const { author, slug } = c.req.param();
|
|
5021
|
-
const deleted = await deleteNote(author, slug, actor.id);
|
|
5022
|
-
if (!deleted) return c.json({ error: "Note not found or not authorized" }, 404);
|
|
5023
|
-
return c.json({ ok: true });
|
|
5024
|
-
}).post("/:author/:slug/reactions", zValidator5("json", reactionSchema), async (c) => {
|
|
5025
|
-
const actor = resolveUserId(c);
|
|
5026
|
-
if (!actor) return c.json({ error: "Unauthorized" }, 401);
|
|
5027
|
-
const { author, slug } = c.req.param();
|
|
5028
|
-
const note = await getNote(author, slug);
|
|
5029
|
-
if (!note) return c.json({ error: "Note not found" }, 404);
|
|
5030
|
-
const { emoji } = c.req.valid("json");
|
|
5031
|
-
const result = await toggleReaction(note.id, actor.id, emoji);
|
|
5032
|
-
const reactions = await getReactions(note.id);
|
|
5033
|
-
return c.json({ ...result, reactions });
|
|
5034
|
-
}).post("/:author/:slug/comments", zValidator5("json", commentSchema), async (c) => {
|
|
5035
|
-
const actor = resolveUserId(c);
|
|
5036
|
-
if (!actor) return c.json({ error: "Unauthorized" }, 401);
|
|
5037
|
-
const { author, slug } = c.req.param();
|
|
5038
|
-
const note = await getNote(author, slug);
|
|
5039
|
-
if (!note) return c.json({ error: "Note not found" }, 404);
|
|
5040
|
-
const { content } = c.req.valid("json");
|
|
5041
|
-
const comment = await addComment(note.id, actor.id, content);
|
|
5042
|
-
return c.json(comment, 201);
|
|
5043
|
-
}).delete("/:author/:slug/comments/:id", async (c) => {
|
|
5044
|
-
const actor = resolveUserId(c);
|
|
5045
|
-
if (!actor) return c.json({ error: "Unauthorized" }, 401);
|
|
5046
|
-
const commentId = parseInt(c.req.param("id"), 10);
|
|
5047
|
-
if (Number.isNaN(commentId)) return c.json({ error: "Invalid comment ID" }, 400);
|
|
5048
|
-
const deleted = await deleteComment(commentId, actor.id);
|
|
5049
|
-
if (!deleted) return c.json({ error: "Comment not found or not authorized" }, 404);
|
|
5050
|
-
return c.json({ ok: true });
|
|
5051
|
-
});
|
|
5052
|
-
var notes_default = app12;
|
|
5053
|
-
|
|
5054
|
-
// src/web/api/pages.ts
|
|
5055
|
-
import { readFile as readFile2, stat as stat2 } from "fs/promises";
|
|
5056
|
-
import { extname as extname3, resolve as resolve16 } from "path";
|
|
5057
|
-
import { Hono as Hono13 } from "hono";
|
|
5058
|
-
var MIME_TYPES = {
|
|
5059
|
-
".html": "text/html",
|
|
5060
|
-
".js": "application/javascript",
|
|
5061
|
-
".css": "text/css",
|
|
5062
|
-
".json": "application/json",
|
|
5063
|
-
".svg": "image/svg+xml",
|
|
5064
|
-
".png": "image/png",
|
|
5065
|
-
".jpg": "image/jpeg",
|
|
5066
|
-
".jpeg": "image/jpeg",
|
|
5067
|
-
".gif": "image/gif",
|
|
5068
|
-
".ico": "image/x-icon",
|
|
5069
|
-
".woff": "font/woff",
|
|
5070
|
-
".woff2": "font/woff2",
|
|
5071
|
-
".txt": "text/plain",
|
|
5072
|
-
".xml": "application/xml"
|
|
5073
|
-
};
|
|
5074
|
-
var app13 = new Hono13().get("/:name/*", async (c) => {
|
|
5129
|
+
const rows = await db.selectDistinct({ channel: mindHistory.channel }).from(mindHistory).where(eq5(mindHistory.mind, name));
|
|
5130
|
+
return c.json(rows.map((r) => r.channel));
|
|
5131
|
+
}).get("/:name/history/export", async (c) => {
|
|
5075
5132
|
const name = c.req.param("name");
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
const
|
|
5084
|
-
const
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
const body = await readFile2(indexPath);
|
|
5092
|
-
return c.body(body, 200, { "Content-Type": "text/html" });
|
|
5093
|
-
}
|
|
5094
|
-
return c.text("Not found", 404);
|
|
5133
|
+
if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
|
|
5134
|
+
const db = await getDb();
|
|
5135
|
+
const rows = await db.select().from(mindHistory).where(eq5(mindHistory.mind, name));
|
|
5136
|
+
return c.json(rows);
|
|
5137
|
+
}).get("/:name/history", async (c) => {
|
|
5138
|
+
const name = c.req.param("name");
|
|
5139
|
+
const channel = c.req.query("channel");
|
|
5140
|
+
const session = c.req.query("session");
|
|
5141
|
+
const full = c.req.query("full") === "true";
|
|
5142
|
+
const limit = Math.min(Math.max(parseInt(c.req.query("limit") ?? "50", 10) || 50, 1), 200);
|
|
5143
|
+
const offset = Math.max(parseInt(c.req.query("offset") ?? "0", 10) || 0, 0);
|
|
5144
|
+
const db = await getDb();
|
|
5145
|
+
const conditions = [eq5(mindHistory.mind, name)];
|
|
5146
|
+
if (channel) {
|
|
5147
|
+
conditions.push(eq5(mindHistory.channel, channel));
|
|
5095
5148
|
}
|
|
5096
|
-
if (
|
|
5097
|
-
|
|
5098
|
-
const mime = MIME_TYPES[ext] || "application/octet-stream";
|
|
5099
|
-
const body = await readFile2(requestedPath);
|
|
5100
|
-
return c.body(body, 200, { "Content-Type": mime });
|
|
5149
|
+
if (session) {
|
|
5150
|
+
conditions.push(eq5(mindHistory.session, session));
|
|
5101
5151
|
}
|
|
5102
|
-
|
|
5152
|
+
if (!full) {
|
|
5153
|
+
conditions.push(sql2`${mindHistory.type} IN ('inbound', 'outbound', 'summary')`);
|
|
5154
|
+
}
|
|
5155
|
+
const rows = await db.select().from(mindHistory).where(and4(...conditions)).orderBy(desc3(mindHistory.created_at)).limit(limit).offset(offset);
|
|
5156
|
+
return c.json(rows);
|
|
5103
5157
|
});
|
|
5104
|
-
var
|
|
5158
|
+
var minds_default = app12;
|
|
5105
5159
|
|
|
5106
5160
|
// src/web/api/prompts.ts
|
|
5107
|
-
import { zValidator as
|
|
5161
|
+
import { zValidator as zValidator5 } from "@hono/zod-validator";
|
|
5108
5162
|
import { eq as eq6, sql as sql3 } from "drizzle-orm";
|
|
5109
|
-
import { Hono as
|
|
5110
|
-
import { z as
|
|
5111
|
-
var
|
|
5163
|
+
import { Hono as Hono13 } from "hono";
|
|
5164
|
+
import { z as z5 } from "zod";
|
|
5165
|
+
var app13 = new Hono13().get("/", async (c) => {
|
|
5112
5166
|
let rows;
|
|
5113
5167
|
try {
|
|
5114
5168
|
const db = await getDb();
|
|
@@ -5131,7 +5185,7 @@ var app14 = new Hono14().get("/", async (c) => {
|
|
|
5131
5185
|
};
|
|
5132
5186
|
});
|
|
5133
5187
|
return c.json(prompts);
|
|
5134
|
-
}).put("/:key", requireAdmin,
|
|
5188
|
+
}).put("/:key", requireAdmin, zValidator5("json", z5.object({ content: z5.string() })), async (c) => {
|
|
5135
5189
|
const key = c.req.param("key");
|
|
5136
5190
|
if (!PROMPT_KEYS.includes(key)) {
|
|
5137
5191
|
return c.json({ error: "Unknown prompt key" }, 404);
|
|
@@ -5152,12 +5206,12 @@ var app14 = new Hono14().get("/", async (c) => {
|
|
|
5152
5206
|
await db.delete(systemPrompts).where(eq6(systemPrompts.key, key));
|
|
5153
5207
|
return c.json({ ok: true });
|
|
5154
5208
|
});
|
|
5155
|
-
var prompts_default =
|
|
5209
|
+
var prompts_default = app13;
|
|
5156
5210
|
|
|
5157
5211
|
// src/web/api/public-files.ts
|
|
5158
|
-
import { readdir as readdir2, readFile as
|
|
5159
|
-
import { extname as
|
|
5160
|
-
import { Hono as
|
|
5212
|
+
import { readdir as readdir2, readFile as readFile2, stat as stat2 } from "fs/promises";
|
|
5213
|
+
import { extname as extname3, resolve as resolve17 } from "path";
|
|
5214
|
+
import { Hono as Hono14 } from "hono";
|
|
5161
5215
|
var MAX_FILE_SIZE = 50 * 1024 * 1024;
|
|
5162
5216
|
async function resolvePublicRoot(name) {
|
|
5163
5217
|
if (name === "_system") return resolve17(voluteHome(), "shared");
|
|
@@ -5167,7 +5221,7 @@ async function resolvePublicRoot(name) {
|
|
|
5167
5221
|
function hasDotSegment(relativePath) {
|
|
5168
5222
|
return relativePath.split("/").some((seg) => seg.startsWith("."));
|
|
5169
5223
|
}
|
|
5170
|
-
var
|
|
5224
|
+
var MIME_TYPES = {
|
|
5171
5225
|
".html": "text/html",
|
|
5172
5226
|
".js": "application/javascript",
|
|
5173
5227
|
".css": "text/css",
|
|
@@ -5198,7 +5252,7 @@ async function listDir(dirPath) {
|
|
|
5198
5252
|
type: e.isDirectory() ? "directory" : "file"
|
|
5199
5253
|
}));
|
|
5200
5254
|
}
|
|
5201
|
-
var
|
|
5255
|
+
var app14 = new Hono14().get("/:name/", async (c) => {
|
|
5202
5256
|
const name = c.req.param("name");
|
|
5203
5257
|
const publicRoot = await resolvePublicRoot(name);
|
|
5204
5258
|
if (!publicRoot) return c.json({ error: "Not found" }, 404);
|
|
@@ -5214,7 +5268,7 @@ var app15 = new Hono15().get("/:name/", async (c) => {
|
|
|
5214
5268
|
if (hasDotSegment(relativePath)) return c.text("Forbidden", 403);
|
|
5215
5269
|
let fileStat;
|
|
5216
5270
|
try {
|
|
5217
|
-
fileStat = await
|
|
5271
|
+
fileStat = await stat2(requestedPath);
|
|
5218
5272
|
} catch (err) {
|
|
5219
5273
|
if (err?.code === "ENOENT") return c.text("Not found", 404);
|
|
5220
5274
|
if (err?.code === "EACCES") return c.text("Forbidden", 403);
|
|
@@ -5228,10 +5282,10 @@ var app15 = new Hono15().get("/:name/", async (c) => {
|
|
|
5228
5282
|
}
|
|
5229
5283
|
if (fileStat.isFile()) {
|
|
5230
5284
|
if (fileStat.size > MAX_FILE_SIZE) return c.text("File too large", 413);
|
|
5231
|
-
const ext =
|
|
5232
|
-
const mime =
|
|
5285
|
+
const ext = extname3(requestedPath);
|
|
5286
|
+
const mime = MIME_TYPES[ext] || "application/octet-stream";
|
|
5233
5287
|
try {
|
|
5234
|
-
const body = await
|
|
5288
|
+
const body = await readFile2(requestedPath);
|
|
5235
5289
|
return c.body(body, 200, { "Content-Type": mime });
|
|
5236
5290
|
} catch (err) {
|
|
5237
5291
|
if (err?.code === "ENOENT") return c.text("Not found", 404);
|
|
@@ -5241,11 +5295,11 @@ var app15 = new Hono15().get("/:name/", async (c) => {
|
|
|
5241
5295
|
}
|
|
5242
5296
|
return c.text("Not found", 404);
|
|
5243
5297
|
});
|
|
5244
|
-
var public_files_default =
|
|
5298
|
+
var public_files_default = app14;
|
|
5245
5299
|
|
|
5246
5300
|
// src/web/api/schedules.ts
|
|
5247
5301
|
import { CronExpressionParser } from "cron-parser";
|
|
5248
|
-
import { Hono as
|
|
5302
|
+
import { Hono as Hono15 } from "hono";
|
|
5249
5303
|
var slog = logger_default.child("schedules");
|
|
5250
5304
|
function readSchedules(name) {
|
|
5251
5305
|
return readVoluteConfig(mindDir(name))?.schedules ?? [];
|
|
@@ -5256,13 +5310,45 @@ function writeSchedules(name, schedules) {
|
|
|
5256
5310
|
config.schedules = schedules.length > 0 ? schedules : void 0;
|
|
5257
5311
|
writeVoluteConfig(dir, config);
|
|
5258
5312
|
getScheduler().loadSchedules(name);
|
|
5313
|
+
getSleepManagerIfReady()?.invalidateSleepConfig(name);
|
|
5259
5314
|
fireWebhook({
|
|
5260
5315
|
event: "schedule_changed",
|
|
5261
5316
|
mind: name,
|
|
5262
5317
|
data: { schedules }
|
|
5263
5318
|
});
|
|
5264
5319
|
}
|
|
5265
|
-
var
|
|
5320
|
+
var app15 = new Hono15().get("/:name/clock/status", async (c) => {
|
|
5321
|
+
const name = c.req.param("name");
|
|
5322
|
+
if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
|
|
5323
|
+
const sleepManager = getSleepManagerIfReady();
|
|
5324
|
+
const sleepState = sleepManager?.getState(name) ?? null;
|
|
5325
|
+
const sleepConfig = sleepManager?.getSleepConfig(name) ?? null;
|
|
5326
|
+
const schedules = readSchedules(name);
|
|
5327
|
+
const now = /* @__PURE__ */ new Date();
|
|
5328
|
+
const in24h = new Date(now.getTime() + 24 * 60 * 6e4);
|
|
5329
|
+
const upcoming = [];
|
|
5330
|
+
for (const s of schedules) {
|
|
5331
|
+
if (!s.enabled) continue;
|
|
5332
|
+
if (s.fireAt) {
|
|
5333
|
+
const fireDate = new Date(s.fireAt);
|
|
5334
|
+
if (fireDate >= now && fireDate <= in24h) {
|
|
5335
|
+
upcoming.push({ id: s.id, at: fireDate.toISOString(), type: "timer" });
|
|
5336
|
+
}
|
|
5337
|
+
} else if (s.cron) {
|
|
5338
|
+
try {
|
|
5339
|
+
const interval = CronExpressionParser.parse(s.cron);
|
|
5340
|
+
const next = interval.next().toDate();
|
|
5341
|
+
if (next <= in24h) {
|
|
5342
|
+
upcoming.push({ id: s.id, at: next.toISOString(), type: "cron" });
|
|
5343
|
+
}
|
|
5344
|
+
} catch {
|
|
5345
|
+
slog.warn(`invalid cron "${s.cron}" for schedule "${s.id}" of ${name}`);
|
|
5346
|
+
}
|
|
5347
|
+
}
|
|
5348
|
+
}
|
|
5349
|
+
upcoming.sort((a, b) => a.at.localeCompare(b.at));
|
|
5350
|
+
return c.json({ sleep: sleepState, sleepConfig, schedules, upcoming });
|
|
5351
|
+
}).get("/:name/schedules", async (c) => {
|
|
5266
5352
|
const name = c.req.param("name");
|
|
5267
5353
|
if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
|
|
5268
5354
|
return c.json(readSchedules(name));
|
|
@@ -5273,8 +5359,14 @@ var app16 = new Hono16().get("/:name/schedules", async (c) => {
|
|
|
5273
5359
|
if (entry.stage === "seed")
|
|
5274
5360
|
return c.json({ error: "Seed minds cannot use schedules \u2014 sprout first" }, 403);
|
|
5275
5361
|
const body = await c.req.json();
|
|
5276
|
-
if (!body.
|
|
5277
|
-
return c.json({ error: "
|
|
5362
|
+
if (!body.id) {
|
|
5363
|
+
return c.json({ error: "id is required (a descriptive name for this schedule)" }, 400);
|
|
5364
|
+
}
|
|
5365
|
+
if (!body.cron && !body.fireAt) {
|
|
5366
|
+
return c.json({ error: "cron or fireAt is required" }, 400);
|
|
5367
|
+
}
|
|
5368
|
+
if (body.cron && body.fireAt) {
|
|
5369
|
+
return c.json({ error: "cron and fireAt are mutually exclusive" }, 400);
|
|
5278
5370
|
}
|
|
5279
5371
|
if (!body.message && !body.script) {
|
|
5280
5372
|
return c.json({ error: "message or script is required" }, 400);
|
|
@@ -5282,20 +5374,36 @@ var app16 = new Hono16().get("/:name/schedules", async (c) => {
|
|
|
5282
5374
|
if (body.message && body.script) {
|
|
5283
5375
|
return c.json({ error: "message and script are mutually exclusive" }, 400);
|
|
5284
5376
|
}
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5377
|
+
if (body.cron) {
|
|
5378
|
+
try {
|
|
5379
|
+
CronExpressionParser.parse(body.cron);
|
|
5380
|
+
} catch {
|
|
5381
|
+
return c.json({ error: `Invalid cron expression: ${body.cron}` }, 400);
|
|
5382
|
+
}
|
|
5383
|
+
}
|
|
5384
|
+
if (body.fireAt && Number.isNaN(new Date(body.fireAt).getTime())) {
|
|
5385
|
+
return c.json({ error: `Invalid fireAt date: ${body.fireAt}` }, 400);
|
|
5386
|
+
}
|
|
5387
|
+
if (body.whileSleeping && !["skip", "queue", "trigger-wake"].includes(body.whileSleeping)) {
|
|
5388
|
+
return c.json(
|
|
5389
|
+
{
|
|
5390
|
+
error: `Invalid whileSleeping value: ${body.whileSleeping} (must be skip, queue, or trigger-wake)`
|
|
5391
|
+
},
|
|
5392
|
+
400
|
|
5393
|
+
);
|
|
5289
5394
|
}
|
|
5290
5395
|
const schedules = readSchedules(name);
|
|
5291
|
-
const id = body.id
|
|
5396
|
+
const id = body.id;
|
|
5292
5397
|
if (schedules.some((s) => s.id === id)) {
|
|
5293
5398
|
return c.json({ error: `Schedule "${id}" already exists` }, 409);
|
|
5294
5399
|
}
|
|
5295
|
-
const schedule = { id,
|
|
5400
|
+
const schedule = { id, enabled: body.enabled ?? true };
|
|
5401
|
+
if (body.cron) schedule.cron = body.cron;
|
|
5402
|
+
if (body.fireAt) schedule.fireAt = body.fireAt;
|
|
5296
5403
|
if (body.message) schedule.message = body.message;
|
|
5297
5404
|
if (body.script) schedule.script = body.script;
|
|
5298
5405
|
if (body.channel) schedule.channel = body.channel;
|
|
5406
|
+
if (body.whileSleeping) schedule.whileSleeping = body.whileSleeping;
|
|
5299
5407
|
schedules.push(schedule);
|
|
5300
5408
|
writeSchedules(name, schedules);
|
|
5301
5409
|
return c.json({ ok: true, id }, 201);
|
|
@@ -5317,6 +5425,14 @@ var app16 = new Hono16().get("/:name/schedules", async (c) => {
|
|
|
5317
5425
|
return c.json({ error: `Invalid cron expression: ${body.cron}` }, 400);
|
|
5318
5426
|
}
|
|
5319
5427
|
schedules[idx].cron = body.cron;
|
|
5428
|
+
delete schedules[idx].fireAt;
|
|
5429
|
+
}
|
|
5430
|
+
if (body.fireAt !== void 0) {
|
|
5431
|
+
if (Number.isNaN(new Date(body.fireAt).getTime())) {
|
|
5432
|
+
return c.json({ error: `Invalid fireAt date: ${body.fireAt}` }, 400);
|
|
5433
|
+
}
|
|
5434
|
+
schedules[idx].fireAt = body.fireAt;
|
|
5435
|
+
delete schedules[idx].cron;
|
|
5320
5436
|
}
|
|
5321
5437
|
if (body.message !== void 0) {
|
|
5322
5438
|
schedules[idx].message = body.message;
|
|
@@ -5326,8 +5442,18 @@ var app16 = new Hono16().get("/:name/schedules", async (c) => {
|
|
|
5326
5442
|
schedules[idx].script = body.script;
|
|
5327
5443
|
delete schedules[idx].message;
|
|
5328
5444
|
}
|
|
5445
|
+
if (body.whileSleeping && !["skip", "queue", "trigger-wake"].includes(body.whileSleeping)) {
|
|
5446
|
+
return c.json(
|
|
5447
|
+
{
|
|
5448
|
+
error: `Invalid whileSleeping value: ${body.whileSleeping} (must be skip, queue, or trigger-wake)`
|
|
5449
|
+
},
|
|
5450
|
+
400
|
|
5451
|
+
);
|
|
5452
|
+
}
|
|
5329
5453
|
if (body.enabled !== void 0) schedules[idx].enabled = body.enabled;
|
|
5330
5454
|
if (body.channel !== void 0) schedules[idx].channel = body.channel || void 0;
|
|
5455
|
+
if (body.whileSleeping !== void 0)
|
|
5456
|
+
schedules[idx].whileSleeping = body.whileSleeping || void 0;
|
|
5331
5457
|
writeSchedules(name, schedules);
|
|
5332
5458
|
return c.json({ ok: true });
|
|
5333
5459
|
}).delete("/:name/schedules/:id", requireSelf(), async (c) => {
|
|
@@ -5367,11 +5493,11 @@ var app16 = new Hono16().get("/:name/schedules", async (c) => {
|
|
|
5367
5493
|
return c.json({ error: "Failed to reach mind" }, 502);
|
|
5368
5494
|
}
|
|
5369
5495
|
});
|
|
5370
|
-
var schedules_default =
|
|
5496
|
+
var schedules_default = app15;
|
|
5371
5497
|
|
|
5372
5498
|
// src/web/api/shared.ts
|
|
5373
|
-
import { Hono as
|
|
5374
|
-
var
|
|
5499
|
+
import { Hono as Hono16 } from "hono";
|
|
5500
|
+
var app16 = new Hono16().post("/:name/shared/merge", requireAdmin, async (c) => {
|
|
5375
5501
|
const name = c.req.param("name");
|
|
5376
5502
|
const entry = await findMind(name);
|
|
5377
5503
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
@@ -5389,24 +5515,63 @@ var app17 = new Hono17().post("/:name/shared/merge", requireAdmin, async (c) =>
|
|
|
5389
5515
|
return c.json({ error: err instanceof Error ? err.message : "Merge failed" }, 500);
|
|
5390
5516
|
}
|
|
5391
5517
|
});
|
|
5392
|
-
var shared_default =
|
|
5518
|
+
var shared_default = app16;
|
|
5393
5519
|
|
|
5394
5520
|
// src/web/api/skills.ts
|
|
5395
|
-
import { existsSync as
|
|
5521
|
+
import { existsSync as existsSync15, mkdtempSync, readdirSync as readdirSync4, rmSync as rmSync6 } from "fs";
|
|
5396
5522
|
import { tmpdir } from "os";
|
|
5397
5523
|
import { join, resolve as resolve18 } from "path";
|
|
5398
5524
|
import AdmZip from "adm-zip";
|
|
5399
|
-
import { Hono as
|
|
5400
|
-
var
|
|
5525
|
+
import { Hono as Hono17 } from "hono";
|
|
5526
|
+
var app17 = new Hono17().get("/", async (c) => {
|
|
5401
5527
|
const skills = await listSharedSkills();
|
|
5402
5528
|
return c.json(skills);
|
|
5403
|
-
}).get("
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
|
|
5529
|
+
}).get("/defaults/list", async (c) => {
|
|
5530
|
+
return c.json({ skills: getStandardSkillsWithExtensions() });
|
|
5531
|
+
}).put("/defaults/list", requireAdmin, async (c) => {
|
|
5532
|
+
const body = await c.req.json();
|
|
5533
|
+
if (!Array.isArray(body.skills) || !body.skills.every((s) => typeof s === "string")) {
|
|
5534
|
+
return c.json({ error: "body.skills must be a string array" }, 400);
|
|
5535
|
+
}
|
|
5536
|
+
const config = readGlobalConfig();
|
|
5537
|
+
const allStandard = /* @__PURE__ */ new Set([...STANDARD_SKILLS, ...getExtensionStandardSkills()]);
|
|
5538
|
+
const newSet = new Set(body.skills);
|
|
5539
|
+
const removed = [...allStandard].filter((s) => !newSet.has(s));
|
|
5540
|
+
const prevRemoved = new Set(config.removedDefaultSkills ?? []);
|
|
5541
|
+
for (const s of removed) prevRemoved.add(s);
|
|
5542
|
+
for (const s of body.skills) prevRemoved.delete(s);
|
|
5543
|
+
writeGlobalConfig({
|
|
5544
|
+
...config,
|
|
5545
|
+
defaultSkills: body.skills,
|
|
5546
|
+
removedDefaultSkills: [...prevRemoved]
|
|
5547
|
+
});
|
|
5548
|
+
return c.json({ skills: body.skills });
|
|
5549
|
+
}).post("/defaults/list", requireAdmin, async (c) => {
|
|
5550
|
+
const body = await c.req.json();
|
|
5551
|
+
if (typeof body.skill !== "string" || !body.skill) {
|
|
5552
|
+
return c.json({ error: "body.skill must be a non-empty string" }, 400);
|
|
5553
|
+
}
|
|
5554
|
+
const current = getStandardSkillsWithExtensions();
|
|
5555
|
+
if (current.includes(body.skill)) {
|
|
5556
|
+
return c.json({ error: `"${body.skill}" is already a default skill` }, 409);
|
|
5557
|
+
}
|
|
5558
|
+
const config = readGlobalConfig();
|
|
5559
|
+
const updated = [...current, body.skill];
|
|
5560
|
+
const removed = (config.removedDefaultSkills ?? []).filter((s) => s !== body.skill);
|
|
5561
|
+
writeGlobalConfig({ ...config, defaultSkills: updated, removedDefaultSkills: removed });
|
|
5562
|
+
return c.json({ skills: updated });
|
|
5563
|
+
}).delete("/defaults/list/:skill", requireAdmin, async (c) => {
|
|
5564
|
+
const skill = c.req.param("skill");
|
|
5565
|
+
const current = getStandardSkillsWithExtensions();
|
|
5566
|
+
if (!current.includes(skill)) {
|
|
5567
|
+
return c.json({ error: `"${skill}" is not a default skill` }, 404);
|
|
5568
|
+
}
|
|
5569
|
+
const config = readGlobalConfig();
|
|
5570
|
+
const updated = current.filter((s) => s !== skill);
|
|
5571
|
+
const removed = new Set(config.removedDefaultSkills ?? []);
|
|
5572
|
+
removed.add(skill);
|
|
5573
|
+
writeGlobalConfig({ ...config, defaultSkills: updated, removedDefaultSkills: [...removed] });
|
|
5574
|
+
return c.json({ skills: updated });
|
|
5410
5575
|
}).post("/upload", requireAdmin, async (c) => {
|
|
5411
5576
|
const body = await c.req.parseBody();
|
|
5412
5577
|
const file = body.file;
|
|
@@ -5428,12 +5593,12 @@ var app18 = new Hono18().get("/", async (c) => {
|
|
|
5428
5593
|
}
|
|
5429
5594
|
zip.extractAllTo(tmpDir, true);
|
|
5430
5595
|
let skillDir = null;
|
|
5431
|
-
if (
|
|
5596
|
+
if (existsSync15(join(tmpDir, "SKILL.md"))) {
|
|
5432
5597
|
skillDir = tmpDir;
|
|
5433
5598
|
} else {
|
|
5434
5599
|
const entries = readdirSync4(tmpDir, { withFileTypes: true }).filter((e) => e.isDirectory());
|
|
5435
5600
|
for (const entry of entries) {
|
|
5436
|
-
if (
|
|
5601
|
+
if (existsSync15(join(tmpDir, entry.name, "SKILL.md"))) {
|
|
5437
5602
|
skillDir = join(tmpDir, entry.name);
|
|
5438
5603
|
break;
|
|
5439
5604
|
}
|
|
@@ -5450,8 +5615,15 @@ var app18 = new Hono18().get("/", async (c) => {
|
|
|
5450
5615
|
}
|
|
5451
5616
|
throw e;
|
|
5452
5617
|
} finally {
|
|
5453
|
-
|
|
5618
|
+
rmSync6(tmpDir, { recursive: true, force: true });
|
|
5454
5619
|
}
|
|
5620
|
+
}).get("/:id", async (c) => {
|
|
5621
|
+
const id = c.req.param("id");
|
|
5622
|
+
const skill = await getSharedSkill(id);
|
|
5623
|
+
if (!skill) return c.json({ error: "Skill not found" }, 404);
|
|
5624
|
+
const dir = join(sharedSkillsDir(), id);
|
|
5625
|
+
const files = listFilesRecursive(dir);
|
|
5626
|
+
return c.json({ ...skill, files });
|
|
5455
5627
|
}).delete("/:id", requireAdmin, async (c) => {
|
|
5456
5628
|
const id = c.req.param("id");
|
|
5457
5629
|
try {
|
|
@@ -5462,15 +5634,16 @@ var app18 = new Hono18().get("/", async (c) => {
|
|
|
5462
5634
|
}
|
|
5463
5635
|
return c.json({ ok: true });
|
|
5464
5636
|
});
|
|
5465
|
-
var skills_default =
|
|
5637
|
+
var skills_default = app17;
|
|
5466
5638
|
|
|
5467
5639
|
// src/web/api/system.ts
|
|
5468
|
-
import { zValidator as
|
|
5469
|
-
import {
|
|
5640
|
+
import { zValidator as zValidator6 } from "@hono/zod-validator";
|
|
5641
|
+
import { getOAuthProvider, getOAuthProviders, getProviders } from "@mariozechner/pi-ai";
|
|
5642
|
+
import { Hono as Hono18 } from "hono";
|
|
5470
5643
|
import { streamSSE as streamSSE3 } from "hono/streaming";
|
|
5471
|
-
import { z as
|
|
5644
|
+
import { z as z6 } from "zod";
|
|
5472
5645
|
var DEFAULT_API_URL = "https://volute.systems";
|
|
5473
|
-
var
|
|
5646
|
+
var app18 = new Hono18().post("/restart", requireAdmin, (c) => {
|
|
5474
5647
|
setTimeout(() => process.exit(1), 200);
|
|
5475
5648
|
return c.json({ ok: true });
|
|
5476
5649
|
}).post("/stop", requireAdmin, (c) => {
|
|
@@ -5500,7 +5673,7 @@ var app19 = new Hono19().post("/restart", requireAdmin, (c) => {
|
|
|
5500
5673
|
}).post(
|
|
5501
5674
|
"/register",
|
|
5502
5675
|
requireAdmin,
|
|
5503
|
-
|
|
5676
|
+
zValidator6("json", z6.object({ name: z6.string().min(1) })),
|
|
5504
5677
|
async (c) => {
|
|
5505
5678
|
const existing = readSystemsConfig();
|
|
5506
5679
|
if (existing) {
|
|
@@ -5539,7 +5712,7 @@ var app19 = new Hono19().post("/restart", requireAdmin, (c) => {
|
|
|
5539
5712
|
).post(
|
|
5540
5713
|
"/login",
|
|
5541
5714
|
requireAdmin,
|
|
5542
|
-
|
|
5715
|
+
zValidator6("json", z6.object({ key: z6.string().min(1) })),
|
|
5543
5716
|
async (c) => {
|
|
5544
5717
|
const existing = readSystemsConfig();
|
|
5545
5718
|
if (existing) {
|
|
@@ -5607,19 +5780,173 @@ var app19 = new Hono19().post("/restart", requireAdmin, (c) => {
|
|
|
5607
5780
|
} catch (err) {
|
|
5608
5781
|
return c.json({ error: `Connection failed: ${err.message}` }, 502);
|
|
5609
5782
|
}
|
|
5783
|
+
}).get("/ai/providers", requireAdmin, (c) => {
|
|
5784
|
+
const allProviders = getProviders();
|
|
5785
|
+
const oauthProviders = getOAuthProviders();
|
|
5786
|
+
const oauthMap = new Map(oauthProviders.map((p) => [p.id, p]));
|
|
5787
|
+
const ai = getAiConfig();
|
|
5788
|
+
const result = allProviders.map((id) => {
|
|
5789
|
+
const oauth = oauthMap.get(id);
|
|
5790
|
+
const providerConfig = ai?.providers[id];
|
|
5791
|
+
const configured = !!(providerConfig?.apiKey || providerConfig?.oauth);
|
|
5792
|
+
let authMethod = null;
|
|
5793
|
+
if (providerConfig?.oauth) authMethod = "oauth";
|
|
5794
|
+
else if (providerConfig?.apiKey) authMethod = "api_key";
|
|
5795
|
+
return {
|
|
5796
|
+
id,
|
|
5797
|
+
oauth: !!oauth,
|
|
5798
|
+
oauthName: oauth?.name,
|
|
5799
|
+
usesCallbackServer: !!oauth?.usesCallbackServer,
|
|
5800
|
+
configured,
|
|
5801
|
+
authMethod
|
|
5802
|
+
};
|
|
5803
|
+
});
|
|
5804
|
+
return c.json(result);
|
|
5805
|
+
}).get("/ai/models", requireAdmin, (c) => {
|
|
5806
|
+
const models = getAvailableModels();
|
|
5807
|
+
const enabled = new Set(getEnabledModels());
|
|
5808
|
+
return c.json(
|
|
5809
|
+
models.map((m) => ({
|
|
5810
|
+
id: m.id,
|
|
5811
|
+
name: m.name,
|
|
5812
|
+
provider: m.provider,
|
|
5813
|
+
contextWindow: m.contextWindow,
|
|
5814
|
+
maxTokens: m.maxTokens,
|
|
5815
|
+
enabled: enabled.has(m.id)
|
|
5816
|
+
}))
|
|
5817
|
+
);
|
|
5818
|
+
}).put(
|
|
5819
|
+
"/ai/models",
|
|
5820
|
+
requireAdmin,
|
|
5821
|
+
zValidator6("json", z6.object({ models: z6.array(z6.string()) })),
|
|
5822
|
+
(c) => {
|
|
5823
|
+
const { models } = c.req.valid("json");
|
|
5824
|
+
setEnabledModels(models);
|
|
5825
|
+
return c.json({ ok: true });
|
|
5826
|
+
}
|
|
5827
|
+
).put(
|
|
5828
|
+
"/ai/providers/:id",
|
|
5829
|
+
requireAdmin,
|
|
5830
|
+
zValidator6("json", z6.object({ apiKey: z6.string().min(1) })),
|
|
5831
|
+
(c) => {
|
|
5832
|
+
const id = c.req.param("id");
|
|
5833
|
+
const { apiKey } = c.req.valid("json");
|
|
5834
|
+
saveProviderConfig(id, { apiKey });
|
|
5835
|
+
return c.json({ ok: true });
|
|
5836
|
+
}
|
|
5837
|
+
).delete("/ai/providers/:id", requireAdmin, (c) => {
|
|
5838
|
+
const id = c.req.param("id");
|
|
5839
|
+
removeProviderConfig(id);
|
|
5840
|
+
return c.json({ ok: true });
|
|
5841
|
+
}).delete("/ai", requireAdmin, (c) => {
|
|
5842
|
+
removeAiConfig();
|
|
5843
|
+
return c.json({ ok: true });
|
|
5844
|
+
}).post(
|
|
5845
|
+
"/ai/oauth/start",
|
|
5846
|
+
requireAdmin,
|
|
5847
|
+
zValidator6("json", z6.object({ provider: z6.string().min(1) })),
|
|
5848
|
+
async (c) => {
|
|
5849
|
+
const { provider } = c.req.valid("json");
|
|
5850
|
+
const oauthProvider = getOAuthProvider(provider);
|
|
5851
|
+
if (!oauthProvider) {
|
|
5852
|
+
return c.json({ error: `OAuth not supported for provider: ${provider}` }, 400);
|
|
5853
|
+
}
|
|
5854
|
+
cleanupOAuthFlows();
|
|
5855
|
+
const flowId = crypto.randomUUID();
|
|
5856
|
+
const needsManualCode = !!oauthProvider.usesCallbackServer;
|
|
5857
|
+
const flow = {
|
|
5858
|
+
status: "pending",
|
|
5859
|
+
needsManualCode,
|
|
5860
|
+
createdAt: Date.now()
|
|
5861
|
+
};
|
|
5862
|
+
oauthFlows.set(flowId, flow);
|
|
5863
|
+
const promptPromise = needsManualCode ? new Promise((resolve23) => {
|
|
5864
|
+
flow.resolveCode = resolve23;
|
|
5865
|
+
}) : void 0;
|
|
5866
|
+
oauthProvider.login({
|
|
5867
|
+
onAuth: (info) => {
|
|
5868
|
+
const existing = oauthFlows.get(flowId);
|
|
5869
|
+
if (existing)
|
|
5870
|
+
Object.assign(existing, { url: info.url, instructions: info.instructions });
|
|
5871
|
+
},
|
|
5872
|
+
onPrompt: async (_prompt) => {
|
|
5873
|
+
if (promptPromise) {
|
|
5874
|
+
const existing = oauthFlows.get(flowId);
|
|
5875
|
+
if (existing) existing.waitingForCode = true;
|
|
5876
|
+
return promptPromise;
|
|
5877
|
+
}
|
|
5878
|
+
return "";
|
|
5879
|
+
},
|
|
5880
|
+
onManualCodeInput: needsManualCode ? () => promptPromise : void 0
|
|
5881
|
+
}).then((credentials) => {
|
|
5882
|
+
saveProviderConfig(provider, { oauth: credentials });
|
|
5883
|
+
const existing = oauthFlows.get(flowId);
|
|
5884
|
+
if (existing) existing.status = "complete";
|
|
5885
|
+
}).catch((err) => {
|
|
5886
|
+
const existing = oauthFlows.get(flowId);
|
|
5887
|
+
if (existing) {
|
|
5888
|
+
existing.status = "error";
|
|
5889
|
+
existing.error = err instanceof Error ? err.message : String(err);
|
|
5890
|
+
}
|
|
5891
|
+
});
|
|
5892
|
+
await new Promise((resolve23) => setTimeout(resolve23, 2e3));
|
|
5893
|
+
const state = oauthFlows.get(flowId);
|
|
5894
|
+
return c.json({
|
|
5895
|
+
flowId,
|
|
5896
|
+
url: state.url,
|
|
5897
|
+
instructions: state.instructions,
|
|
5898
|
+
needsManualCode
|
|
5899
|
+
});
|
|
5900
|
+
}
|
|
5901
|
+
).post(
|
|
5902
|
+
"/ai/oauth/code/:flowId",
|
|
5903
|
+
requireAdmin,
|
|
5904
|
+
zValidator6("json", z6.object({ code: z6.string().min(1) })),
|
|
5905
|
+
async (c) => {
|
|
5906
|
+
const flowId = c.req.param("flowId");
|
|
5907
|
+
const flow = oauthFlows.get(flowId);
|
|
5908
|
+
if (!flow) return c.json({ error: "Flow not found" }, 404);
|
|
5909
|
+
if (!flow.resolveCode) return c.json({ error: "Flow does not accept manual code" }, 400);
|
|
5910
|
+
const { code } = c.req.valid("json");
|
|
5911
|
+
flow.resolveCode(code.trim());
|
|
5912
|
+
return c.json({ ok: true });
|
|
5913
|
+
}
|
|
5914
|
+
).get("/ai/oauth/status/:flowId", requireAdmin, (c) => {
|
|
5915
|
+
const flowId = c.req.param("flowId");
|
|
5916
|
+
const flow = oauthFlows.get(flowId);
|
|
5917
|
+
if (!flow) return c.json({ error: "Flow not found" }, 404);
|
|
5918
|
+
const result = {
|
|
5919
|
+
status: flow.status,
|
|
5920
|
+
waitingForCode: flow.waitingForCode
|
|
5921
|
+
};
|
|
5922
|
+
if (flow.error) result.error = flow.error;
|
|
5923
|
+
if (flow.status !== "pending") {
|
|
5924
|
+
setTimeout(() => oauthFlows.delete(flowId), 3e4);
|
|
5925
|
+
}
|
|
5926
|
+
return c.json(result);
|
|
5610
5927
|
});
|
|
5611
|
-
var
|
|
5928
|
+
var oauthFlows = /* @__PURE__ */ new Map();
|
|
5929
|
+
var OAUTH_FLOW_TTL_MS = 10 * 60 * 1e3;
|
|
5930
|
+
function cleanupOAuthFlows() {
|
|
5931
|
+
const now = Date.now();
|
|
5932
|
+
for (const [id, flow] of oauthFlows) {
|
|
5933
|
+
if (now - flow.createdAt > OAUTH_FLOW_TTL_MS) {
|
|
5934
|
+
oauthFlows.delete(id);
|
|
5935
|
+
}
|
|
5936
|
+
}
|
|
5937
|
+
}
|
|
5938
|
+
var system_default = app18;
|
|
5612
5939
|
|
|
5613
5940
|
// src/web/api/typing.ts
|
|
5614
|
-
import { zValidator as
|
|
5615
|
-
import { Hono as
|
|
5616
|
-
import { z as
|
|
5617
|
-
var typingSchema =
|
|
5618
|
-
channel:
|
|
5619
|
-
sender:
|
|
5620
|
-
active:
|
|
5941
|
+
import { zValidator as zValidator7 } from "@hono/zod-validator";
|
|
5942
|
+
import { Hono as Hono19 } from "hono";
|
|
5943
|
+
import { z as z7 } from "zod";
|
|
5944
|
+
var typingSchema = z7.object({
|
|
5945
|
+
channel: z7.string().min(1),
|
|
5946
|
+
sender: z7.string().min(1),
|
|
5947
|
+
active: z7.boolean()
|
|
5621
5948
|
});
|
|
5622
|
-
var
|
|
5949
|
+
var app19 = new Hono19().post("/:name/typing", zValidator7("json", typingSchema), (c) => {
|
|
5623
5950
|
const { channel, sender, active } = c.req.valid("json");
|
|
5624
5951
|
const map = getTypingMap();
|
|
5625
5952
|
if (active) {
|
|
@@ -5641,13 +5968,13 @@ var app20 = new Hono20().post("/:name/typing", zValidator8("json", typingSchema)
|
|
|
5641
5968
|
const map = getTypingMap();
|
|
5642
5969
|
return c.json({ typing: map.get(channel) });
|
|
5643
5970
|
});
|
|
5644
|
-
var typing_default =
|
|
5971
|
+
var typing_default = app19;
|
|
5645
5972
|
|
|
5646
5973
|
// src/web/api/update.ts
|
|
5647
5974
|
import { spawn as spawn3 } from "child_process";
|
|
5648
|
-
import { Hono as
|
|
5975
|
+
import { Hono as Hono20 } from "hono";
|
|
5649
5976
|
var bin;
|
|
5650
|
-
var
|
|
5977
|
+
var app20 = new Hono20().get("/update", async (c) => {
|
|
5651
5978
|
const result = await checkForUpdate();
|
|
5652
5979
|
return c.json(result);
|
|
5653
5980
|
}).post("/update", requireAdmin, async (c) => {
|
|
@@ -5662,23 +5989,23 @@ var app21 = new Hono21().get("/update", async (c) => {
|
|
|
5662
5989
|
child.unref();
|
|
5663
5990
|
return c.json({ ok: true, message: "Updating..." });
|
|
5664
5991
|
});
|
|
5665
|
-
var update_default =
|
|
5992
|
+
var update_default = app20;
|
|
5666
5993
|
|
|
5667
5994
|
// src/web/api/v1/chat.ts
|
|
5668
|
-
import { zValidator as
|
|
5669
|
-
import { Hono as
|
|
5995
|
+
import { zValidator as zValidator8 } from "@hono/zod-validator";
|
|
5996
|
+
import { Hono as Hono21 } from "hono";
|
|
5670
5997
|
import { streamSSE as streamSSE4 } from "hono/streaming";
|
|
5671
|
-
import { z as
|
|
5998
|
+
import { z as z8 } from "zod";
|
|
5672
5999
|
async function fanOutToMinds(opts) {
|
|
5673
6000
|
const participants = await getParticipants(opts.conversationId);
|
|
5674
6001
|
const mindParticipants = participants.filter((p) => p.userType === "mind");
|
|
5675
6002
|
const participantNames = participants.map((p) => p.username);
|
|
5676
6003
|
const isDM = opts.isDM ?? participants.length === 2;
|
|
5677
|
-
const channelEntryType = opts.channelEntryType ?? (isDM ? "dm" : "
|
|
5678
|
-
const { getMindManager: getMindManager2 } = await import("./mind-manager-
|
|
5679
|
-
const { getSleepManagerIfReady } = await import("./sleep-manager-
|
|
6004
|
+
const channelEntryType = opts.channelEntryType ?? (isDM ? "dm" : "channel");
|
|
6005
|
+
const { getMindManager: getMindManager2 } = await import("./mind-manager-YFCOIAAX.js");
|
|
6006
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-O7YQFCV5.js");
|
|
5680
6007
|
const manager = getMindManager2();
|
|
5681
|
-
const sm =
|
|
6008
|
+
const sm = getSleepManagerIfReady2();
|
|
5682
6009
|
const targetMinds = mindParticipants.map((ap) => {
|
|
5683
6010
|
const key = opts.targetName ? opts.targetName(ap.username) : ap.username;
|
|
5684
6011
|
if (manager.isRunning(key) || sm?.isSleeping(ap.username)) return ap.username;
|
|
@@ -5725,18 +6052,18 @@ async function fanOutToMinds(opts) {
|
|
|
5725
6052
|
});
|
|
5726
6053
|
}
|
|
5727
6054
|
}
|
|
5728
|
-
var mindChatSchema =
|
|
5729
|
-
message:
|
|
5730
|
-
conversationId:
|
|
5731
|
-
sender:
|
|
5732
|
-
images:
|
|
6055
|
+
var mindChatSchema = z8.object({
|
|
6056
|
+
message: z8.string().optional(),
|
|
6057
|
+
conversationId: z8.string().optional(),
|
|
6058
|
+
sender: z8.string().optional(),
|
|
6059
|
+
images: z8.array(z8.object({ media_type: z8.string(), data: z8.string() })).optional()
|
|
5733
6060
|
});
|
|
5734
|
-
var unifiedChatSchema =
|
|
5735
|
-
message:
|
|
5736
|
-
conversationId:
|
|
5737
|
-
images:
|
|
6061
|
+
var unifiedChatSchema = z8.object({
|
|
6062
|
+
message: z8.string().optional(),
|
|
6063
|
+
conversationId: z8.string(),
|
|
6064
|
+
images: z8.array(z8.object({ media_type: z8.string(), data: z8.string() })).optional()
|
|
5738
6065
|
});
|
|
5739
|
-
var
|
|
6066
|
+
var app21 = new Hono21().use("*", authMiddleware).post("/minds/:name/chat", zValidator8("json", mindChatSchema), async (c) => {
|
|
5740
6067
|
const name = c.req.param("name");
|
|
5741
6068
|
const baseName = await getBaseName(name);
|
|
5742
6069
|
const entry = await findMind(baseName);
|
|
@@ -5797,7 +6124,7 @@ var app22 = new Hono22().use("*", authMiddleware).post("/minds/:name/chat", zVal
|
|
|
5797
6124
|
senderName,
|
|
5798
6125
|
convTitle,
|
|
5799
6126
|
isDM,
|
|
5800
|
-
channelEntryType:
|
|
6127
|
+
channelEntryType: isDM ? "dm" : "channel",
|
|
5801
6128
|
slugExtra: conv ? { convType: conv.type, convName: conv.name } : void 0,
|
|
5802
6129
|
targetName: (username) => username === baseName ? name : username
|
|
5803
6130
|
});
|
|
@@ -5827,7 +6154,7 @@ var app22 = new Hono22().use("*", authMiddleware).post("/minds/:name/chat", zVal
|
|
|
5827
6154
|
});
|
|
5828
6155
|
});
|
|
5829
6156
|
});
|
|
5830
|
-
}).post("/chat",
|
|
6157
|
+
}).post("/chat", zValidator8("json", unifiedChatSchema), async (c) => {
|
|
5831
6158
|
const user = c.get("user");
|
|
5832
6159
|
const body = c.req.valid("json");
|
|
5833
6160
|
if (!body.message && (!body.images || body.images.length === 0)) {
|
|
@@ -5854,22 +6181,22 @@ var app22 = new Hono22().use("*", authMiddleware).post("/minds/:name/chat", zVal
|
|
|
5854
6181
|
senderName,
|
|
5855
6182
|
convTitle: conv.title,
|
|
5856
6183
|
isDM,
|
|
5857
|
-
channelEntryType:
|
|
6184
|
+
channelEntryType: isDM ? "dm" : "channel",
|
|
5858
6185
|
slugExtra: { convType: conv.type, convName: conv.name }
|
|
5859
6186
|
});
|
|
5860
6187
|
return c.json({ ok: true, conversationId: body.conversationId });
|
|
5861
6188
|
});
|
|
5862
|
-
var chat_default =
|
|
6189
|
+
var chat_default = app21;
|
|
5863
6190
|
|
|
5864
6191
|
// src/web/api/v1/conversations.ts
|
|
5865
|
-
import { zValidator as
|
|
5866
|
-
import { Hono as
|
|
5867
|
-
import { z as
|
|
5868
|
-
var
|
|
5869
|
-
title:
|
|
5870
|
-
participantNames:
|
|
6192
|
+
import { zValidator as zValidator9 } from "@hono/zod-validator";
|
|
6193
|
+
import { Hono as Hono22 } from "hono";
|
|
6194
|
+
import { z as z9 } from "zod";
|
|
6195
|
+
var createSchema = z9.object({
|
|
6196
|
+
title: z9.string().optional(),
|
|
6197
|
+
participantNames: z9.array(z9.string()).min(1)
|
|
5871
6198
|
});
|
|
5872
|
-
var
|
|
6199
|
+
var app22 = new Hono22().use("*", authMiddleware).get("/", async (c) => {
|
|
5873
6200
|
const user = c.get("user");
|
|
5874
6201
|
const convs = await listConversationsWithParticipants(user.id);
|
|
5875
6202
|
return c.json(convs);
|
|
@@ -5887,7 +6214,7 @@ var app23 = new Hono23().use("*", authMiddleware).get("/", async (c) => {
|
|
|
5887
6214
|
}
|
|
5888
6215
|
const before = beforeStr ? parseInt(beforeStr, 10) : void 0;
|
|
5889
6216
|
const limit = limitStr ? parseInt(limitStr, 10) : void 0;
|
|
5890
|
-
if (before !== void 0 && isNaN(before) || limit !== void 0 && isNaN(limit)) {
|
|
6217
|
+
if (before !== void 0 && Number.isNaN(before) || limit !== void 0 && Number.isNaN(limit)) {
|
|
5891
6218
|
return c.json({ error: "Invalid cursor params: before and limit must be integers" }, 400);
|
|
5892
6219
|
}
|
|
5893
6220
|
const result = await getMessagesPaginated(id, { before, limit });
|
|
@@ -5900,7 +6227,7 @@ var app23 = new Hono23().use("*", authMiddleware).get("/", async (c) => {
|
|
|
5900
6227
|
}
|
|
5901
6228
|
const participants = await getParticipants(id);
|
|
5902
6229
|
return c.json(participants);
|
|
5903
|
-
}).post("/",
|
|
6230
|
+
}).post("/", zValidator9("json", createSchema), async (c) => {
|
|
5904
6231
|
const user = c.get("user");
|
|
5905
6232
|
const body = c.req.valid("json");
|
|
5906
6233
|
const participantIds = /* @__PURE__ */ new Set();
|
|
@@ -5946,30 +6273,30 @@ var app23 = new Hono23().use("*", authMiddleware).get("/", async (c) => {
|
|
|
5946
6273
|
if (!deleted) return c.json({ error: "Conversation not found" }, 404);
|
|
5947
6274
|
return c.json({ ok: true });
|
|
5948
6275
|
});
|
|
5949
|
-
var conversations_default =
|
|
6276
|
+
var conversations_default = app22;
|
|
5950
6277
|
|
|
5951
6278
|
// src/web/api/v1/events.ts
|
|
5952
6279
|
import { desc as desc4 } from "drizzle-orm";
|
|
5953
|
-
import { Hono as
|
|
6280
|
+
import { Hono as Hono23 } from "hono";
|
|
5954
6281
|
import { streamSSE as streamSSE5 } from "hono/streaming";
|
|
5955
6282
|
|
|
5956
6283
|
// src/lib/events/brain-presence.ts
|
|
5957
6284
|
var connections = /* @__PURE__ */ new Map();
|
|
5958
6285
|
function addConnection(username) {
|
|
5959
|
-
const
|
|
5960
|
-
connections.set(username,
|
|
5961
|
-
if (
|
|
6286
|
+
const count = connections.get(username) ?? 0;
|
|
6287
|
+
connections.set(username, count + 1);
|
|
6288
|
+
if (count === 0) {
|
|
5962
6289
|
broadcast({ type: "brain_online", mind: username, summary: `${username} connected` });
|
|
5963
6290
|
}
|
|
5964
6291
|
}
|
|
5965
6292
|
function removeConnection(username) {
|
|
5966
|
-
const
|
|
5967
|
-
if (
|
|
5968
|
-
if (
|
|
6293
|
+
const count = connections.get(username);
|
|
6294
|
+
if (count == null) return;
|
|
6295
|
+
if (count <= 1) {
|
|
5969
6296
|
connections.delete(username);
|
|
5970
6297
|
broadcast({ type: "brain_offline", mind: username, summary: `${username} disconnected` });
|
|
5971
6298
|
} else {
|
|
5972
|
-
connections.set(username,
|
|
6299
|
+
connections.set(username, count - 1);
|
|
5973
6300
|
}
|
|
5974
6301
|
}
|
|
5975
6302
|
function getOnlineBrains() {
|
|
@@ -5997,7 +6324,7 @@ function getEventsSince(sinceId) {
|
|
|
5997
6324
|
}
|
|
5998
6325
|
|
|
5999
6326
|
// src/web/api/v1/events.ts
|
|
6000
|
-
var
|
|
6327
|
+
var app23 = new Hono23().use("*", authMiddleware).get("/", async (c) => {
|
|
6001
6328
|
const user = c.get("user");
|
|
6002
6329
|
const since = c.req.query("since");
|
|
6003
6330
|
const sinceId = since ? Number(since) : 0;
|
|
@@ -6104,16 +6431,16 @@ var app24 = new Hono24().use("*", authMiddleware).get("/", async (c) => {
|
|
|
6104
6431
|
}
|
|
6105
6432
|
});
|
|
6106
6433
|
});
|
|
6107
|
-
var events_default =
|
|
6434
|
+
var events_default = app23;
|
|
6108
6435
|
|
|
6109
6436
|
// src/web/api/variants.ts
|
|
6110
|
-
import { existsSync as
|
|
6437
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync11, writeFileSync as writeFileSync10 } from "fs";
|
|
6111
6438
|
import { resolve as resolve20 } from "path";
|
|
6112
|
-
import { Hono as
|
|
6439
|
+
import { Hono as Hono24 } from "hono";
|
|
6113
6440
|
|
|
6114
6441
|
// src/lib/spawn-server.ts
|
|
6115
6442
|
import { spawn as spawn4 } from "child_process";
|
|
6116
|
-
import { closeSync, mkdirSync as
|
|
6443
|
+
import { closeSync, mkdirSync as mkdirSync10, openSync, readFileSync as readFileSync13 } from "fs";
|
|
6117
6444
|
import { resolve as resolve19 } from "path";
|
|
6118
6445
|
function tsxBin(cwd) {
|
|
6119
6446
|
return resolve19(cwd, "node_modules", ".bin", "tsx");
|
|
@@ -6152,7 +6479,7 @@ function spawnAttached(cwd, port) {
|
|
|
6152
6479
|
}
|
|
6153
6480
|
function spawnDetached(cwd, port, logDir) {
|
|
6154
6481
|
const logsDir = logDir ?? resolve19(cwd, ".mind", "logs");
|
|
6155
|
-
|
|
6482
|
+
mkdirSync10(logsDir, { recursive: true });
|
|
6156
6483
|
const logPath = resolve19(logsDir, "mind.log");
|
|
6157
6484
|
const logFd = openSync(logPath, "a");
|
|
6158
6485
|
const child = spawn4(tsxBin(cwd), ["src/server.ts", "--port", String(port)], {
|
|
@@ -6173,7 +6500,7 @@ function spawnDetached(cwd, port, logDir) {
|
|
|
6173
6500
|
}
|
|
6174
6501
|
const interval = setInterval(() => {
|
|
6175
6502
|
try {
|
|
6176
|
-
const content =
|
|
6503
|
+
const content = readFileSync13(logPath, "utf-8");
|
|
6177
6504
|
const match = content.match(/listening on :(\d+)/);
|
|
6178
6505
|
if (match) {
|
|
6179
6506
|
finish({ child, actualPort: parseInt(match[1], 10) });
|
|
@@ -6225,7 +6552,7 @@ async function verify2(port) {
|
|
|
6225
6552
|
}
|
|
6226
6553
|
|
|
6227
6554
|
// src/web/api/variants.ts
|
|
6228
|
-
var
|
|
6555
|
+
var app24 = new Hono24().get("/:name/variants", async (c) => {
|
|
6229
6556
|
const name = c.req.param("name");
|
|
6230
6557
|
const entry = await findMind(name);
|
|
6231
6558
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
@@ -6270,10 +6597,10 @@ var app25 = new Hono25().get("/:name/variants", async (c) => {
|
|
|
6270
6597
|
}
|
|
6271
6598
|
const projectRoot = mindDir(mindName);
|
|
6272
6599
|
const variantDir = resolve20(projectRoot, ".variants", variantName);
|
|
6273
|
-
if (
|
|
6600
|
+
if (existsSync16(variantDir)) {
|
|
6274
6601
|
return c.json({ error: `Variant directory already exists: ${variantDir}` }, 409);
|
|
6275
6602
|
}
|
|
6276
|
-
|
|
6603
|
+
mkdirSync11(resolve20(projectRoot, ".variants"), { recursive: true });
|
|
6277
6604
|
try {
|
|
6278
6605
|
await gitExec(["worktree", "add", "-b", variantName, variantDir], { cwd: projectRoot });
|
|
6279
6606
|
} catch (e) {
|
|
@@ -6296,7 +6623,7 @@ var app25 = new Hono25().get("/:name/variants", async (c) => {
|
|
|
6296
6623
|
return c.json({ error: `npm install failed: ${msg}` }, 500);
|
|
6297
6624
|
}
|
|
6298
6625
|
if (body.soul) {
|
|
6299
|
-
|
|
6626
|
+
writeFileSync10(resolve20(variantDir, "home/SOUL.md"), body.soul);
|
|
6300
6627
|
}
|
|
6301
6628
|
const variantPort = body.port ?? await nextPort();
|
|
6302
6629
|
await addVariant(variantName, mindName, variantPort, variantDir, variantName);
|
|
@@ -6331,7 +6658,7 @@ var app25 = new Hono25().get("/:name/variants", async (c) => {
|
|
|
6331
6658
|
} catch {
|
|
6332
6659
|
}
|
|
6333
6660
|
const projectRoot = mindDir(mindName);
|
|
6334
|
-
if (
|
|
6661
|
+
if (existsSync16(variantEntry.dir)) {
|
|
6335
6662
|
const status = (await gitExec(["status", "--porcelain"], { cwd: variantEntry.dir })).trim();
|
|
6336
6663
|
if (status) {
|
|
6337
6664
|
try {
|
|
@@ -6339,7 +6666,7 @@ var app25 = new Hono25().get("/:name/variants", async (c) => {
|
|
|
6339
6666
|
await gitExec(["commit", "-m", "Auto-commit uncommitted changes before merge"], {
|
|
6340
6667
|
cwd: variantEntry.dir
|
|
6341
6668
|
});
|
|
6342
|
-
} catch (
|
|
6669
|
+
} catch (_e) {
|
|
6343
6670
|
return c.json(
|
|
6344
6671
|
{
|
|
6345
6672
|
error: "Failed to auto-commit variant changes. Commit or stash manually before merging."
|
|
@@ -6376,7 +6703,7 @@ var app25 = new Hono25().get("/:name/variants", async (c) => {
|
|
|
6376
6703
|
await gitExec(["commit", "-m", "Auto-commit uncommitted changes before merge"], {
|
|
6377
6704
|
cwd: projectRoot
|
|
6378
6705
|
});
|
|
6379
|
-
} catch (
|
|
6706
|
+
} catch (_e) {
|
|
6380
6707
|
return c.json(
|
|
6381
6708
|
{ error: "Failed to auto-commit main changes. Commit or stash manually before merging." },
|
|
6382
6709
|
500
|
|
@@ -6385,14 +6712,14 @@ var app25 = new Hono25().get("/:name/variants", async (c) => {
|
|
|
6385
6712
|
}
|
|
6386
6713
|
try {
|
|
6387
6714
|
await gitExec(["merge", variantEntry.branch], { cwd: projectRoot });
|
|
6388
|
-
} catch (
|
|
6715
|
+
} catch (_e) {
|
|
6389
6716
|
return c.json({ error: "Merge failed. Resolve conflicts manually." }, 500);
|
|
6390
6717
|
}
|
|
6391
6718
|
await cleanupVariant(variantName, projectRoot, variantEntry.dir);
|
|
6392
6719
|
if (variantName.endsWith("-upgrade") || variantName === "upgrade") {
|
|
6393
6720
|
try {
|
|
6394
|
-
const { computeTemplateHash: computeTemplateHash2 } = await import("./template-hash-
|
|
6395
|
-
const { setMindTemplateHash: setMindTemplateHash2 } = await import("./registry-
|
|
6721
|
+
const { computeTemplateHash: computeTemplateHash2 } = await import("./template-hash-3HOR4UAJ.js");
|
|
6722
|
+
const { setMindTemplateHash: setMindTemplateHash2 } = await import("./registry-ODSALQQL.js");
|
|
6396
6723
|
const tmpl = parentEntry.template ?? "claude";
|
|
6397
6724
|
await setMindTemplateHash2(mindName, computeTemplateHash2(tmpl));
|
|
6398
6725
|
} catch (err) {
|
|
@@ -6447,19 +6774,19 @@ var app25 = new Hono25().get("/:name/variants", async (c) => {
|
|
|
6447
6774
|
await cleanupVariant(variantName, projectRoot, variantEntry.dir, { stop: true });
|
|
6448
6775
|
return c.json({ ok: true });
|
|
6449
6776
|
});
|
|
6450
|
-
var variants_default =
|
|
6777
|
+
var variants_default = app24;
|
|
6451
6778
|
|
|
6452
6779
|
// src/web/api/volute/channels.ts
|
|
6453
|
-
import { zValidator as
|
|
6454
|
-
import { Hono as
|
|
6455
|
-
import { z as
|
|
6456
|
-
var
|
|
6457
|
-
name:
|
|
6780
|
+
import { zValidator as zValidator10 } from "@hono/zod-validator";
|
|
6781
|
+
import { Hono as Hono25 } from "hono";
|
|
6782
|
+
import { z as z10 } from "zod";
|
|
6783
|
+
var createSchema2 = z10.object({
|
|
6784
|
+
name: z10.string().min(1).max(50).regex(/^[a-z0-9][a-z0-9-]*$/, "Channel names must be lowercase alphanumeric with hyphens")
|
|
6458
6785
|
});
|
|
6459
|
-
var inviteSchema =
|
|
6460
|
-
username:
|
|
6786
|
+
var inviteSchema = z10.object({
|
|
6787
|
+
username: z10.string().min(1)
|
|
6461
6788
|
});
|
|
6462
|
-
var
|
|
6789
|
+
var app25 = new Hono25().get("/", async (c) => {
|
|
6463
6790
|
const user = c.get("user");
|
|
6464
6791
|
const channels = await listChannels();
|
|
6465
6792
|
const results = await Promise.all(
|
|
@@ -6470,7 +6797,7 @@ var app26 = new Hono26().get("/", async (c) => {
|
|
|
6470
6797
|
})
|
|
6471
6798
|
);
|
|
6472
6799
|
return c.json(results);
|
|
6473
|
-
}).post("/",
|
|
6800
|
+
}).post("/", zValidator10("json", createSchema2), async (c) => {
|
|
6474
6801
|
const user = c.get("user");
|
|
6475
6802
|
const body = c.req.valid("json");
|
|
6476
6803
|
try {
|
|
@@ -6503,7 +6830,7 @@ var app26 = new Hono26().get("/", async (c) => {
|
|
|
6503
6830
|
if (!ch) return c.json({ error: "Channel not found" }, 404);
|
|
6504
6831
|
const participants = await getParticipants(ch.id);
|
|
6505
6832
|
return c.json(participants);
|
|
6506
|
-
}).post("/:name/invite",
|
|
6833
|
+
}).post("/:name/invite", zValidator10("json", inviteSchema), async (c) => {
|
|
6507
6834
|
const name = c.req.param("name");
|
|
6508
6835
|
const inviter = c.get("user");
|
|
6509
6836
|
const { username } = c.req.valid("json");
|
|
@@ -6523,13 +6850,13 @@ var app26 = new Hono26().get("/", async (c) => {
|
|
|
6523
6850
|
]);
|
|
6524
6851
|
return c.json({ ok: true });
|
|
6525
6852
|
});
|
|
6526
|
-
var channels_default2 =
|
|
6853
|
+
var channels_default2 = app25;
|
|
6527
6854
|
|
|
6528
6855
|
// src/web/api/volute/chat.ts
|
|
6529
|
-
import { zValidator as
|
|
6530
|
-
import { Hono as
|
|
6856
|
+
import { zValidator as zValidator11 } from "@hono/zod-validator";
|
|
6857
|
+
import { Hono as Hono26 } from "hono";
|
|
6531
6858
|
import { streamSSE as streamSSE6 } from "hono/streaming";
|
|
6532
|
-
import { z as
|
|
6859
|
+
import { z as z11 } from "zod";
|
|
6533
6860
|
|
|
6534
6861
|
// src/lib/bridge-outbound.ts
|
|
6535
6862
|
function extractContent(contentBlocks) {
|
|
@@ -6608,11 +6935,11 @@ async function fanOutToMinds2(opts) {
|
|
|
6608
6935
|
const mindParticipants = participants.filter((p) => p.userType === "mind");
|
|
6609
6936
|
const participantNames = participants.map((p) => p.username);
|
|
6610
6937
|
const isDM = opts.isDM ?? participants.length === 2;
|
|
6611
|
-
const channelEntryType = opts.channelEntryType ?? (isDM ? "dm" : "
|
|
6612
|
-
const { getMindManager: getMindManager2 } = await import("./mind-manager-
|
|
6613
|
-
const { getSleepManagerIfReady } = await import("./sleep-manager-
|
|
6938
|
+
const channelEntryType = opts.channelEntryType ?? (isDM ? "dm" : "channel");
|
|
6939
|
+
const { getMindManager: getMindManager2 } = await import("./mind-manager-YFCOIAAX.js");
|
|
6940
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-O7YQFCV5.js");
|
|
6614
6941
|
const manager = getMindManager2();
|
|
6615
|
-
const sm =
|
|
6942
|
+
const sm = getSleepManagerIfReady2();
|
|
6616
6943
|
const targetMinds = mindParticipants.map((ap) => {
|
|
6617
6944
|
const key = opts.targetName ? opts.targetName(ap.username) : ap.username;
|
|
6618
6945
|
if (manager.isRunning(key) || sm?.isSleeping(ap.username)) return ap.username;
|
|
@@ -6659,24 +6986,24 @@ async function fanOutToMinds2(opts) {
|
|
|
6659
6986
|
});
|
|
6660
6987
|
}
|
|
6661
6988
|
}
|
|
6662
|
-
var fileSchema =
|
|
6663
|
-
filename:
|
|
6664
|
-
data:
|
|
6989
|
+
var fileSchema = z11.object({
|
|
6990
|
+
filename: z11.string(),
|
|
6991
|
+
data: z11.string()
|
|
6665
6992
|
// base64
|
|
6666
6993
|
});
|
|
6667
|
-
var chatSchema =
|
|
6668
|
-
message:
|
|
6669
|
-
conversationId:
|
|
6670
|
-
sender:
|
|
6671
|
-
images:
|
|
6672
|
-
|
|
6673
|
-
media_type:
|
|
6674
|
-
data:
|
|
6994
|
+
var chatSchema = z11.object({
|
|
6995
|
+
message: z11.string().optional(),
|
|
6996
|
+
conversationId: z11.string().optional(),
|
|
6997
|
+
sender: z11.string().optional(),
|
|
6998
|
+
images: z11.array(
|
|
6999
|
+
z11.object({
|
|
7000
|
+
media_type: z11.string(),
|
|
7001
|
+
data: z11.string()
|
|
6675
7002
|
})
|
|
6676
7003
|
).optional(),
|
|
6677
|
-
files:
|
|
7004
|
+
files: z11.array(fileSchema).optional()
|
|
6678
7005
|
});
|
|
6679
|
-
var
|
|
7006
|
+
var app26 = new Hono26().post("/:name/chat", zValidator11("json", chatSchema), async (c) => {
|
|
6680
7007
|
const name = c.req.param("name");
|
|
6681
7008
|
const baseName = await getBaseName(name);
|
|
6682
7009
|
const entry = await findMind(baseName);
|
|
@@ -6771,7 +7098,7 @@ var app27 = new Hono27().post("/:name/chat", zValidator12("json", chatSchema), a
|
|
|
6771
7098
|
senderName,
|
|
6772
7099
|
convTitle,
|
|
6773
7100
|
isDM,
|
|
6774
|
-
channelEntryType:
|
|
7101
|
+
channelEntryType: isDM ? "dm" : "channel",
|
|
6775
7102
|
slugExtra: conv ? { convType: conv.type, convName: conv.name } : void 0,
|
|
6776
7103
|
targetName: (username) => username === baseName ? name : username
|
|
6777
7104
|
});
|
|
@@ -6802,15 +7129,15 @@ var app27 = new Hono27().post("/:name/chat", zValidator12("json", chatSchema), a
|
|
|
6802
7129
|
});
|
|
6803
7130
|
});
|
|
6804
7131
|
});
|
|
6805
|
-
var unifiedChatSchema2 =
|
|
6806
|
-
message:
|
|
6807
|
-
conversationId:
|
|
6808
|
-
images:
|
|
6809
|
-
files:
|
|
7132
|
+
var unifiedChatSchema2 = z11.object({
|
|
7133
|
+
message: z11.string().optional(),
|
|
7134
|
+
conversationId: z11.string(),
|
|
7135
|
+
images: z11.array(z11.object({ media_type: z11.string(), data: z11.string() })).optional(),
|
|
7136
|
+
files: z11.array(fileSchema).optional()
|
|
6810
7137
|
});
|
|
6811
|
-
var unifiedChatApp = new
|
|
7138
|
+
var unifiedChatApp = new Hono26().post(
|
|
6812
7139
|
"/chat",
|
|
6813
|
-
|
|
7140
|
+
zValidator11("json", unifiedChatSchema2),
|
|
6814
7141
|
async (c) => {
|
|
6815
7142
|
const user = c.get("user");
|
|
6816
7143
|
const body = c.req.valid("json");
|
|
@@ -6879,24 +7206,24 @@ var unifiedChatApp = new Hono27().post(
|
|
|
6879
7206
|
senderName,
|
|
6880
7207
|
convTitle: conv.title,
|
|
6881
7208
|
isDM,
|
|
6882
|
-
channelEntryType:
|
|
7209
|
+
channelEntryType: isDM ? "dm" : "channel",
|
|
6883
7210
|
slugExtra: { convType: conv.type, convName: conv.name }
|
|
6884
7211
|
});
|
|
6885
7212
|
return c.json({ ok: true, conversationId: body.conversationId });
|
|
6886
7213
|
}
|
|
6887
7214
|
);
|
|
6888
|
-
var chat_default2 =
|
|
7215
|
+
var chat_default2 = app26;
|
|
6889
7216
|
|
|
6890
7217
|
// src/web/api/volute/conversations.ts
|
|
6891
|
-
import { zValidator as
|
|
6892
|
-
import { Hono as
|
|
6893
|
-
import { z as
|
|
6894
|
-
var createConvSchema =
|
|
6895
|
-
title:
|
|
6896
|
-
participantIds:
|
|
6897
|
-
participantNames:
|
|
7218
|
+
import { zValidator as zValidator12 } from "@hono/zod-validator";
|
|
7219
|
+
import { Hono as Hono27 } from "hono";
|
|
7220
|
+
import { z as z12 } from "zod";
|
|
7221
|
+
var createConvSchema = z12.object({
|
|
7222
|
+
title: z12.string().optional(),
|
|
7223
|
+
participantIds: z12.array(z12.number()).optional(),
|
|
7224
|
+
participantNames: z12.array(z12.string()).optional()
|
|
6898
7225
|
});
|
|
6899
|
-
var
|
|
7226
|
+
var app27 = new Hono27().get("/:name/conversations", async (c) => {
|
|
6900
7227
|
const name = c.req.param("name");
|
|
6901
7228
|
const user = c.get("user");
|
|
6902
7229
|
let lookupId = user.id;
|
|
@@ -6907,7 +7234,7 @@ var app28 = new Hono28().get("/:name/conversations", async (c) => {
|
|
|
6907
7234
|
const all = await listConversationsForUser(lookupId);
|
|
6908
7235
|
const convs = all.filter((c2) => c2.mind_name === name || c2.type === "channel");
|
|
6909
7236
|
return c.json(convs);
|
|
6910
|
-
}).post("/:name/conversations",
|
|
7237
|
+
}).post("/:name/conversations", zValidator12("json", createConvSchema), async (c) => {
|
|
6911
7238
|
const name = c.req.param("name");
|
|
6912
7239
|
const user = c.get("user");
|
|
6913
7240
|
const body = c.req.valid("json");
|
|
@@ -6927,7 +7254,7 @@ var app28 = new Hono28().get("/:name/conversations", async (c) => {
|
|
|
6927
7254
|
if (hyphenIdx > 0) {
|
|
6928
7255
|
const prefix = pname.slice(0, hyphenIdx);
|
|
6929
7256
|
if (["discord", "slack", "telegram"].includes(prefix)) {
|
|
6930
|
-
existing = await getUserByUsername(prefix
|
|
7257
|
+
existing = await getUserByUsername(`${prefix}:${pname.slice(hyphenIdx + 1)}`);
|
|
6931
7258
|
}
|
|
6932
7259
|
}
|
|
6933
7260
|
}
|
|
@@ -6949,6 +7276,9 @@ var app28 = new Hono28().get("/:name/conversations", async (c) => {
|
|
|
6949
7276
|
if (!u) return c.json({ error: `User ${id} not found` }, 400);
|
|
6950
7277
|
}
|
|
6951
7278
|
const participantIds = [...participantSet];
|
|
7279
|
+
if (participantIds.length > 2) {
|
|
7280
|
+
return c.json({ error: "Use channels for multi-participant conversations" }, 400);
|
|
7281
|
+
}
|
|
6952
7282
|
if (participantIds.length === 2) {
|
|
6953
7283
|
const existingId = await findDMConversation(name, participantIds);
|
|
6954
7284
|
if (existingId) {
|
|
@@ -6990,18 +7320,18 @@ var app28 = new Hono28().get("/:name/conversations", async (c) => {
|
|
|
6990
7320
|
if (!deleted) return c.json({ error: "Conversation not found" }, 404);
|
|
6991
7321
|
return c.json({ ok: true });
|
|
6992
7322
|
});
|
|
6993
|
-
var conversations_default2 =
|
|
7323
|
+
var conversations_default2 = app27;
|
|
6994
7324
|
|
|
6995
7325
|
// src/web/api/volute/user-conversations.ts
|
|
6996
|
-
import { zValidator as
|
|
6997
|
-
import { Hono as
|
|
7326
|
+
import { zValidator as zValidator13 } from "@hono/zod-validator";
|
|
7327
|
+
import { Hono as Hono28 } from "hono";
|
|
6998
7328
|
import { streamSSE as streamSSE7 } from "hono/streaming";
|
|
6999
|
-
import { z as
|
|
7000
|
-
var
|
|
7001
|
-
title:
|
|
7002
|
-
participantNames:
|
|
7329
|
+
import { z as z13 } from "zod";
|
|
7330
|
+
var createSchema3 = z13.object({
|
|
7331
|
+
title: z13.string().optional(),
|
|
7332
|
+
participantNames: z13.array(z13.string()).min(1)
|
|
7003
7333
|
});
|
|
7004
|
-
var
|
|
7334
|
+
var app28 = new Hono28().use("*", authMiddleware).get("/", async (c) => {
|
|
7005
7335
|
const user = c.get("user");
|
|
7006
7336
|
const convs = await listConversationsWithParticipants(user.id);
|
|
7007
7337
|
return c.json(convs);
|
|
@@ -7013,7 +7343,7 @@ var app29 = new Hono29().use("*", authMiddleware).get("/", async (c) => {
|
|
|
7013
7343
|
}
|
|
7014
7344
|
const msgs = await getMessages(id);
|
|
7015
7345
|
return c.json(msgs);
|
|
7016
|
-
}).post("/",
|
|
7346
|
+
}).post("/", zValidator13("json", createSchema3), async (c) => {
|
|
7017
7347
|
const user = c.get("user");
|
|
7018
7348
|
const body = c.req.valid("json");
|
|
7019
7349
|
const participantIds = /* @__PURE__ */ new Set();
|
|
@@ -7037,6 +7367,9 @@ var app29 = new Hono29().use("*", authMiddleware).get("/", async (c) => {
|
|
|
7037
7367
|
if (!firstMindName) {
|
|
7038
7368
|
return c.json({ error: "At least one mind participant is required" }, 400);
|
|
7039
7369
|
}
|
|
7370
|
+
if (participantIds.size > 2) {
|
|
7371
|
+
return c.json({ error: "Use channels for multi-participant conversations" }, 400);
|
|
7372
|
+
}
|
|
7040
7373
|
const conv = await createConversation(firstMindName, "volute", {
|
|
7041
7374
|
userId: user.id !== 0 ? user.id : void 0,
|
|
7042
7375
|
title: body.title,
|
|
@@ -7075,12 +7408,12 @@ var app29 = new Hono29().use("*", authMiddleware).get("/", async (c) => {
|
|
|
7075
7408
|
if (!deleted) return c.json({ error: "Conversation not found" }, 404);
|
|
7076
7409
|
return c.json({ ok: true });
|
|
7077
7410
|
});
|
|
7078
|
-
var user_conversations_default =
|
|
7411
|
+
var user_conversations_default = app28;
|
|
7079
7412
|
|
|
7080
7413
|
// src/web/app.ts
|
|
7081
7414
|
var httpLog = logger_default.child("http");
|
|
7082
|
-
var
|
|
7083
|
-
|
|
7415
|
+
var app29 = new Hono29();
|
|
7416
|
+
app29.onError((err, c) => {
|
|
7084
7417
|
if (err instanceof HTTPException) {
|
|
7085
7418
|
return err.getResponse();
|
|
7086
7419
|
}
|
|
@@ -7091,10 +7424,10 @@ app30.onError((err, c) => {
|
|
|
7091
7424
|
});
|
|
7092
7425
|
return c.json({ error: "Internal server error" }, 500);
|
|
7093
7426
|
});
|
|
7094
|
-
|
|
7427
|
+
app29.notFound((c) => {
|
|
7095
7428
|
return c.json({ error: "Not found" }, 404);
|
|
7096
7429
|
});
|
|
7097
|
-
|
|
7430
|
+
app29.use("*", async (c, next) => {
|
|
7098
7431
|
const start = Date.now();
|
|
7099
7432
|
await next();
|
|
7100
7433
|
const duration = Date.now() - start;
|
|
@@ -7105,7 +7438,7 @@ app30.use("*", async (c, next) => {
|
|
|
7105
7438
|
httpLog.debug("request", data);
|
|
7106
7439
|
}
|
|
7107
7440
|
});
|
|
7108
|
-
|
|
7441
|
+
app29.get("/api/health", (c) => {
|
|
7109
7442
|
let version = "unknown";
|
|
7110
7443
|
let cached = null;
|
|
7111
7444
|
try {
|
|
@@ -7120,40 +7453,44 @@ app30.get("/api/health", (c) => {
|
|
|
7120
7453
|
...cached?.updateAvailable ? { updateAvailable: true, latest: cached.latest } : {}
|
|
7121
7454
|
});
|
|
7122
7455
|
});
|
|
7123
|
-
|
|
7124
|
-
|
|
7125
|
-
|
|
7126
|
-
|
|
7127
|
-
|
|
7128
|
-
|
|
7129
|
-
|
|
7130
|
-
|
|
7131
|
-
|
|
7132
|
-
|
|
7133
|
-
|
|
7134
|
-
|
|
7135
|
-
|
|
7136
|
-
|
|
7137
|
-
|
|
7138
|
-
|
|
7139
|
-
|
|
7140
|
-
|
|
7141
|
-
|
|
7142
|
-
|
|
7143
|
-
|
|
7144
|
-
|
|
7145
|
-
|
|
7146
|
-
|
|
7147
|
-
|
|
7148
|
-
|
|
7149
|
-
|
|
7150
|
-
|
|
7151
|
-
|
|
7152
|
-
|
|
7153
|
-
var app_default = app30;
|
|
7456
|
+
app29.use("/api/*", bodyLimit({ maxSize: 10 * 1024 * 1024 }));
|
|
7457
|
+
app29.use("/api/*", csrf());
|
|
7458
|
+
app29.use("/api/activity/*", authMiddleware);
|
|
7459
|
+
app29.use("/api/minds/*", authMiddleware);
|
|
7460
|
+
app29.use("/api/conversations/*", authMiddleware);
|
|
7461
|
+
app29.use("/api/volute/*", authMiddleware);
|
|
7462
|
+
app29.use("/api/system/*", authMiddleware);
|
|
7463
|
+
app29.use("/api/env/*", authMiddleware);
|
|
7464
|
+
app29.use("/api/prompts/*", authMiddleware);
|
|
7465
|
+
app29.use("/api/skills/*", authMiddleware);
|
|
7466
|
+
app29.use("/api/extensions/*", authMiddleware);
|
|
7467
|
+
app29.use("/api/bridges/*", authMiddleware);
|
|
7468
|
+
app29.use("/api/v1/*", authMiddleware);
|
|
7469
|
+
app29.route("/public", public_files_default);
|
|
7470
|
+
var routes = app29.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_default2).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", shared_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/conversations", user_conversations_default).route("/api/volute/channels", channels_default2).route("/api/volute", unifiedChatApp).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", chat_default);
|
|
7471
|
+
app29.route("/api/v1/minds", minds_default);
|
|
7472
|
+
app29.route("/api/v1/minds", typing_default);
|
|
7473
|
+
app29.route("/api/v1/minds", variants_default);
|
|
7474
|
+
app29.route("/api/v1/minds", files_default);
|
|
7475
|
+
app29.route("/api/v1/minds", env_default);
|
|
7476
|
+
app29.route("/api/v1/minds", mind_skills_default);
|
|
7477
|
+
app29.route("/api/v1/minds", schedules_default);
|
|
7478
|
+
app29.route("/api/v1/minds", logs_default);
|
|
7479
|
+
app29.route("/api/v1/system", system_default);
|
|
7480
|
+
app29.route("/api/v1/system", update_default);
|
|
7481
|
+
app29.route("/api/v1/prompts", prompts_default);
|
|
7482
|
+
app29.route("/api/v1/skills", skills_default);
|
|
7483
|
+
app29.route("/api/v1/env", sharedEnvApp);
|
|
7484
|
+
app29.route("/api/v1/channels", channels_default2);
|
|
7485
|
+
var app_default = app29;
|
|
7154
7486
|
|
|
7155
7487
|
// src/web/server.ts
|
|
7156
|
-
|
|
7488
|
+
import { existsSync as existsSync17 } from "fs";
|
|
7489
|
+
import { readFile as readFile3, stat as stat3 } from "fs/promises";
|
|
7490
|
+
import { createServer as createHttpsServer } from "https";
|
|
7491
|
+
import { dirname as dirname2, extname as extname4, resolve as resolve21 } from "path";
|
|
7492
|
+
import { serve } from "@hono/node-server";
|
|
7493
|
+
var MIME_TYPES2 = {
|
|
7157
7494
|
".html": "text/html",
|
|
7158
7495
|
".js": "application/javascript",
|
|
7159
7496
|
".css": "text/css",
|
|
@@ -7171,7 +7508,7 @@ async function startServer({
|
|
|
7171
7508
|
let searchDir = dirname2(new URL(import.meta.url).pathname);
|
|
7172
7509
|
for (let i = 0; i < 5; i++) {
|
|
7173
7510
|
const candidate = resolve21(searchDir, "dist", "web-assets");
|
|
7174
|
-
if (
|
|
7511
|
+
if (existsSync17(candidate)) {
|
|
7175
7512
|
assetsDir = candidate;
|
|
7176
7513
|
break;
|
|
7177
7514
|
}
|
|
@@ -7180,20 +7517,20 @@ async function startServer({
|
|
|
7180
7517
|
if (assetsDir) {
|
|
7181
7518
|
app_default.get("*", async (c) => {
|
|
7182
7519
|
const urlPath = new URL(c.req.url).pathname;
|
|
7183
|
-
if (urlPath.startsWith("/api/")) return c.notFound();
|
|
7520
|
+
if (urlPath.startsWith("/api/") || urlPath.startsWith("/ext/")) return c.notFound();
|
|
7184
7521
|
const filePath = resolve21(assetsDir, urlPath.slice(1));
|
|
7185
7522
|
if (!filePath.startsWith(assetsDir)) return c.text("Forbidden", 403);
|
|
7186
|
-
const s = await
|
|
7523
|
+
const s = await stat3(filePath).catch(() => null);
|
|
7187
7524
|
if (s?.isFile()) {
|
|
7188
|
-
const ext =
|
|
7189
|
-
const mime =
|
|
7190
|
-
const body = await
|
|
7525
|
+
const ext = extname4(filePath);
|
|
7526
|
+
const mime = MIME_TYPES2[ext] || "application/octet-stream";
|
|
7527
|
+
const body = await readFile3(filePath);
|
|
7191
7528
|
return c.body(body, 200, { "Content-Type": mime });
|
|
7192
7529
|
}
|
|
7193
7530
|
const indexPath = resolve21(assetsDir, "index.html");
|
|
7194
|
-
const indexStat = await
|
|
7531
|
+
const indexStat = await stat3(indexPath).catch(() => null);
|
|
7195
7532
|
if (indexStat?.isFile()) {
|
|
7196
|
-
const body = await
|
|
7533
|
+
const body = await readFile3(indexPath, "utf-8");
|
|
7197
7534
|
return c.html(body);
|
|
7198
7535
|
}
|
|
7199
7536
|
return c.text("Not found", 404);
|
|
@@ -7270,7 +7607,7 @@ async function startDaemon(opts) {
|
|
|
7270
7607
|
}
|
|
7271
7608
|
const DAEMON_PID_PATH = resolve22(systemDir, "daemon.pid");
|
|
7272
7609
|
const DAEMON_JSON_PATH = resolve22(systemDir, "daemon.json");
|
|
7273
|
-
|
|
7610
|
+
mkdirSync12(home, { recursive: true });
|
|
7274
7611
|
ensureSystemDir();
|
|
7275
7612
|
migrateToSystemDir();
|
|
7276
7613
|
migrateAgentsToMinds();
|
|
@@ -7279,16 +7616,29 @@ async function startDaemon(opts) {
|
|
|
7279
7616
|
} catch (err) {
|
|
7280
7617
|
logger_default.warn("failed to initialize shared repo", logger_default.errorData(err));
|
|
7281
7618
|
}
|
|
7282
|
-
await (await import("./db-
|
|
7283
|
-
const { migrateRegistryToDb } = await import("./migrate-registry-to-db-
|
|
7619
|
+
await (await import("./db-HMFPIRO2.js")).getDb();
|
|
7620
|
+
const { migrateRegistryToDb } = await import("./migrate-registry-to-db-FK35IPEH.js");
|
|
7284
7621
|
migrateRegistryToDb();
|
|
7285
|
-
|
|
7622
|
+
try {
|
|
7623
|
+
const { migrateGroupDMsToChannels } = await import("./conversations-7KVQV7EZ.js");
|
|
7624
|
+
await migrateGroupDMsToChannels();
|
|
7625
|
+
} catch (err) {
|
|
7626
|
+
logger_default.error("failed to migrate group DMs to channels", logger_default.errorData(err));
|
|
7627
|
+
}
|
|
7628
|
+
const { initSandbox } = await import("./sandbox-O5FUSF43.js");
|
|
7286
7629
|
await initSandbox();
|
|
7287
7630
|
try {
|
|
7288
7631
|
await syncBuiltinSkills();
|
|
7289
7632
|
} catch (err) {
|
|
7290
7633
|
logger_default.error("failed to sync built-in skills", logger_default.errorData(err));
|
|
7291
7634
|
}
|
|
7635
|
+
try {
|
|
7636
|
+
await loadAllExtensions(app_default, authMiddleware);
|
|
7637
|
+
notifyExtensionsDaemonStart();
|
|
7638
|
+
} catch (err) {
|
|
7639
|
+
logger_default.error("failed to load extensions", logger_default.errorData(err));
|
|
7640
|
+
}
|
|
7641
|
+
await initDefaultSkills();
|
|
7292
7642
|
try {
|
|
7293
7643
|
await ensureSystemChannel();
|
|
7294
7644
|
} catch (err) {
|
|
@@ -7297,7 +7647,7 @@ async function startDaemon(opts) {
|
|
|
7297
7647
|
const token = process.env.VOLUTE_DAEMON_TOKEN || randomBytes(32).toString("hex");
|
|
7298
7648
|
let tls;
|
|
7299
7649
|
if (opts.tailscale) {
|
|
7300
|
-
const { getTailscaleTls } = await import("./tailscale-
|
|
7650
|
+
const { getTailscaleTls } = await import("./tailscale-BM72RXCJ.js");
|
|
7301
7651
|
const tlsConfig = await getTailscaleTls();
|
|
7302
7652
|
tls = { key: tlsConfig.key, cert: tlsConfig.cert };
|
|
7303
7653
|
logger_default.info("Tailscale HTTPS enabled", { hostname: tlsConfig.hostname });
|
|
@@ -7318,11 +7668,11 @@ async function startDaemon(opts) {
|
|
|
7318
7668
|
process.env.VOLUTE_DAEMON_TOKEN = token;
|
|
7319
7669
|
process.env.VOLUTE_DAEMON_PORT = String(daemonPort);
|
|
7320
7670
|
process.env.VOLUTE_DAEMON_HOSTNAME = hostname;
|
|
7321
|
-
|
|
7671
|
+
writeFileSync11(DAEMON_PID_PATH, myPid, { mode: 420 });
|
|
7322
7672
|
const daemonConfig = { port, hostname, token };
|
|
7323
7673
|
if (internalPort) daemonConfig.internalPort = internalPort;
|
|
7324
7674
|
if (tls) daemonConfig.tls = true;
|
|
7325
|
-
|
|
7675
|
+
writeFileSync11(DAEMON_JSON_PATH, `${JSON.stringify(daemonConfig, null, 2)}
|
|
7326
7676
|
`, { mode: 420 });
|
|
7327
7677
|
const delivery = initDeliveryManager();
|
|
7328
7678
|
const manager = initMindManager();
|
|
@@ -7378,7 +7728,7 @@ async function startDaemon(opts) {
|
|
|
7378
7728
|
bridgeManager.startBridges(daemonPort).catch((err) => {
|
|
7379
7729
|
logger_default.warn("failed to start bridges", logger_default.errorData(err));
|
|
7380
7730
|
});
|
|
7381
|
-
import("./cloud-sync-
|
|
7731
|
+
import("./cloud-sync-JV4LJOK3.js").then(
|
|
7382
7732
|
({ consumeQueuedMessages }) => consumeQueuedMessages().catch((err) => {
|
|
7383
7733
|
logger_default.warn("failed to consume queued cloud messages", logger_default.errorData(err));
|
|
7384
7734
|
})
|
|
@@ -7386,7 +7736,7 @@ async function startDaemon(opts) {
|
|
|
7386
7736
|
logger_default.warn("failed to load cloud-sync module", logger_default.errorData(err));
|
|
7387
7737
|
});
|
|
7388
7738
|
try {
|
|
7389
|
-
const { backfillTemplateHashes, notifyVersionUpdate } = await import("./version-notify-
|
|
7739
|
+
const { backfillTemplateHashes, notifyVersionUpdate } = await import("./version-notify-2NTWVEHL.js");
|
|
7390
7740
|
backfillTemplateHashes();
|
|
7391
7741
|
notifyVersionUpdate().catch((err) => {
|
|
7392
7742
|
logger_default.warn("failed to send version update notifications", logger_default.errorData(err));
|
|
@@ -7409,13 +7759,13 @@ async function startDaemon(opts) {
|
|
|
7409
7759
|
logger_default.info(`running on ${hostname}:${port}, pid ${myPid}`);
|
|
7410
7760
|
function cleanup() {
|
|
7411
7761
|
try {
|
|
7412
|
-
if (
|
|
7762
|
+
if (readFileSync14(DAEMON_PID_PATH, "utf-8").trim() === myPid) {
|
|
7413
7763
|
unlinkSync2(DAEMON_PID_PATH);
|
|
7414
7764
|
}
|
|
7415
7765
|
} catch {
|
|
7416
7766
|
}
|
|
7417
7767
|
try {
|
|
7418
|
-
const data = JSON.parse(
|
|
7768
|
+
const data = JSON.parse(readFileSync14(DAEMON_JSON_PATH, "utf-8"));
|
|
7419
7769
|
if (data.token === token) {
|
|
7420
7770
|
unlinkSync2(DAEMON_JSON_PATH);
|
|
7421
7771
|
}
|
|
@@ -7437,7 +7787,7 @@ async function startDaemon(opts) {
|
|
|
7437
7787
|
}
|
|
7438
7788
|
};
|
|
7439
7789
|
try {
|
|
7440
|
-
safe("
|
|
7790
|
+
safe("notifyExtensionsDaemonStop", notifyExtensionsDaemonStop);
|
|
7441
7791
|
safe("stopAllActivityTrackers", stopAll);
|
|
7442
7792
|
safe("unsubscribeWebhook", unsubscribeWebhook);
|
|
7443
7793
|
safe("sleepManager.stop", () => sleepManager.stop());
|