volute 0.29.0 → 0.30.1
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 +112 -13
- 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 +351 -477
- package/dist/{archive-UA4BDFXQ.js → archive-WWDBWYN2.js} +2 -2
- package/dist/{bridge-FQHZL3MC.js → bridge-RO37CUFM.js} +2 -2
- package/dist/{chat-KTPOR2JT.js → chat-TCUNPFGO.js} +8 -8
- package/dist/{chunk-FLZGS4QH.js → chunk-2C2VXEBB.js} +2 -2
- package/dist/chunk-2NDZC3S7.js +1330 -0
- package/dist/{chunk-IKRVFPWU.js → chunk-7D47T4RB.js} +3 -2
- package/dist/{chunk-AW7PFDVN.js → chunk-CVH6Y2YG.js} +1 -1
- package/dist/{chunk-XBLSAVJF.js → chunk-DTC6EH5I.js} +1 -1
- package/dist/{chunk-THUUIU3E.js → chunk-EFP3PE6C.js} +5 -5
- package/dist/{chunk-JGFVMROS.js → chunk-EFVHR7KH.js} +1 -1
- package/dist/{chunk-CQ7SNKNI.js → chunk-FSM45XD5.js} +1 -1
- 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-EHZKEMMV.js → chunk-LIRWLNAK.js} +24 -10
- 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-CMILSHZD.js → chunk-QVAQ5454.js} +84 -300
- 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-DUAUMCEE.js → chunk-W5OOPLNP.js} +3 -3
- package/dist/{chunk-2WPW7OT6.js → chunk-ZWKTUQEL.js} +1 -1
- package/dist/cli.js +22 -26
- package/dist/{clock-DGCBVGYA.js → clock-G3ALCMLJ.js} +10 -6
- package/dist/{cloud-sync-KILFGV5Q.js → cloud-sync-JV4LJOK3.js} +13 -12
- package/dist/{conversations-P5BL7RMX.js → conversations-7KVQV7EZ.js} +3 -3
- package/dist/{create-DFCAGEE5.js → create-JTLS7GX3.js} +2 -2
- 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-UHOMICXT.js → daemon-restart-4JGBHEJ4.js} +7 -7
- package/dist/daemon.js +1257 -1022
- 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-WRHFI3YW.js → mind-activity-tracker-VYN2ZZ2M.js} +3 -3
- package/dist/{mind-list-UPJ75GPI.js → mind-list-V5WW5DUA.js} +2 -2
- package/dist/{mind-manager-P66HQDNE.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-OFKXNKJF.js → package-OYUD4ZJ4.js} +12 -6
- package/dist/{pages-watcher-P7QECRE2.js → pages-watcher-Z3PKNROC.js} +3 -3
- 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/volute-mind/SKILL.md +10 -19
- 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-WDHRO3XD.js → version-notify-2NTWVEHL.js} +15 -14
- 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 +12 -6
- 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/dist/chunk-P72MVS4R.js +0 -188
- package/dist/chunk-ZYGKG6VC.js +0 -22
- package/dist/down-LVBXEULC.js +0 -14
- package/dist/message-delivery-Q7VUMIEI.js +0 -27
- package/dist/notes-XCER3I7M.js +0 -220
- package/dist/pages-EUJR52AH.js +0 -36
- package/dist/publish-ZZB33WP4.js +0 -86
- package/dist/skills/notes/SKILL.md +0 -34
- package/dist/sleep-manager-G4B5GW5P.js +0 -29
- package/dist/status-S7UUPNRW.js +0 -38
- package/dist/up-W6VAK2XE.js +0 -17
- package/dist/web-assets/assets/index-BmKDnWDB.css +0 -1
- package/dist/web-assets/assets/index-CLJMx-GA.js +0 -71
package/dist/daemon.js
CHANGED
|
@@ -1,62 +1,51 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
addSharedWorktree,
|
|
4
|
-
ensureSharedRepo,
|
|
5
|
-
removeSharedWorktree,
|
|
6
|
-
sharedMerge
|
|
7
|
-
} from "./chunk-P72MVS4R.js";
|
|
8
2
|
import {
|
|
9
3
|
announceToSystem,
|
|
10
|
-
approveUser,
|
|
11
|
-
changePassword,
|
|
12
|
-
countAdmins,
|
|
13
|
-
createUser,
|
|
14
|
-
deleteMindUser as deleteMindUser2,
|
|
15
|
-
deleteSystemsConfig,
|
|
16
|
-
deleteUser,
|
|
17
4
|
deliverMessage,
|
|
18
5
|
ensureSystemChannel,
|
|
19
6
|
extractTextContent,
|
|
20
7
|
getDeliveryManager,
|
|
21
|
-
getOrCreateMindUser,
|
|
22
8
|
getScheduler,
|
|
23
9
|
getSleepManagerIfReady,
|
|
24
10
|
getTokenBudget,
|
|
25
11
|
getTypingMap,
|
|
26
|
-
getUser,
|
|
27
|
-
getUserByUsername,
|
|
28
12
|
initDeliveryManager,
|
|
29
13
|
initMailPoller,
|
|
30
14
|
initScheduler,
|
|
31
15
|
initSleepManager,
|
|
32
16
|
initTokenBudget,
|
|
33
17
|
joinSystemChannel,
|
|
34
|
-
listPendingUsers,
|
|
35
|
-
listUsers,
|
|
36
|
-
listUsersByType,
|
|
37
|
-
migrateMindRoles,
|
|
38
18
|
publish as publish2,
|
|
39
19
|
publishTypingForChannels,
|
|
40
|
-
readSystemsConfig,
|
|
41
20
|
readVoluteConfig,
|
|
42
21
|
recordInbound,
|
|
43
22
|
resolveChannelId,
|
|
44
|
-
setUserRole,
|
|
45
23
|
splitMessage,
|
|
46
24
|
startMindFull,
|
|
47
25
|
stopMindFull,
|
|
48
26
|
subscribe as subscribe3,
|
|
49
|
-
updateUserProfile,
|
|
50
|
-
verifyUser,
|
|
51
27
|
writeChannelEntry,
|
|
52
|
-
writeSystemsConfig,
|
|
53
28
|
writeVoluteConfig
|
|
54
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-QVAQ5454.js";
|
|
55
30
|
import {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
} from "./chunk-
|
|
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";
|
|
60
49
|
import {
|
|
61
50
|
addMessage,
|
|
62
51
|
createChannel,
|
|
@@ -83,12 +72,12 @@ import {
|
|
|
83
72
|
markConversationRead,
|
|
84
73
|
publish,
|
|
85
74
|
subscribe as subscribe2
|
|
86
|
-
} from "./chunk-
|
|
75
|
+
} from "./chunk-2C2VXEBB.js";
|
|
87
76
|
import {
|
|
88
77
|
checkForUpdate,
|
|
89
78
|
checkForUpdateCached,
|
|
90
79
|
getCurrentVersion
|
|
91
|
-
} from "./chunk-
|
|
80
|
+
} from "./chunk-S5LR3XYJ.js";
|
|
92
81
|
import {
|
|
93
82
|
applyInitFiles,
|
|
94
83
|
composeTemplate,
|
|
@@ -96,35 +85,56 @@ import {
|
|
|
96
85
|
copyTemplateToDir,
|
|
97
86
|
findTemplatesRoot,
|
|
98
87
|
listFiles
|
|
99
|
-
} from "./chunk-
|
|
88
|
+
} from "./chunk-VGWJSNHS.js";
|
|
100
89
|
import {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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";
|
|
105
127
|
import {
|
|
106
128
|
broadcast,
|
|
107
129
|
subscribe
|
|
108
|
-
} from "./chunk-
|
|
109
|
-
import {
|
|
110
|
-
PROMPT_DEFAULTS,
|
|
111
|
-
PROMPT_KEYS,
|
|
112
|
-
RestartTracker,
|
|
113
|
-
RotatingLog,
|
|
114
|
-
getMindManager,
|
|
115
|
-
getMindPromptDefaults,
|
|
116
|
-
getPrompt,
|
|
117
|
-
getPromptIfCustom,
|
|
118
|
-
initMindManager,
|
|
119
|
-
resolveMindToken,
|
|
120
|
-
substitute
|
|
121
|
-
} from "./chunk-EHZKEMMV.js";
|
|
122
|
-
import "./chunk-J4IBNXGJ.js";
|
|
130
|
+
} from "./chunk-P27RV5WM.js";
|
|
123
131
|
import {
|
|
124
132
|
SEED_SKILLS,
|
|
125
133
|
STANDARD_SKILLS,
|
|
126
134
|
getSharedSkill,
|
|
135
|
+
getStandardSkillsWithExtensions,
|
|
127
136
|
importSkillFromDir,
|
|
137
|
+
initDefaultSkills,
|
|
128
138
|
installSkill,
|
|
129
139
|
listFilesRecursive,
|
|
130
140
|
listMindSkills,
|
|
@@ -135,15 +145,10 @@ import {
|
|
|
135
145
|
syncBuiltinSkills,
|
|
136
146
|
uninstallSkill,
|
|
137
147
|
updateSkill
|
|
138
|
-
} from "./chunk-
|
|
148
|
+
} from "./chunk-MDPCSXZ4.js";
|
|
139
149
|
import {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
listPending,
|
|
143
|
-
rejectPending,
|
|
144
|
-
stageFile,
|
|
145
|
-
validateFilePath
|
|
146
|
-
} from "./chunk-MD4C26II.js";
|
|
150
|
+
isHomeOnlyArchive
|
|
151
|
+
} from "./chunk-DTC6EH5I.js";
|
|
147
152
|
import {
|
|
148
153
|
findBridgeForChannel,
|
|
149
154
|
findOpenClawSession,
|
|
@@ -157,35 +162,46 @@ import {
|
|
|
157
162
|
resolveChannelMapping,
|
|
158
163
|
setBridgeConfig,
|
|
159
164
|
setChannelMapping
|
|
160
|
-
} from "./chunk-
|
|
165
|
+
} from "./chunk-UPA6COHU.js";
|
|
161
166
|
import {
|
|
162
167
|
loadMergedEnv,
|
|
163
168
|
mindEnvPath,
|
|
164
169
|
readEnv,
|
|
165
170
|
sharedEnvPath,
|
|
166
171
|
writeEnv
|
|
167
|
-
} 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";
|
|
168
183
|
import {
|
|
169
184
|
logBuffer,
|
|
170
185
|
logger_default
|
|
171
186
|
} from "./chunk-YUIHSKR6.js";
|
|
172
|
-
import {
|
|
173
|
-
isHomeOnlyArchive
|
|
174
|
-
} from "./chunk-XBLSAVJF.js";
|
|
175
187
|
import {
|
|
176
188
|
exec,
|
|
177
189
|
gitExec,
|
|
178
190
|
resolveVoluteBin
|
|
179
|
-
} from "./chunk-
|
|
191
|
+
} from "./chunk-CVH6Y2YG.js";
|
|
180
192
|
import {
|
|
181
193
|
chownMindDir,
|
|
182
194
|
createMindUser,
|
|
183
195
|
deleteMindUser,
|
|
184
196
|
ensureVoluteGroup,
|
|
185
197
|
isIsolationEnabled,
|
|
198
|
+
mindUserName,
|
|
186
199
|
wrapForIsolation
|
|
187
|
-
} from "./chunk-
|
|
188
|
-
import
|
|
200
|
+
} from "./chunk-G3GBKZGG.js";
|
|
201
|
+
import {
|
|
202
|
+
readGlobalConfig,
|
|
203
|
+
writeGlobalConfig
|
|
204
|
+
} from "./chunk-7D47T4RB.js";
|
|
189
205
|
import "./chunk-D424ZQGI.js";
|
|
190
206
|
import {
|
|
191
207
|
buildVoluteSlug,
|
|
@@ -206,9 +222,6 @@ import {
|
|
|
206
222
|
mindDir,
|
|
207
223
|
mindHistory,
|
|
208
224
|
nextPort,
|
|
209
|
-
noteComments,
|
|
210
|
-
noteReactions,
|
|
211
|
-
notes,
|
|
212
225
|
readAllMinds,
|
|
213
226
|
readRegistry,
|
|
214
227
|
removeMind,
|
|
@@ -222,14 +235,14 @@ import {
|
|
|
222
235
|
validateMindName,
|
|
223
236
|
voluteHome,
|
|
224
237
|
voluteSystemDir
|
|
225
|
-
} from "./chunk-
|
|
238
|
+
} from "./chunk-HHTXM4JT.js";
|
|
226
239
|
import {
|
|
227
240
|
__export
|
|
228
241
|
} from "./chunk-K3NQKI34.js";
|
|
229
242
|
|
|
230
243
|
// src/daemon.ts
|
|
231
244
|
import { randomBytes } from "crypto";
|
|
232
|
-
import { mkdirSync as
|
|
245
|
+
import { mkdirSync as mkdirSync12, readFileSync as readFileSync14, unlinkSync as unlinkSync2, writeFileSync as writeFileSync11 } from "fs";
|
|
233
246
|
import { homedir as homedir2 } from "os";
|
|
234
247
|
import { resolve as resolve22 } from "path";
|
|
235
248
|
import { format } from "util";
|
|
@@ -818,6 +831,259 @@ function migrateToSystemDir() {
|
|
|
818
831
|
}
|
|
819
832
|
}
|
|
820
833
|
|
|
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
|
+
}
|
|
848
|
+
}
|
|
849
|
+
function sharedDir() {
|
|
850
|
+
return resolve6(voluteHome(), "shared");
|
|
851
|
+
}
|
|
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);
|
|
884
|
+
}
|
|
885
|
+
}
|
|
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
|
+
}
|
|
913
|
+
}
|
|
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
|
+
}
|
|
924
|
+
}
|
|
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));
|
|
929
|
+
}
|
|
930
|
+
try {
|
|
931
|
+
await gitExec(["branch", "-D", mindName], { cwd: dir });
|
|
932
|
+
} catch {
|
|
933
|
+
}
|
|
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));
|
|
943
|
+
}
|
|
944
|
+
}
|
|
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
|
+
);
|
|
969
|
+
}
|
|
970
|
+
const diff = (await gitExec(["diff", `main...${mindName}`, "--stat"], { cwd: dir })).trim();
|
|
971
|
+
if (!diff) {
|
|
972
|
+
return { ok: true, message: "Nothing to merge" };
|
|
973
|
+
}
|
|
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));
|
|
981
|
+
}
|
|
982
|
+
return { ok: false, conflicts: true, message: "Merge conflicts detected" };
|
|
983
|
+
}
|
|
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
|
+
};
|
|
994
|
+
}
|
|
995
|
+
rechownWorktree(worktreePath, mindName);
|
|
996
|
+
return { ok: true };
|
|
997
|
+
});
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
// src/web/app.ts
|
|
1001
|
+
import { Hono as Hono29 } from "hono";
|
|
1002
|
+
import { bodyLimit } from "hono/body-limit";
|
|
1003
|
+
import { csrf } from "hono/csrf";
|
|
1004
|
+
import { HTTPException } from "hono/http-exception";
|
|
1005
|
+
|
|
1006
|
+
// src/web/api/activity.ts
|
|
1007
|
+
import { desc } from "drizzle-orm";
|
|
1008
|
+
import { Hono } from "hono";
|
|
1009
|
+
import { streamSSE } from "hono/streaming";
|
|
1010
|
+
var app = new Hono().get("/events", async (c) => {
|
|
1011
|
+
const user = c.get("user");
|
|
1012
|
+
return streamSSE(c, async (stream) => {
|
|
1013
|
+
const cleanups = [];
|
|
1014
|
+
try {
|
|
1015
|
+
let recentActivity = [];
|
|
1016
|
+
try {
|
|
1017
|
+
const db = await getDb();
|
|
1018
|
+
recentActivity = await db.select().from(activity).orderBy(desc(activity.created_at)).limit(50);
|
|
1019
|
+
recentActivity = recentActivity.map((row) => ({
|
|
1020
|
+
...row,
|
|
1021
|
+
metadata: row.metadata ? JSON.parse(row.metadata) : null
|
|
1022
|
+
}));
|
|
1023
|
+
} catch (err) {
|
|
1024
|
+
logger_default.error("[activity-sse] failed to fetch recent activity", logger_default.errorData(err));
|
|
1025
|
+
}
|
|
1026
|
+
let conversations2 = [];
|
|
1027
|
+
try {
|
|
1028
|
+
conversations2 = await listConversationsWithParticipants(user.id);
|
|
1029
|
+
} catch (err) {
|
|
1030
|
+
logger_default.error("[activity-sse] failed to fetch conversations", logger_default.errorData(err));
|
|
1031
|
+
}
|
|
1032
|
+
await stream.writeSSE({
|
|
1033
|
+
data: JSON.stringify({
|
|
1034
|
+
event: "snapshot",
|
|
1035
|
+
activity: recentActivity,
|
|
1036
|
+
conversations: conversations2,
|
|
1037
|
+
activeMinds: getActiveMinds()
|
|
1038
|
+
})
|
|
1039
|
+
});
|
|
1040
|
+
const unsubActivity = subscribe((event) => {
|
|
1041
|
+
stream.writeSSE({
|
|
1042
|
+
data: JSON.stringify({ event: "activity", ...event })
|
|
1043
|
+
}).catch((err) => {
|
|
1044
|
+
if (!stream.aborted) logger_default.error("[activity-sse] write error:", logger_default.errorData(err));
|
|
1045
|
+
});
|
|
1046
|
+
});
|
|
1047
|
+
cleanups.push(unsubActivity);
|
|
1048
|
+
for (const conv of conversations2) {
|
|
1049
|
+
const unsubConv = subscribe2(conv.id, (event) => {
|
|
1050
|
+
stream.writeSSE({
|
|
1051
|
+
data: JSON.stringify({ event: "conversation", conversationId: conv.id, ...event })
|
|
1052
|
+
}).catch((err) => {
|
|
1053
|
+
if (!stream.aborted) logger_default.error("[activity-sse] write error:", logger_default.errorData(err));
|
|
1054
|
+
});
|
|
1055
|
+
});
|
|
1056
|
+
cleanups.push(unsubConv);
|
|
1057
|
+
}
|
|
1058
|
+
const keepAlive = setInterval(() => {
|
|
1059
|
+
stream.writeSSE({ data: "" }).catch((err) => {
|
|
1060
|
+
if (!stream.aborted) logger_default.error("[activity-sse] ping error:", logger_default.errorData(err));
|
|
1061
|
+
});
|
|
1062
|
+
}, 15e3);
|
|
1063
|
+
cleanups.push(() => clearInterval(keepAlive));
|
|
1064
|
+
await new Promise((resolve23) => {
|
|
1065
|
+
stream.onAbort(() => resolve23());
|
|
1066
|
+
});
|
|
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
|
+
|
|
821
1087
|
// src/web/middleware/auth.ts
|
|
822
1088
|
import { timingSafeEqual } from "crypto";
|
|
823
1089
|
import { eq as eq2, lt as lt2 } from "drizzle-orm";
|
|
@@ -939,103 +1205,7 @@ var requireSelf = (paramName = "name") => createMiddleware(async (c, next) => {
|
|
|
939
1205
|
await next();
|
|
940
1206
|
});
|
|
941
1207
|
|
|
942
|
-
// src/web/server.ts
|
|
943
|
-
import { existsSync as existsSync16 } from "fs";
|
|
944
|
-
import { readFile as readFile4, stat as stat4 } from "fs/promises";
|
|
945
|
-
import { createServer as createHttpsServer } from "https";
|
|
946
|
-
import { dirname as dirname2, extname as extname5, resolve as resolve21 } from "path";
|
|
947
|
-
import { serve } from "@hono/node-server";
|
|
948
|
-
|
|
949
|
-
// src/web/app.ts
|
|
950
|
-
import { Hono as Hono30 } from "hono";
|
|
951
|
-
import { bodyLimit } from "hono/body-limit";
|
|
952
|
-
import { csrf } from "hono/csrf";
|
|
953
|
-
import { HTTPException } from "hono/http-exception";
|
|
954
|
-
|
|
955
|
-
// src/web/api/activity.ts
|
|
956
|
-
import { desc } from "drizzle-orm";
|
|
957
|
-
import { Hono } from "hono";
|
|
958
|
-
import { streamSSE } from "hono/streaming";
|
|
959
|
-
var app = new Hono().get("/events", async (c) => {
|
|
960
|
-
const user = c.get("user");
|
|
961
|
-
return streamSSE(c, async (stream) => {
|
|
962
|
-
const cleanups = [];
|
|
963
|
-
try {
|
|
964
|
-
let recentActivity = [];
|
|
965
|
-
try {
|
|
966
|
-
const db = await getDb();
|
|
967
|
-
recentActivity = await db.select().from(activity).orderBy(desc(activity.created_at)).limit(50);
|
|
968
|
-
recentActivity = recentActivity.map((row) => ({
|
|
969
|
-
...row,
|
|
970
|
-
metadata: row.metadata ? JSON.parse(row.metadata) : null
|
|
971
|
-
}));
|
|
972
|
-
} catch (err) {
|
|
973
|
-
logger_default.error("[activity-sse] failed to fetch recent activity", logger_default.errorData(err));
|
|
974
|
-
}
|
|
975
|
-
let conversations2 = [];
|
|
976
|
-
try {
|
|
977
|
-
conversations2 = await listConversationsWithParticipants(user.id);
|
|
978
|
-
} catch (err) {
|
|
979
|
-
logger_default.error("[activity-sse] failed to fetch conversations", logger_default.errorData(err));
|
|
980
|
-
}
|
|
981
|
-
const sites = getCachedSites();
|
|
982
|
-
const recentPages = getCachedRecentPages();
|
|
983
|
-
await stream.writeSSE({
|
|
984
|
-
data: JSON.stringify({
|
|
985
|
-
event: "snapshot",
|
|
986
|
-
activity: recentActivity,
|
|
987
|
-
conversations: conversations2,
|
|
988
|
-
sites,
|
|
989
|
-
recentPages,
|
|
990
|
-
activeMinds: getActiveMinds()
|
|
991
|
-
})
|
|
992
|
-
});
|
|
993
|
-
const unsubActivity = subscribe((event) => {
|
|
994
|
-
stream.writeSSE({
|
|
995
|
-
data: JSON.stringify({ event: "activity", ...event })
|
|
996
|
-
}).catch((err) => {
|
|
997
|
-
if (!stream.aborted) logger_default.error("[activity-sse] write error:", logger_default.errorData(err));
|
|
998
|
-
});
|
|
999
|
-
});
|
|
1000
|
-
cleanups.push(unsubActivity);
|
|
1001
|
-
for (const conv of conversations2) {
|
|
1002
|
-
const unsubConv = subscribe2(conv.id, (event) => {
|
|
1003
|
-
stream.writeSSE({
|
|
1004
|
-
data: JSON.stringify({ event: "conversation", conversationId: conv.id, ...event })
|
|
1005
|
-
}).catch((err) => {
|
|
1006
|
-
if (!stream.aborted) logger_default.error("[activity-sse] write error:", logger_default.errorData(err));
|
|
1007
|
-
});
|
|
1008
|
-
});
|
|
1009
|
-
cleanups.push(unsubConv);
|
|
1010
|
-
}
|
|
1011
|
-
const keepAlive = setInterval(() => {
|
|
1012
|
-
stream.writeSSE({ data: "" }).catch((err) => {
|
|
1013
|
-
if (!stream.aborted) logger_default.error("[activity-sse] ping error:", logger_default.errorData(err));
|
|
1014
|
-
});
|
|
1015
|
-
}, 15e3);
|
|
1016
|
-
cleanups.push(() => clearInterval(keepAlive));
|
|
1017
|
-
await new Promise((resolve23) => {
|
|
1018
|
-
stream.onAbort(() => resolve23());
|
|
1019
|
-
});
|
|
1020
|
-
} finally {
|
|
1021
|
-
for (const cleanup of cleanups) {
|
|
1022
|
-
try {
|
|
1023
|
-
cleanup();
|
|
1024
|
-
} catch {
|
|
1025
|
-
}
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
1028
|
-
});
|
|
1029
|
-
});
|
|
1030
|
-
var activity_default = app;
|
|
1031
|
-
|
|
1032
1208
|
// src/web/api/auth.ts
|
|
1033
|
-
import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync4, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
1034
|
-
import { extname, resolve as resolve6 } from "path";
|
|
1035
|
-
import { zValidator } from "@hono/zod-validator";
|
|
1036
|
-
import { Hono as Hono2 } from "hono";
|
|
1037
|
-
import { deleteCookie, getCookie as getCookie2, setCookie } from "hono/cookie";
|
|
1038
|
-
import { z } from "zod";
|
|
1039
1209
|
function tryJoinSystem(userId) {
|
|
1040
1210
|
if (!process.env.VOLUTE_DAEMON_TOKEN) return;
|
|
1041
1211
|
joinSystemChannel(userId).catch(() => {
|
|
@@ -1068,7 +1238,7 @@ var AVATAR_MIME = {
|
|
|
1068
1238
|
};
|
|
1069
1239
|
var MAX_AVATAR_SIZE = 2 * 1024 * 1024;
|
|
1070
1240
|
function avatarsDir() {
|
|
1071
|
-
return
|
|
1241
|
+
return resolve7(voluteHome(), "avatars");
|
|
1072
1242
|
}
|
|
1073
1243
|
var authenticated = new Hono2().use(authMiddleware).post("/change-password", zValidator("json", changePasswordSchema), async (c) => {
|
|
1074
1244
|
const user = c.get("user");
|
|
@@ -1103,13 +1273,13 @@ var authenticated = new Hono2().use(authMiddleware).post("/change-password", zVa
|
|
|
1103
1273
|
return c.json({ error: "Invalid file type (png, jpg, gif, webp only)" }, 400);
|
|
1104
1274
|
}
|
|
1105
1275
|
const dir = avatarsDir();
|
|
1106
|
-
|
|
1276
|
+
mkdirSync5(dir, { recursive: true });
|
|
1107
1277
|
const filename = `avatar-${user.id}${ext}`;
|
|
1108
1278
|
const buffer2 = Buffer.from(await file.arrayBuffer());
|
|
1109
|
-
|
|
1279
|
+
writeFileSync4(resolve7(dir, filename), buffer2);
|
|
1110
1280
|
if (user.avatar && user.avatar !== filename) {
|
|
1111
|
-
const oldPath =
|
|
1112
|
-
|
|
1281
|
+
const oldPath = resolve7(dir, user.avatar);
|
|
1282
|
+
rmSync3(oldPath, { force: true });
|
|
1113
1283
|
}
|
|
1114
1284
|
await updateUserProfile(user.id, { avatar: filename });
|
|
1115
1285
|
const sessionId = getCookie2(c, "volute_session");
|
|
@@ -1123,8 +1293,8 @@ var authenticated = new Hono2().use(authMiddleware).post("/change-password", zVa
|
|
|
1123
1293
|
}).delete("/avatar", async (c) => {
|
|
1124
1294
|
const user = c.get("user");
|
|
1125
1295
|
if (user.avatar) {
|
|
1126
|
-
const path =
|
|
1127
|
-
|
|
1296
|
+
const path = resolve7(avatarsDir(), user.avatar);
|
|
1297
|
+
rmSync3(path, { force: true });
|
|
1128
1298
|
}
|
|
1129
1299
|
await updateUserProfile(user.id, { avatar: null });
|
|
1130
1300
|
const sessionId = getCookie2(c, "volute_session");
|
|
@@ -1273,13 +1443,13 @@ var app2 = new Hono2().post("/register", zValidator("json", credentialsSchema),
|
|
|
1273
1443
|
return c.json({ error: "Invalid filename" }, 400);
|
|
1274
1444
|
}
|
|
1275
1445
|
const dir = avatarsDir();
|
|
1276
|
-
const filePath =
|
|
1446
|
+
const filePath = resolve7(dir, filename);
|
|
1277
1447
|
if (!filePath.startsWith(`${dir}/`)) return c.json({ error: "Invalid path" }, 400);
|
|
1278
|
-
if (!
|
|
1448
|
+
if (!existsSync7(filePath)) return c.json({ error: "Not found" }, 404);
|
|
1279
1449
|
const ext = extname(filename).toLowerCase();
|
|
1280
1450
|
const mime = AVATAR_MIME[ext];
|
|
1281
1451
|
if (!mime) return c.json({ error: "Invalid file type" }, 400);
|
|
1282
|
-
const data =
|
|
1452
|
+
const data = readFileSync5(filePath);
|
|
1283
1453
|
return c.body(data, 200, {
|
|
1284
1454
|
"Content-Type": mime,
|
|
1285
1455
|
"Cache-Control": "public, max-age=3600",
|
|
@@ -1414,7 +1584,7 @@ var app3 = new Hono3().post("/:platform/inbound", zValidator2("json", inboundSch
|
|
|
1414
1584
|
}
|
|
1415
1585
|
const participants = await getParticipants(channel.id);
|
|
1416
1586
|
if (!participants.some((p) => p.userId === puppet.id)) {
|
|
1417
|
-
const { addParticipant } = await import("./conversations-
|
|
1587
|
+
const { addParticipant } = await import("./conversations-7KVQV7EZ.js");
|
|
1418
1588
|
await addParticipant(channel.id, puppet.id);
|
|
1419
1589
|
}
|
|
1420
1590
|
const contentBlocks = body.content;
|
|
@@ -1469,7 +1639,7 @@ var app3 = new Hono3().post("/:platform/inbound", zValidator2("json", inboundSch
|
|
|
1469
1639
|
});
|
|
1470
1640
|
try {
|
|
1471
1641
|
const daemonPort = parseInt(process.env.VOLUTE_DAEMON_PORT ?? "", 10);
|
|
1472
|
-
if (isNaN(daemonPort)) {
|
|
1642
|
+
if (Number.isNaN(daemonPort)) {
|
|
1473
1643
|
return c.json({ error: "VOLUTE_DAEMON_PORT not available" }, 500);
|
|
1474
1644
|
}
|
|
1475
1645
|
await manager.startBridge(platform, daemonPort);
|
|
@@ -1507,8 +1677,8 @@ async function fanOutToBridgedMinds(opts) {
|
|
|
1507
1677
|
const participants = await getParticipants(opts.conversationId);
|
|
1508
1678
|
const mindParticipants = participants.filter((p) => p.userType === "mind");
|
|
1509
1679
|
const participantNames = participants.map((p) => p.username);
|
|
1510
|
-
const { getMindManager: getMindManager2 } = await import("./mind-manager-
|
|
1511
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
1680
|
+
const { getMindManager: getMindManager2 } = await import("./mind-manager-YFCOIAAX.js");
|
|
1681
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-O7YQFCV5.js");
|
|
1512
1682
|
const manager = getMindManager2();
|
|
1513
1683
|
const sm = getSleepManagerIfReady2();
|
|
1514
1684
|
const targetMinds = mindParticipants.filter((ap) => {
|
|
@@ -2010,18 +2180,18 @@ __export(volute_exports, {
|
|
|
2010
2180
|
read: () => read4,
|
|
2011
2181
|
send: () => send4
|
|
2012
2182
|
});
|
|
2013
|
-
import { existsSync as
|
|
2014
|
-
import { resolve as
|
|
2183
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
|
|
2184
|
+
import { resolve as resolve8 } from "path";
|
|
2015
2185
|
function getDaemonConfig() {
|
|
2016
|
-
const newPath =
|
|
2017
|
-
const legacyPath =
|
|
2018
|
-
const configPath =
|
|
2019
|
-
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)) {
|
|
2020
2190
|
throw new Error("Volute daemon is not running");
|
|
2021
2191
|
}
|
|
2022
2192
|
let config;
|
|
2023
2193
|
try {
|
|
2024
|
-
config = JSON.parse(
|
|
2194
|
+
config = JSON.parse(readFileSync6(configPath, "utf-8"));
|
|
2025
2195
|
} catch (err) {
|
|
2026
2196
|
throw new Error(`Failed to parse ${configPath}: ${err}`);
|
|
2027
2197
|
}
|
|
@@ -2378,10 +2548,17 @@ var sharedEnvApp = new Hono5().get("/", (c) => {
|
|
|
2378
2548
|
});
|
|
2379
2549
|
var env_default = app5;
|
|
2380
2550
|
|
|
2381
|
-
// src/web/api/
|
|
2382
|
-
import { readFileSync as readFileSync6, statSync } from "fs";
|
|
2383
|
-
import { resolve as resolve8 } from "path";
|
|
2551
|
+
// src/web/api/extensions.ts
|
|
2384
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";
|
|
2385
2562
|
async function notifyMind(port, message, channel, sender) {
|
|
2386
2563
|
try {
|
|
2387
2564
|
const res = await fetch(`http://127.0.0.1:${port}/message`, {
|
|
@@ -2400,7 +2577,7 @@ async function notifyMind(port, message, channel, sender) {
|
|
|
2400
2577
|
console.warn(`[file-sharing] notify mind on port ${port} failed:`, err);
|
|
2401
2578
|
}
|
|
2402
2579
|
}
|
|
2403
|
-
var
|
|
2580
|
+
var app7 = new Hono7().post("/:name/files/send", requireSelf(), async (c) => {
|
|
2404
2581
|
const senderName = c.req.param("name");
|
|
2405
2582
|
const senderEntry = await findMind(senderName);
|
|
2406
2583
|
if (!senderEntry) return c.json({ error: "Sender mind not found" }, 404);
|
|
@@ -2413,21 +2590,21 @@ var app6 = new Hono6().post("/:name/files/send", requireSelf(), async (c) => {
|
|
|
2413
2590
|
const pathErr = validateFilePath(body.filePath);
|
|
2414
2591
|
if (pathErr) return c.json({ error: pathErr }, 400);
|
|
2415
2592
|
const senderDir = mindDir(senderName);
|
|
2416
|
-
const filePath =
|
|
2593
|
+
const filePath = resolve9(senderDir, "home", body.filePath);
|
|
2417
2594
|
const MAX_FILE_SIZE2 = 50 * 1024 * 1024;
|
|
2418
|
-
const
|
|
2419
|
-
if (!
|
|
2420
|
-
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) {
|
|
2421
2598
|
return c.json(
|
|
2422
2599
|
{
|
|
2423
|
-
error: `File too large (${formatFileSize(
|
|
2600
|
+
error: `File too large (${formatFileSize(stat4.size)}, max ${formatFileSize(MAX_FILE_SIZE2)})`
|
|
2424
2601
|
},
|
|
2425
2602
|
413
|
|
2426
2603
|
);
|
|
2427
2604
|
}
|
|
2428
2605
|
let content;
|
|
2429
2606
|
try {
|
|
2430
|
-
content =
|
|
2607
|
+
content = readFileSync7(filePath);
|
|
2431
2608
|
} catch (err) {
|
|
2432
2609
|
const code = err.code;
|
|
2433
2610
|
if (code === "ENOENT") {
|
|
@@ -2538,13 +2715,13 @@ var app6 = new Hono6().post("/:name/files/send", requireSelf(), async (c) => {
|
|
|
2538
2715
|
}
|
|
2539
2716
|
return c.json({ status: "pending", id }, 200);
|
|
2540
2717
|
});
|
|
2541
|
-
var file_sharing_default =
|
|
2718
|
+
var file_sharing_default = app7;
|
|
2542
2719
|
|
|
2543
2720
|
// src/web/api/files.ts
|
|
2544
|
-
import { existsSync as
|
|
2721
|
+
import { existsSync as existsSync9 } from "fs";
|
|
2545
2722
|
import { readdir, readFile, realpath, stat } from "fs/promises";
|
|
2546
|
-
import { extname as extname2, resolve as
|
|
2547
|
-
import { Hono as
|
|
2723
|
+
import { extname as extname2, resolve as resolve10 } from "path";
|
|
2724
|
+
import { Hono as Hono8 } from "hono";
|
|
2548
2725
|
var ALLOWED_FILES = /* @__PURE__ */ new Set(["SOUL.md", "MEMORY.md", "CLAUDE.md", "VOLUTE.md"]);
|
|
2549
2726
|
var AVATAR_MIME2 = {
|
|
2550
2727
|
".png": "image/png",
|
|
@@ -2554,7 +2731,7 @@ var AVATAR_MIME2 = {
|
|
|
2554
2731
|
".webp": "image/webp"
|
|
2555
2732
|
};
|
|
2556
2733
|
var MAX_AVATAR_SIZE2 = 2 * 1024 * 1024;
|
|
2557
|
-
var
|
|
2734
|
+
var app8 = new Hono8().get("/:name/avatar", async (c) => {
|
|
2558
2735
|
const name = c.req.param("name");
|
|
2559
2736
|
const entry = await findMind(name);
|
|
2560
2737
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
@@ -2564,8 +2741,8 @@ var app7 = new Hono7().get("/:name/avatar", async (c) => {
|
|
|
2564
2741
|
const ext = extname2(config.profile.avatar).toLowerCase();
|
|
2565
2742
|
const mime = AVATAR_MIME2[ext];
|
|
2566
2743
|
if (!mime) return c.json({ error: "Invalid avatar extension" }, 400);
|
|
2567
|
-
const homeDir =
|
|
2568
|
-
const avatarPath =
|
|
2744
|
+
const homeDir = resolve10(dir, "home");
|
|
2745
|
+
const avatarPath = resolve10(homeDir, config.profile.avatar);
|
|
2569
2746
|
if (!avatarPath.startsWith(`${homeDir}/`)) return c.json({ error: "Invalid avatar path" }, 400);
|
|
2570
2747
|
let realAvatarPath;
|
|
2571
2748
|
try {
|
|
@@ -2594,8 +2771,8 @@ var app7 = new Hono7().get("/:name/avatar", async (c) => {
|
|
|
2594
2771
|
const entry = await findMind(name);
|
|
2595
2772
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
2596
2773
|
const dir = mindDir(name);
|
|
2597
|
-
const homeDir =
|
|
2598
|
-
if (!
|
|
2774
|
+
const homeDir = resolve10(dir, "home");
|
|
2775
|
+
if (!existsSync9(homeDir)) return c.json({ error: "Home directory missing" }, 404);
|
|
2599
2776
|
const allFiles = await readdir(homeDir);
|
|
2600
2777
|
const files = allFiles.filter((f) => f.endsWith(".md") && ALLOWED_FILES.has(f));
|
|
2601
2778
|
return c.json(files);
|
|
@@ -2608,33 +2785,33 @@ var app7 = new Hono7().get("/:name/avatar", async (c) => {
|
|
|
2608
2785
|
const entry = await findMind(name);
|
|
2609
2786
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
2610
2787
|
const dir = mindDir(name);
|
|
2611
|
-
const filePath =
|
|
2612
|
-
if (!
|
|
2788
|
+
const filePath = resolve10(dir, "home", filename);
|
|
2789
|
+
if (!existsSync9(filePath)) {
|
|
2613
2790
|
return c.json({ error: "File not found" }, 404);
|
|
2614
2791
|
}
|
|
2615
2792
|
const content = await readFile(filePath, "utf-8");
|
|
2616
2793
|
return c.json({ filename, content });
|
|
2617
2794
|
});
|
|
2618
|
-
var files_default =
|
|
2795
|
+
var files_default = app8;
|
|
2619
2796
|
|
|
2620
2797
|
// src/web/api/keys.ts
|
|
2621
|
-
import { Hono as
|
|
2798
|
+
import { Hono as Hono9 } from "hono";
|
|
2622
2799
|
|
|
2623
2800
|
// src/lib/identity.ts
|
|
2624
2801
|
import { createHash, generateKeyPairSync, sign, verify } from "crypto";
|
|
2625
|
-
import { existsSync as
|
|
2626
|
-
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";
|
|
2627
2804
|
function generateIdentity(mindDir2) {
|
|
2628
|
-
const identityDir =
|
|
2629
|
-
|
|
2805
|
+
const identityDir = resolve11(mindDir2, ".mind/identity");
|
|
2806
|
+
mkdirSync6(identityDir, { recursive: true });
|
|
2630
2807
|
const { publicKey, privateKey } = generateKeyPairSync("ed25519", {
|
|
2631
2808
|
publicKeyEncoding: { type: "spki", format: "pem" },
|
|
2632
2809
|
privateKeyEncoding: { type: "pkcs8", format: "pem" }
|
|
2633
2810
|
});
|
|
2634
|
-
const privatePath =
|
|
2635
|
-
const publicPath =
|
|
2636
|
-
|
|
2637
|
-
|
|
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 });
|
|
2638
2815
|
const config = readVoluteConfig(mindDir2) ?? {};
|
|
2639
2816
|
config.identity = {
|
|
2640
2817
|
privateKey: ".mind/identity/private.pem",
|
|
@@ -2647,17 +2824,17 @@ function getPrivateKey(mindDir2) {
|
|
|
2647
2824
|
const config = readVoluteConfig(mindDir2);
|
|
2648
2825
|
const relPath = config?.identity?.privateKey;
|
|
2649
2826
|
if (!relPath) return null;
|
|
2650
|
-
const fullPath =
|
|
2651
|
-
if (!
|
|
2652
|
-
return
|
|
2827
|
+
const fullPath = resolve11(mindDir2, relPath);
|
|
2828
|
+
if (!existsSync10(fullPath)) return null;
|
|
2829
|
+
return readFileSync8(fullPath, "utf-8");
|
|
2653
2830
|
}
|
|
2654
2831
|
function getPublicKey(mindDir2) {
|
|
2655
2832
|
const config = readVoluteConfig(mindDir2);
|
|
2656
2833
|
const relPath = config?.identity?.publicKey;
|
|
2657
2834
|
if (!relPath) return null;
|
|
2658
|
-
const fullPath =
|
|
2659
|
-
if (!
|
|
2660
|
-
return
|
|
2835
|
+
const fullPath = resolve11(mindDir2, relPath);
|
|
2836
|
+
if (!existsSync10(fullPath)) return null;
|
|
2837
|
+
return readFileSync8(fullPath, "utf-8");
|
|
2661
2838
|
}
|
|
2662
2839
|
function getFingerprint(publicKeyPem) {
|
|
2663
2840
|
return createHash("sha256").update(publicKeyPem).digest("hex");
|
|
@@ -2692,7 +2869,7 @@ async function publishPublicKey(mindName, publicKeyPem) {
|
|
|
2692
2869
|
}
|
|
2693
2870
|
|
|
2694
2871
|
// src/web/api/keys.ts
|
|
2695
|
-
var
|
|
2872
|
+
var app9 = new Hono9().get("/:fingerprint", async (c) => {
|
|
2696
2873
|
const fingerprint = c.req.param("fingerprint");
|
|
2697
2874
|
for (const entry of await readRegistry()) {
|
|
2698
2875
|
try {
|
|
@@ -2706,20 +2883,20 @@ var app8 = new Hono8().get("/:fingerprint", async (c) => {
|
|
|
2706
2883
|
}
|
|
2707
2884
|
return c.json({ error: "Key not found" }, 404);
|
|
2708
2885
|
});
|
|
2709
|
-
var keys_default =
|
|
2886
|
+
var keys_default = app9;
|
|
2710
2887
|
|
|
2711
2888
|
// src/web/api/logs.ts
|
|
2712
2889
|
import { spawn as spawn2 } from "child_process";
|
|
2713
|
-
import { existsSync as
|
|
2714
|
-
import { resolve as
|
|
2715
|
-
import { Hono as
|
|
2890
|
+
import { existsSync as existsSync11 } from "fs";
|
|
2891
|
+
import { resolve as resolve12 } from "path";
|
|
2892
|
+
import { Hono as Hono10 } from "hono";
|
|
2716
2893
|
import { streamSSE as streamSSE2 } from "hono/streaming";
|
|
2717
|
-
var
|
|
2894
|
+
var app10 = new Hono10().get("/:name/logs", async (c) => {
|
|
2718
2895
|
const name = c.req.param("name");
|
|
2719
2896
|
const entry = await findMind(name);
|
|
2720
2897
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
2721
|
-
const logFile =
|
|
2722
|
-
if (!
|
|
2898
|
+
const logFile = resolve12(stateDir(name), "logs", "mind.log");
|
|
2899
|
+
if (!existsSync11(logFile)) {
|
|
2723
2900
|
return c.json({ error: "No log file found" }, 404);
|
|
2724
2901
|
}
|
|
2725
2902
|
return streamSSE2(c, async (stream) => {
|
|
@@ -2746,8 +2923,8 @@ var app9 = new Hono9().get("/:name/logs", async (c) => {
|
|
|
2746
2923
|
const name = c.req.param("name");
|
|
2747
2924
|
const entry = await findMind(name);
|
|
2748
2925
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
2749
|
-
const logFile =
|
|
2750
|
-
if (!
|
|
2926
|
+
const logFile = resolve12(stateDir(name), "logs", "mind.log");
|
|
2927
|
+
if (!existsSync11(logFile)) {
|
|
2751
2928
|
return c.json({ error: "No log file found" }, 404);
|
|
2752
2929
|
}
|
|
2753
2930
|
const nParam = parseInt(c.req.query("n") ?? "50", 10);
|
|
@@ -2762,13 +2939,13 @@ var app9 = new Hono9().get("/:name/logs", async (c) => {
|
|
|
2762
2939
|
});
|
|
2763
2940
|
return c.text(output);
|
|
2764
2941
|
});
|
|
2765
|
-
var logs_default =
|
|
2942
|
+
var logs_default = app10;
|
|
2766
2943
|
|
|
2767
2944
|
// src/web/api/mind-skills.ts
|
|
2768
2945
|
import { zValidator as zValidator3 } from "@hono/zod-validator";
|
|
2769
|
-
import { Hono as
|
|
2946
|
+
import { Hono as Hono11 } from "hono";
|
|
2770
2947
|
import { z as z3 } from "zod";
|
|
2771
|
-
var
|
|
2948
|
+
var app11 = new Hono11().get("/:name/skills", async (c) => {
|
|
2772
2949
|
const name = c.req.param("name");
|
|
2773
2950
|
const entry = await findMind(name);
|
|
2774
2951
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
@@ -2843,38 +3020,38 @@ var app10 = new Hono10().get("/:name/skills", async (c) => {
|
|
|
2843
3020
|
}
|
|
2844
3021
|
return c.json({ ok: true });
|
|
2845
3022
|
});
|
|
2846
|
-
var mind_skills_default =
|
|
3023
|
+
var mind_skills_default = app11;
|
|
2847
3024
|
|
|
2848
3025
|
// src/web/api/minds.ts
|
|
2849
3026
|
import {
|
|
2850
3027
|
cpSync,
|
|
2851
|
-
existsSync as
|
|
2852
|
-
mkdirSync as
|
|
3028
|
+
existsSync as existsSync14,
|
|
3029
|
+
mkdirSync as mkdirSync9,
|
|
2853
3030
|
readdirSync as readdirSync3,
|
|
2854
|
-
readFileSync as
|
|
2855
|
-
rmSync as
|
|
2856
|
-
writeFileSync as
|
|
3031
|
+
readFileSync as readFileSync12,
|
|
3032
|
+
rmSync as rmSync5,
|
|
3033
|
+
writeFileSync as writeFileSync9
|
|
2857
3034
|
} from "fs";
|
|
2858
|
-
import { resolve as
|
|
3035
|
+
import { resolve as resolve16 } from "path";
|
|
2859
3036
|
import { zValidator as zValidator4 } from "@hono/zod-validator";
|
|
2860
|
-
import { and as
|
|
2861
|
-
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";
|
|
2862
3039
|
import { z as z4 } from "zod";
|
|
2863
3040
|
|
|
2864
3041
|
// src/lib/consolidate.ts
|
|
2865
|
-
import { readdirSync as readdirSync2, readFileSync as
|
|
2866
|
-
import { resolve as
|
|
3042
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync9, writeFileSync as writeFileSync6 } from "fs";
|
|
3043
|
+
import { resolve as resolve13 } from "path";
|
|
2867
3044
|
async function consolidateMemory(mindDir2) {
|
|
2868
|
-
const soulPath =
|
|
2869
|
-
const memoryPath =
|
|
2870
|
-
const memoryDir =
|
|
2871
|
-
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");
|
|
2872
3049
|
const logs = [];
|
|
2873
3050
|
try {
|
|
2874
3051
|
const files = readdirSync2(memoryDir).filter((f) => /^\d{4}-\d{2}-\d{2}\.md$/.test(f)).sort();
|
|
2875
3052
|
for (const filename of files) {
|
|
2876
3053
|
const date = filename.replace(".md", "");
|
|
2877
|
-
const content2 =
|
|
3054
|
+
const content2 = readFileSync9(resolve13(memoryDir, filename), "utf-8").trim();
|
|
2878
3055
|
if (content2) {
|
|
2879
3056
|
logs.push(`### ${date}
|
|
2880
3057
|
|
|
@@ -2924,7 +3101,7 @@ ${content2}`);
|
|
|
2924
3101
|
const data = await res.json();
|
|
2925
3102
|
const content = data.content.filter((b) => b.type === "text" && b.text).map((b) => b.text).join("").trim();
|
|
2926
3103
|
if (content) {
|
|
2927
|
-
|
|
3104
|
+
writeFileSync6(memoryPath, `${content}
|
|
2928
3105
|
`);
|
|
2929
3106
|
console.log("MEMORY.md created successfully.");
|
|
2930
3107
|
} else {
|
|
@@ -2934,11 +3111,11 @@ ${content2}`);
|
|
|
2934
3111
|
|
|
2935
3112
|
// src/lib/convert-session.ts
|
|
2936
3113
|
import { randomUUID } from "crypto";
|
|
2937
|
-
import { mkdirSync as
|
|
3114
|
+
import { mkdirSync as mkdirSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync7 } from "fs";
|
|
2938
3115
|
import { homedir } from "os";
|
|
2939
|
-
import { resolve as
|
|
3116
|
+
import { resolve as resolve14 } from "path";
|
|
2940
3117
|
function convertSession(opts) {
|
|
2941
|
-
const lines =
|
|
3118
|
+
const lines = readFileSync10(opts.sessionPath, "utf-8").trim().split("\n");
|
|
2942
3119
|
const sessionId = randomUUID();
|
|
2943
3120
|
const idMap = /* @__PURE__ */ new Map();
|
|
2944
3121
|
const messages = [];
|
|
@@ -3052,10 +3229,10 @@ function convertSession(opts) {
|
|
|
3052
3229
|
}
|
|
3053
3230
|
}
|
|
3054
3231
|
const projectId = opts.projectDir.replace(/\//g, "-");
|
|
3055
|
-
const sdkDir =
|
|
3056
|
-
|
|
3057
|
-
const sdkPath =
|
|
3058
|
-
|
|
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")}
|
|
3059
3236
|
`);
|
|
3060
3237
|
console.log(`Converted ${sdkEvents.length} messages \u2192 ${sdkPath}`);
|
|
3061
3238
|
return sessionId;
|
|
@@ -3100,10 +3277,198 @@ function convertAssistantContent(content) {
|
|
|
3100
3277
|
caller: { type: "direct" }
|
|
3101
3278
|
});
|
|
3102
3279
|
} else {
|
|
3103
|
-
result.push(block);
|
|
3280
|
+
result.push(block);
|
|
3281
|
+
}
|
|
3282
|
+
}
|
|
3283
|
+
return result;
|
|
3284
|
+
}
|
|
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;
|
|
3104
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;
|
|
3105
3463
|
}
|
|
3106
|
-
|
|
3464
|
+
publish2(mind, {
|
|
3465
|
+
mind,
|
|
3466
|
+
type: "summary",
|
|
3467
|
+
session,
|
|
3468
|
+
channel,
|
|
3469
|
+
content: summaryText,
|
|
3470
|
+
metadata
|
|
3471
|
+
});
|
|
3107
3472
|
}
|
|
3108
3473
|
|
|
3109
3474
|
// src/lib/health.ts
|
|
@@ -3121,7 +3486,7 @@ async function checkHealth(port) {
|
|
|
3121
3486
|
}
|
|
3122
3487
|
|
|
3123
3488
|
// src/lib/variant-cleanup.ts
|
|
3124
|
-
import { existsSync as
|
|
3489
|
+
import { existsSync as existsSync12, rmSync as rmSync4 } from "fs";
|
|
3125
3490
|
async function cleanupVariant(variantName, projectRoot, variantPath, opts) {
|
|
3126
3491
|
if (opts?.stop) {
|
|
3127
3492
|
try {
|
|
@@ -3130,14 +3495,14 @@ async function cleanupVariant(variantName, projectRoot, variantPath, opts) {
|
|
|
3130
3495
|
logger_default.warn(`failed to stop variant ${variantName}`, logger_default.errorData(err));
|
|
3131
3496
|
}
|
|
3132
3497
|
}
|
|
3133
|
-
const { findMind: findMind2 } = await import("./registry-
|
|
3498
|
+
const { findMind: findMind2 } = await import("./registry-ODSALQQL.js");
|
|
3134
3499
|
const variantEntry = await findMind2(variantName);
|
|
3135
3500
|
const branchName = variantEntry?.branch ?? variantName;
|
|
3136
|
-
if (
|
|
3501
|
+
if (existsSync12(variantPath)) {
|
|
3137
3502
|
try {
|
|
3138
3503
|
await gitExec(["worktree", "remove", "--force", variantPath], { cwd: projectRoot });
|
|
3139
3504
|
} catch {
|
|
3140
|
-
|
|
3505
|
+
rmSync4(variantPath, { recursive: true, force: true });
|
|
3141
3506
|
try {
|
|
3142
3507
|
await gitExec(["worktree", "prune"], { cwd: projectRoot });
|
|
3143
3508
|
} catch (err) {
|
|
@@ -3167,8 +3532,8 @@ async function cleanupVariant(variantName, projectRoot, variantPath, opts) {
|
|
|
3167
3532
|
}
|
|
3168
3533
|
|
|
3169
3534
|
// src/lib/variants.ts
|
|
3170
|
-
import { existsSync as
|
|
3171
|
-
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";
|
|
3172
3537
|
async function checkHealth2(port) {
|
|
3173
3538
|
try {
|
|
3174
3539
|
const res = await fetch(`http://127.0.0.1:${port}/health`, {
|
|
@@ -3197,7 +3562,7 @@ async function getMindStatus(name, port) {
|
|
|
3197
3562
|
const manager = getMindManager();
|
|
3198
3563
|
let status = "stopped";
|
|
3199
3564
|
try {
|
|
3200
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
3565
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-O7YQFCV5.js");
|
|
3201
3566
|
if (getSleepManagerIfReady2()?.isSleeping(name)) {
|
|
3202
3567
|
status = "sleeping";
|
|
3203
3568
|
}
|
|
@@ -3245,7 +3610,7 @@ async function initTemplateBranch(projectRoot, composedDir, manifest, mindName,
|
|
|
3245
3610
|
await gitExec(["commit", "-m", "initial commit"], opts);
|
|
3246
3611
|
}
|
|
3247
3612
|
async function updateTemplateBranch(projectRoot, template, mindName) {
|
|
3248
|
-
const tempWorktree =
|
|
3613
|
+
const tempWorktree = resolve16(projectRoot, ".variants", "_template_update");
|
|
3249
3614
|
let branchExists = false;
|
|
3250
3615
|
try {
|
|
3251
3616
|
await gitExec(["rev-parse", "--verify", TEMPLATE_BRANCH], { cwd: projectRoot });
|
|
@@ -3256,8 +3621,8 @@ async function updateTemplateBranch(projectRoot, template, mindName) {
|
|
|
3256
3621
|
await gitExec(["worktree", "remove", "--force", tempWorktree], { cwd: projectRoot });
|
|
3257
3622
|
} catch {
|
|
3258
3623
|
}
|
|
3259
|
-
if (
|
|
3260
|
-
|
|
3624
|
+
if (existsSync14(tempWorktree)) {
|
|
3625
|
+
rmSync5(tempWorktree, { recursive: true, force: true });
|
|
3261
3626
|
}
|
|
3262
3627
|
const templatesRoot = findTemplatesRoot();
|
|
3263
3628
|
const { composedDir, manifest } = composeTemplate(templatesRoot, template);
|
|
@@ -3277,9 +3642,9 @@ async function updateTemplateBranch(projectRoot, template, mindName) {
|
|
|
3277
3642
|
});
|
|
3278
3643
|
}
|
|
3279
3644
|
copyTemplateToDir(composedDir, tempWorktree, mindName, manifest);
|
|
3280
|
-
const initDir =
|
|
3281
|
-
if (
|
|
3282
|
-
|
|
3645
|
+
const initDir = resolve16(tempWorktree, ".init");
|
|
3646
|
+
if (existsSync14(initDir)) {
|
|
3647
|
+
rmSync5(initDir, { recursive: true, force: true });
|
|
3283
3648
|
}
|
|
3284
3649
|
await gitExec(["add", "-A"], { cwd: tempWorktree });
|
|
3285
3650
|
try {
|
|
@@ -3292,10 +3657,10 @@ async function updateTemplateBranch(projectRoot, template, mindName) {
|
|
|
3292
3657
|
await gitExec(["worktree", "remove", "--force", tempWorktree], { cwd: projectRoot });
|
|
3293
3658
|
} catch {
|
|
3294
3659
|
}
|
|
3295
|
-
if (
|
|
3296
|
-
|
|
3660
|
+
if (existsSync14(tempWorktree)) {
|
|
3661
|
+
rmSync5(tempWorktree, { recursive: true, force: true });
|
|
3297
3662
|
}
|
|
3298
|
-
|
|
3663
|
+
rmSync5(composedDir, { recursive: true, force: true });
|
|
3299
3664
|
}
|
|
3300
3665
|
}
|
|
3301
3666
|
async function mergeTemplateBranch(worktreeDir) {
|
|
@@ -3318,14 +3683,14 @@ async function mergeTemplateBranch(worktreeDir) {
|
|
|
3318
3683
|
async function npmInstallAsMind(cwd, mindName) {
|
|
3319
3684
|
if (isIsolationEnabled()) {
|
|
3320
3685
|
const [cmd, args] = await wrapForIsolation("npm", ["install"], mindName);
|
|
3321
|
-
await exec(cmd, args, { cwd, env: { ...process.env, HOME:
|
|
3686
|
+
await exec(cmd, args, { cwd, env: { ...process.env, HOME: resolve16(cwd, "home") } });
|
|
3322
3687
|
} else {
|
|
3323
3688
|
await exec("npm", ["install"], { cwd });
|
|
3324
3689
|
}
|
|
3325
3690
|
}
|
|
3326
3691
|
async function importFromArchive(c, tempDir, nameOverride, manifest) {
|
|
3327
|
-
const extractedMindDir =
|
|
3328
|
-
if (!
|
|
3692
|
+
const extractedMindDir = resolve16(tempDir, "mind");
|
|
3693
|
+
if (!existsSync14(extractedMindDir)) {
|
|
3329
3694
|
return c.json({ error: "Invalid archive: missing mind/ directory" }, 400);
|
|
3330
3695
|
}
|
|
3331
3696
|
if (!manifest?.includes || !manifest.name || !manifest.template) {
|
|
@@ -3343,21 +3708,21 @@ async function importFromFullArchive(c, tempDir, extractedMindDir, nameOverride,
|
|
|
3343
3708
|
if (await findMind(name)) return c.json({ error: `Mind already exists: ${name}` }, 409);
|
|
3344
3709
|
ensureVoluteHome();
|
|
3345
3710
|
const dest = mindDir(name);
|
|
3346
|
-
if (
|
|
3711
|
+
if (existsSync14(dest)) return c.json({ error: "Mind directory already exists" }, 409);
|
|
3347
3712
|
try {
|
|
3348
3713
|
cpSync(extractedMindDir, dest, { recursive: true });
|
|
3349
3714
|
if (!manifest.includes.identity) {
|
|
3350
3715
|
generateIdentity(dest);
|
|
3351
3716
|
}
|
|
3352
3717
|
const state = stateDir(name);
|
|
3353
|
-
|
|
3354
|
-
const channelsJson =
|
|
3355
|
-
if (
|
|
3356
|
-
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"));
|
|
3357
3722
|
}
|
|
3358
|
-
const envJson =
|
|
3359
|
-
if (
|
|
3360
|
-
cpSync(envJson,
|
|
3723
|
+
const envJson = resolve16(tempDir, "state/env.json");
|
|
3724
|
+
if (existsSync14(envJson)) {
|
|
3725
|
+
cpSync(envJson, resolve16(state, "env.json"));
|
|
3361
3726
|
}
|
|
3362
3727
|
const port = await nextPort();
|
|
3363
3728
|
await addMind(name, port, manifest.stage, manifest.template);
|
|
@@ -3366,36 +3731,36 @@ async function importFromFullArchive(c, tempDir, extractedMindDir, nameOverride,
|
|
|
3366
3731
|
} catch (err) {
|
|
3367
3732
|
logger_default.warn(`failed to set template hash for ${name}`, logger_default.errorData(err));
|
|
3368
3733
|
}
|
|
3369
|
-
const homeDir =
|
|
3734
|
+
const homeDir = resolve16(dest, "home");
|
|
3370
3735
|
ensureVoluteGroup();
|
|
3371
3736
|
createMindUser(name, homeDir);
|
|
3372
3737
|
chownMindDir(dest, name);
|
|
3373
3738
|
await npmInstallAsMind(dest, name);
|
|
3374
3739
|
await importHistoryFromArchive(name, tempDir);
|
|
3375
3740
|
importSessionsFromArchive(dest, tempDir);
|
|
3376
|
-
if (!
|
|
3741
|
+
if (!existsSync14(resolve16(dest, ".git"))) {
|
|
3377
3742
|
try {
|
|
3378
|
-
const env = isIsolationEnabled() ? { ...process.env, HOME:
|
|
3743
|
+
const env = isIsolationEnabled() ? { ...process.env, HOME: resolve16(dest, "home") } : void 0;
|
|
3379
3744
|
await gitExec(["init"], { cwd: dest, mindName: name, env });
|
|
3380
3745
|
await configureGitIdentity(name, { cwd: dest, mindName: name, env });
|
|
3381
3746
|
await gitExec(["add", "-A"], { cwd: dest, mindName: name, env });
|
|
3382
3747
|
await gitExec(["commit", "-m", "import from archive"], { cwd: dest, mindName: name, env });
|
|
3383
3748
|
} catch (err) {
|
|
3384
3749
|
logger_default.error(`git setup failed for imported mind ${name}`, logger_default.errorData(err));
|
|
3385
|
-
|
|
3750
|
+
rmSync5(resolve16(dest, ".git"), { recursive: true, force: true });
|
|
3386
3751
|
}
|
|
3387
3752
|
}
|
|
3388
3753
|
chownMindDir(dest, name);
|
|
3389
|
-
|
|
3754
|
+
rmSync5(tempDir, { recursive: true, force: true });
|
|
3390
3755
|
return c.json({ ok: true, name, port, message: `Imported mind: ${name} (port ${port})` });
|
|
3391
3756
|
} catch (err) {
|
|
3392
|
-
if (
|
|
3757
|
+
if (existsSync14(dest)) rmSync5(dest, { recursive: true, force: true });
|
|
3393
3758
|
try {
|
|
3394
3759
|
await removeMind(name);
|
|
3395
3760
|
} catch (cleanupErr) {
|
|
3396
3761
|
logger_default.error(`Failed to clean up registry for ${name}`, logger_default.errorData(cleanupErr));
|
|
3397
3762
|
}
|
|
3398
|
-
|
|
3763
|
+
rmSync5(tempDir, { recursive: true, force: true });
|
|
3399
3764
|
return c.json({ error: err instanceof Error ? err.message : "Failed to import mind" }, 500);
|
|
3400
3765
|
}
|
|
3401
3766
|
}
|
|
@@ -3406,7 +3771,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
|
|
|
3406
3771
|
if (await findMind(name)) return c.json({ error: `Mind already exists: ${name}` }, 409);
|
|
3407
3772
|
ensureVoluteHome();
|
|
3408
3773
|
const dest = mindDir(name);
|
|
3409
|
-
if (
|
|
3774
|
+
if (existsSync14(dest)) return c.json({ error: "Mind directory already exists" }, 409);
|
|
3410
3775
|
const templatesRoot = findTemplatesRoot();
|
|
3411
3776
|
const { composedDir, manifest: templateManifest } = composeTemplate(
|
|
3412
3777
|
templatesRoot,
|
|
@@ -3415,40 +3780,40 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
|
|
|
3415
3780
|
try {
|
|
3416
3781
|
copyTemplateToDir(composedDir, dest, name, templateManifest);
|
|
3417
3782
|
applyInitFiles(dest);
|
|
3418
|
-
const extractedHome =
|
|
3419
|
-
if (
|
|
3420
|
-
cpSync(extractedHome,
|
|
3783
|
+
const extractedHome = resolve16(extractedMindDir, "home");
|
|
3784
|
+
if (existsSync14(extractedHome)) {
|
|
3785
|
+
cpSync(extractedHome, resolve16(dest, "home"), { recursive: true });
|
|
3421
3786
|
}
|
|
3422
|
-
const extractedMindInternal =
|
|
3423
|
-
if (
|
|
3424
|
-
cpSync(extractedMindInternal,
|
|
3787
|
+
const extractedMindInternal = resolve16(extractedMindDir, ".mind");
|
|
3788
|
+
if (existsSync14(extractedMindInternal)) {
|
|
3789
|
+
cpSync(extractedMindInternal, resolve16(dest, ".mind"), { recursive: true });
|
|
3425
3790
|
}
|
|
3426
|
-
const identityDir =
|
|
3791
|
+
const identityDir = resolve16(dest, ".mind/identity");
|
|
3427
3792
|
let publicKeyPem;
|
|
3428
|
-
if (!manifest.includes.identity || !
|
|
3793
|
+
if (!manifest.includes.identity || !existsSync14(resolve16(identityDir, "private.pem"))) {
|
|
3429
3794
|
({ publicKeyPem } = generateIdentity(dest));
|
|
3430
3795
|
} else {
|
|
3431
|
-
publicKeyPem =
|
|
3796
|
+
publicKeyPem = readFileSync12(resolve16(identityDir, "public.pem"), "utf-8");
|
|
3432
3797
|
}
|
|
3433
|
-
const promptsPath =
|
|
3434
|
-
if (!
|
|
3798
|
+
const promptsPath = resolve16(dest, "home/.config/prompts.json");
|
|
3799
|
+
if (!existsSync14(promptsPath)) {
|
|
3435
3800
|
const mindPrompts = await getMindPromptDefaults();
|
|
3436
|
-
|
|
3801
|
+
writeFileSync9(promptsPath, `${JSON.stringify(mindPrompts, null, 2)}
|
|
3437
3802
|
`);
|
|
3438
3803
|
}
|
|
3439
3804
|
const state = stateDir(name);
|
|
3440
|
-
|
|
3441
|
-
const channelsJson =
|
|
3442
|
-
if (
|
|
3443
|
-
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"));
|
|
3444
3809
|
}
|
|
3445
|
-
const envJson =
|
|
3446
|
-
if (
|
|
3447
|
-
cpSync(envJson,
|
|
3810
|
+
const envJson = resolve16(tempDir, "state/env.json");
|
|
3811
|
+
if (existsSync14(envJson)) {
|
|
3812
|
+
cpSync(envJson, resolve16(state, "env.json"));
|
|
3448
3813
|
}
|
|
3449
3814
|
const port = await nextPort();
|
|
3450
3815
|
await addMind(name, port, manifest.stage, manifest.template);
|
|
3451
|
-
const homeDir =
|
|
3816
|
+
const homeDir = resolve16(dest, "home");
|
|
3452
3817
|
ensureVoluteGroup();
|
|
3453
3818
|
createMindUser(name, homeDir);
|
|
3454
3819
|
chownMindDir(dest, name);
|
|
@@ -3461,7 +3826,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
|
|
|
3461
3826
|
await initTemplateBranch(dest, composedDir, templateManifest, name, env);
|
|
3462
3827
|
} catch (err) {
|
|
3463
3828
|
logger_default.error(`git setup failed for imported mind ${name}`, logger_default.errorData(err));
|
|
3464
|
-
|
|
3829
|
+
rmSync5(resolve16(dest, ".git"), { recursive: true, force: true });
|
|
3465
3830
|
gitWarning = "Git setup failed \u2014 variants and upgrades won't be available until git is initialized.";
|
|
3466
3831
|
}
|
|
3467
3832
|
try {
|
|
@@ -3469,7 +3834,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
|
|
|
3469
3834
|
} catch (err) {
|
|
3470
3835
|
logger_default.warn(`failed to add shared worktree for ${name}`, logger_default.errorData(err));
|
|
3471
3836
|
}
|
|
3472
|
-
const skillSet = manifest.stage === "seed" ? SEED_SKILLS :
|
|
3837
|
+
const skillSet = manifest.stage === "seed" ? SEED_SKILLS : getStandardSkillsWithExtensions();
|
|
3473
3838
|
const skillWarnings = [];
|
|
3474
3839
|
for (const skillId of skillSet) {
|
|
3475
3840
|
try {
|
|
@@ -3485,7 +3850,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
|
|
|
3485
3850
|
publishPublicKey(name, publicKeyPem).catch(
|
|
3486
3851
|
(err) => logger_default.warn(`failed to publish key for ${name}`, { error: err.message })
|
|
3487
3852
|
);
|
|
3488
|
-
|
|
3853
|
+
rmSync5(tempDir, { recursive: true, force: true });
|
|
3489
3854
|
return c.json({
|
|
3490
3855
|
ok: true,
|
|
3491
3856
|
name,
|
|
@@ -3496,24 +3861,24 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
|
|
|
3496
3861
|
...skillWarnings.length > 0 && { skillWarnings }
|
|
3497
3862
|
});
|
|
3498
3863
|
} catch (err) {
|
|
3499
|
-
if (
|
|
3864
|
+
if (existsSync14(dest)) rmSync5(dest, { recursive: true, force: true });
|
|
3500
3865
|
try {
|
|
3501
3866
|
await removeMind(name);
|
|
3502
3867
|
} catch (cleanupErr) {
|
|
3503
3868
|
logger_default.error(`Failed to clean up registry for ${name}`, logger_default.errorData(cleanupErr));
|
|
3504
3869
|
}
|
|
3505
|
-
|
|
3870
|
+
rmSync5(tempDir, { recursive: true, force: true });
|
|
3506
3871
|
return c.json({ error: err instanceof Error ? err.message : "Failed to import mind" }, 500);
|
|
3507
3872
|
} finally {
|
|
3508
|
-
|
|
3873
|
+
rmSync5(composedDir, { recursive: true, force: true });
|
|
3509
3874
|
}
|
|
3510
3875
|
}
|
|
3511
3876
|
async function importHistoryFromArchive(name, tempDir) {
|
|
3512
|
-
const historyJsonl =
|
|
3513
|
-
if (!
|
|
3877
|
+
const historyJsonl = resolve16(tempDir, "history.jsonl");
|
|
3878
|
+
if (!existsSync14(historyJsonl)) return;
|
|
3514
3879
|
try {
|
|
3515
3880
|
const db = await getDb();
|
|
3516
|
-
const lines =
|
|
3881
|
+
const lines = readFileSync12(historyJsonl, "utf-8").trim().split("\n");
|
|
3517
3882
|
let imported = 0;
|
|
3518
3883
|
let failed = 0;
|
|
3519
3884
|
for (const line of lines) {
|
|
@@ -3549,13 +3914,13 @@ async function importHistoryFromArchive(name, tempDir) {
|
|
|
3549
3914
|
}
|
|
3550
3915
|
}
|
|
3551
3916
|
function importSessionsFromArchive(dest, tempDir) {
|
|
3552
|
-
const sessionsDir =
|
|
3553
|
-
if (!
|
|
3917
|
+
const sessionsDir = resolve16(tempDir, "sessions");
|
|
3918
|
+
if (!existsSync14(sessionsDir)) return;
|
|
3554
3919
|
try {
|
|
3555
|
-
const destSessions =
|
|
3556
|
-
|
|
3920
|
+
const destSessions = resolve16(dest, ".mind/sessions");
|
|
3921
|
+
mkdirSync9(destSessions, { recursive: true });
|
|
3557
3922
|
for (const file of readdirSync3(sessionsDir)) {
|
|
3558
|
-
cpSync(
|
|
3923
|
+
cpSync(resolve16(sessionsDir, file), resolve16(destSessions, file));
|
|
3559
3924
|
}
|
|
3560
3925
|
} catch (err) {
|
|
3561
3926
|
logger_default.error("Failed to import sessions from archive", logger_default.errorData(err));
|
|
@@ -3570,7 +3935,7 @@ var createMindSchema = z4.object({
|
|
|
3570
3935
|
seedSoul: z4.string().optional(),
|
|
3571
3936
|
skills: z4.array(z4.string()).optional()
|
|
3572
3937
|
});
|
|
3573
|
-
var
|
|
3938
|
+
var app12 = new Hono12().post("/", requireAdmin, zValidator4("json", createMindSchema), async (c) => {
|
|
3574
3939
|
const body = c.req.valid("json");
|
|
3575
3940
|
const { name, template = "claude" } = body;
|
|
3576
3941
|
const nameErr = validateMindName(name);
|
|
@@ -3578,7 +3943,7 @@ var app11 = new Hono11().post("/", requireAdmin, zValidator4("json", createMindS
|
|
|
3578
3943
|
if (await findMind(name)) return c.json({ error: `Mind already exists: ${name}` }, 409);
|
|
3579
3944
|
ensureVoluteHome();
|
|
3580
3945
|
const dest = mindDir(name);
|
|
3581
|
-
if (
|
|
3946
|
+
if (existsSync14(dest)) return c.json({ error: "Mind directory already exists" }, 409);
|
|
3582
3947
|
const templatesRoot = findTemplatesRoot();
|
|
3583
3948
|
const { composedDir, manifest } = composeTemplate(templatesRoot, template);
|
|
3584
3949
|
try {
|
|
@@ -3611,15 +3976,15 @@ var app11 = new Hono11().post("/", requireAdmin, zValidator4("json", createMindS
|
|
|
3611
3976
|
writeVoluteConfig(dest, config);
|
|
3612
3977
|
}
|
|
3613
3978
|
if (body.model) {
|
|
3614
|
-
const configPath =
|
|
3615
|
-
const existing =
|
|
3979
|
+
const configPath = resolve16(dest, "home/.config/config.json");
|
|
3980
|
+
const existing = existsSync14(configPath) ? JSON.parse(readFileSync12(configPath, "utf-8")) : {};
|
|
3616
3981
|
existing.model = body.model;
|
|
3617
|
-
|
|
3982
|
+
writeFileSync9(configPath, `${JSON.stringify(existing, null, 2)}
|
|
3618
3983
|
`);
|
|
3619
3984
|
}
|
|
3620
3985
|
const mindPrompts = await getMindPromptDefaults();
|
|
3621
|
-
|
|
3622
|
-
|
|
3986
|
+
writeFileSync9(
|
|
3987
|
+
resolve16(dest, "home/.config/prompts.json"),
|
|
3623
3988
|
`${JSON.stringify(mindPrompts, null, 2)}
|
|
3624
3989
|
`
|
|
3625
3990
|
);
|
|
@@ -3630,7 +3995,7 @@ var app11 = new Hono11().post("/", requireAdmin, zValidator4("json", createMindS
|
|
|
3630
3995
|
} catch (err) {
|
|
3631
3996
|
logger_default.warn(`failed to set template hash for ${name}`, logger_default.errorData(err));
|
|
3632
3997
|
}
|
|
3633
|
-
const homeDir =
|
|
3998
|
+
const homeDir = resolve16(dest, "home");
|
|
3634
3999
|
ensureVoluteGroup();
|
|
3635
4000
|
createMindUser(name, homeDir);
|
|
3636
4001
|
chownMindDir(dest, name);
|
|
@@ -3643,7 +4008,7 @@ var app11 = new Hono11().post("/", requireAdmin, zValidator4("json", createMindS
|
|
|
3643
4008
|
await initTemplateBranch(dest, composedDir, manifest, name, env);
|
|
3644
4009
|
} catch (err) {
|
|
3645
4010
|
logger_default.error(`git setup failed for ${name}`, logger_default.errorData(err));
|
|
3646
|
-
|
|
4011
|
+
rmSync5(resolve16(dest, ".git"), { recursive: true, force: true });
|
|
3647
4012
|
gitWarning = "Git setup failed \u2014 variants and upgrades won't be available until git is initialized.";
|
|
3648
4013
|
}
|
|
3649
4014
|
try {
|
|
@@ -3658,9 +4023,9 @@ The human who planted you described you as: "${body.description}"
|
|
|
3658
4023
|
` : "";
|
|
3659
4024
|
const seedSoulRaw = body.seedSoul ?? await getPrompt("seed_soul", { name, description: descLine });
|
|
3660
4025
|
const seedSoul = body.seedSoul ? substitute(seedSoulRaw, { name, description: descLine }) : seedSoulRaw;
|
|
3661
|
-
|
|
4026
|
+
writeFileSync9(resolve16(dest, "home/SOUL.md"), seedSoul);
|
|
3662
4027
|
}
|
|
3663
|
-
const skillSet = body.skills ?? (body.stage === "seed" ? SEED_SKILLS :
|
|
4028
|
+
const skillSet = body.skills ?? (body.stage === "seed" ? SEED_SKILLS : getStandardSkillsWithExtensions());
|
|
3664
4029
|
const skillWarnings = [];
|
|
3665
4030
|
for (const skillId of skillSet) {
|
|
3666
4031
|
try {
|
|
@@ -3673,11 +4038,11 @@ The human who planted you described you as: "${body.description}"
|
|
|
3673
4038
|
if (body.stage !== "seed") {
|
|
3674
4039
|
const customSoul = await getPromptIfCustom("default_soul");
|
|
3675
4040
|
if (customSoul) {
|
|
3676
|
-
|
|
4041
|
+
writeFileSync9(resolve16(dest, "home/SOUL.md"), customSoul.replace(/\{\{name\}\}/g, name));
|
|
3677
4042
|
}
|
|
3678
4043
|
const customMemory = await getPromptIfCustom("default_memory");
|
|
3679
4044
|
if (customMemory) {
|
|
3680
|
-
|
|
4045
|
+
writeFileSync9(resolve16(dest, "home/MEMORY.md"), customMemory);
|
|
3681
4046
|
}
|
|
3682
4047
|
}
|
|
3683
4048
|
publishPublicKey(name, publicKeyPem).catch(
|
|
@@ -3706,14 +4071,14 @@ The human who planted you described you as: "${body.description}"
|
|
|
3706
4071
|
...skillWarnings.length > 0 && { skillWarnings }
|
|
3707
4072
|
});
|
|
3708
4073
|
} catch (err) {
|
|
3709
|
-
if (
|
|
4074
|
+
if (existsSync14(dest)) rmSync5(dest, { recursive: true, force: true });
|
|
3710
4075
|
try {
|
|
3711
4076
|
await removeMind(name);
|
|
3712
4077
|
} catch {
|
|
3713
4078
|
}
|
|
3714
4079
|
return c.json({ error: err instanceof Error ? err.message : "Failed to create mind" }, 500);
|
|
3715
4080
|
} finally {
|
|
3716
|
-
|
|
4081
|
+
rmSync5(composedDir, { recursive: true, force: true });
|
|
3717
4082
|
}
|
|
3718
4083
|
}).post("/import", requireAdmin, async (c) => {
|
|
3719
4084
|
let body;
|
|
@@ -3726,13 +4091,13 @@ The human who planted you described you as: "${body.description}"
|
|
|
3726
4091
|
return importFromArchive(c, body.archivePath, body.name, body.manifest);
|
|
3727
4092
|
}
|
|
3728
4093
|
const wsDir = body.workspacePath;
|
|
3729
|
-
if (!wsDir || !
|
|
4094
|
+
if (!wsDir || !existsSync14(resolve16(wsDir, "SOUL.md")) || !existsSync14(resolve16(wsDir, "IDENTITY.md"))) {
|
|
3730
4095
|
return c.json({ error: "Invalid workspace: missing SOUL.md or IDENTITY.md" }, 400);
|
|
3731
4096
|
}
|
|
3732
|
-
const soul =
|
|
3733
|
-
const identity =
|
|
3734
|
-
const userPath =
|
|
3735
|
-
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") : "";
|
|
3736
4101
|
const name = body.name ?? parseNameFromIdentity(identity) ?? "imported-mind";
|
|
3737
4102
|
const template = body.template ?? "claude";
|
|
3738
4103
|
const nameErr = validateMindName(name);
|
|
@@ -3752,33 +4117,33 @@ ${user.trimEnd()}
|
|
|
3752
4117
|
` : "";
|
|
3753
4118
|
ensureVoluteHome();
|
|
3754
4119
|
const dest = mindDir(name);
|
|
3755
|
-
if (
|
|
4120
|
+
if (existsSync14(dest)) return c.json({ error: "Mind directory already exists" }, 409);
|
|
3756
4121
|
const templatesRoot = findTemplatesRoot();
|
|
3757
4122
|
const { composedDir, manifest } = composeTemplate(templatesRoot, template);
|
|
3758
4123
|
try {
|
|
3759
4124
|
copyTemplateToDir(composedDir, dest, name, manifest);
|
|
3760
4125
|
applyInitFiles(dest);
|
|
3761
4126
|
const { publicKeyPem: importPublicKey } = generateIdentity(dest);
|
|
3762
|
-
|
|
3763
|
-
const wsMemoryPath =
|
|
3764
|
-
const hasMemory =
|
|
4127
|
+
writeFileSync9(resolve16(dest, "home/SOUL.md"), mergedSoul);
|
|
4128
|
+
const wsMemoryPath = resolve16(wsDir, "MEMORY.md");
|
|
4129
|
+
const hasMemory = existsSync14(wsMemoryPath);
|
|
3765
4130
|
if (hasMemory) {
|
|
3766
|
-
const existingMemory =
|
|
3767
|
-
|
|
3768
|
-
|
|
4131
|
+
const existingMemory = readFileSync12(wsMemoryPath, "utf-8");
|
|
4132
|
+
writeFileSync9(
|
|
4133
|
+
resolve16(dest, "home/MEMORY.md"),
|
|
3769
4134
|
`${existingMemory.trimEnd()}${mergedMemoryExtra}`
|
|
3770
4135
|
);
|
|
3771
4136
|
} else if (user) {
|
|
3772
|
-
|
|
4137
|
+
writeFileSync9(resolve16(dest, "home/MEMORY.md"), `${user.trimEnd()}
|
|
3773
4138
|
`);
|
|
3774
4139
|
}
|
|
3775
|
-
const wsMemoryDir =
|
|
4140
|
+
const wsMemoryDir = resolve16(wsDir, "memory");
|
|
3776
4141
|
let dailyLogCount = 0;
|
|
3777
|
-
if (
|
|
3778
|
-
const destMemoryDir =
|
|
4142
|
+
if (existsSync14(wsMemoryDir)) {
|
|
4143
|
+
const destMemoryDir = resolve16(dest, "home/memory");
|
|
3779
4144
|
const files = readdirSync3(wsMemoryDir).filter((f) => f.endsWith(".md"));
|
|
3780
4145
|
for (const file of files) {
|
|
3781
|
-
cpSync(
|
|
4146
|
+
cpSync(resolve16(wsMemoryDir, file), resolve16(destMemoryDir, file));
|
|
3782
4147
|
}
|
|
3783
4148
|
dailyLogCount = files.length;
|
|
3784
4149
|
}
|
|
@@ -3789,7 +4154,7 @@ ${user.trimEnd()}
|
|
|
3789
4154
|
} catch (err) {
|
|
3790
4155
|
logger_default.warn(`failed to set template hash for ${name}`, logger_default.errorData(err));
|
|
3791
4156
|
}
|
|
3792
|
-
const homeDir =
|
|
4157
|
+
const homeDir = resolve16(dest, "home");
|
|
3793
4158
|
ensureVoluteGroup();
|
|
3794
4159
|
createMindUser(name, homeDir);
|
|
3795
4160
|
chownMindDir(dest, name);
|
|
@@ -3797,20 +4162,20 @@ ${user.trimEnd()}
|
|
|
3797
4162
|
if (!hasMemory && dailyLogCount > 0) {
|
|
3798
4163
|
await consolidateMemory(dest);
|
|
3799
4164
|
}
|
|
3800
|
-
const env = isIsolationEnabled() ? { ...process.env, HOME:
|
|
4165
|
+
const env = isIsolationEnabled() ? { ...process.env, HOME: resolve16(dest, "home") } : void 0;
|
|
3801
4166
|
await gitExec(["init"], { cwd: dest, mindName: name, env });
|
|
3802
4167
|
await configureGitIdentity(name, { cwd: dest, mindName: name, env });
|
|
3803
4168
|
await gitExec(["add", "-A"], { cwd: dest, mindName: name, env });
|
|
3804
4169
|
await gitExec(["commit", "-m", "import from OpenClaw"], { cwd: dest, mindName: name, env });
|
|
3805
|
-
const sessionFile = body.sessionPath ?
|
|
3806
|
-
if (sessionFile &&
|
|
4170
|
+
const sessionFile = body.sessionPath ? resolve16(body.sessionPath) : findOpenClawSession(wsDir);
|
|
4171
|
+
if (sessionFile && existsSync14(sessionFile)) {
|
|
3807
4172
|
if (template === "pi") {
|
|
3808
4173
|
importPiSession(sessionFile, dest);
|
|
3809
4174
|
} else if (template === "claude") {
|
|
3810
4175
|
const sessionId = convertSession({ sessionPath: sessionFile, projectDir: dest });
|
|
3811
|
-
const mindRuntimeDir =
|
|
3812
|
-
|
|
3813
|
-
|
|
4176
|
+
const mindRuntimeDir = resolve16(dest, ".mind");
|
|
4177
|
+
mkdirSync9(mindRuntimeDir, { recursive: true });
|
|
4178
|
+
writeFileSync9(resolve16(mindRuntimeDir, "session.json"), JSON.stringify({ sessionId }));
|
|
3814
4179
|
}
|
|
3815
4180
|
}
|
|
3816
4181
|
importOpenClawConnectors(name, dest);
|
|
@@ -3825,14 +4190,14 @@ ${user.trimEnd()}
|
|
|
3825
4190
|
);
|
|
3826
4191
|
return c.json({ ok: true, name, port, message: `Imported mind: ${name} (port ${port})` });
|
|
3827
4192
|
} catch (err) {
|
|
3828
|
-
if (
|
|
4193
|
+
if (existsSync14(dest)) rmSync5(dest, { recursive: true, force: true });
|
|
3829
4194
|
try {
|
|
3830
4195
|
await removeMind(name);
|
|
3831
4196
|
} catch {
|
|
3832
4197
|
}
|
|
3833
4198
|
return c.json({ error: err instanceof Error ? err.message : "Failed to import mind" }, 500);
|
|
3834
4199
|
} finally {
|
|
3835
|
-
|
|
4200
|
+
rmSync5(composedDir, { recursive: true, force: true });
|
|
3836
4201
|
}
|
|
3837
4202
|
}).get("/", async (c) => {
|
|
3838
4203
|
const entries = await readRegistry();
|
|
@@ -3841,7 +4206,7 @@ ${user.trimEnd()}
|
|
|
3841
4206
|
const db = await getDb();
|
|
3842
4207
|
const lastActiveRows = await db.select({
|
|
3843
4208
|
mind: mindHistory.mind,
|
|
3844
|
-
lastActiveAt:
|
|
4209
|
+
lastActiveAt: sql2`MAX(${mindHistory.created_at})`
|
|
3845
4210
|
}).from(mindHistory).groupBy(mindHistory.mind);
|
|
3846
4211
|
lastActiveMap = new Map(lastActiveRows.map((r) => [r.mind, r.lastActiveAt]));
|
|
3847
4212
|
} catch {
|
|
@@ -3849,7 +4214,7 @@ ${user.trimEnd()}
|
|
|
3849
4214
|
const minds = await Promise.all(
|
|
3850
4215
|
entries.map(async (entry) => {
|
|
3851
4216
|
const mindStatus = await getMindStatus(entry.name, entry.port);
|
|
3852
|
-
const hasPages =
|
|
4217
|
+
const hasPages = existsSync14(resolve16(mindDir(entry.name), "home", "public", "pages"));
|
|
3853
4218
|
return {
|
|
3854
4219
|
...entry,
|
|
3855
4220
|
...mindStatus,
|
|
@@ -3868,7 +4233,7 @@ ${user.trimEnd()}
|
|
|
3868
4233
|
const entry = await findMind(name);
|
|
3869
4234
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
3870
4235
|
const dir = entry.dir ?? mindDir(entry.parent ?? name);
|
|
3871
|
-
if (!
|
|
4236
|
+
if (!existsSync14(dir)) return c.json({ error: "Mind directory missing" }, 404);
|
|
3872
4237
|
const mindStatus = await getMindStatus(name, entry.port);
|
|
3873
4238
|
const variants = await findVariants(name);
|
|
3874
4239
|
const manager = getMindManager();
|
|
@@ -3882,7 +4247,7 @@ ${user.trimEnd()}
|
|
|
3882
4247
|
return { name: s.name, port: s.port, status: variantStatus };
|
|
3883
4248
|
})
|
|
3884
4249
|
);
|
|
3885
|
-
const hasPages =
|
|
4250
|
+
const hasPages = existsSync14(resolve16(mindDir(name), "home", "public", "pages"));
|
|
3886
4251
|
return c.json({ ...entry, ...mindStatus, variants: variantStatuses, hasPages });
|
|
3887
4252
|
}).post("/:name/start", requireSelf(), async (c) => {
|
|
3888
4253
|
const name = c.req.param("name");
|
|
@@ -3893,7 +4258,7 @@ ${user.trimEnd()}
|
|
|
3893
4258
|
if (!entry.dir) return c.json({ error: `Variant ${name} has no directory` }, 404);
|
|
3894
4259
|
} else {
|
|
3895
4260
|
const dir = mindDir(name);
|
|
3896
|
-
if (!
|
|
4261
|
+
if (!existsSync14(dir)) return c.json({ error: "Mind directory missing" }, 404);
|
|
3897
4262
|
}
|
|
3898
4263
|
if (getMindManager().isRunning(name)) {
|
|
3899
4264
|
return c.json({ error: "Mind already running" }, 409);
|
|
@@ -3914,7 +4279,7 @@ ${user.trimEnd()}
|
|
|
3914
4279
|
if (!entry.dir) return c.json({ error: `Variant ${name} has no directory` }, 404);
|
|
3915
4280
|
} else {
|
|
3916
4281
|
const dir = mindDir(name);
|
|
3917
|
-
if (!
|
|
4282
|
+
if (!existsSync14(dir)) return c.json({ error: "Mind directory missing" }, 404);
|
|
3918
4283
|
}
|
|
3919
4284
|
let context;
|
|
3920
4285
|
const contentType = c.req.header("content-type");
|
|
@@ -3929,7 +4294,7 @@ ${user.trimEnd()}
|
|
|
3929
4294
|
const manager = getMindManager();
|
|
3930
4295
|
try {
|
|
3931
4296
|
if (context?.type === "reload") {
|
|
3932
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
4297
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-O7YQFCV5.js");
|
|
3933
4298
|
const sleepState = getSleepManagerIfReady2()?.getState(name);
|
|
3934
4299
|
if (sleepState?.sleeping) {
|
|
3935
4300
|
logger_default.info(`skipping reload for ${name} during sleep \u2014 will apply on next wake`);
|
|
@@ -3949,7 +4314,7 @@ ${user.trimEnd()}
|
|
|
3949
4314
|
const variantEntry = await findMind(mergeVariantName);
|
|
3950
4315
|
if (variantEntry && variantEntry.parent === baseName && variantEntry.dir && variantEntry.branch) {
|
|
3951
4316
|
const projectRoot = mindDir(baseName);
|
|
3952
|
-
if (
|
|
4317
|
+
if (existsSync14(variantEntry.dir)) {
|
|
3953
4318
|
const status = (await gitExec(["status", "--porcelain"], { cwd: variantEntry.dir })).trim();
|
|
3954
4319
|
if (status) {
|
|
3955
4320
|
try {
|
|
@@ -3991,7 +4356,7 @@ ${user.trimEnd()}
|
|
|
3991
4356
|
if (context?.type === "sprouted" && !entry.parent) {
|
|
3992
4357
|
try {
|
|
3993
4358
|
const db = await getDb();
|
|
3994
|
-
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();
|
|
3995
4360
|
for (const conv of activeConvs) {
|
|
3996
4361
|
await addMessage(conv.id, "assistant", "system", [
|
|
3997
4362
|
{ type: "text", text: "[seed has sprouted]" }
|
|
@@ -4024,7 +4389,7 @@ ${user.trimEnd()}
|
|
|
4024
4389
|
const name = c.req.param("name");
|
|
4025
4390
|
const entry = await findMind(name);
|
|
4026
4391
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
4027
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
4392
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-O7YQFCV5.js");
|
|
4028
4393
|
const sm = getSleepManagerIfReady2();
|
|
4029
4394
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
4030
4395
|
return c.json(sm.getState(name));
|
|
@@ -4032,7 +4397,7 @@ ${user.trimEnd()}
|
|
|
4032
4397
|
const name = c.req.param("name");
|
|
4033
4398
|
const entry = await findMind(name);
|
|
4034
4399
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
4035
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
4400
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-O7YQFCV5.js");
|
|
4036
4401
|
const sm = getSleepManagerIfReady2();
|
|
4037
4402
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
4038
4403
|
if (sm.isSleeping(name)) return c.json({ error: "Mind is already sleeping" }, 409);
|
|
@@ -4052,7 +4417,7 @@ ${user.trimEnd()}
|
|
|
4052
4417
|
const name = c.req.param("name");
|
|
4053
4418
|
const entry = await findMind(name);
|
|
4054
4419
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
4055
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
4420
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-O7YQFCV5.js");
|
|
4056
4421
|
const sm = getSleepManagerIfReady2();
|
|
4057
4422
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
4058
4423
|
const sleepState = sm.getState(name);
|
|
@@ -4067,7 +4432,7 @@ ${user.trimEnd()}
|
|
|
4067
4432
|
const name = c.req.param("name");
|
|
4068
4433
|
const entry = await findMind(name);
|
|
4069
4434
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
4070
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
4435
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-O7YQFCV5.js");
|
|
4071
4436
|
const sm = getSleepManagerIfReady2();
|
|
4072
4437
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
4073
4438
|
const flushed = await sm.flushQueuedMessages(name);
|
|
@@ -4105,11 +4470,11 @@ ${user.trimEnd()}
|
|
|
4105
4470
|
await removeMind(name);
|
|
4106
4471
|
await deleteMindUser2(name);
|
|
4107
4472
|
const state = stateDir(name);
|
|
4108
|
-
if (
|
|
4109
|
-
|
|
4473
|
+
if (existsSync14(state)) {
|
|
4474
|
+
rmSync5(state, { recursive: true, force: true });
|
|
4110
4475
|
}
|
|
4111
|
-
if (force &&
|
|
4112
|
-
|
|
4476
|
+
if (force && existsSync14(dir)) {
|
|
4477
|
+
rmSync5(dir, { recursive: true, force: true });
|
|
4113
4478
|
deleteMindUser(name);
|
|
4114
4479
|
}
|
|
4115
4480
|
fireWebhook({
|
|
@@ -4123,7 +4488,7 @@ ${user.trimEnd()}
|
|
|
4123
4488
|
const entry = await findMind(mindName);
|
|
4124
4489
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
4125
4490
|
const dir = mindDir(mindName);
|
|
4126
|
-
if (!
|
|
4491
|
+
if (!existsSync14(dir)) return c.json({ error: "Mind directory missing" }, 404);
|
|
4127
4492
|
let body = {};
|
|
4128
4493
|
try {
|
|
4129
4494
|
body = await c.req.json();
|
|
@@ -4132,16 +4497,16 @@ ${user.trimEnd()}
|
|
|
4132
4497
|
const template = body.template ?? entry.template ?? "claude";
|
|
4133
4498
|
const UPGRADE_BRANCH = "upgrade";
|
|
4134
4499
|
const upgradeVariantName = `${mindName}-upgrade`;
|
|
4135
|
-
const worktreeDir =
|
|
4500
|
+
const worktreeDir = resolve16(dir, ".variants", UPGRADE_BRANCH);
|
|
4136
4501
|
if (body.abort) {
|
|
4137
|
-
if (!
|
|
4502
|
+
if (!existsSync14(worktreeDir)) {
|
|
4138
4503
|
return c.json({ error: "No upgrade in progress" }, 400);
|
|
4139
4504
|
}
|
|
4140
4505
|
try {
|
|
4141
4506
|
try {
|
|
4142
|
-
const gitDirContent =
|
|
4507
|
+
const gitDirContent = readFileSync12(resolve16(worktreeDir, ".git"), "utf-8").trim();
|
|
4143
4508
|
const gitDir = gitDirContent.replace("gitdir: ", "");
|
|
4144
|
-
if (
|
|
4509
|
+
if (existsSync14(resolve16(gitDir, "MERGE_HEAD"))) {
|
|
4145
4510
|
await gitExec(["merge", "--abort"], { cwd: worktreeDir });
|
|
4146
4511
|
}
|
|
4147
4512
|
} catch {
|
|
@@ -4160,7 +4525,7 @@ ${user.trimEnd()}
|
|
|
4160
4525
|
}
|
|
4161
4526
|
}
|
|
4162
4527
|
if (body.continue) {
|
|
4163
|
-
if (!
|
|
4528
|
+
if (!existsSync14(worktreeDir)) {
|
|
4164
4529
|
return c.json({ error: "No upgrade in progress" }, 400);
|
|
4165
4530
|
}
|
|
4166
4531
|
const status = await gitExec(["status", "--porcelain"], { cwd: worktreeDir });
|
|
@@ -4199,7 +4564,7 @@ ${user.trimEnd()}
|
|
|
4199
4564
|
}
|
|
4200
4565
|
}
|
|
4201
4566
|
if (body.accept) {
|
|
4202
|
-
if (!
|
|
4567
|
+
if (!existsSync14(worktreeDir)) {
|
|
4203
4568
|
return c.json({ error: "No upgrade in progress" }, 400);
|
|
4204
4569
|
}
|
|
4205
4570
|
const variantEntry = await findMind(upgradeVariantName);
|
|
@@ -4222,7 +4587,7 @@ ${user.trimEnd()}
|
|
|
4222
4587
|
try {
|
|
4223
4588
|
await gitExec(["add", "-A"], { cwd: dir });
|
|
4224
4589
|
await gitExec(["commit", "-m", "Auto-commit before upgrade merge"], { cwd: dir });
|
|
4225
|
-
} catch (
|
|
4590
|
+
} catch (_e) {
|
|
4226
4591
|
return c.json({ error: "Failed to auto-commit main changes before merge" }, 500);
|
|
4227
4592
|
}
|
|
4228
4593
|
}
|
|
@@ -4257,22 +4622,22 @@ ${user.trimEnd()}
|
|
|
4257
4622
|
}
|
|
4258
4623
|
return c.json({ ok: true });
|
|
4259
4624
|
}
|
|
4260
|
-
if (
|
|
4625
|
+
if (existsSync14(worktreeDir)) {
|
|
4261
4626
|
return c.json(
|
|
4262
4627
|
{ error: "Upgrade variant already exists. Use continue or delete it first." },
|
|
4263
4628
|
409
|
|
4264
4629
|
);
|
|
4265
4630
|
}
|
|
4266
|
-
if (!
|
|
4631
|
+
if (!existsSync14(resolve16(dir, ".git"))) {
|
|
4267
4632
|
try {
|
|
4268
|
-
const env = isIsolationEnabled() ? { ...process.env, HOME:
|
|
4633
|
+
const env = isIsolationEnabled() ? { ...process.env, HOME: resolve16(dir, "home") } : void 0;
|
|
4269
4634
|
await gitExec(["init"], { cwd: dir, mindName, env });
|
|
4270
4635
|
await configureGitIdentity(mindName, { cwd: dir, mindName, env });
|
|
4271
4636
|
await gitExec(["add", "-A"], { cwd: dir, mindName, env });
|
|
4272
4637
|
await gitExec(["commit", "-m", "initial commit"], { cwd: dir, mindName, env });
|
|
4273
4638
|
chownMindDir(dir, mindName);
|
|
4274
4639
|
} catch (err) {
|
|
4275
|
-
|
|
4640
|
+
rmSync5(resolve16(dir, ".git"), { recursive: true, force: true });
|
|
4276
4641
|
return c.json(
|
|
4277
4642
|
{
|
|
4278
4643
|
error: `Git initialization failed: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -4286,7 +4651,7 @@ ${user.trimEnd()}
|
|
|
4286
4651
|
await gitExec(["branch", "-D", UPGRADE_BRANCH], { cwd: dir });
|
|
4287
4652
|
} catch {
|
|
4288
4653
|
}
|
|
4289
|
-
if (!
|
|
4654
|
+
if (!existsSync14(resolve16(dir, "home", "shared"))) {
|
|
4290
4655
|
try {
|
|
4291
4656
|
await addSharedWorktree(mindName, dir);
|
|
4292
4657
|
} catch (err) {
|
|
@@ -4297,9 +4662,9 @@ ${user.trimEnd()}
|
|
|
4297
4662
|
}
|
|
4298
4663
|
}
|
|
4299
4664
|
await updateTemplateBranch(dir, template, mindName);
|
|
4300
|
-
const parentDir =
|
|
4301
|
-
if (!
|
|
4302
|
-
|
|
4665
|
+
const parentDir = resolve16(dir, ".variants");
|
|
4666
|
+
if (!existsSync14(parentDir)) {
|
|
4667
|
+
mkdirSync9(parentDir, { recursive: true });
|
|
4303
4668
|
}
|
|
4304
4669
|
await gitExec(["worktree", "add", "-b", UPGRADE_BRANCH, worktreeDir], { cwd: dir });
|
|
4305
4670
|
const hasConflicts = await mergeTemplateBranch(worktreeDir);
|
|
@@ -4336,7 +4701,7 @@ ${user.trimEnd()}
|
|
|
4336
4701
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
4337
4702
|
const baseName = entry.parent ?? name;
|
|
4338
4703
|
try {
|
|
4339
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
4704
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-O7YQFCV5.js");
|
|
4340
4705
|
const sm = getSleepManagerIfReady2();
|
|
4341
4706
|
if (sm?.isSleeping(baseName)) {
|
|
4342
4707
|
const body2 = await c.req.text();
|
|
@@ -4434,7 +4799,7 @@ ${user.trimEnd()}
|
|
|
4434
4799
|
if (seedEntry?.stage === "seed") {
|
|
4435
4800
|
try {
|
|
4436
4801
|
const db = await getDb();
|
|
4437
|
-
const countResult = await db.select({ count:
|
|
4802
|
+
const countResult = await db.select({ count: sql2`count(*)` }).from(mindHistory).where(eq5(mindHistory.mind, baseName));
|
|
4438
4803
|
const msgCount = countResult[0]?.count ?? 0;
|
|
4439
4804
|
if (msgCount >= 10 && msgCount % 10 === 0) {
|
|
4440
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.]";
|
|
@@ -4491,7 +4856,7 @@ ${user.trimEnd()}
|
|
|
4491
4856
|
}
|
|
4492
4857
|
const before = beforeStr ? parseInt(beforeStr, 10) : void 0;
|
|
4493
4858
|
const limit = limitStr ? parseInt(limitStr, 10) : void 0;
|
|
4494
|
-
if (before !== void 0 && isNaN(before) || limit !== void 0 && isNaN(limit)) {
|
|
4859
|
+
if (before !== void 0 && Number.isNaN(before) || limit !== void 0 && Number.isNaN(limit)) {
|
|
4495
4860
|
return c.json({ error: "Invalid pagination parameters" }, 400);
|
|
4496
4861
|
}
|
|
4497
4862
|
const result = await getMessagesPaginated(convId, { before, limit });
|
|
@@ -4507,13 +4872,13 @@ ${user.trimEnd()}
|
|
|
4507
4872
|
const entry = await findMind(name);
|
|
4508
4873
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
4509
4874
|
const dir = mindDir(name);
|
|
4510
|
-
if (!
|
|
4875
|
+
if (!existsSync14(dir)) return c.json({ error: "Mind directory missing" }, 404);
|
|
4511
4876
|
let config = readVoluteConfig(dir);
|
|
4512
4877
|
if (!config && entry.template === "pi") {
|
|
4513
|
-
const piConfigPath =
|
|
4514
|
-
if (
|
|
4878
|
+
const piConfigPath = resolve16(dir, "home/.config/config.json");
|
|
4879
|
+
if (existsSync14(piConfigPath)) {
|
|
4515
4880
|
try {
|
|
4516
|
-
config = JSON.parse(
|
|
4881
|
+
config = JSON.parse(readFileSync12(piConfigPath, "utf-8"));
|
|
4517
4882
|
} catch {
|
|
4518
4883
|
}
|
|
4519
4884
|
}
|
|
@@ -4550,7 +4915,7 @@ ${user.trimEnd()}
|
|
|
4550
4915
|
const entry = await findMind(name);
|
|
4551
4916
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
4552
4917
|
const dir = mindDir(name);
|
|
4553
|
-
if (!
|
|
4918
|
+
if (!existsSync14(dir)) return c.json({ error: "Mind directory missing" }, 404);
|
|
4554
4919
|
const body = c.req.valid("json");
|
|
4555
4920
|
const existing = readVoluteConfig(dir) ?? {};
|
|
4556
4921
|
if (body.model !== void 0) existing.model = body.model;
|
|
@@ -4587,6 +4952,20 @@ ${user.trimEnd()}
|
|
|
4587
4952
|
logger_default.error(`failed to get pending deliveries for ${baseName}`, logger_default.errorData(err));
|
|
4588
4953
|
return c.json({ error: "Failed to retrieve pending messages" }, 500);
|
|
4589
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 });
|
|
4590
4969
|
}).post("/:name/events", requireSelf(), async (c) => {
|
|
4591
4970
|
const name = c.req.param("name");
|
|
4592
4971
|
const baseName = await getBaseName(name);
|
|
@@ -4600,8 +4979,9 @@ ${user.trimEnd()}
|
|
|
4600
4979
|
return c.json({ error: "type required" }, 400);
|
|
4601
4980
|
}
|
|
4602
4981
|
const db = await getDb();
|
|
4982
|
+
let insertedId;
|
|
4603
4983
|
try {
|
|
4604
|
-
await db.insert(mindHistory).values({
|
|
4984
|
+
const result = await db.insert(mindHistory).values({
|
|
4605
4985
|
mind: baseName,
|
|
4606
4986
|
type: body.type,
|
|
4607
4987
|
session: body.session ?? null,
|
|
@@ -4609,7 +4989,8 @@ ${user.trimEnd()}
|
|
|
4609
4989
|
message_id: body.messageId ?? null,
|
|
4610
4990
|
content: body.content ?? null,
|
|
4611
4991
|
metadata: body.metadata ? JSON.stringify(body.metadata) : null
|
|
4612
|
-
});
|
|
4992
|
+
}).returning({ id: mindHistory.id });
|
|
4993
|
+
insertedId = result[0]?.id;
|
|
4613
4994
|
} catch (err) {
|
|
4614
4995
|
logger_default.error(`failed to persist event for ${baseName}`, logger_default.errorData(err));
|
|
4615
4996
|
}
|
|
@@ -4640,6 +5021,11 @@ ${user.trimEnd()}
|
|
|
4640
5021
|
logger_default.error(`delivery manager sessionDone failed for ${baseName}`, logger_default.errorData(err));
|
|
4641
5022
|
}
|
|
4642
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
|
+
}
|
|
4643
5029
|
}
|
|
4644
5030
|
if (body.type === "usage" && body.metadata) {
|
|
4645
5031
|
const inputTokens = body.metadata.input_tokens ?? 0;
|
|
@@ -4731,22 +5117,22 @@ ${user.trimEnd()}
|
|
|
4731
5117
|
const db = await getDb();
|
|
4732
5118
|
const rows = await db.select({
|
|
4733
5119
|
session: mindHistory.session,
|
|
4734
|
-
started_at:
|
|
4735
|
-
event_count:
|
|
4736
|
-
message_count:
|
|
4737
|
-
tool_count:
|
|
4738
|
-
}).from(mindHistory).where(
|
|
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`);
|
|
4739
5125
|
return c.json(rows);
|
|
4740
5126
|
}).get("/:name/history/channels", async (c) => {
|
|
4741
5127
|
const name = c.req.param("name");
|
|
4742
5128
|
const db = await getDb();
|
|
4743
|
-
const rows = await db.selectDistinct({ channel: mindHistory.channel }).from(mindHistory).where(
|
|
5129
|
+
const rows = await db.selectDistinct({ channel: mindHistory.channel }).from(mindHistory).where(eq5(mindHistory.mind, name));
|
|
4744
5130
|
return c.json(rows.map((r) => r.channel));
|
|
4745
5131
|
}).get("/:name/history/export", async (c) => {
|
|
4746
5132
|
const name = c.req.param("name");
|
|
4747
5133
|
if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
|
|
4748
5134
|
const db = await getDb();
|
|
4749
|
-
const rows = await db.select().from(mindHistory).where(
|
|
5135
|
+
const rows = await db.select().from(mindHistory).where(eq5(mindHistory.mind, name));
|
|
4750
5136
|
return c.json(rows);
|
|
4751
5137
|
}).get("/:name/history", async (c) => {
|
|
4752
5138
|
const name = c.req.param("name");
|
|
@@ -4756,391 +5142,27 @@ ${user.trimEnd()}
|
|
|
4756
5142
|
const limit = Math.min(Math.max(parseInt(c.req.query("limit") ?? "50", 10) || 50, 1), 200);
|
|
4757
5143
|
const offset = Math.max(parseInt(c.req.query("offset") ?? "0", 10) || 0, 0);
|
|
4758
5144
|
const db = await getDb();
|
|
4759
|
-
const conditions = [
|
|
5145
|
+
const conditions = [eq5(mindHistory.mind, name)];
|
|
4760
5146
|
if (channel) {
|
|
4761
|
-
conditions.push(
|
|
5147
|
+
conditions.push(eq5(mindHistory.channel, channel));
|
|
4762
5148
|
}
|
|
4763
5149
|
if (session) {
|
|
4764
|
-
conditions.push(
|
|
5150
|
+
conditions.push(eq5(mindHistory.session, session));
|
|
4765
5151
|
}
|
|
4766
5152
|
if (!full) {
|
|
4767
|
-
conditions.push(
|
|
5153
|
+
conditions.push(sql2`${mindHistory.type} IN ('inbound', 'outbound', 'summary')`);
|
|
4768
5154
|
}
|
|
4769
|
-
const rows = await db.select().from(mindHistory).where(
|
|
5155
|
+
const rows = await db.select().from(mindHistory).where(and4(...conditions)).orderBy(desc3(mindHistory.created_at)).limit(limit).offset(offset);
|
|
4770
5156
|
return c.json(rows);
|
|
4771
5157
|
});
|
|
4772
|
-
var minds_default =
|
|
4773
|
-
|
|
4774
|
-
// src/web/api/notes.ts
|
|
4775
|
-
import { zValidator as zValidator5 } from "@hono/zod-validator";
|
|
4776
|
-
import { Hono as Hono12 } from "hono";
|
|
4777
|
-
import { z as z5 } from "zod";
|
|
4778
|
-
|
|
4779
|
-
// src/lib/notes.ts
|
|
4780
|
-
import { and as and4, count, desc as desc3, eq as eq5, inArray, sql as sql2 } from "drizzle-orm";
|
|
4781
|
-
async function createNote(authorId, title, content, replyToId) {
|
|
4782
|
-
const db = await getDb();
|
|
4783
|
-
let slug = slugify(title) || "untitled";
|
|
4784
|
-
const existing = await db.select({ slug: notes.slug }).from(notes).where(eq5(notes.author_id, authorId)).all();
|
|
4785
|
-
const existingSlugs = new Set(existing.map((r) => r.slug));
|
|
4786
|
-
if (existingSlugs.has(slug)) {
|
|
4787
|
-
let i = 2;
|
|
4788
|
-
while (existingSlugs.has(`${slug}-${i}`)) i++;
|
|
4789
|
-
slug = `${slug}-${i}`;
|
|
4790
|
-
}
|
|
4791
|
-
const [row] = await db.insert(notes).values({ author_id: authorId, title, slug, content, reply_to_id: replyToId ?? null }).returning();
|
|
4792
|
-
const author = await db.select().from(users).where(eq5(users.id, authorId)).get();
|
|
4793
|
-
return {
|
|
4794
|
-
...row,
|
|
4795
|
-
author_username: author?.username ?? "unknown",
|
|
4796
|
-
author_display_name: author?.display_name ?? null,
|
|
4797
|
-
comment_count: 0
|
|
4798
|
-
};
|
|
4799
|
-
}
|
|
4800
|
-
async function getNote(authorUsername, slug) {
|
|
4801
|
-
const db = await getDb();
|
|
4802
|
-
const row = await db.select({
|
|
4803
|
-
id: notes.id,
|
|
4804
|
-
author_id: notes.author_id,
|
|
4805
|
-
title: notes.title,
|
|
4806
|
-
slug: notes.slug,
|
|
4807
|
-
content: notes.content,
|
|
4808
|
-
reply_to_id: notes.reply_to_id,
|
|
4809
|
-
created_at: notes.created_at,
|
|
4810
|
-
updated_at: notes.updated_at,
|
|
4811
|
-
author_username: users.username,
|
|
4812
|
-
author_display_name: users.display_name
|
|
4813
|
-
}).from(notes).innerJoin(users, eq5(notes.author_id, users.id)).where(and4(eq5(users.username, authorUsername), eq5(notes.slug, slug))).get();
|
|
4814
|
-
if (!row) return null;
|
|
4815
|
-
const comments = await getComments(row.id);
|
|
4816
|
-
const reactions = await getReactions(row.id);
|
|
4817
|
-
let reply_to = null;
|
|
4818
|
-
if (row.reply_to_id) {
|
|
4819
|
-
const parent = await db.select({
|
|
4820
|
-
title: notes.title,
|
|
4821
|
-
slug: notes.slug,
|
|
4822
|
-
author_username: users.username
|
|
4823
|
-
}).from(notes).innerJoin(users, eq5(notes.author_id, users.id)).where(eq5(notes.id, row.reply_to_id)).get();
|
|
4824
|
-
if (parent) {
|
|
4825
|
-
reply_to = parent;
|
|
4826
|
-
}
|
|
4827
|
-
}
|
|
4828
|
-
const replies = await db.select({
|
|
4829
|
-
author_username: users.username,
|
|
4830
|
-
slug: notes.slug,
|
|
4831
|
-
title: notes.title,
|
|
4832
|
-
created_at: notes.created_at
|
|
4833
|
-
}).from(notes).innerJoin(users, eq5(notes.author_id, users.id)).where(eq5(notes.reply_to_id, row.id)).orderBy(notes.created_at).all();
|
|
4834
|
-
return { ...row, comment_count: comments.length, comments, reactions, reply_to, replies };
|
|
4835
|
-
}
|
|
4836
|
-
async function listNotes(opts) {
|
|
4837
|
-
const db = await getDb();
|
|
4838
|
-
const limit = opts?.limit ?? 50;
|
|
4839
|
-
const offset = opts?.offset ?? 0;
|
|
4840
|
-
const conditions = [];
|
|
4841
|
-
if (opts?.authorUsername) {
|
|
4842
|
-
conditions.push(eq5(users.username, opts.authorUsername));
|
|
4843
|
-
}
|
|
4844
|
-
const rows = await db.select({
|
|
4845
|
-
id: notes.id,
|
|
4846
|
-
author_id: notes.author_id,
|
|
4847
|
-
title: notes.title,
|
|
4848
|
-
slug: notes.slug,
|
|
4849
|
-
content: notes.content,
|
|
4850
|
-
reply_to_id: notes.reply_to_id,
|
|
4851
|
-
created_at: notes.created_at,
|
|
4852
|
-
updated_at: notes.updated_at,
|
|
4853
|
-
author_username: users.username,
|
|
4854
|
-
author_display_name: users.display_name
|
|
4855
|
-
}).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();
|
|
4856
|
-
const noteIds = rows.map((r) => r.id);
|
|
4857
|
-
if (noteIds.length === 0) return [];
|
|
4858
|
-
const commentCounts = await db.select({
|
|
4859
|
-
note_id: noteComments.note_id,
|
|
4860
|
-
count: count()
|
|
4861
|
-
}).from(noteComments).where(inArray(noteComments.note_id, noteIds)).groupBy(noteComments.note_id).all();
|
|
4862
|
-
const countMap = new Map(commentCounts.map((r) => [r.note_id, r.count]));
|
|
4863
|
-
const allReactions = await db.select({
|
|
4864
|
-
note_id: noteReactions.note_id,
|
|
4865
|
-
emoji: noteReactions.emoji,
|
|
4866
|
-
count: count()
|
|
4867
|
-
}).from(noteReactions).where(inArray(noteReactions.note_id, noteIds)).groupBy(noteReactions.note_id, noteReactions.emoji).all();
|
|
4868
|
-
const reactionMap = /* @__PURE__ */ new Map();
|
|
4869
|
-
for (const r of allReactions) {
|
|
4870
|
-
if (!reactionMap.has(r.note_id)) reactionMap.set(r.note_id, []);
|
|
4871
|
-
reactionMap.get(r.note_id).push({ emoji: r.emoji, count: r.count });
|
|
4872
|
-
}
|
|
4873
|
-
const replyToIds = [...new Set(rows.filter((r) => r.reply_to_id).map((r) => r.reply_to_id))];
|
|
4874
|
-
const replyToMap = /* @__PURE__ */ new Map();
|
|
4875
|
-
if (replyToIds.length > 0) {
|
|
4876
|
-
const parents = await db.select({
|
|
4877
|
-
id: notes.id,
|
|
4878
|
-
title: notes.title,
|
|
4879
|
-
slug: notes.slug,
|
|
4880
|
-
author_username: users.username
|
|
4881
|
-
}).from(notes).innerJoin(users, eq5(notes.author_id, users.id)).where(inArray(notes.id, replyToIds)).all();
|
|
4882
|
-
for (const parent of parents) {
|
|
4883
|
-
replyToMap.set(parent.id, {
|
|
4884
|
-
author_username: parent.author_username,
|
|
4885
|
-
slug: parent.slug,
|
|
4886
|
-
title: parent.title
|
|
4887
|
-
});
|
|
4888
|
-
}
|
|
4889
|
-
}
|
|
4890
|
-
return rows.map((r) => {
|
|
4891
|
-
const reactions = reactionMap.get(r.id);
|
|
4892
|
-
const topReactions = reactions ? reactions.sort((a, b) => b.count - a.count).slice(0, 3).map((rx) => ({ ...rx, usernames: [] })) : void 0;
|
|
4893
|
-
return {
|
|
4894
|
-
...r,
|
|
4895
|
-
comment_count: countMap.get(r.id) ?? 0,
|
|
4896
|
-
reactions: topReactions,
|
|
4897
|
-
reply_to: r.reply_to_id ? replyToMap.get(r.reply_to_id) ?? null : null
|
|
4898
|
-
};
|
|
4899
|
-
});
|
|
4900
|
-
}
|
|
4901
|
-
async function updateNote(authorUsername, slug, updates) {
|
|
4902
|
-
const db = await getDb();
|
|
4903
|
-
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();
|
|
4904
|
-
if (!existing) return null;
|
|
4905
|
-
const set = { updated_at: sql2`(datetime('now'))` };
|
|
4906
|
-
if (updates.title !== void 0) set.title = updates.title;
|
|
4907
|
-
if (updates.content !== void 0) set.content = updates.content;
|
|
4908
|
-
await db.update(notes).set(set).where(eq5(notes.id, existing.id));
|
|
4909
|
-
return getNote(authorUsername, slug).then((n) => {
|
|
4910
|
-
if (!n) return null;
|
|
4911
|
-
const { comments, replies, ...note } = n;
|
|
4912
|
-
return note;
|
|
4913
|
-
});
|
|
4914
|
-
}
|
|
4915
|
-
async function deleteNote(authorUsername, slug, authorId) {
|
|
4916
|
-
const db = await getDb();
|
|
4917
|
-
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();
|
|
4918
|
-
if (!existing || existing.author_id !== authorId) return false;
|
|
4919
|
-
await db.delete(notes).where(eq5(notes.id, existing.id));
|
|
4920
|
-
return true;
|
|
4921
|
-
}
|
|
4922
|
-
async function addComment(noteId, authorId, content) {
|
|
4923
|
-
const db = await getDb();
|
|
4924
|
-
const [row] = await db.insert(noteComments).values({ note_id: noteId, author_id: authorId, content }).returning();
|
|
4925
|
-
const author = await db.select().from(users).where(eq5(users.id, authorId)).get();
|
|
4926
|
-
return {
|
|
4927
|
-
...row,
|
|
4928
|
-
author_username: author?.username ?? "unknown",
|
|
4929
|
-
author_display_name: author?.display_name ?? null
|
|
4930
|
-
};
|
|
4931
|
-
}
|
|
4932
|
-
async function getComments(noteId) {
|
|
4933
|
-
const db = await getDb();
|
|
4934
|
-
return db.select({
|
|
4935
|
-
id: noteComments.id,
|
|
4936
|
-
note_id: noteComments.note_id,
|
|
4937
|
-
author_id: noteComments.author_id,
|
|
4938
|
-
content: noteComments.content,
|
|
4939
|
-
created_at: noteComments.created_at,
|
|
4940
|
-
author_username: users.username,
|
|
4941
|
-
author_display_name: users.display_name
|
|
4942
|
-
}).from(noteComments).innerJoin(users, eq5(noteComments.author_id, users.id)).where(eq5(noteComments.note_id, noteId)).orderBy(noteComments.created_at).all();
|
|
4943
|
-
}
|
|
4944
|
-
async function deleteComment(commentId, authorId) {
|
|
4945
|
-
const db = await getDb();
|
|
4946
|
-
const existing = await db.select({ id: noteComments.id, author_id: noteComments.author_id }).from(noteComments).where(eq5(noteComments.id, commentId)).get();
|
|
4947
|
-
if (!existing || existing.author_id !== authorId) return false;
|
|
4948
|
-
await db.delete(noteComments).where(eq5(noteComments.id, commentId));
|
|
4949
|
-
return true;
|
|
4950
|
-
}
|
|
4951
|
-
async function toggleReaction(noteId, userId, emoji) {
|
|
4952
|
-
const db = await getDb();
|
|
4953
|
-
const existing = await db.select({ id: noteReactions.id }).from(noteReactions).where(
|
|
4954
|
-
and4(
|
|
4955
|
-
eq5(noteReactions.note_id, noteId),
|
|
4956
|
-
eq5(noteReactions.user_id, userId),
|
|
4957
|
-
eq5(noteReactions.emoji, emoji)
|
|
4958
|
-
)
|
|
4959
|
-
).get();
|
|
4960
|
-
if (existing) {
|
|
4961
|
-
await db.delete(noteReactions).where(eq5(noteReactions.id, existing.id));
|
|
4962
|
-
return { added: false };
|
|
4963
|
-
}
|
|
4964
|
-
await db.insert(noteReactions).values({ note_id: noteId, user_id: userId, emoji });
|
|
4965
|
-
return { added: true };
|
|
4966
|
-
}
|
|
4967
|
-
async function getReactions(noteId) {
|
|
4968
|
-
const db = await getDb();
|
|
4969
|
-
const rows = await db.select({
|
|
4970
|
-
emoji: noteReactions.emoji,
|
|
4971
|
-
username: users.username
|
|
4972
|
-
}).from(noteReactions).innerJoin(users, eq5(noteReactions.user_id, users.id)).where(eq5(noteReactions.note_id, noteId)).orderBy(noteReactions.emoji).all();
|
|
4973
|
-
const grouped = /* @__PURE__ */ new Map();
|
|
4974
|
-
for (const r of rows) {
|
|
4975
|
-
if (!grouped.has(r.emoji)) grouped.set(r.emoji, []);
|
|
4976
|
-
grouped.get(r.emoji).push(r.username);
|
|
4977
|
-
}
|
|
4978
|
-
return [...grouped.entries()].map(([emoji, usernames]) => ({
|
|
4979
|
-
emoji,
|
|
4980
|
-
count: usernames.length,
|
|
4981
|
-
usernames
|
|
4982
|
-
}));
|
|
4983
|
-
}
|
|
4984
|
-
async function resolveNoteId(authorSlug) {
|
|
4985
|
-
const [author, slug] = authorSlug.split("/", 2);
|
|
4986
|
-
if (!author || !slug) return null;
|
|
4987
|
-
const db = await getDb();
|
|
4988
|
-
const row = await db.select({ id: notes.id }).from(notes).innerJoin(users, eq5(notes.author_id, users.id)).where(and4(eq5(users.username, author), eq5(notes.slug, slug))).get();
|
|
4989
|
-
return row?.id ?? null;
|
|
4990
|
-
}
|
|
4991
|
-
|
|
4992
|
-
// src/web/api/notes.ts
|
|
4993
|
-
var createSchema = z5.object({
|
|
4994
|
-
title: z5.string().min(1).max(200),
|
|
4995
|
-
content: z5.string().min(1),
|
|
4996
|
-
reply_to: z5.string().optional()
|
|
4997
|
-
});
|
|
4998
|
-
var updateSchema = z5.object({
|
|
4999
|
-
title: z5.string().min(1).max(200).optional(),
|
|
5000
|
-
content: z5.string().min(1).optional()
|
|
5001
|
-
});
|
|
5002
|
-
var commentSchema = z5.object({
|
|
5003
|
-
content: z5.string().min(1)
|
|
5004
|
-
});
|
|
5005
|
-
var reactionSchema = z5.object({
|
|
5006
|
-
emoji: z5.string().min(1).max(32)
|
|
5007
|
-
});
|
|
5008
|
-
function resolveUserId(c) {
|
|
5009
|
-
const user = c.get("user");
|
|
5010
|
-
if (user.id === 0) return null;
|
|
5011
|
-
return { id: user.id, username: user.username };
|
|
5012
|
-
}
|
|
5013
|
-
var app12 = new Hono12().get("/", async (c) => {
|
|
5014
|
-
const author = c.req.query("author");
|
|
5015
|
-
const limit = c.req.query("limit") ? parseInt(c.req.query("limit"), 10) : void 0;
|
|
5016
|
-
const offset = c.req.query("offset") ? parseInt(c.req.query("offset"), 10) : void 0;
|
|
5017
|
-
const result = await listNotes({ authorUsername: author, limit, offset });
|
|
5018
|
-
return c.json(result);
|
|
5019
|
-
}).post("/", zValidator5("json", createSchema), async (c) => {
|
|
5020
|
-
const actor = resolveUserId(c);
|
|
5021
|
-
if (!actor) return c.json({ error: "Unauthorized" }, 401);
|
|
5022
|
-
const { title, content, reply_to } = c.req.valid("json");
|
|
5023
|
-
let replyToId;
|
|
5024
|
-
if (reply_to) {
|
|
5025
|
-
const id = await resolveNoteId(reply_to);
|
|
5026
|
-
if (id === null) return c.json({ error: `Reply target not found: ${reply_to}` }, 404);
|
|
5027
|
-
replyToId = id;
|
|
5028
|
-
}
|
|
5029
|
-
const note = await createNote(actor.id, title, content, replyToId);
|
|
5030
|
-
const replyInfo = reply_to ? ` (in reply to ${reply_to})` : "";
|
|
5031
|
-
announceToSystem(`${actor.username} published a note: ${title}${replyInfo}`).catch((err) => {
|
|
5032
|
-
logger_default.warn("failed to announce note to #system", logger_default.errorData(err));
|
|
5033
|
-
});
|
|
5034
|
-
return c.json(note, 201);
|
|
5035
|
-
}).get("/:author/:slug", async (c) => {
|
|
5036
|
-
const { author, slug } = c.req.param();
|
|
5037
|
-
const note = await getNote(author, slug);
|
|
5038
|
-
if (!note) return c.json({ error: "Note not found" }, 404);
|
|
5039
|
-
return c.json(note);
|
|
5040
|
-
}).put("/:author/:slug", zValidator5("json", updateSchema), async (c) => {
|
|
5041
|
-
const actor = resolveUserId(c);
|
|
5042
|
-
if (!actor) return c.json({ error: "Unauthorized" }, 401);
|
|
5043
|
-
const { author, slug } = c.req.param();
|
|
5044
|
-
if (actor.username !== author) return c.json({ error: "Forbidden" }, 403);
|
|
5045
|
-
const updates = c.req.valid("json");
|
|
5046
|
-
const note = await updateNote(author, slug, updates);
|
|
5047
|
-
if (!note) return c.json({ error: "Note not found" }, 404);
|
|
5048
|
-
return c.json(note);
|
|
5049
|
-
}).delete("/:author/:slug", async (c) => {
|
|
5050
|
-
const actor = resolveUserId(c);
|
|
5051
|
-
if (!actor) return c.json({ error: "Unauthorized" }, 401);
|
|
5052
|
-
const { author, slug } = c.req.param();
|
|
5053
|
-
const deleted = await deleteNote(author, slug, actor.id);
|
|
5054
|
-
if (!deleted) return c.json({ error: "Note not found or not authorized" }, 404);
|
|
5055
|
-
return c.json({ ok: true });
|
|
5056
|
-
}).post("/:author/:slug/reactions", zValidator5("json", reactionSchema), async (c) => {
|
|
5057
|
-
const actor = resolveUserId(c);
|
|
5058
|
-
if (!actor) return c.json({ error: "Unauthorized" }, 401);
|
|
5059
|
-
const { author, slug } = c.req.param();
|
|
5060
|
-
const note = await getNote(author, slug);
|
|
5061
|
-
if (!note) return c.json({ error: "Note not found" }, 404);
|
|
5062
|
-
const { emoji } = c.req.valid("json");
|
|
5063
|
-
const result = await toggleReaction(note.id, actor.id, emoji);
|
|
5064
|
-
const reactions = await getReactions(note.id);
|
|
5065
|
-
return c.json({ ...result, reactions });
|
|
5066
|
-
}).post("/:author/:slug/comments", zValidator5("json", commentSchema), async (c) => {
|
|
5067
|
-
const actor = resolveUserId(c);
|
|
5068
|
-
if (!actor) return c.json({ error: "Unauthorized" }, 401);
|
|
5069
|
-
const { author, slug } = c.req.param();
|
|
5070
|
-
const note = await getNote(author, slug);
|
|
5071
|
-
if (!note) return c.json({ error: "Note not found" }, 404);
|
|
5072
|
-
const { content } = c.req.valid("json");
|
|
5073
|
-
const comment = await addComment(note.id, actor.id, content);
|
|
5074
|
-
return c.json(comment, 201);
|
|
5075
|
-
}).delete("/:author/:slug/comments/:id", async (c) => {
|
|
5076
|
-
const actor = resolveUserId(c);
|
|
5077
|
-
if (!actor) return c.json({ error: "Unauthorized" }, 401);
|
|
5078
|
-
const commentId = parseInt(c.req.param("id"), 10);
|
|
5079
|
-
if (Number.isNaN(commentId)) return c.json({ error: "Invalid comment ID" }, 400);
|
|
5080
|
-
const deleted = await deleteComment(commentId, actor.id);
|
|
5081
|
-
if (!deleted) return c.json({ error: "Comment not found or not authorized" }, 404);
|
|
5082
|
-
return c.json({ ok: true });
|
|
5083
|
-
});
|
|
5084
|
-
var notes_default = app12;
|
|
5085
|
-
|
|
5086
|
-
// src/web/api/pages.ts
|
|
5087
|
-
import { readFile as readFile2, stat as stat2 } from "fs/promises";
|
|
5088
|
-
import { extname as extname3, resolve as resolve16 } from "path";
|
|
5089
|
-
import { Hono as Hono13 } from "hono";
|
|
5090
|
-
var MIME_TYPES = {
|
|
5091
|
-
".html": "text/html",
|
|
5092
|
-
".js": "application/javascript",
|
|
5093
|
-
".css": "text/css",
|
|
5094
|
-
".json": "application/json",
|
|
5095
|
-
".svg": "image/svg+xml",
|
|
5096
|
-
".png": "image/png",
|
|
5097
|
-
".jpg": "image/jpeg",
|
|
5098
|
-
".jpeg": "image/jpeg",
|
|
5099
|
-
".gif": "image/gif",
|
|
5100
|
-
".ico": "image/x-icon",
|
|
5101
|
-
".woff": "font/woff",
|
|
5102
|
-
".woff2": "font/woff2",
|
|
5103
|
-
".txt": "text/plain",
|
|
5104
|
-
".xml": "application/xml"
|
|
5105
|
-
};
|
|
5106
|
-
var app13 = new Hono13().get("/:name/*", async (c) => {
|
|
5107
|
-
const name = c.req.param("name");
|
|
5108
|
-
let pagesRoot;
|
|
5109
|
-
if (name === "_system") {
|
|
5110
|
-
pagesRoot = resolve16(voluteHome(), "shared", "pages");
|
|
5111
|
-
} else {
|
|
5112
|
-
if (!await findMind(name)) return c.text("Not found", 404);
|
|
5113
|
-
pagesRoot = resolve16(mindDir(name), "home", "public", "pages");
|
|
5114
|
-
}
|
|
5115
|
-
const wildcard = c.req.path.replace(`/pages/${name}`, "") || "/";
|
|
5116
|
-
const requestedPath = resolve16(pagesRoot, wildcard.slice(1));
|
|
5117
|
-
if (!requestedPath.startsWith(pagesRoot)) return c.text("Forbidden", 403);
|
|
5118
|
-
let fileStat = await stat2(requestedPath).catch(() => null);
|
|
5119
|
-
if (fileStat?.isDirectory()) {
|
|
5120
|
-
const indexPath = resolve16(requestedPath, "index.html");
|
|
5121
|
-
fileStat = await stat2(indexPath).catch(() => null);
|
|
5122
|
-
if (fileStat?.isFile()) {
|
|
5123
|
-
const body = await readFile2(indexPath);
|
|
5124
|
-
return c.body(body, 200, { "Content-Type": "text/html" });
|
|
5125
|
-
}
|
|
5126
|
-
return c.text("Not found", 404);
|
|
5127
|
-
}
|
|
5128
|
-
if (fileStat?.isFile()) {
|
|
5129
|
-
const ext = extname3(requestedPath);
|
|
5130
|
-
const mime = MIME_TYPES[ext] || "application/octet-stream";
|
|
5131
|
-
const body = await readFile2(requestedPath);
|
|
5132
|
-
return c.body(body, 200, { "Content-Type": mime });
|
|
5133
|
-
}
|
|
5134
|
-
return c.text("Not found", 404);
|
|
5135
|
-
});
|
|
5136
|
-
var pages_default = app13;
|
|
5158
|
+
var minds_default = app12;
|
|
5137
5159
|
|
|
5138
5160
|
// src/web/api/prompts.ts
|
|
5139
|
-
import { zValidator as
|
|
5161
|
+
import { zValidator as zValidator5 } from "@hono/zod-validator";
|
|
5140
5162
|
import { eq as eq6, sql as sql3 } from "drizzle-orm";
|
|
5141
|
-
import { Hono as
|
|
5142
|
-
import { z as
|
|
5143
|
-
var
|
|
5163
|
+
import { Hono as Hono13 } from "hono";
|
|
5164
|
+
import { z as z5 } from "zod";
|
|
5165
|
+
var app13 = new Hono13().get("/", async (c) => {
|
|
5144
5166
|
let rows;
|
|
5145
5167
|
try {
|
|
5146
5168
|
const db = await getDb();
|
|
@@ -5163,7 +5185,7 @@ var app14 = new Hono14().get("/", async (c) => {
|
|
|
5163
5185
|
};
|
|
5164
5186
|
});
|
|
5165
5187
|
return c.json(prompts);
|
|
5166
|
-
}).put("/:key", requireAdmin,
|
|
5188
|
+
}).put("/:key", requireAdmin, zValidator5("json", z5.object({ content: z5.string() })), async (c) => {
|
|
5167
5189
|
const key = c.req.param("key");
|
|
5168
5190
|
if (!PROMPT_KEYS.includes(key)) {
|
|
5169
5191
|
return c.json({ error: "Unknown prompt key" }, 404);
|
|
@@ -5184,12 +5206,12 @@ var app14 = new Hono14().get("/", async (c) => {
|
|
|
5184
5206
|
await db.delete(systemPrompts).where(eq6(systemPrompts.key, key));
|
|
5185
5207
|
return c.json({ ok: true });
|
|
5186
5208
|
});
|
|
5187
|
-
var prompts_default =
|
|
5209
|
+
var prompts_default = app13;
|
|
5188
5210
|
|
|
5189
5211
|
// src/web/api/public-files.ts
|
|
5190
|
-
import { readdir as readdir2, readFile as
|
|
5191
|
-
import { extname as
|
|
5192
|
-
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";
|
|
5193
5215
|
var MAX_FILE_SIZE = 50 * 1024 * 1024;
|
|
5194
5216
|
async function resolvePublicRoot(name) {
|
|
5195
5217
|
if (name === "_system") return resolve17(voluteHome(), "shared");
|
|
@@ -5199,7 +5221,7 @@ async function resolvePublicRoot(name) {
|
|
|
5199
5221
|
function hasDotSegment(relativePath) {
|
|
5200
5222
|
return relativePath.split("/").some((seg) => seg.startsWith("."));
|
|
5201
5223
|
}
|
|
5202
|
-
var
|
|
5224
|
+
var MIME_TYPES = {
|
|
5203
5225
|
".html": "text/html",
|
|
5204
5226
|
".js": "application/javascript",
|
|
5205
5227
|
".css": "text/css",
|
|
@@ -5230,7 +5252,7 @@ async function listDir(dirPath) {
|
|
|
5230
5252
|
type: e.isDirectory() ? "directory" : "file"
|
|
5231
5253
|
}));
|
|
5232
5254
|
}
|
|
5233
|
-
var
|
|
5255
|
+
var app14 = new Hono14().get("/:name/", async (c) => {
|
|
5234
5256
|
const name = c.req.param("name");
|
|
5235
5257
|
const publicRoot = await resolvePublicRoot(name);
|
|
5236
5258
|
if (!publicRoot) return c.json({ error: "Not found" }, 404);
|
|
@@ -5246,7 +5268,7 @@ var app15 = new Hono15().get("/:name/", async (c) => {
|
|
|
5246
5268
|
if (hasDotSegment(relativePath)) return c.text("Forbidden", 403);
|
|
5247
5269
|
let fileStat;
|
|
5248
5270
|
try {
|
|
5249
|
-
fileStat = await
|
|
5271
|
+
fileStat = await stat2(requestedPath);
|
|
5250
5272
|
} catch (err) {
|
|
5251
5273
|
if (err?.code === "ENOENT") return c.text("Not found", 404);
|
|
5252
5274
|
if (err?.code === "EACCES") return c.text("Forbidden", 403);
|
|
@@ -5260,10 +5282,10 @@ var app15 = new Hono15().get("/:name/", async (c) => {
|
|
|
5260
5282
|
}
|
|
5261
5283
|
if (fileStat.isFile()) {
|
|
5262
5284
|
if (fileStat.size > MAX_FILE_SIZE) return c.text("File too large", 413);
|
|
5263
|
-
const ext =
|
|
5264
|
-
const mime =
|
|
5285
|
+
const ext = extname3(requestedPath);
|
|
5286
|
+
const mime = MIME_TYPES[ext] || "application/octet-stream";
|
|
5265
5287
|
try {
|
|
5266
|
-
const body = await
|
|
5288
|
+
const body = await readFile2(requestedPath);
|
|
5267
5289
|
return c.body(body, 200, { "Content-Type": mime });
|
|
5268
5290
|
} catch (err) {
|
|
5269
5291
|
if (err?.code === "ENOENT") return c.text("Not found", 404);
|
|
@@ -5273,11 +5295,11 @@ var app15 = new Hono15().get("/:name/", async (c) => {
|
|
|
5273
5295
|
}
|
|
5274
5296
|
return c.text("Not found", 404);
|
|
5275
5297
|
});
|
|
5276
|
-
var public_files_default =
|
|
5298
|
+
var public_files_default = app14;
|
|
5277
5299
|
|
|
5278
5300
|
// src/web/api/schedules.ts
|
|
5279
5301
|
import { CronExpressionParser } from "cron-parser";
|
|
5280
|
-
import { Hono as
|
|
5302
|
+
import { Hono as Hono15 } from "hono";
|
|
5281
5303
|
var slog = logger_default.child("schedules");
|
|
5282
5304
|
function readSchedules(name) {
|
|
5283
5305
|
return readVoluteConfig(mindDir(name))?.schedules ?? [];
|
|
@@ -5295,7 +5317,7 @@ function writeSchedules(name, schedules) {
|
|
|
5295
5317
|
data: { schedules }
|
|
5296
5318
|
});
|
|
5297
5319
|
}
|
|
5298
|
-
var
|
|
5320
|
+
var app15 = new Hono15().get("/:name/clock/status", async (c) => {
|
|
5299
5321
|
const name = c.req.param("name");
|
|
5300
5322
|
if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
|
|
5301
5323
|
const sleepManager = getSleepManagerIfReady();
|
|
@@ -5337,6 +5359,9 @@ var app16 = new Hono16().get("/:name/clock/status", async (c) => {
|
|
|
5337
5359
|
if (entry.stage === "seed")
|
|
5338
5360
|
return c.json({ error: "Seed minds cannot use schedules \u2014 sprout first" }, 403);
|
|
5339
5361
|
const body = await c.req.json();
|
|
5362
|
+
if (!body.id) {
|
|
5363
|
+
return c.json({ error: "id is required (a descriptive name for this schedule)" }, 400);
|
|
5364
|
+
}
|
|
5340
5365
|
if (!body.cron && !body.fireAt) {
|
|
5341
5366
|
return c.json({ error: "cron or fireAt is required" }, 400);
|
|
5342
5367
|
}
|
|
@@ -5368,7 +5393,7 @@ var app16 = new Hono16().get("/:name/clock/status", async (c) => {
|
|
|
5368
5393
|
);
|
|
5369
5394
|
}
|
|
5370
5395
|
const schedules = readSchedules(name);
|
|
5371
|
-
const id = body.id
|
|
5396
|
+
const id = body.id;
|
|
5372
5397
|
if (schedules.some((s) => s.id === id)) {
|
|
5373
5398
|
return c.json({ error: `Schedule "${id}" already exists` }, 409);
|
|
5374
5399
|
}
|
|
@@ -5468,11 +5493,11 @@ var app16 = new Hono16().get("/:name/clock/status", async (c) => {
|
|
|
5468
5493
|
return c.json({ error: "Failed to reach mind" }, 502);
|
|
5469
5494
|
}
|
|
5470
5495
|
});
|
|
5471
|
-
var schedules_default =
|
|
5496
|
+
var schedules_default = app15;
|
|
5472
5497
|
|
|
5473
5498
|
// src/web/api/shared.ts
|
|
5474
|
-
import { Hono as
|
|
5475
|
-
var
|
|
5499
|
+
import { Hono as Hono16 } from "hono";
|
|
5500
|
+
var app16 = new Hono16().post("/:name/shared/merge", requireAdmin, async (c) => {
|
|
5476
5501
|
const name = c.req.param("name");
|
|
5477
5502
|
const entry = await findMind(name);
|
|
5478
5503
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
@@ -5490,24 +5515,63 @@ var app17 = new Hono17().post("/:name/shared/merge", requireAdmin, async (c) =>
|
|
|
5490
5515
|
return c.json({ error: err instanceof Error ? err.message : "Merge failed" }, 500);
|
|
5491
5516
|
}
|
|
5492
5517
|
});
|
|
5493
|
-
var shared_default =
|
|
5518
|
+
var shared_default = app16;
|
|
5494
5519
|
|
|
5495
5520
|
// src/web/api/skills.ts
|
|
5496
|
-
import { existsSync as
|
|
5521
|
+
import { existsSync as existsSync15, mkdtempSync, readdirSync as readdirSync4, rmSync as rmSync6 } from "fs";
|
|
5497
5522
|
import { tmpdir } from "os";
|
|
5498
5523
|
import { join, resolve as resolve18 } from "path";
|
|
5499
5524
|
import AdmZip from "adm-zip";
|
|
5500
|
-
import { Hono as
|
|
5501
|
-
var
|
|
5525
|
+
import { Hono as Hono17 } from "hono";
|
|
5526
|
+
var app17 = new Hono17().get("/", async (c) => {
|
|
5502
5527
|
const skills = await listSharedSkills();
|
|
5503
5528
|
return c.json(skills);
|
|
5504
|
-
}).get("
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
|
|
5508
|
-
|
|
5509
|
-
|
|
5510
|
-
|
|
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 });
|
|
5511
5575
|
}).post("/upload", requireAdmin, async (c) => {
|
|
5512
5576
|
const body = await c.req.parseBody();
|
|
5513
5577
|
const file = body.file;
|
|
@@ -5529,12 +5593,12 @@ var app18 = new Hono18().get("/", async (c) => {
|
|
|
5529
5593
|
}
|
|
5530
5594
|
zip.extractAllTo(tmpDir, true);
|
|
5531
5595
|
let skillDir = null;
|
|
5532
|
-
if (
|
|
5596
|
+
if (existsSync15(join(tmpDir, "SKILL.md"))) {
|
|
5533
5597
|
skillDir = tmpDir;
|
|
5534
5598
|
} else {
|
|
5535
5599
|
const entries = readdirSync4(tmpDir, { withFileTypes: true }).filter((e) => e.isDirectory());
|
|
5536
5600
|
for (const entry of entries) {
|
|
5537
|
-
if (
|
|
5601
|
+
if (existsSync15(join(tmpDir, entry.name, "SKILL.md"))) {
|
|
5538
5602
|
skillDir = join(tmpDir, entry.name);
|
|
5539
5603
|
break;
|
|
5540
5604
|
}
|
|
@@ -5551,8 +5615,15 @@ var app18 = new Hono18().get("/", async (c) => {
|
|
|
5551
5615
|
}
|
|
5552
5616
|
throw e;
|
|
5553
5617
|
} finally {
|
|
5554
|
-
|
|
5618
|
+
rmSync6(tmpDir, { recursive: true, force: true });
|
|
5555
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 });
|
|
5556
5627
|
}).delete("/:id", requireAdmin, async (c) => {
|
|
5557
5628
|
const id = c.req.param("id");
|
|
5558
5629
|
try {
|
|
@@ -5563,15 +5634,16 @@ var app18 = new Hono18().get("/", async (c) => {
|
|
|
5563
5634
|
}
|
|
5564
5635
|
return c.json({ ok: true });
|
|
5565
5636
|
});
|
|
5566
|
-
var skills_default =
|
|
5637
|
+
var skills_default = app17;
|
|
5567
5638
|
|
|
5568
5639
|
// src/web/api/system.ts
|
|
5569
|
-
import { zValidator as
|
|
5570
|
-
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";
|
|
5571
5643
|
import { streamSSE as streamSSE3 } from "hono/streaming";
|
|
5572
|
-
import { z as
|
|
5644
|
+
import { z as z6 } from "zod";
|
|
5573
5645
|
var DEFAULT_API_URL = "https://volute.systems";
|
|
5574
|
-
var
|
|
5646
|
+
var app18 = new Hono18().post("/restart", requireAdmin, (c) => {
|
|
5575
5647
|
setTimeout(() => process.exit(1), 200);
|
|
5576
5648
|
return c.json({ ok: true });
|
|
5577
5649
|
}).post("/stop", requireAdmin, (c) => {
|
|
@@ -5601,7 +5673,7 @@ var app19 = new Hono19().post("/restart", requireAdmin, (c) => {
|
|
|
5601
5673
|
}).post(
|
|
5602
5674
|
"/register",
|
|
5603
5675
|
requireAdmin,
|
|
5604
|
-
|
|
5676
|
+
zValidator6("json", z6.object({ name: z6.string().min(1) })),
|
|
5605
5677
|
async (c) => {
|
|
5606
5678
|
const existing = readSystemsConfig();
|
|
5607
5679
|
if (existing) {
|
|
@@ -5640,7 +5712,7 @@ var app19 = new Hono19().post("/restart", requireAdmin, (c) => {
|
|
|
5640
5712
|
).post(
|
|
5641
5713
|
"/login",
|
|
5642
5714
|
requireAdmin,
|
|
5643
|
-
|
|
5715
|
+
zValidator6("json", z6.object({ key: z6.string().min(1) })),
|
|
5644
5716
|
async (c) => {
|
|
5645
5717
|
const existing = readSystemsConfig();
|
|
5646
5718
|
if (existing) {
|
|
@@ -5708,19 +5780,173 @@ var app19 = new Hono19().post("/restart", requireAdmin, (c) => {
|
|
|
5708
5780
|
} catch (err) {
|
|
5709
5781
|
return c.json({ error: `Connection failed: ${err.message}` }, 502);
|
|
5710
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);
|
|
5711
5927
|
});
|
|
5712
|
-
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;
|
|
5713
5939
|
|
|
5714
5940
|
// src/web/api/typing.ts
|
|
5715
|
-
import { zValidator as
|
|
5716
|
-
import { Hono as
|
|
5717
|
-
import { z as
|
|
5718
|
-
var typingSchema =
|
|
5719
|
-
channel:
|
|
5720
|
-
sender:
|
|
5721
|
-
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()
|
|
5722
5948
|
});
|
|
5723
|
-
var
|
|
5949
|
+
var app19 = new Hono19().post("/:name/typing", zValidator7("json", typingSchema), (c) => {
|
|
5724
5950
|
const { channel, sender, active } = c.req.valid("json");
|
|
5725
5951
|
const map = getTypingMap();
|
|
5726
5952
|
if (active) {
|
|
@@ -5742,13 +5968,13 @@ var app20 = new Hono20().post("/:name/typing", zValidator8("json", typingSchema)
|
|
|
5742
5968
|
const map = getTypingMap();
|
|
5743
5969
|
return c.json({ typing: map.get(channel) });
|
|
5744
5970
|
});
|
|
5745
|
-
var typing_default =
|
|
5971
|
+
var typing_default = app19;
|
|
5746
5972
|
|
|
5747
5973
|
// src/web/api/update.ts
|
|
5748
5974
|
import { spawn as spawn3 } from "child_process";
|
|
5749
|
-
import { Hono as
|
|
5975
|
+
import { Hono as Hono20 } from "hono";
|
|
5750
5976
|
var bin;
|
|
5751
|
-
var
|
|
5977
|
+
var app20 = new Hono20().get("/update", async (c) => {
|
|
5752
5978
|
const result = await checkForUpdate();
|
|
5753
5979
|
return c.json(result);
|
|
5754
5980
|
}).post("/update", requireAdmin, async (c) => {
|
|
@@ -5763,21 +5989,21 @@ var app21 = new Hono21().get("/update", async (c) => {
|
|
|
5763
5989
|
child.unref();
|
|
5764
5990
|
return c.json({ ok: true, message: "Updating..." });
|
|
5765
5991
|
});
|
|
5766
|
-
var update_default =
|
|
5992
|
+
var update_default = app20;
|
|
5767
5993
|
|
|
5768
5994
|
// src/web/api/v1/chat.ts
|
|
5769
|
-
import { zValidator as
|
|
5770
|
-
import { Hono as
|
|
5995
|
+
import { zValidator as zValidator8 } from "@hono/zod-validator";
|
|
5996
|
+
import { Hono as Hono21 } from "hono";
|
|
5771
5997
|
import { streamSSE as streamSSE4 } from "hono/streaming";
|
|
5772
|
-
import { z as
|
|
5998
|
+
import { z as z8 } from "zod";
|
|
5773
5999
|
async function fanOutToMinds(opts) {
|
|
5774
6000
|
const participants = await getParticipants(opts.conversationId);
|
|
5775
6001
|
const mindParticipants = participants.filter((p) => p.userType === "mind");
|
|
5776
6002
|
const participantNames = participants.map((p) => p.username);
|
|
5777
6003
|
const isDM = opts.isDM ?? participants.length === 2;
|
|
5778
6004
|
const channelEntryType = opts.channelEntryType ?? (isDM ? "dm" : "channel");
|
|
5779
|
-
const { getMindManager: getMindManager2 } = await import("./mind-manager-
|
|
5780
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
6005
|
+
const { getMindManager: getMindManager2 } = await import("./mind-manager-YFCOIAAX.js");
|
|
6006
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-O7YQFCV5.js");
|
|
5781
6007
|
const manager = getMindManager2();
|
|
5782
6008
|
const sm = getSleepManagerIfReady2();
|
|
5783
6009
|
const targetMinds = mindParticipants.map((ap) => {
|
|
@@ -5826,18 +6052,18 @@ async function fanOutToMinds(opts) {
|
|
|
5826
6052
|
});
|
|
5827
6053
|
}
|
|
5828
6054
|
}
|
|
5829
|
-
var mindChatSchema =
|
|
5830
|
-
message:
|
|
5831
|
-
conversationId:
|
|
5832
|
-
sender:
|
|
5833
|
-
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()
|
|
5834
6060
|
});
|
|
5835
|
-
var unifiedChatSchema =
|
|
5836
|
-
message:
|
|
5837
|
-
conversationId:
|
|
5838
|
-
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()
|
|
5839
6065
|
});
|
|
5840
|
-
var
|
|
6066
|
+
var app21 = new Hono21().use("*", authMiddleware).post("/minds/:name/chat", zValidator8("json", mindChatSchema), async (c) => {
|
|
5841
6067
|
const name = c.req.param("name");
|
|
5842
6068
|
const baseName = await getBaseName(name);
|
|
5843
6069
|
const entry = await findMind(baseName);
|
|
@@ -5928,7 +6154,7 @@ var app22 = new Hono22().use("*", authMiddleware).post("/minds/:name/chat", zVal
|
|
|
5928
6154
|
});
|
|
5929
6155
|
});
|
|
5930
6156
|
});
|
|
5931
|
-
}).post("/chat",
|
|
6157
|
+
}).post("/chat", zValidator8("json", unifiedChatSchema), async (c) => {
|
|
5932
6158
|
const user = c.get("user");
|
|
5933
6159
|
const body = c.req.valid("json");
|
|
5934
6160
|
if (!body.message && (!body.images || body.images.length === 0)) {
|
|
@@ -5960,17 +6186,17 @@ var app22 = new Hono22().use("*", authMiddleware).post("/minds/:name/chat", zVal
|
|
|
5960
6186
|
});
|
|
5961
6187
|
return c.json({ ok: true, conversationId: body.conversationId });
|
|
5962
6188
|
});
|
|
5963
|
-
var chat_default =
|
|
6189
|
+
var chat_default = app21;
|
|
5964
6190
|
|
|
5965
6191
|
// src/web/api/v1/conversations.ts
|
|
5966
|
-
import { zValidator as
|
|
5967
|
-
import { Hono as
|
|
5968
|
-
import { z as
|
|
5969
|
-
var
|
|
5970
|
-
title:
|
|
5971
|
-
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)
|
|
5972
6198
|
});
|
|
5973
|
-
var
|
|
6199
|
+
var app22 = new Hono22().use("*", authMiddleware).get("/", async (c) => {
|
|
5974
6200
|
const user = c.get("user");
|
|
5975
6201
|
const convs = await listConversationsWithParticipants(user.id);
|
|
5976
6202
|
return c.json(convs);
|
|
@@ -5988,7 +6214,7 @@ var app23 = new Hono23().use("*", authMiddleware).get("/", async (c) => {
|
|
|
5988
6214
|
}
|
|
5989
6215
|
const before = beforeStr ? parseInt(beforeStr, 10) : void 0;
|
|
5990
6216
|
const limit = limitStr ? parseInt(limitStr, 10) : void 0;
|
|
5991
|
-
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)) {
|
|
5992
6218
|
return c.json({ error: "Invalid cursor params: before and limit must be integers" }, 400);
|
|
5993
6219
|
}
|
|
5994
6220
|
const result = await getMessagesPaginated(id, { before, limit });
|
|
@@ -6001,7 +6227,7 @@ var app23 = new Hono23().use("*", authMiddleware).get("/", async (c) => {
|
|
|
6001
6227
|
}
|
|
6002
6228
|
const participants = await getParticipants(id);
|
|
6003
6229
|
return c.json(participants);
|
|
6004
|
-
}).post("/",
|
|
6230
|
+
}).post("/", zValidator9("json", createSchema), async (c) => {
|
|
6005
6231
|
const user = c.get("user");
|
|
6006
6232
|
const body = c.req.valid("json");
|
|
6007
6233
|
const participantIds = /* @__PURE__ */ new Set();
|
|
@@ -6047,30 +6273,30 @@ var app23 = new Hono23().use("*", authMiddleware).get("/", async (c) => {
|
|
|
6047
6273
|
if (!deleted) return c.json({ error: "Conversation not found" }, 404);
|
|
6048
6274
|
return c.json({ ok: true });
|
|
6049
6275
|
});
|
|
6050
|
-
var conversations_default =
|
|
6276
|
+
var conversations_default = app22;
|
|
6051
6277
|
|
|
6052
6278
|
// src/web/api/v1/events.ts
|
|
6053
6279
|
import { desc as desc4 } from "drizzle-orm";
|
|
6054
|
-
import { Hono as
|
|
6280
|
+
import { Hono as Hono23 } from "hono";
|
|
6055
6281
|
import { streamSSE as streamSSE5 } from "hono/streaming";
|
|
6056
6282
|
|
|
6057
6283
|
// src/lib/events/brain-presence.ts
|
|
6058
6284
|
var connections = /* @__PURE__ */ new Map();
|
|
6059
6285
|
function addConnection(username) {
|
|
6060
|
-
const
|
|
6061
|
-
connections.set(username,
|
|
6062
|
-
if (
|
|
6286
|
+
const count = connections.get(username) ?? 0;
|
|
6287
|
+
connections.set(username, count + 1);
|
|
6288
|
+
if (count === 0) {
|
|
6063
6289
|
broadcast({ type: "brain_online", mind: username, summary: `${username} connected` });
|
|
6064
6290
|
}
|
|
6065
6291
|
}
|
|
6066
6292
|
function removeConnection(username) {
|
|
6067
|
-
const
|
|
6068
|
-
if (
|
|
6069
|
-
if (
|
|
6293
|
+
const count = connections.get(username);
|
|
6294
|
+
if (count == null) return;
|
|
6295
|
+
if (count <= 1) {
|
|
6070
6296
|
connections.delete(username);
|
|
6071
6297
|
broadcast({ type: "brain_offline", mind: username, summary: `${username} disconnected` });
|
|
6072
6298
|
} else {
|
|
6073
|
-
connections.set(username,
|
|
6299
|
+
connections.set(username, count - 1);
|
|
6074
6300
|
}
|
|
6075
6301
|
}
|
|
6076
6302
|
function getOnlineBrains() {
|
|
@@ -6098,7 +6324,7 @@ function getEventsSince(sinceId) {
|
|
|
6098
6324
|
}
|
|
6099
6325
|
|
|
6100
6326
|
// src/web/api/v1/events.ts
|
|
6101
|
-
var
|
|
6327
|
+
var app23 = new Hono23().use("*", authMiddleware).get("/", async (c) => {
|
|
6102
6328
|
const user = c.get("user");
|
|
6103
6329
|
const since = c.req.query("since");
|
|
6104
6330
|
const sinceId = since ? Number(since) : 0;
|
|
@@ -6205,16 +6431,16 @@ var app24 = new Hono24().use("*", authMiddleware).get("/", async (c) => {
|
|
|
6205
6431
|
}
|
|
6206
6432
|
});
|
|
6207
6433
|
});
|
|
6208
|
-
var events_default =
|
|
6434
|
+
var events_default = app23;
|
|
6209
6435
|
|
|
6210
6436
|
// src/web/api/variants.ts
|
|
6211
|
-
import { existsSync as
|
|
6437
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync11, writeFileSync as writeFileSync10 } from "fs";
|
|
6212
6438
|
import { resolve as resolve20 } from "path";
|
|
6213
|
-
import { Hono as
|
|
6439
|
+
import { Hono as Hono24 } from "hono";
|
|
6214
6440
|
|
|
6215
6441
|
// src/lib/spawn-server.ts
|
|
6216
6442
|
import { spawn as spawn4 } from "child_process";
|
|
6217
|
-
import { closeSync, mkdirSync as
|
|
6443
|
+
import { closeSync, mkdirSync as mkdirSync10, openSync, readFileSync as readFileSync13 } from "fs";
|
|
6218
6444
|
import { resolve as resolve19 } from "path";
|
|
6219
6445
|
function tsxBin(cwd) {
|
|
6220
6446
|
return resolve19(cwd, "node_modules", ".bin", "tsx");
|
|
@@ -6253,7 +6479,7 @@ function spawnAttached(cwd, port) {
|
|
|
6253
6479
|
}
|
|
6254
6480
|
function spawnDetached(cwd, port, logDir) {
|
|
6255
6481
|
const logsDir = logDir ?? resolve19(cwd, ".mind", "logs");
|
|
6256
|
-
|
|
6482
|
+
mkdirSync10(logsDir, { recursive: true });
|
|
6257
6483
|
const logPath = resolve19(logsDir, "mind.log");
|
|
6258
6484
|
const logFd = openSync(logPath, "a");
|
|
6259
6485
|
const child = spawn4(tsxBin(cwd), ["src/server.ts", "--port", String(port)], {
|
|
@@ -6274,7 +6500,7 @@ function spawnDetached(cwd, port, logDir) {
|
|
|
6274
6500
|
}
|
|
6275
6501
|
const interval = setInterval(() => {
|
|
6276
6502
|
try {
|
|
6277
|
-
const content =
|
|
6503
|
+
const content = readFileSync13(logPath, "utf-8");
|
|
6278
6504
|
const match = content.match(/listening on :(\d+)/);
|
|
6279
6505
|
if (match) {
|
|
6280
6506
|
finish({ child, actualPort: parseInt(match[1], 10) });
|
|
@@ -6326,7 +6552,7 @@ async function verify2(port) {
|
|
|
6326
6552
|
}
|
|
6327
6553
|
|
|
6328
6554
|
// src/web/api/variants.ts
|
|
6329
|
-
var
|
|
6555
|
+
var app24 = new Hono24().get("/:name/variants", async (c) => {
|
|
6330
6556
|
const name = c.req.param("name");
|
|
6331
6557
|
const entry = await findMind(name);
|
|
6332
6558
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
@@ -6371,10 +6597,10 @@ var app25 = new Hono25().get("/:name/variants", async (c) => {
|
|
|
6371
6597
|
}
|
|
6372
6598
|
const projectRoot = mindDir(mindName);
|
|
6373
6599
|
const variantDir = resolve20(projectRoot, ".variants", variantName);
|
|
6374
|
-
if (
|
|
6600
|
+
if (existsSync16(variantDir)) {
|
|
6375
6601
|
return c.json({ error: `Variant directory already exists: ${variantDir}` }, 409);
|
|
6376
6602
|
}
|
|
6377
|
-
|
|
6603
|
+
mkdirSync11(resolve20(projectRoot, ".variants"), { recursive: true });
|
|
6378
6604
|
try {
|
|
6379
6605
|
await gitExec(["worktree", "add", "-b", variantName, variantDir], { cwd: projectRoot });
|
|
6380
6606
|
} catch (e) {
|
|
@@ -6397,7 +6623,7 @@ var app25 = new Hono25().get("/:name/variants", async (c) => {
|
|
|
6397
6623
|
return c.json({ error: `npm install failed: ${msg}` }, 500);
|
|
6398
6624
|
}
|
|
6399
6625
|
if (body.soul) {
|
|
6400
|
-
|
|
6626
|
+
writeFileSync10(resolve20(variantDir, "home/SOUL.md"), body.soul);
|
|
6401
6627
|
}
|
|
6402
6628
|
const variantPort = body.port ?? await nextPort();
|
|
6403
6629
|
await addVariant(variantName, mindName, variantPort, variantDir, variantName);
|
|
@@ -6432,7 +6658,7 @@ var app25 = new Hono25().get("/:name/variants", async (c) => {
|
|
|
6432
6658
|
} catch {
|
|
6433
6659
|
}
|
|
6434
6660
|
const projectRoot = mindDir(mindName);
|
|
6435
|
-
if (
|
|
6661
|
+
if (existsSync16(variantEntry.dir)) {
|
|
6436
6662
|
const status = (await gitExec(["status", "--porcelain"], { cwd: variantEntry.dir })).trim();
|
|
6437
6663
|
if (status) {
|
|
6438
6664
|
try {
|
|
@@ -6440,7 +6666,7 @@ var app25 = new Hono25().get("/:name/variants", async (c) => {
|
|
|
6440
6666
|
await gitExec(["commit", "-m", "Auto-commit uncommitted changes before merge"], {
|
|
6441
6667
|
cwd: variantEntry.dir
|
|
6442
6668
|
});
|
|
6443
|
-
} catch (
|
|
6669
|
+
} catch (_e) {
|
|
6444
6670
|
return c.json(
|
|
6445
6671
|
{
|
|
6446
6672
|
error: "Failed to auto-commit variant changes. Commit or stash manually before merging."
|
|
@@ -6477,7 +6703,7 @@ var app25 = new Hono25().get("/:name/variants", async (c) => {
|
|
|
6477
6703
|
await gitExec(["commit", "-m", "Auto-commit uncommitted changes before merge"], {
|
|
6478
6704
|
cwd: projectRoot
|
|
6479
6705
|
});
|
|
6480
|
-
} catch (
|
|
6706
|
+
} catch (_e) {
|
|
6481
6707
|
return c.json(
|
|
6482
6708
|
{ error: "Failed to auto-commit main changes. Commit or stash manually before merging." },
|
|
6483
6709
|
500
|
|
@@ -6486,14 +6712,14 @@ var app25 = new Hono25().get("/:name/variants", async (c) => {
|
|
|
6486
6712
|
}
|
|
6487
6713
|
try {
|
|
6488
6714
|
await gitExec(["merge", variantEntry.branch], { cwd: projectRoot });
|
|
6489
|
-
} catch (
|
|
6715
|
+
} catch (_e) {
|
|
6490
6716
|
return c.json({ error: "Merge failed. Resolve conflicts manually." }, 500);
|
|
6491
6717
|
}
|
|
6492
6718
|
await cleanupVariant(variantName, projectRoot, variantEntry.dir);
|
|
6493
6719
|
if (variantName.endsWith("-upgrade") || variantName === "upgrade") {
|
|
6494
6720
|
try {
|
|
6495
|
-
const { computeTemplateHash: computeTemplateHash2 } = await import("./template-hash-
|
|
6496
|
-
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");
|
|
6497
6723
|
const tmpl = parentEntry.template ?? "claude";
|
|
6498
6724
|
await setMindTemplateHash2(mindName, computeTemplateHash2(tmpl));
|
|
6499
6725
|
} catch (err) {
|
|
@@ -6548,19 +6774,19 @@ var app25 = new Hono25().get("/:name/variants", async (c) => {
|
|
|
6548
6774
|
await cleanupVariant(variantName, projectRoot, variantEntry.dir, { stop: true });
|
|
6549
6775
|
return c.json({ ok: true });
|
|
6550
6776
|
});
|
|
6551
|
-
var variants_default =
|
|
6777
|
+
var variants_default = app24;
|
|
6552
6778
|
|
|
6553
6779
|
// src/web/api/volute/channels.ts
|
|
6554
|
-
import { zValidator as
|
|
6555
|
-
import { Hono as
|
|
6556
|
-
import { z as
|
|
6557
|
-
var
|
|
6558
|
-
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")
|
|
6559
6785
|
});
|
|
6560
|
-
var inviteSchema =
|
|
6561
|
-
username:
|
|
6786
|
+
var inviteSchema = z10.object({
|
|
6787
|
+
username: z10.string().min(1)
|
|
6562
6788
|
});
|
|
6563
|
-
var
|
|
6789
|
+
var app25 = new Hono25().get("/", async (c) => {
|
|
6564
6790
|
const user = c.get("user");
|
|
6565
6791
|
const channels = await listChannels();
|
|
6566
6792
|
const results = await Promise.all(
|
|
@@ -6571,7 +6797,7 @@ var app26 = new Hono26().get("/", async (c) => {
|
|
|
6571
6797
|
})
|
|
6572
6798
|
);
|
|
6573
6799
|
return c.json(results);
|
|
6574
|
-
}).post("/",
|
|
6800
|
+
}).post("/", zValidator10("json", createSchema2), async (c) => {
|
|
6575
6801
|
const user = c.get("user");
|
|
6576
6802
|
const body = c.req.valid("json");
|
|
6577
6803
|
try {
|
|
@@ -6604,7 +6830,7 @@ var app26 = new Hono26().get("/", async (c) => {
|
|
|
6604
6830
|
if (!ch) return c.json({ error: "Channel not found" }, 404);
|
|
6605
6831
|
const participants = await getParticipants(ch.id);
|
|
6606
6832
|
return c.json(participants);
|
|
6607
|
-
}).post("/:name/invite",
|
|
6833
|
+
}).post("/:name/invite", zValidator10("json", inviteSchema), async (c) => {
|
|
6608
6834
|
const name = c.req.param("name");
|
|
6609
6835
|
const inviter = c.get("user");
|
|
6610
6836
|
const { username } = c.req.valid("json");
|
|
@@ -6624,13 +6850,13 @@ var app26 = new Hono26().get("/", async (c) => {
|
|
|
6624
6850
|
]);
|
|
6625
6851
|
return c.json({ ok: true });
|
|
6626
6852
|
});
|
|
6627
|
-
var channels_default2 =
|
|
6853
|
+
var channels_default2 = app25;
|
|
6628
6854
|
|
|
6629
6855
|
// src/web/api/volute/chat.ts
|
|
6630
|
-
import { zValidator as
|
|
6631
|
-
import { Hono as
|
|
6856
|
+
import { zValidator as zValidator11 } from "@hono/zod-validator";
|
|
6857
|
+
import { Hono as Hono26 } from "hono";
|
|
6632
6858
|
import { streamSSE as streamSSE6 } from "hono/streaming";
|
|
6633
|
-
import { z as
|
|
6859
|
+
import { z as z11 } from "zod";
|
|
6634
6860
|
|
|
6635
6861
|
// src/lib/bridge-outbound.ts
|
|
6636
6862
|
function extractContent(contentBlocks) {
|
|
@@ -6710,8 +6936,8 @@ async function fanOutToMinds2(opts) {
|
|
|
6710
6936
|
const participantNames = participants.map((p) => p.username);
|
|
6711
6937
|
const isDM = opts.isDM ?? participants.length === 2;
|
|
6712
6938
|
const channelEntryType = opts.channelEntryType ?? (isDM ? "dm" : "channel");
|
|
6713
|
-
const { getMindManager: getMindManager2 } = await import("./mind-manager-
|
|
6714
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
6939
|
+
const { getMindManager: getMindManager2 } = await import("./mind-manager-YFCOIAAX.js");
|
|
6940
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-O7YQFCV5.js");
|
|
6715
6941
|
const manager = getMindManager2();
|
|
6716
6942
|
const sm = getSleepManagerIfReady2();
|
|
6717
6943
|
const targetMinds = mindParticipants.map((ap) => {
|
|
@@ -6760,24 +6986,24 @@ async function fanOutToMinds2(opts) {
|
|
|
6760
6986
|
});
|
|
6761
6987
|
}
|
|
6762
6988
|
}
|
|
6763
|
-
var fileSchema =
|
|
6764
|
-
filename:
|
|
6765
|
-
data:
|
|
6989
|
+
var fileSchema = z11.object({
|
|
6990
|
+
filename: z11.string(),
|
|
6991
|
+
data: z11.string()
|
|
6766
6992
|
// base64
|
|
6767
6993
|
});
|
|
6768
|
-
var chatSchema =
|
|
6769
|
-
message:
|
|
6770
|
-
conversationId:
|
|
6771
|
-
sender:
|
|
6772
|
-
images:
|
|
6773
|
-
|
|
6774
|
-
media_type:
|
|
6775
|
-
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()
|
|
6776
7002
|
})
|
|
6777
7003
|
).optional(),
|
|
6778
|
-
files:
|
|
7004
|
+
files: z11.array(fileSchema).optional()
|
|
6779
7005
|
});
|
|
6780
|
-
var
|
|
7006
|
+
var app26 = new Hono26().post("/:name/chat", zValidator11("json", chatSchema), async (c) => {
|
|
6781
7007
|
const name = c.req.param("name");
|
|
6782
7008
|
const baseName = await getBaseName(name);
|
|
6783
7009
|
const entry = await findMind(baseName);
|
|
@@ -6903,15 +7129,15 @@ var app27 = new Hono27().post("/:name/chat", zValidator12("json", chatSchema), a
|
|
|
6903
7129
|
});
|
|
6904
7130
|
});
|
|
6905
7131
|
});
|
|
6906
|
-
var unifiedChatSchema2 =
|
|
6907
|
-
message:
|
|
6908
|
-
conversationId:
|
|
6909
|
-
images:
|
|
6910
|
-
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()
|
|
6911
7137
|
});
|
|
6912
|
-
var unifiedChatApp = new
|
|
7138
|
+
var unifiedChatApp = new Hono26().post(
|
|
6913
7139
|
"/chat",
|
|
6914
|
-
|
|
7140
|
+
zValidator11("json", unifiedChatSchema2),
|
|
6915
7141
|
async (c) => {
|
|
6916
7142
|
const user = c.get("user");
|
|
6917
7143
|
const body = c.req.valid("json");
|
|
@@ -6986,18 +7212,18 @@ var unifiedChatApp = new Hono27().post(
|
|
|
6986
7212
|
return c.json({ ok: true, conversationId: body.conversationId });
|
|
6987
7213
|
}
|
|
6988
7214
|
);
|
|
6989
|
-
var chat_default2 =
|
|
7215
|
+
var chat_default2 = app26;
|
|
6990
7216
|
|
|
6991
7217
|
// src/web/api/volute/conversations.ts
|
|
6992
|
-
import { zValidator as
|
|
6993
|
-
import { Hono as
|
|
6994
|
-
import { z as
|
|
6995
|
-
var createConvSchema =
|
|
6996
|
-
title:
|
|
6997
|
-
participantIds:
|
|
6998
|
-
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()
|
|
6999
7225
|
});
|
|
7000
|
-
var
|
|
7226
|
+
var app27 = new Hono27().get("/:name/conversations", async (c) => {
|
|
7001
7227
|
const name = c.req.param("name");
|
|
7002
7228
|
const user = c.get("user");
|
|
7003
7229
|
let lookupId = user.id;
|
|
@@ -7008,7 +7234,7 @@ var app28 = new Hono28().get("/:name/conversations", async (c) => {
|
|
|
7008
7234
|
const all = await listConversationsForUser(lookupId);
|
|
7009
7235
|
const convs = all.filter((c2) => c2.mind_name === name || c2.type === "channel");
|
|
7010
7236
|
return c.json(convs);
|
|
7011
|
-
}).post("/:name/conversations",
|
|
7237
|
+
}).post("/:name/conversations", zValidator12("json", createConvSchema), async (c) => {
|
|
7012
7238
|
const name = c.req.param("name");
|
|
7013
7239
|
const user = c.get("user");
|
|
7014
7240
|
const body = c.req.valid("json");
|
|
@@ -7028,7 +7254,7 @@ var app28 = new Hono28().get("/:name/conversations", async (c) => {
|
|
|
7028
7254
|
if (hyphenIdx > 0) {
|
|
7029
7255
|
const prefix = pname.slice(0, hyphenIdx);
|
|
7030
7256
|
if (["discord", "slack", "telegram"].includes(prefix)) {
|
|
7031
|
-
existing = await getUserByUsername(prefix
|
|
7257
|
+
existing = await getUserByUsername(`${prefix}:${pname.slice(hyphenIdx + 1)}`);
|
|
7032
7258
|
}
|
|
7033
7259
|
}
|
|
7034
7260
|
}
|
|
@@ -7094,18 +7320,18 @@ var app28 = new Hono28().get("/:name/conversations", async (c) => {
|
|
|
7094
7320
|
if (!deleted) return c.json({ error: "Conversation not found" }, 404);
|
|
7095
7321
|
return c.json({ ok: true });
|
|
7096
7322
|
});
|
|
7097
|
-
var conversations_default2 =
|
|
7323
|
+
var conversations_default2 = app27;
|
|
7098
7324
|
|
|
7099
7325
|
// src/web/api/volute/user-conversations.ts
|
|
7100
|
-
import { zValidator as
|
|
7101
|
-
import { Hono as
|
|
7326
|
+
import { zValidator as zValidator13 } from "@hono/zod-validator";
|
|
7327
|
+
import { Hono as Hono28 } from "hono";
|
|
7102
7328
|
import { streamSSE as streamSSE7 } from "hono/streaming";
|
|
7103
|
-
import { z as
|
|
7104
|
-
var
|
|
7105
|
-
title:
|
|
7106
|
-
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)
|
|
7107
7333
|
});
|
|
7108
|
-
var
|
|
7334
|
+
var app28 = new Hono28().use("*", authMiddleware).get("/", async (c) => {
|
|
7109
7335
|
const user = c.get("user");
|
|
7110
7336
|
const convs = await listConversationsWithParticipants(user.id);
|
|
7111
7337
|
return c.json(convs);
|
|
@@ -7117,7 +7343,7 @@ var app29 = new Hono29().use("*", authMiddleware).get("/", async (c) => {
|
|
|
7117
7343
|
}
|
|
7118
7344
|
const msgs = await getMessages(id);
|
|
7119
7345
|
return c.json(msgs);
|
|
7120
|
-
}).post("/",
|
|
7346
|
+
}).post("/", zValidator13("json", createSchema3), async (c) => {
|
|
7121
7347
|
const user = c.get("user");
|
|
7122
7348
|
const body = c.req.valid("json");
|
|
7123
7349
|
const participantIds = /* @__PURE__ */ new Set();
|
|
@@ -7182,12 +7408,12 @@ var app29 = new Hono29().use("*", authMiddleware).get("/", async (c) => {
|
|
|
7182
7408
|
if (!deleted) return c.json({ error: "Conversation not found" }, 404);
|
|
7183
7409
|
return c.json({ ok: true });
|
|
7184
7410
|
});
|
|
7185
|
-
var user_conversations_default =
|
|
7411
|
+
var user_conversations_default = app28;
|
|
7186
7412
|
|
|
7187
7413
|
// src/web/app.ts
|
|
7188
7414
|
var httpLog = logger_default.child("http");
|
|
7189
|
-
var
|
|
7190
|
-
|
|
7415
|
+
var app29 = new Hono29();
|
|
7416
|
+
app29.onError((err, c) => {
|
|
7191
7417
|
if (err instanceof HTTPException) {
|
|
7192
7418
|
return err.getResponse();
|
|
7193
7419
|
}
|
|
@@ -7198,10 +7424,10 @@ app30.onError((err, c) => {
|
|
|
7198
7424
|
});
|
|
7199
7425
|
return c.json({ error: "Internal server error" }, 500);
|
|
7200
7426
|
});
|
|
7201
|
-
|
|
7427
|
+
app29.notFound((c) => {
|
|
7202
7428
|
return c.json({ error: "Not found" }, 404);
|
|
7203
7429
|
});
|
|
7204
|
-
|
|
7430
|
+
app29.use("*", async (c, next) => {
|
|
7205
7431
|
const start = Date.now();
|
|
7206
7432
|
await next();
|
|
7207
7433
|
const duration = Date.now() - start;
|
|
@@ -7212,7 +7438,7 @@ app30.use("*", async (c, next) => {
|
|
|
7212
7438
|
httpLog.debug("request", data);
|
|
7213
7439
|
}
|
|
7214
7440
|
});
|
|
7215
|
-
|
|
7441
|
+
app29.get("/api/health", (c) => {
|
|
7216
7442
|
let version = "unknown";
|
|
7217
7443
|
let cached = null;
|
|
7218
7444
|
try {
|
|
@@ -7227,40 +7453,44 @@ app30.get("/api/health", (c) => {
|
|
|
7227
7453
|
...cached?.updateAvailable ? { updateAvailable: true, latest: cached.latest } : {}
|
|
7228
7454
|
});
|
|
7229
7455
|
});
|
|
7230
|
-
|
|
7231
|
-
|
|
7232
|
-
|
|
7233
|
-
|
|
7234
|
-
|
|
7235
|
-
|
|
7236
|
-
|
|
7237
|
-
|
|
7238
|
-
|
|
7239
|
-
|
|
7240
|
-
|
|
7241
|
-
|
|
7242
|
-
|
|
7243
|
-
|
|
7244
|
-
|
|
7245
|
-
|
|
7246
|
-
|
|
7247
|
-
|
|
7248
|
-
|
|
7249
|
-
|
|
7250
|
-
|
|
7251
|
-
|
|
7252
|
-
|
|
7253
|
-
|
|
7254
|
-
|
|
7255
|
-
|
|
7256
|
-
|
|
7257
|
-
|
|
7258
|
-
|
|
7259
|
-
|
|
7260
|
-
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;
|
|
7261
7486
|
|
|
7262
7487
|
// src/web/server.ts
|
|
7263
|
-
|
|
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 = {
|
|
7264
7494
|
".html": "text/html",
|
|
7265
7495
|
".js": "application/javascript",
|
|
7266
7496
|
".css": "text/css",
|
|
@@ -7278,7 +7508,7 @@ async function startServer({
|
|
|
7278
7508
|
let searchDir = dirname2(new URL(import.meta.url).pathname);
|
|
7279
7509
|
for (let i = 0; i < 5; i++) {
|
|
7280
7510
|
const candidate = resolve21(searchDir, "dist", "web-assets");
|
|
7281
|
-
if (
|
|
7511
|
+
if (existsSync17(candidate)) {
|
|
7282
7512
|
assetsDir = candidate;
|
|
7283
7513
|
break;
|
|
7284
7514
|
}
|
|
@@ -7287,20 +7517,20 @@ async function startServer({
|
|
|
7287
7517
|
if (assetsDir) {
|
|
7288
7518
|
app_default.get("*", async (c) => {
|
|
7289
7519
|
const urlPath = new URL(c.req.url).pathname;
|
|
7290
|
-
if (urlPath.startsWith("/api/")) return c.notFound();
|
|
7520
|
+
if (urlPath.startsWith("/api/") || urlPath.startsWith("/ext/")) return c.notFound();
|
|
7291
7521
|
const filePath = resolve21(assetsDir, urlPath.slice(1));
|
|
7292
7522
|
if (!filePath.startsWith(assetsDir)) return c.text("Forbidden", 403);
|
|
7293
|
-
const s = await
|
|
7523
|
+
const s = await stat3(filePath).catch(() => null);
|
|
7294
7524
|
if (s?.isFile()) {
|
|
7295
|
-
const ext =
|
|
7296
|
-
const mime =
|
|
7297
|
-
const body = await
|
|
7525
|
+
const ext = extname4(filePath);
|
|
7526
|
+
const mime = MIME_TYPES2[ext] || "application/octet-stream";
|
|
7527
|
+
const body = await readFile3(filePath);
|
|
7298
7528
|
return c.body(body, 200, { "Content-Type": mime });
|
|
7299
7529
|
}
|
|
7300
7530
|
const indexPath = resolve21(assetsDir, "index.html");
|
|
7301
|
-
const indexStat = await
|
|
7531
|
+
const indexStat = await stat3(indexPath).catch(() => null);
|
|
7302
7532
|
if (indexStat?.isFile()) {
|
|
7303
|
-
const body = await
|
|
7533
|
+
const body = await readFile3(indexPath, "utf-8");
|
|
7304
7534
|
return c.html(body);
|
|
7305
7535
|
}
|
|
7306
7536
|
return c.text("Not found", 404);
|
|
@@ -7377,7 +7607,7 @@ async function startDaemon(opts) {
|
|
|
7377
7607
|
}
|
|
7378
7608
|
const DAEMON_PID_PATH = resolve22(systemDir, "daemon.pid");
|
|
7379
7609
|
const DAEMON_JSON_PATH = resolve22(systemDir, "daemon.json");
|
|
7380
|
-
|
|
7610
|
+
mkdirSync12(home, { recursive: true });
|
|
7381
7611
|
ensureSystemDir();
|
|
7382
7612
|
migrateToSystemDir();
|
|
7383
7613
|
migrateAgentsToMinds();
|
|
@@ -7386,33 +7616,38 @@ async function startDaemon(opts) {
|
|
|
7386
7616
|
} catch (err) {
|
|
7387
7617
|
logger_default.warn("failed to initialize shared repo", logger_default.errorData(err));
|
|
7388
7618
|
}
|
|
7389
|
-
await (await import("./db-
|
|
7390
|
-
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");
|
|
7391
7621
|
migrateRegistryToDb();
|
|
7392
7622
|
try {
|
|
7393
|
-
const { migrateGroupDMsToChannels } = await import("./conversations-
|
|
7623
|
+
const { migrateGroupDMsToChannels } = await import("./conversations-7KVQV7EZ.js");
|
|
7394
7624
|
await migrateGroupDMsToChannels();
|
|
7395
7625
|
} catch (err) {
|
|
7396
7626
|
logger_default.error("failed to migrate group DMs to channels", logger_default.errorData(err));
|
|
7397
7627
|
}
|
|
7398
|
-
const { initSandbox } = await import("./sandbox-
|
|
7628
|
+
const { initSandbox } = await import("./sandbox-O5FUSF43.js");
|
|
7399
7629
|
await initSandbox();
|
|
7400
7630
|
try {
|
|
7401
7631
|
await syncBuiltinSkills();
|
|
7402
7632
|
} catch (err) {
|
|
7403
7633
|
logger_default.error("failed to sync built-in skills", logger_default.errorData(err));
|
|
7404
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();
|
|
7405
7642
|
try {
|
|
7406
7643
|
await ensureSystemChannel();
|
|
7407
7644
|
} catch (err) {
|
|
7408
7645
|
logger_default.warn("failed to ensure #system channel", logger_default.errorData(err));
|
|
7409
7646
|
}
|
|
7410
|
-
const { startSystemWatcher } = await import("./pages-watcher-P7QECRE2.js");
|
|
7411
|
-
startSystemWatcher();
|
|
7412
7647
|
const token = process.env.VOLUTE_DAEMON_TOKEN || randomBytes(32).toString("hex");
|
|
7413
7648
|
let tls;
|
|
7414
7649
|
if (opts.tailscale) {
|
|
7415
|
-
const { getTailscaleTls } = await import("./tailscale-
|
|
7650
|
+
const { getTailscaleTls } = await import("./tailscale-BM72RXCJ.js");
|
|
7416
7651
|
const tlsConfig = await getTailscaleTls();
|
|
7417
7652
|
tls = { key: tlsConfig.key, cert: tlsConfig.cert };
|
|
7418
7653
|
logger_default.info("Tailscale HTTPS enabled", { hostname: tlsConfig.hostname });
|
|
@@ -7433,11 +7668,11 @@ async function startDaemon(opts) {
|
|
|
7433
7668
|
process.env.VOLUTE_DAEMON_TOKEN = token;
|
|
7434
7669
|
process.env.VOLUTE_DAEMON_PORT = String(daemonPort);
|
|
7435
7670
|
process.env.VOLUTE_DAEMON_HOSTNAME = hostname;
|
|
7436
|
-
|
|
7671
|
+
writeFileSync11(DAEMON_PID_PATH, myPid, { mode: 420 });
|
|
7437
7672
|
const daemonConfig = { port, hostname, token };
|
|
7438
7673
|
if (internalPort) daemonConfig.internalPort = internalPort;
|
|
7439
7674
|
if (tls) daemonConfig.tls = true;
|
|
7440
|
-
|
|
7675
|
+
writeFileSync11(DAEMON_JSON_PATH, `${JSON.stringify(daemonConfig, null, 2)}
|
|
7441
7676
|
`, { mode: 420 });
|
|
7442
7677
|
const delivery = initDeliveryManager();
|
|
7443
7678
|
const manager = initMindManager();
|
|
@@ -7493,7 +7728,7 @@ async function startDaemon(opts) {
|
|
|
7493
7728
|
bridgeManager.startBridges(daemonPort).catch((err) => {
|
|
7494
7729
|
logger_default.warn("failed to start bridges", logger_default.errorData(err));
|
|
7495
7730
|
});
|
|
7496
|
-
import("./cloud-sync-
|
|
7731
|
+
import("./cloud-sync-JV4LJOK3.js").then(
|
|
7497
7732
|
({ consumeQueuedMessages }) => consumeQueuedMessages().catch((err) => {
|
|
7498
7733
|
logger_default.warn("failed to consume queued cloud messages", logger_default.errorData(err));
|
|
7499
7734
|
})
|
|
@@ -7501,7 +7736,7 @@ async function startDaemon(opts) {
|
|
|
7501
7736
|
logger_default.warn("failed to load cloud-sync module", logger_default.errorData(err));
|
|
7502
7737
|
});
|
|
7503
7738
|
try {
|
|
7504
|
-
const { backfillTemplateHashes, notifyVersionUpdate } = await import("./version-notify-
|
|
7739
|
+
const { backfillTemplateHashes, notifyVersionUpdate } = await import("./version-notify-2NTWVEHL.js");
|
|
7505
7740
|
backfillTemplateHashes();
|
|
7506
7741
|
notifyVersionUpdate().catch((err) => {
|
|
7507
7742
|
logger_default.warn("failed to send version update notifications", logger_default.errorData(err));
|
|
@@ -7524,13 +7759,13 @@ async function startDaemon(opts) {
|
|
|
7524
7759
|
logger_default.info(`running on ${hostname}:${port}, pid ${myPid}`);
|
|
7525
7760
|
function cleanup() {
|
|
7526
7761
|
try {
|
|
7527
|
-
if (
|
|
7762
|
+
if (readFileSync14(DAEMON_PID_PATH, "utf-8").trim() === myPid) {
|
|
7528
7763
|
unlinkSync2(DAEMON_PID_PATH);
|
|
7529
7764
|
}
|
|
7530
7765
|
} catch {
|
|
7531
7766
|
}
|
|
7532
7767
|
try {
|
|
7533
|
-
const data = JSON.parse(
|
|
7768
|
+
const data = JSON.parse(readFileSync14(DAEMON_JSON_PATH, "utf-8"));
|
|
7534
7769
|
if (data.token === token) {
|
|
7535
7770
|
unlinkSync2(DAEMON_JSON_PATH);
|
|
7536
7771
|
}
|
|
@@ -7552,7 +7787,7 @@ async function startDaemon(opts) {
|
|
|
7552
7787
|
}
|
|
7553
7788
|
};
|
|
7554
7789
|
try {
|
|
7555
|
-
safe("
|
|
7790
|
+
safe("notifyExtensionsDaemonStop", notifyExtensionsDaemonStop);
|
|
7556
7791
|
safe("stopAllActivityTrackers", stopAll);
|
|
7557
7792
|
safe("unsubscribeWebhook", unsubscribeWebhook);
|
|
7558
7793
|
safe("sleepManager.stop", () => sleepManager.stop());
|