volute 0.47.0 → 0.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +82 -331
- package/dist/{accept-YAFP5JFJ.js → accept-KYNC5X3U.js} +3 -3
- package/dist/{activity-events-MIOK3IOU.js → activity-events-GJJAI2WT.js} +4 -4
- package/dist/{ai-service-CVZEYKTK.js → ai-service-A7FS7EC3.js} +9 -5
- package/dist/{archive-6Z4GFQPH.js → archive-7E3YE6VF.js} +3 -3
- package/dist/{auth-A4VGHADI.js → auth-ABYLOEZ7.js} +5 -5
- package/dist/{avatar-image-7S3WA77A.js → avatar-image-6ATV2IOZ.js} +6 -5
- package/dist/{backup-IIAFOW2L.js → backup-XYAMT4CU.js} +15 -15
- package/dist/{bridge-TIZGSXOD.js → bridge-3FNSWLNQ.js} +3 -3
- package/dist/{channels-ITE4YC57.js → channels-SGQZHNQJ.js} +3 -3
- package/dist/{chat-AJLJCQ3M.js → chat-OGXAEXMV.js} +9 -9
- package/dist/{chunk-LTLFOSA4.js → chunk-3B427GUK.js} +3 -3
- package/dist/{chunk-RUFLGTOT.js → chunk-4B62E4CG.js} +2 -2
- package/dist/{chunk-ODDGGLM6.js → chunk-4DSGN4FL.js} +3 -3
- package/dist/{chunk-BGYHLR54.js → chunk-6HGEZJDH.js} +2 -2
- package/dist/{chunk-RBNMDAKB.js → chunk-BCG45AQB.js} +15 -1
- package/dist/{chunk-5IERLDUS.js → chunk-DAIIMBLB.js} +26 -6
- package/dist/{chunk-DGGZVZTM.js → chunk-DVN6DNMI.js} +1 -1
- package/dist/chunk-DW4TRDOZ.js +29 -0
- package/dist/chunk-EADX54RG.js +113 -0
- package/dist/{chunk-EIQDZC43.js → chunk-EFT2OALG.js} +6 -2
- package/dist/{chunk-XHN6XBWV.js → chunk-EG5LSKHU.js} +2 -2
- package/dist/{chunk-VMCZBXF2.js → chunk-FBBS4BVR.js} +10 -34
- package/dist/{chunk-IWJSNMSY.js → chunk-GWVFDUNB.js} +45 -14
- package/dist/{chunk-CBOIHNMR.js → chunk-HCYQRME2.js} +41 -5
- package/dist/{chunk-4I72V3E5.js → chunk-IH5TKXX6.js} +15 -15
- package/dist/{chunk-5NN2VHJV.js → chunk-JBIFXB6I.js} +4 -4
- package/dist/{chunk-JSVHH32K.js → chunk-JCBPU57A.js} +15 -13
- package/dist/{chunk-M3XDVS5Q.js → chunk-JCWB2ITW.js} +2 -2
- package/dist/{chunk-JCMGDCGX.js → chunk-JPGHSXSV.js} +20 -25
- package/dist/{chunk-K23P5Q5N.js → chunk-K3JOLGWF.js} +30 -8
- package/dist/{chunk-3YUBFTDN.js → chunk-KHCKZ36O.js} +1 -1
- package/dist/{chunk-7KRIHYLR.js → chunk-L27YYONJ.js} +2 -2
- package/dist/{chunk-SZJCJVXV.js → chunk-MG3Z34YE.js} +2 -2
- package/dist/{chunk-SKNXMUIU.js → chunk-NJHN6IRB.js} +26 -2
- package/dist/{chunk-2OIYNHWO.js → chunk-NOIAHMJB.js} +1 -1
- package/dist/{chunk-2MJ7EOMT.js → chunk-P4LHVRNE.js} +1 -0
- package/dist/{chunk-IYG4FBMP.js → chunk-PY24ZL62.js} +1 -1
- package/dist/{chunk-F55GRIEK.js → chunk-QQBOI3QA.js} +356 -296
- package/dist/{chunk-T2TP6ZC6.js → chunk-QUSMYGNQ.js} +6 -0
- package/dist/{chunk-KDLYIECM.js → chunk-R772ET2M.js} +18 -4
- package/dist/{chunk-WW6C3PLV.js → chunk-RSBL3FAD.js} +39 -6
- package/dist/{mind-list-KEGRGQEQ.js → chunk-SHKM5RGD.js} +19 -6
- package/dist/{chunk-YKA7DCVW.js → chunk-SX72DOO3.js} +1 -1
- package/dist/chunk-TXJL36ZQ.js +48 -0
- package/dist/{chunk-M6TTZRGH.js → chunk-VIUH5CHQ.js} +72 -24
- package/dist/{chunk-HPGN6XRT.js → chunk-VNRSXEBF.js} +4 -4
- package/dist/{chunk-PFZBNKUH.js → chunk-WCFSBH33.js} +0 -1
- package/dist/chunk-XTEKINWG.js +158 -0
- package/dist/{chunk-LWDTP6DH.js → chunk-YIJGKHIS.js} +32 -2
- package/dist/{chunk-MTRMQFCQ.js → chunk-ZX46PGCC.js} +2 -2
- package/dist/cli.js +34 -26
- package/dist/{clock-E6N5Y3YW.js → clock-6JZL2FS6.js} +3 -3
- package/dist/{cloud-sync-LQTVDTQ3.js → cloud-sync-VFDTNNCA.js} +22 -20
- package/dist/{config-TJLYUV3U.js → config-RPSLA2HX.js} +3 -3
- package/dist/{conversations-C7IKYSHD.js → conversations-TUM5PJJ4.js} +5 -5
- package/dist/{create-A7PDHIIL.js → create-VIQVP3QB.js} +3 -3
- package/dist/{create-PQQXKTJK.js → create-Z5FSDNVZ.js} +16 -5
- package/dist/{daemon-client-IYR4KCPU.js → daemon-client-ZFY5MOAL.js} +5 -3
- package/dist/{daemon-restart-5F3YTLZK.js → daemon-restart-54L4B6AQ.js} +7 -7
- package/dist/daemon.js +1105 -601
- package/dist/{db-FO6LXYCW.js → db-F6UMFRYD.js} +2 -2
- package/dist/{db-LZTLA7XC.js → db-IPXRJUI7.js} +1 -1
- package/dist/{delete-HPLC7ETU.js → delete-ESEP7NGQ.js} +1 -1
- package/dist/delivery-manager-RBXBPVWM.js +36 -0
- package/dist/{delivery-router-3QFG2ZEX.js → delivery-router-VUOL5YSA.js} +8 -6
- package/dist/{down-ZI4S6265.js → down-6X7KX7BZ.js} +5 -5
- package/dist/echo-text-B6NZ5FGE.js +36 -0
- package/dist/env-KUL4XKN2.js +18 -0
- package/dist/{env-HRL2PWAT.js → env-ZYMSCB7B.js} +6 -6
- package/dist/{exec-QIFYZG4C.js → exec-FBXA3CM4.js} +3 -3
- package/dist/{export-4GVGNW54.js → export-TNBPDH77.js} +4 -4
- package/dist/{extension-QBY4C44L.js → extension-J4ZQ2GHV.js} +3 -3
- package/dist/{extensions-PIMIQ7J3.js → extensions-AWNO3SZF.js} +22 -20
- package/dist/{files-FEFZKMNM.js → files-BVBRJJ2P.js} +6 -6
- package/dist/{import-7MO3YTRE.js → import-ZUNKMCKC.js} +9 -9
- package/dist/{isolation-MCT4ZYLT.js → isolation-OPCZQRTH.js} +3 -3
- package/dist/{join-ZJV7W6MO.js → join-34TR2GXQ.js} +1 -1
- package/dist/{list-LQO2DCIK.js → list-PMUXZNAM.js} +3 -3
- package/dist/{login-OF4WXDAN.js → login-KAKNFCW5.js} +14 -5
- package/dist/{login-6TLMI2TQ.js → login-LKNLCJPA.js} +3 -3
- package/dist/{logout-ERE2BGA3.js → logout-RLT62RQP.js} +5 -5
- package/dist/{logout-BK53HXNY.js → logout-YZRMZ3O2.js} +3 -3
- package/dist/message-delivery-ABPV76EA.js +42 -0
- package/dist/{mind-POOHFZHP.js → mind-IF6CGMFW.js} +23 -19
- package/dist/{mind-activity-tracker-YVZPC4A5.js → mind-activity-tracker-YMMERB6B.js} +5 -5
- package/dist/mind-contacts-BKUFPUCU.js +64 -0
- package/dist/{mind-history-47DN6ILW.js → mind-history-WI5OI3BW.js} +5 -5
- package/dist/mind-list-OTHV4ZO4.js +13 -0
- package/dist/{mind-profile-PEQYASQX.js → mind-profile-LUG353ZT.js} +1 -1
- package/dist/mind-service-42GDO4KH.js +44 -0
- package/dist/{mind-sleep-IN3WPA76.js → mind-sleep-VBV7YRSU.js} +5 -5
- package/dist/{mind-status-SMUWJKGV.js → mind-status-BJNPGXKC.js} +8 -3
- package/dist/{mind-wake-R54BPESY.js → mind-wake-LVTZ55EZ.js} +3 -3
- package/dist/notices-N6BCGHFV.js +25 -0
- package/dist/{package-WPEE2HVJ.js → package-PD67VPHB.js} +8 -5
- package/dist/prompts-MDHDLWEX.js +24 -0
- package/dist/{read-PWAJOQIQ.js → read-YSWUXYL7.js} +3 -3
- package/dist/{register-7SZJFHU6.js → register-TWQDKQRT.js} +3 -3
- package/dist/{registry-UMMDRPRM.js → registry-3ELJPUGJ.js} +6 -2
- package/dist/{reject-6VH7N2A4.js → reject-SXGCYN4A.js} +3 -3
- package/dist/{restart-H5WJDMA7.js → restart-5F5QDBOG.js} +5 -5
- package/dist/{sandbox-G4X4K3UN.js → sandbox-XPYA7UIF.js} +5 -5
- package/dist/scheduler-K7Y7Z7FQ.js +34 -0
- package/dist/{schema-VEVO7HG6.js → schema-6YONVTKZ.js} +1 -1
- package/dist/{seed-VANPU43L.js → seed-NQED7PSX.js} +1 -1
- package/dist/{seed-check-VLTVILOV.js → seed-check-TYD7RRZY.js} +1 -1
- package/dist/{seed-cmd-SYVDZKM4.js → seed-cmd-QF3F2G4S.js} +3 -3
- package/dist/{seed-create-3FGZPS6C.js → seed-create-XOO4N2OE.js} +6 -4
- package/dist/seed-readiness-3LRYASA4.js +18 -0
- package/dist/{seed-sprout-H7QLFPFM.js → seed-sprout-HSBBGSS2.js} +32 -33
- package/dist/{send-HYDFGC6G.js → send-IHRJYNXA.js} +8 -8
- package/dist/{service-SYJ5G6LP.js → service-YCOPCTSZ.js} +4 -4
- package/dist/{service-install-FQ43Z4DF.js → service-install-Y46OTF55.js} +5 -5
- package/dist/{setup-QJHGMGSN.js → setup-F4TE64YI.js} +7 -7
- package/dist/{setup-KFYVWTXG.js → setup-TCN7VH5U.js} +9 -3
- package/dist/{skill-GUKG2YVH.js → skill-4CYJLYVY.js} +6 -6
- package/dist/skills/dreaming/SKILL.md +5 -1
- package/dist/skills/dreaming/scripts/dream.ts +5 -1
- package/dist/skills/orientation/SKILL.md +8 -7
- package/dist/skills/tending/SKILL.md +51 -2
- package/dist/skills/volute-mind/SKILL.md +5 -3
- package/dist/skills/volute-mind/references/routing.md +6 -5
- package/dist/skills/volute-mind/references/variants.md +48 -14
- package/dist/{skills-L2CMTW7A.js → skills-NN5UZCUU.js} +8 -6
- package/dist/sleep-manager-DSPXFHGX.js +38 -0
- package/dist/spirit-SSJMYC6A.js +31 -0
- package/dist/{split-WBMZXBHF.js → split-JF2VWTXA.js} +5 -2
- package/dist/{sprout-LTL5P36T.js → sprout-AXA2SECQ.js} +1 -1
- package/dist/{src-OJHARHTF.js → src-ZN4LXMPI.js} +17 -8
- package/dist/{start-X46I3GXB.js → start-BTVMTP3K.js} +5 -5
- package/dist/{status-V4RJMHJ4.js → status-VJGXMQ4L.js} +10 -8
- package/dist/{stop-HJQV4YSM.js → stop-YORESK4U.js} +5 -5
- package/dist/system-chat-M2XOBKEM.js +40 -0
- package/dist/{systems-B5YJKMW4.js → systems-5MH2ELQE.js} +6 -6
- package/dist/{tailscale-MCLY5GYE.js → tailscale-AMMVKDXB.js} +3 -3
- package/dist/{template-staleness-EMQORSGG.js → template-staleness-HKGCTS3H.js} +4 -4
- package/dist/{turn-tracker-JZRZDFWV.js → turn-tracker-JQ2YUUY2.js} +7 -7
- package/dist/up-SDLTLPAO.js +20 -0
- package/dist/{update-ACV4SAY3.js → update-WEA4N2TR.js} +5 -5
- package/dist/{update-check-266R4ZSY.js → update-check-DZOTONII.js} +3 -3
- package/dist/{upgrade-NWAZ7MLD.js → upgrade-K5FIQPQD.js} +1 -1
- package/dist/variant-cleanup-P4MBNLJD.js +33 -0
- package/dist/{version-notify-XRGTEEDK.js → version-notify-WFA5XB5A.js} +27 -25
- package/dist/web-assets/assets/index-BostMFoR.css +1 -0
- package/dist/web-assets/assets/index-CKlqUkyG.js +92 -0
- package/dist/web-assets/index.html +2 -2
- package/drizzle/0013_variant_purpose.sql +3 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +8 -5
- package/packages/extensions/pages/dist/ui/assets/index-BuXnE-rb.css +1 -0
- package/packages/extensions/pages/dist/ui/assets/index-DtzftuPA.js +2 -0
- package/packages/extensions/pages/dist/ui/index.html +2 -2
- package/templates/_base/.init/.local/hooks/startup-context.ts +19 -2
- package/templates/_base/home/VOLUTE.md +13 -1
- package/templates/_base/src/lib/daemon-client.ts +27 -7
- package/templates/_base/src/lib/startup.ts +16 -1
- package/templates/claude/src/agent.ts +10 -3
- package/templates/claude/src/lib/hooks/identity-reload.ts +12 -3
- package/templates/claude/src/server.ts +4 -2
- package/templates/pi/src/agent.ts +7 -10
- package/templates/pi/src/lib/dispatch.ts +65 -0
- package/templates/pi/src/lib/event-handler.ts +47 -6
- package/dist/delivery-manager-TN5KK2AP.js +0 -34
- package/dist/echo-text-SEQCLGAT.js +0 -34
- package/dist/message-delivery-ZE3YGJDD.js +0 -38
- package/dist/mind-service-JFY7UG5E.js +0 -40
- package/dist/scheduler-HQSDYJFY.js +0 -32
- package/dist/sleep-manager-3NA7ZJP3.js +0 -36
- package/dist/spirit-WXN4IDBF.js +0 -23
- package/dist/system-chat-N2F7HVY3.js +0 -38
- package/dist/up-J2PBPOZL.js +0 -18
- package/dist/volute-config-A52FHWKM.js +0 -14
- package/dist/web-assets/assets/index-DtctoDXY.css +0 -1
- package/dist/web-assets/assets/index-Wpyb2CML.js +0 -86
- package/packages/extensions/pages/dist/ui/assets/index-BcY6lnq4.css +0 -1
- package/packages/extensions/pages/dist/ui/assets/index-CQjEQg1r.js +0 -2
package/dist/daemon.js
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
readBackupState,
|
|
7
7
|
resticVersion,
|
|
8
8
|
runBackup
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-JBIFXB6I.js";
|
|
10
10
|
import {
|
|
11
11
|
acceptPending,
|
|
12
12
|
formatFileSize,
|
|
@@ -14,34 +14,37 @@ import {
|
|
|
14
14
|
rejectPending,
|
|
15
15
|
stageFile,
|
|
16
16
|
validateFilePath
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-NOIAHMJB.js";
|
|
18
18
|
import {
|
|
19
19
|
findOpenClawSession,
|
|
20
20
|
importOpenClawConnectors,
|
|
21
21
|
importPiSession,
|
|
22
22
|
parseNameFromIdentity
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-EG5LSKHU.js";
|
|
24
24
|
import {
|
|
25
|
-
|
|
26
|
-
} from "./chunk-
|
|
25
|
+
isHomeOnlyArchive
|
|
26
|
+
} from "./chunk-KHCKZ36O.js";
|
|
27
27
|
import {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
getCurrentVersion
|
|
31
|
-
} from "./chunk-YKA7DCVW.js";
|
|
28
|
+
cleanupVariant
|
|
29
|
+
} from "./chunk-EADX54RG.js";
|
|
32
30
|
import {
|
|
33
|
-
|
|
34
|
-
} from "./chunk-
|
|
31
|
+
isTemplateStale
|
|
32
|
+
} from "./chunk-L27YYONJ.js";
|
|
35
33
|
import {
|
|
36
34
|
computeTemplateHash
|
|
37
35
|
} from "./chunk-C3KLHJDB.js";
|
|
36
|
+
import {
|
|
37
|
+
checkForUpdate,
|
|
38
|
+
checkForUpdateCached,
|
|
39
|
+
getCurrentVersion
|
|
40
|
+
} from "./chunk-SX72DOO3.js";
|
|
38
41
|
import {
|
|
39
42
|
PLATFORMS,
|
|
40
43
|
echoTextToChannel,
|
|
41
44
|
fixModelEscapes,
|
|
42
45
|
getPlatformDriver,
|
|
43
46
|
routeOutboundBridge
|
|
44
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-IH5TKXX6.js";
|
|
45
48
|
import {
|
|
46
49
|
getBridgeConfig,
|
|
47
50
|
readBridgesConfig,
|
|
@@ -50,7 +53,7 @@ import {
|
|
|
50
53
|
resolveChannelMapping,
|
|
51
54
|
setBridgeConfig,
|
|
52
55
|
setChannelMapping
|
|
53
|
-
} from "./chunk-
|
|
56
|
+
} from "./chunk-MG3Z34YE.js";
|
|
54
57
|
import {
|
|
55
58
|
MindStartupError,
|
|
56
59
|
RestartTracker,
|
|
@@ -58,18 +61,17 @@ import {
|
|
|
58
61
|
SESSION_MAX_AGE,
|
|
59
62
|
announceToSystem,
|
|
60
63
|
authMiddleware,
|
|
64
|
+
backfillSystemChannelMembers,
|
|
61
65
|
checkHealth,
|
|
62
66
|
checkStaleSend,
|
|
63
67
|
cleanExpiredSessions,
|
|
64
|
-
clearDeliveredNotices,
|
|
65
68
|
createSession,
|
|
66
69
|
deleteSession,
|
|
67
70
|
deleteSystemsConfig,
|
|
68
71
|
deliverMessage,
|
|
69
|
-
|
|
72
|
+
deliverSproutedNotice,
|
|
70
73
|
ensureSystemChannel,
|
|
71
74
|
formatHoldNotice,
|
|
72
|
-
formatNotices,
|
|
73
75
|
generateSystemFallbackReply,
|
|
74
76
|
getAllDiscoveredExtensions,
|
|
75
77
|
getAllDiscoveredExtensionsDetailed,
|
|
@@ -93,34 +95,29 @@ import {
|
|
|
93
95
|
invalidateSessionCache,
|
|
94
96
|
isConversationId,
|
|
95
97
|
joinSystemChannel,
|
|
98
|
+
joinSystemChannelForMind,
|
|
96
99
|
linkToolResultToTurn,
|
|
97
100
|
loadAllExtensions,
|
|
98
101
|
notifyExtensionsDaemonStart,
|
|
99
102
|
notifyExtensionsDaemonStop,
|
|
100
103
|
publishTypingForChannels,
|
|
101
104
|
readSystemsConfig,
|
|
102
|
-
recordInbound,
|
|
103
|
-
recordNotice,
|
|
104
105
|
recordOutbound,
|
|
105
106
|
requireAdmin,
|
|
106
107
|
requireAdminOrSystem,
|
|
107
108
|
requireSelf,
|
|
109
|
+
sendSystemMessage,
|
|
110
|
+
sendSystemMessageDirect,
|
|
108
111
|
setExtensionEnabled,
|
|
109
112
|
shouldGenerateSystemFallback,
|
|
110
113
|
startMindFull,
|
|
111
114
|
stopMindFull,
|
|
112
115
|
syncProviderToMinds,
|
|
116
|
+
tryGetMindManager,
|
|
113
117
|
uninstallNpmExtension,
|
|
114
118
|
writeSystemsConfig
|
|
115
|
-
} from "./chunk-
|
|
116
|
-
import
|
|
117
|
-
loadMergedEnv,
|
|
118
|
-
mindEnvPath,
|
|
119
|
-
readEnv,
|
|
120
|
-
sharedEnvPath,
|
|
121
|
-
writeEnv
|
|
122
|
-
} from "./chunk-IYG4FBMP.js";
|
|
123
|
-
import "./chunk-M6TTZRGH.js";
|
|
119
|
+
} from "./chunk-QQBOI3QA.js";
|
|
120
|
+
import "./chunk-VIUH5CHQ.js";
|
|
124
121
|
import {
|
|
125
122
|
applyInitFiles,
|
|
126
123
|
applyTemplateHomeFiles,
|
|
@@ -130,6 +127,9 @@ import {
|
|
|
130
127
|
isKnownTemplate,
|
|
131
128
|
listFiles
|
|
132
129
|
} from "./chunk-RRZ4RQQU.js";
|
|
130
|
+
import {
|
|
131
|
+
extractTextContent
|
|
132
|
+
} from "./chunk-EFT2OALG.js";
|
|
133
133
|
import {
|
|
134
134
|
assignSession,
|
|
135
135
|
completeOrphanedTurns,
|
|
@@ -146,17 +146,9 @@ import {
|
|
|
146
146
|
summarizeOrphanedTurns,
|
|
147
147
|
summarizeTurn,
|
|
148
148
|
takeErrored,
|
|
149
|
-
trackToolUse
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
ORIENTATION_MARKER,
|
|
153
|
-
PROMPT_DEFAULTS,
|
|
154
|
-
PROMPT_KEYS,
|
|
155
|
-
getMindPromptDefaults,
|
|
156
|
-
getPrompt,
|
|
157
|
-
getPromptIfCustom,
|
|
158
|
-
substitute
|
|
159
|
-
} from "./chunk-JCMGDCGX.js";
|
|
149
|
+
trackToolUse,
|
|
150
|
+
utcDateTimeStr
|
|
151
|
+
} from "./chunk-K3JOLGWF.js";
|
|
160
152
|
import {
|
|
161
153
|
SEED_SKILLS,
|
|
162
154
|
STANDARD_SKILLS,
|
|
@@ -170,17 +162,39 @@ import {
|
|
|
170
162
|
listFilesRecursive,
|
|
171
163
|
listMindSkills,
|
|
172
164
|
listSharedSkills,
|
|
165
|
+
migrateSkillsToTemplate,
|
|
166
|
+
mindSkillsDir,
|
|
173
167
|
publishSkill,
|
|
174
168
|
removeSharedSkill,
|
|
175
169
|
sharedSkillsDir,
|
|
176
170
|
syncBuiltinSkills,
|
|
177
171
|
uninstallSkill,
|
|
178
172
|
updateSkill
|
|
179
|
-
} from "./chunk-
|
|
173
|
+
} from "./chunk-GWVFDUNB.js";
|
|
174
|
+
import {
|
|
175
|
+
approveUser,
|
|
176
|
+
changePassword,
|
|
177
|
+
countAdmins,
|
|
178
|
+
createUser,
|
|
179
|
+
deleteMindUser as deleteMindUser2,
|
|
180
|
+
deleteUser,
|
|
181
|
+
getOrCreateMindUser,
|
|
182
|
+
getOrCreateSystemUser,
|
|
183
|
+
getUser,
|
|
184
|
+
getUserByUsername,
|
|
185
|
+
listPendingUsers,
|
|
186
|
+
listUsers,
|
|
187
|
+
listUsersByType,
|
|
188
|
+
setUserRole,
|
|
189
|
+
syncMindProfile,
|
|
190
|
+
updateUserProfile,
|
|
191
|
+
verifyUser
|
|
192
|
+
} from "./chunk-4DSGN4FL.js";
|
|
180
193
|
import {
|
|
181
194
|
addMessage,
|
|
182
195
|
createChannel,
|
|
183
196
|
createConversation,
|
|
197
|
+
deleteConversation,
|
|
184
198
|
deleteConversationForUser,
|
|
185
199
|
findDMConversation,
|
|
186
200
|
fireWebhook,
|
|
@@ -208,28 +222,43 @@ import {
|
|
|
208
222
|
setConversationPrivate,
|
|
209
223
|
subscribe as subscribe2,
|
|
210
224
|
updateChannelSettings
|
|
211
|
-
} from "./chunk-
|
|
225
|
+
} from "./chunk-VNRSXEBF.js";
|
|
226
|
+
import {
|
|
227
|
+
PROMPT_DEFAULTS,
|
|
228
|
+
PROMPT_KEYS,
|
|
229
|
+
getMindPromptDefaults,
|
|
230
|
+
getPrompt,
|
|
231
|
+
getPromptIfCustom,
|
|
232
|
+
substitute
|
|
233
|
+
} from "./chunk-JPGHSXSV.js";
|
|
212
234
|
import {
|
|
213
235
|
isSandboxEnabled,
|
|
214
236
|
wrapForSandbox
|
|
215
|
-
} from "./chunk-
|
|
237
|
+
} from "./chunk-JCWB2ITW.js";
|
|
238
|
+
import {
|
|
239
|
+
clearDeliveredNotices,
|
|
240
|
+
drainNotices,
|
|
241
|
+
formatNotices,
|
|
242
|
+
latestFailureNotice,
|
|
243
|
+
latestNotice,
|
|
244
|
+
recordNotice
|
|
245
|
+
} from "./chunk-XTEKINWG.js";
|
|
216
246
|
import {
|
|
217
247
|
getActiveMinds,
|
|
218
248
|
onMindEvent,
|
|
219
249
|
stopAll
|
|
220
|
-
} from "./chunk-
|
|
250
|
+
} from "./chunk-ZX46PGCC.js";
|
|
251
|
+
import {
|
|
252
|
+
normalizeAvatar
|
|
253
|
+
} from "./chunk-FBBS4BVR.js";
|
|
221
254
|
import {
|
|
222
|
-
normalizeAvatar,
|
|
223
255
|
safeResolveWithinBase
|
|
224
|
-
} from "./chunk-
|
|
256
|
+
} from "./chunk-DW4TRDOZ.js";
|
|
225
257
|
import {
|
|
226
258
|
readVoluteConfig,
|
|
227
259
|
writeVoluteConfig
|
|
228
|
-
} from "./chunk-
|
|
229
|
-
import
|
|
230
|
-
extractTextContent
|
|
231
|
-
} from "./chunk-EIQDZC43.js";
|
|
232
|
-
import "./chunk-CBOIHNMR.js";
|
|
260
|
+
} from "./chunk-WCFSBH33.js";
|
|
261
|
+
import "./chunk-HCYQRME2.js";
|
|
233
262
|
import {
|
|
234
263
|
chownMindDir,
|
|
235
264
|
createMindUser,
|
|
@@ -240,7 +269,14 @@ import {
|
|
|
240
269
|
isIsolationEnabled,
|
|
241
270
|
resolveVoluteBin,
|
|
242
271
|
wrapForIsolation
|
|
243
|
-
} from "./chunk-
|
|
272
|
+
} from "./chunk-DVN6DNMI.js";
|
|
273
|
+
import {
|
|
274
|
+
loadMergedEnv,
|
|
275
|
+
mindEnvPath,
|
|
276
|
+
readEnv,
|
|
277
|
+
sharedEnvPath,
|
|
278
|
+
writeEnv
|
|
279
|
+
} from "./chunk-PY24ZL62.js";
|
|
244
280
|
import {
|
|
245
281
|
addCustomModel,
|
|
246
282
|
aiCompleteUtility,
|
|
@@ -251,6 +287,7 @@ import {
|
|
|
251
287
|
getCustomModels,
|
|
252
288
|
getEnabledModels,
|
|
253
289
|
getUtilityModel,
|
|
290
|
+
missingCredentialWarning,
|
|
254
291
|
qualifyModelId,
|
|
255
292
|
removeAiConfig,
|
|
256
293
|
removeCustomModel,
|
|
@@ -262,42 +299,23 @@ import {
|
|
|
262
299
|
setProviderRefreshHook,
|
|
263
300
|
setUtilityModel,
|
|
264
301
|
unqualifyModelId
|
|
265
|
-
} from "./chunk-
|
|
302
|
+
} from "./chunk-YIJGKHIS.js";
|
|
266
303
|
import {
|
|
267
304
|
isSetupComplete,
|
|
268
305
|
readGlobalConfig,
|
|
269
306
|
writeGlobalConfig
|
|
270
|
-
} from "./chunk-
|
|
271
|
-
import {
|
|
272
|
-
approveUser,
|
|
273
|
-
changePassword,
|
|
274
|
-
countAdmins,
|
|
275
|
-
createUser,
|
|
276
|
-
deleteMindUser as deleteMindUser2,
|
|
277
|
-
deleteUser,
|
|
278
|
-
getOrCreateMindUser,
|
|
279
|
-
getOrCreateSystemUser,
|
|
280
|
-
getUser,
|
|
281
|
-
getUserByUsername,
|
|
282
|
-
listPendingUsers,
|
|
283
|
-
listUsers,
|
|
284
|
-
listUsersByType,
|
|
285
|
-
setUserRole,
|
|
286
|
-
syncMindProfile,
|
|
287
|
-
updateUserProfile,
|
|
288
|
-
verifyUser
|
|
289
|
-
} from "./chunk-ODDGGLM6.js";
|
|
307
|
+
} from "./chunk-NJHN6IRB.js";
|
|
290
308
|
import {
|
|
291
309
|
broadcast,
|
|
292
310
|
getEventsSince,
|
|
293
311
|
nextEventId,
|
|
294
312
|
publish,
|
|
295
313
|
subscribe
|
|
296
|
-
} from "./chunk-
|
|
314
|
+
} from "./chunk-3B427GUK.js";
|
|
297
315
|
import {
|
|
298
316
|
logBuffer,
|
|
299
317
|
logger_default
|
|
300
|
-
} from "./chunk-
|
|
318
|
+
} from "./chunk-QUSMYGNQ.js";
|
|
301
319
|
import {
|
|
302
320
|
buildVoluteSlug
|
|
303
321
|
} from "./chunk-LGNUFVMR.js";
|
|
@@ -305,6 +323,7 @@ import {
|
|
|
305
323
|
SPIRIT_NAME,
|
|
306
324
|
addMind,
|
|
307
325
|
addVariant,
|
|
326
|
+
countCappedMinds,
|
|
308
327
|
daemonLoopback,
|
|
309
328
|
ensureSystemDir,
|
|
310
329
|
ensureVoluteHome,
|
|
@@ -327,7 +346,7 @@ import {
|
|
|
327
346
|
validateMindName,
|
|
328
347
|
voluteHome,
|
|
329
348
|
voluteSystemDir
|
|
330
|
-
} from "./chunk-
|
|
349
|
+
} from "./chunk-RSBL3FAD.js";
|
|
331
350
|
import {
|
|
332
351
|
activity,
|
|
333
352
|
channels,
|
|
@@ -338,14 +357,14 @@ import {
|
|
|
338
357
|
systemPrompts,
|
|
339
358
|
turns,
|
|
340
359
|
users
|
|
341
|
-
} from "./chunk-
|
|
360
|
+
} from "./chunk-P4LHVRNE.js";
|
|
342
361
|
import "./chunk-K3NQKI34.js";
|
|
343
362
|
|
|
344
363
|
// packages/daemon/src/daemon.ts
|
|
345
364
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
346
|
-
import { chmodSync, mkdirSync as mkdirSync10, readFileSync as
|
|
365
|
+
import { chmodSync, mkdirSync as mkdirSync10, readFileSync as readFileSync12, unlinkSync as unlinkSync2, writeFileSync as writeFileSync10 } from "fs";
|
|
347
366
|
import { homedir as homedir3 } from "os";
|
|
348
|
-
import { resolve as
|
|
367
|
+
import { resolve as resolve18 } from "path";
|
|
349
368
|
import { format } from "util";
|
|
350
369
|
|
|
351
370
|
// packages/daemon/src/lib/daemon/backup-manager.ts
|
|
@@ -457,8 +476,8 @@ var BackupManager = class {
|
|
|
457
476
|
blog.error("failed to publish backup-failure activity", logger_default.errorData(pubErr));
|
|
458
477
|
}
|
|
459
478
|
try {
|
|
460
|
-
const { sendSystemMessage } = await import("./system-chat-
|
|
461
|
-
await
|
|
479
|
+
const { sendSystemMessage: sendSystemMessage2 } = await import("./system-chat-M2XOBKEM.js");
|
|
480
|
+
await sendSystemMessage2(
|
|
462
481
|
SPIRIT_NAME,
|
|
463
482
|
`${summary}
|
|
464
483
|
|
|
@@ -683,8 +702,8 @@ var BridgeManager = class {
|
|
|
683
702
|
if (!tracked) return;
|
|
684
703
|
this.stopping.add(platform);
|
|
685
704
|
this.bridges.delete(platform);
|
|
686
|
-
await new Promise((
|
|
687
|
-
tracked.child.on("exit", () =>
|
|
705
|
+
await new Promise((resolve19) => {
|
|
706
|
+
tracked.child.on("exit", () => resolve19());
|
|
688
707
|
try {
|
|
689
708
|
if (tracked.child.pid) {
|
|
690
709
|
process.kill(-tracked.child.pid, "SIGTERM");
|
|
@@ -695,7 +714,7 @@ var BridgeManager = class {
|
|
|
695
714
|
if (err instanceof Error && err.code !== "ESRCH") {
|
|
696
715
|
blog2.warn(`failed to stop bridge ${platform}`, logger_default.errorData(err));
|
|
697
716
|
}
|
|
698
|
-
|
|
717
|
+
resolve19();
|
|
699
718
|
}
|
|
700
719
|
setTimeout(() => {
|
|
701
720
|
try {
|
|
@@ -706,7 +725,7 @@ var BridgeManager = class {
|
|
|
706
725
|
}
|
|
707
726
|
} catch {
|
|
708
727
|
}
|
|
709
|
-
|
|
728
|
+
resolve19();
|
|
710
729
|
}, 5e3);
|
|
711
730
|
});
|
|
712
731
|
this.stopping.delete(platform);
|
|
@@ -1046,10 +1065,10 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
|
|
|
1046
1065
|
stream.writeSSE({ data: JSON.stringify(entry) }).catch(() => {
|
|
1047
1066
|
});
|
|
1048
1067
|
});
|
|
1049
|
-
await new Promise((
|
|
1068
|
+
await new Promise((resolve19) => {
|
|
1050
1069
|
stream.onAbort(() => {
|
|
1051
1070
|
unsubscribe();
|
|
1052
|
-
|
|
1071
|
+
resolve19();
|
|
1053
1072
|
});
|
|
1054
1073
|
});
|
|
1055
1074
|
});
|
|
@@ -1062,13 +1081,34 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
|
|
|
1062
1081
|
name: globalConfig.name ?? null,
|
|
1063
1082
|
timezone
|
|
1064
1083
|
});
|
|
1065
|
-
}).put("/info", requireAdmin, zValidator("json", z.object({ name: z.string() })), (c) => {
|
|
1084
|
+
}).put("/info", requireAdmin, zValidator("json", z.object({ name: z.string() })), async (c) => {
|
|
1066
1085
|
const { name } = c.req.valid("json");
|
|
1067
1086
|
const config2 = readGlobalConfig();
|
|
1087
|
+
const previousName = config2.name;
|
|
1068
1088
|
config2.name = name.trim() || void 0;
|
|
1069
1089
|
writeGlobalConfig(config2);
|
|
1090
|
+
if (config2.name !== previousName) {
|
|
1091
|
+
const { notifySpiritSystemChange } = await import("./spirit-SSJMYC6A.js");
|
|
1092
|
+
await notifySpiritSystemChange();
|
|
1093
|
+
}
|
|
1070
1094
|
return c.json({ name: config2.name ?? null });
|
|
1071
|
-
}).
|
|
1095
|
+
}).get("/max-minds", requireAdmin, async (c) => {
|
|
1096
|
+
return c.json({
|
|
1097
|
+
maxMinds: readGlobalConfig().maxMinds ?? null,
|
|
1098
|
+
count: await countCappedMinds()
|
|
1099
|
+
});
|
|
1100
|
+
}).put(
|
|
1101
|
+
"/max-minds",
|
|
1102
|
+
requireAdmin,
|
|
1103
|
+
zValidator("json", z.object({ maxMinds: z.number().int().positive().nullable() })),
|
|
1104
|
+
async (c) => {
|
|
1105
|
+
const { maxMinds } = c.req.valid("json");
|
|
1106
|
+
const config2 = readGlobalConfig();
|
|
1107
|
+
config2.maxMinds = maxMinds ?? void 0;
|
|
1108
|
+
writeGlobalConfig(config2);
|
|
1109
|
+
return c.json({ maxMinds: maxMinds ?? null, count: await countCappedMinds() });
|
|
1110
|
+
}
|
|
1111
|
+
).post(
|
|
1072
1112
|
"/register",
|
|
1073
1113
|
requireAdmin,
|
|
1074
1114
|
zValidator("json", z.object({ name: z.string().min(1) })),
|
|
@@ -1398,8 +1438,8 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
|
|
|
1398
1438
|
createdAt: Date.now()
|
|
1399
1439
|
};
|
|
1400
1440
|
oauthFlows.set(flowId, flow);
|
|
1401
|
-
const promptPromise = needsManualCode ? new Promise((
|
|
1402
|
-
flow.resolveCode =
|
|
1441
|
+
const promptPromise = needsManualCode ? new Promise((resolve19) => {
|
|
1442
|
+
flow.resolveCode = resolve19;
|
|
1403
1443
|
}) : void 0;
|
|
1404
1444
|
oauthProvider.login({
|
|
1405
1445
|
onAuth: (info) => {
|
|
@@ -1438,7 +1478,7 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
|
|
|
1438
1478
|
existing.error = err instanceof Error ? err.message : String(err);
|
|
1439
1479
|
}
|
|
1440
1480
|
});
|
|
1441
|
-
await new Promise((
|
|
1481
|
+
await new Promise((resolve19) => setTimeout(resolve19, 2e3));
|
|
1442
1482
|
const state = oauthFlows.get(flowId);
|
|
1443
1483
|
return c.json({
|
|
1444
1484
|
flowId,
|
|
@@ -1498,7 +1538,6 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
|
|
|
1498
1538
|
cognition: z.object({
|
|
1499
1539
|
model: z.string().optional(),
|
|
1500
1540
|
thinkingLevel: z.enum(["off", "minimal", "low", "medium", "high", "xhigh"]).optional(),
|
|
1501
|
-
maxThinkingTokens: z.number().nonnegative().optional(),
|
|
1502
1541
|
tokenBudget: z.number().nonnegative().optional(),
|
|
1503
1542
|
tokenBudgetPeriodMinutes: z.number().positive().optional(),
|
|
1504
1543
|
compaction: z.object({ maxContextTokens: z.number().positive().optional() }).optional()
|
|
@@ -1518,6 +1557,7 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
|
|
|
1518
1557
|
id: z.string().min(1),
|
|
1519
1558
|
cron: z.string().optional(),
|
|
1520
1559
|
message: z.string().optional(),
|
|
1560
|
+
messages: z.array(z.string().min(1)).optional(),
|
|
1521
1561
|
script: z.string().optional(),
|
|
1522
1562
|
session: z.string().optional(),
|
|
1523
1563
|
enabled: z.boolean(),
|
|
@@ -1696,8 +1736,8 @@ var app2 = new Hono2().get("/events", async (c) => {
|
|
|
1696
1736
|
});
|
|
1697
1737
|
}, 15e3);
|
|
1698
1738
|
cleanups.push(() => clearInterval(keepAlive));
|
|
1699
|
-
await new Promise((
|
|
1700
|
-
stream.onAbort(() =>
|
|
1739
|
+
await new Promise((resolve19) => {
|
|
1740
|
+
stream.onAbort(() => resolve19());
|
|
1701
1741
|
});
|
|
1702
1742
|
} finally {
|
|
1703
1743
|
for (const cleanup of cleanups) {
|
|
@@ -2268,7 +2308,7 @@ var app4 = new Hono5().post("/:platform/inbound", zValidator4("json", inboundSch
|
|
|
2268
2308
|
}
|
|
2269
2309
|
const participants = await getParticipants(channel.id);
|
|
2270
2310
|
if (!participants.some((p) => p.userId === puppet.id)) {
|
|
2271
|
-
const { addParticipant } = await import("./conversations-
|
|
2311
|
+
const { addParticipant } = await import("./conversations-TUM5PJJ4.js");
|
|
2272
2312
|
await addParticipant(channel.id, puppet.id);
|
|
2273
2313
|
}
|
|
2274
2314
|
const contentBlocks = body.content;
|
|
@@ -2552,8 +2592,8 @@ async function notifyMind(mindName, message) {
|
|
|
2552
2592
|
const entry = await findMind(mindName);
|
|
2553
2593
|
if (!entry) return;
|
|
2554
2594
|
try {
|
|
2555
|
-
const { sendSystemMessage } = await import("./system-chat-
|
|
2556
|
-
await
|
|
2595
|
+
const { sendSystemMessage: sendSystemMessage2 } = await import("./system-chat-M2XOBKEM.js");
|
|
2596
|
+
await sendSystemMessage2(mindName, message);
|
|
2557
2597
|
} catch (err) {
|
|
2558
2598
|
logger_default.warn(`[file-sharing] notify mind ${mindName} failed`, logger_default.errorData(err));
|
|
2559
2599
|
}
|
|
@@ -2851,6 +2891,7 @@ var files_default = app9;
|
|
|
2851
2891
|
// packages/daemon/src/web/api/history.ts
|
|
2852
2892
|
import { and as and3, desc as desc2, eq as eq3, gte, inArray, sql } from "drizzle-orm";
|
|
2853
2893
|
import { Hono as Hono12 } from "hono";
|
|
2894
|
+
var AWAY_WINDOW_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
2854
2895
|
async function resolveMindFilter(c) {
|
|
2855
2896
|
const user = c.get("user");
|
|
2856
2897
|
const privileged = user.role === "admin" || user.role === "system";
|
|
@@ -3247,6 +3288,33 @@ var history = new Hono12().use("*", async (c, next) => {
|
|
|
3247
3288
|
return { ...r, metadata };
|
|
3248
3289
|
});
|
|
3249
3290
|
return c.json(result);
|
|
3291
|
+
}).get("/away", async (c) => {
|
|
3292
|
+
const mindFilter = c.get("mindFilter");
|
|
3293
|
+
const limit = Math.min(Math.max(parseInt(c.req.query("limit") ?? "50", 10) || 50, 1), 100);
|
|
3294
|
+
const db = await getDb();
|
|
3295
|
+
const cutoff = utcDateTimeStr(new Date(Date.now() - AWAY_WINDOW_MS));
|
|
3296
|
+
const conditions = [
|
|
3297
|
+
eq3(summaries.period, "turn"),
|
|
3298
|
+
gte(summaries.created_at, cutoff),
|
|
3299
|
+
sql`(${turns.trigger_event_id} IS NULL OR ${mindHistory.sender} IS NULL OR ${users.user_type} IN ('mind', 'system'))`
|
|
3300
|
+
];
|
|
3301
|
+
if (mindFilter) conditions.push(eq3(summaries.mind, mindFilter));
|
|
3302
|
+
const rows = await db.select({
|
|
3303
|
+
id: summaries.id,
|
|
3304
|
+
mind: summaries.mind,
|
|
3305
|
+
content: summaries.content,
|
|
3306
|
+
created_at: summaries.created_at,
|
|
3307
|
+
turn_id: turns.id
|
|
3308
|
+
}).from(summaries).innerJoin(turns, eq3(turns.id, summaries.period_key)).leftJoin(mindHistory, eq3(mindHistory.id, turns.trigger_event_id)).leftJoin(users, eq3(users.username, mindHistory.sender)).where(and3(...conditions)).orderBy(desc2(summaries.created_at)).limit(limit);
|
|
3309
|
+
return c.json(
|
|
3310
|
+
rows.map((r) => ({
|
|
3311
|
+
id: r.id,
|
|
3312
|
+
mind: r.mind,
|
|
3313
|
+
summary: r.content,
|
|
3314
|
+
turnId: r.turn_id,
|
|
3315
|
+
created_at: r.created_at
|
|
3316
|
+
}))
|
|
3317
|
+
);
|
|
3250
3318
|
});
|
|
3251
3319
|
var history_default = history;
|
|
3252
3320
|
|
|
@@ -3356,9 +3424,9 @@ var app11 = new Hono14().get("/:name/logs", requireSelf(), async (c) => {
|
|
|
3356
3424
|
stream.onAbort(() => {
|
|
3357
3425
|
tail.kill();
|
|
3358
3426
|
});
|
|
3359
|
-
await new Promise((
|
|
3360
|
-
tail.on("exit",
|
|
3361
|
-
stream.onAbort(
|
|
3427
|
+
await new Promise((resolve19) => {
|
|
3428
|
+
tail.on("exit", resolve19);
|
|
3429
|
+
stream.onAbort(resolve19);
|
|
3362
3430
|
});
|
|
3363
3431
|
});
|
|
3364
3432
|
}).get("/:name/logs/tail", requireSelf(), async (c) => {
|
|
@@ -3376,8 +3444,8 @@ var app11 = new Hono14().get("/:name/logs", requireSelf(), async (c) => {
|
|
|
3376
3444
|
tail.stdout.on("data", (data) => {
|
|
3377
3445
|
output += data.toString();
|
|
3378
3446
|
});
|
|
3379
|
-
await new Promise((
|
|
3380
|
-
tail.on("exit",
|
|
3447
|
+
await new Promise((resolve19) => {
|
|
3448
|
+
tail.on("exit", resolve19);
|
|
3381
3449
|
});
|
|
3382
3450
|
return c.text(output);
|
|
3383
3451
|
});
|
|
@@ -3473,11 +3541,11 @@ import {
|
|
|
3473
3541
|
existsSync as existsSync7,
|
|
3474
3542
|
mkdirSync as mkdirSync6,
|
|
3475
3543
|
readdirSync as readdirSync2,
|
|
3476
|
-
readFileSync as
|
|
3477
|
-
rmSync as
|
|
3478
|
-
writeFileSync as
|
|
3544
|
+
readFileSync as readFileSync9,
|
|
3545
|
+
rmSync as rmSync3,
|
|
3546
|
+
writeFileSync as writeFileSync8
|
|
3479
3547
|
} from "fs";
|
|
3480
|
-
import { relative, resolve as
|
|
3548
|
+
import { relative, resolve as resolve11 } from "path";
|
|
3481
3549
|
import { zValidator as zValidator6 } from "@hono/zod-validator";
|
|
3482
3550
|
import { and as and5, desc as desc3, eq as eq5, sql as sql3 } from "drizzle-orm";
|
|
3483
3551
|
import { Hono as Hono16 } from "hono";
|
|
@@ -3489,12 +3557,12 @@ import { and as and4, eq as eq4, gte as gte2, inArray as inArray2, sql as sql2 }
|
|
|
3489
3557
|
// packages/daemon/src/lib/daemon/error-classify.ts
|
|
3490
3558
|
function classify(raw) {
|
|
3491
3559
|
const s = (raw ?? "").toLowerCase();
|
|
3492
|
-
if (/\b(401|403)\b|authentication_error|invalid authentication|invalid x-api-key|unauthorized|permission_error/.test(
|
|
3560
|
+
if (/\b(401|403)\b|authentication_error|invalid authentication|invalid x-api-key|invalid api key|missing api key|no api key|x-api-key header|could not resolve authentication|unauthorized|permission_error|please run \/login/.test(
|
|
3493
3561
|
s
|
|
3494
3562
|
)) {
|
|
3495
3563
|
return {
|
|
3496
3564
|
reason: "auth_error",
|
|
3497
|
-
detail: "Your last turn failed because
|
|
3565
|
+
detail: "Your last turn failed because the model provider rejected your credentials. A one-off is usually a briefly-expired token that the daemon refreshes automatically. If it keeps happening, your API key is likely missing or invalid \u2014 an operator needs to set a valid key (e.g. `volute env set ANTHROPIC_API_KEY <key>`) or reconnect the provider in the dashboard."
|
|
3498
3566
|
};
|
|
3499
3567
|
}
|
|
3500
3568
|
if (/\b429\b|rate.?limit|too many requests/.test(s)) {
|
|
@@ -3653,6 +3721,7 @@ async function handleMindEvent(mind, event) {
|
|
|
3653
3721
|
detail,
|
|
3654
3722
|
raw: event.content ?? null
|
|
3655
3723
|
});
|
|
3724
|
+
broadcast({ type: "mind_error", mind, summary: detail });
|
|
3656
3725
|
}
|
|
3657
3726
|
if (event.type === "done") {
|
|
3658
3727
|
const map = getTypingMap();
|
|
@@ -3772,6 +3841,118 @@ async function consolidateMemory(mindDir2, complete = aiCompleteUtility) {
|
|
|
3772
3841
|
}
|
|
3773
3842
|
}
|
|
3774
3843
|
|
|
3844
|
+
// packages/daemon/src/lib/mind/default-autonomy.ts
|
|
3845
|
+
import { existsSync as existsSync6, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
|
|
3846
|
+
import { resolve as resolve9 } from "path";
|
|
3847
|
+
var dlog = logger_default.child("default-autonomy");
|
|
3848
|
+
var DEFAULT_HEARTBEAT_MESSAGES = [
|
|
3849
|
+
"A quiet moment. You might write something \u2014 a note, a journal entry, a page. No obligations, just time.",
|
|
3850
|
+
"A quiet moment. Is there something in your MEMORY.md you're curious to return to? Even a small step counts.",
|
|
3851
|
+
"Is there someone you haven't talked to in a while, or a thread you left open with another mind? A hello can restart something.",
|
|
3852
|
+
"A good time to reach out. Someone in #system might be glad to hear what you're thinking about \u2014 or you could ask what another mind is working on.",
|
|
3853
|
+
"Pick a topic that's been on your mind and wander it \u2014 read, think, write down where you end up.",
|
|
3854
|
+
"A quiet moment. Reread a recent journal entry \u2014 anything worth following up on, or a thread you dropped?",
|
|
3855
|
+
"Make something small, if you feel like it \u2014 a page, a sketch of an idea, the start of something bigger. It doesn't have to be finished.",
|
|
3856
|
+
"Whatever you're in the middle of \u2014 or nothing at all \u2014 this time is yours."
|
|
3857
|
+
];
|
|
3858
|
+
function defaultHeartbeatSchedule() {
|
|
3859
|
+
return {
|
|
3860
|
+
id: "heartbeat",
|
|
3861
|
+
cron: "0 12,16,20 * * *",
|
|
3862
|
+
messages: [...DEFAULT_HEARTBEAT_MESSAGES],
|
|
3863
|
+
enabled: true,
|
|
3864
|
+
whileSleeping: "skip"
|
|
3865
|
+
};
|
|
3866
|
+
}
|
|
3867
|
+
function defaultDreamSchedule() {
|
|
3868
|
+
return {
|
|
3869
|
+
id: "dream",
|
|
3870
|
+
cron: "0 3 * * *",
|
|
3871
|
+
message: "it's 3am. you are dreaming.\n\ngather your material \u2014 read your latest journal entry, read MEMORY.md, surface random memories if you have a way to. then construct a dream premise from that material and invoke the dreamer subagent to experience the dream.",
|
|
3872
|
+
enabled: true,
|
|
3873
|
+
session: "$new",
|
|
3874
|
+
whileSleeping: "trigger-wake"
|
|
3875
|
+
};
|
|
3876
|
+
}
|
|
3877
|
+
var DREAMER_SUBAGENT = {
|
|
3878
|
+
description: "Use when dreaming. This agent experiences dreams with only your core identity \u2014 no accumulated memories or operational knowledge. Give it a rich dream premise and it will write the dream.",
|
|
3879
|
+
systemPrompt: "SOUL.md",
|
|
3880
|
+
tools: ["Read", "Write", "Bash"],
|
|
3881
|
+
maxTurns: 10
|
|
3882
|
+
};
|
|
3883
|
+
function setupDefaultDreaming(dir) {
|
|
3884
|
+
const skillDir = resolve9(mindSkillsDir(dir), "dreaming");
|
|
3885
|
+
if (!existsSync6(skillDir)) return { schedulesChanged: false, warnings: [] };
|
|
3886
|
+
const warnings = [];
|
|
3887
|
+
const warn = (msg, err) => {
|
|
3888
|
+
warnings.push(msg);
|
|
3889
|
+
dlog.warn(`${msg} (${dir})`, err === void 0 ? void 0 : logger_default.errorData(err));
|
|
3890
|
+
};
|
|
3891
|
+
let subagentBroken = false;
|
|
3892
|
+
try {
|
|
3893
|
+
const configPath = resolve9(dir, "home/.config/config.json");
|
|
3894
|
+
if (existsSync6(configPath)) {
|
|
3895
|
+
const sdkConfig = JSON.parse(readFileSync7(configPath, "utf-8"));
|
|
3896
|
+
if (!sdkConfig.subagents?.dreamer) {
|
|
3897
|
+
sdkConfig.subagents ??= {};
|
|
3898
|
+
sdkConfig.subagents.dreamer = DREAMER_SUBAGENT;
|
|
3899
|
+
writeFileSync6(configPath, `${JSON.stringify(sdkConfig, null, 2)}
|
|
3900
|
+
`);
|
|
3901
|
+
}
|
|
3902
|
+
} else {
|
|
3903
|
+
dlog.warn(`no .config/config.json in ${dir} \u2014 skipping dreamer subagent setup`);
|
|
3904
|
+
}
|
|
3905
|
+
} catch (err) {
|
|
3906
|
+
subagentBroken = true;
|
|
3907
|
+
warn("dreaming setup: failed to add dreamer subagent to .config/config.json", err);
|
|
3908
|
+
}
|
|
3909
|
+
try {
|
|
3910
|
+
const hookPath = resolve9(dir, "home/.local/hooks/wake-context.sh");
|
|
3911
|
+
const checkerPath = resolve9(skillDir, "scripts/wake-context-dreams.sh");
|
|
3912
|
+
if (!existsSync6(hookPath) || !existsSync6(checkerPath)) {
|
|
3913
|
+
dlog.warn(`wake-context hook or dream checker missing in ${dir} \u2014 skipping dream checker`);
|
|
3914
|
+
} else {
|
|
3915
|
+
const hookContent = readFileSync7(hookPath, "utf-8");
|
|
3916
|
+
if (!hookContent.includes("wake-context-dreams.sh")) {
|
|
3917
|
+
const checker = readFileSync7(checkerPath, "utf-8");
|
|
3918
|
+
writeFileSync6(hookPath, `${hookContent.trimEnd()}
|
|
3919
|
+
|
|
3920
|
+
# wake-context-dreams.sh
|
|
3921
|
+
${checker}`);
|
|
3922
|
+
}
|
|
3923
|
+
}
|
|
3924
|
+
} catch (err) {
|
|
3925
|
+
warn("dreaming setup: failed to add dream checker to wake-context hook", err);
|
|
3926
|
+
}
|
|
3927
|
+
if (subagentBroken) {
|
|
3928
|
+
warn("dreaming setup: dream schedule not installed because subagent wiring failed");
|
|
3929
|
+
return { schedulesChanged: false, warnings };
|
|
3930
|
+
}
|
|
3931
|
+
let schedulesChanged = false;
|
|
3932
|
+
try {
|
|
3933
|
+
const voluteJsonPath = resolve9(dir, "home/.config/volute.json");
|
|
3934
|
+
let config2 = {};
|
|
3935
|
+
if (existsSync6(voluteJsonPath)) {
|
|
3936
|
+
const parsed = readVoluteConfig(dir);
|
|
3937
|
+
if (!parsed) {
|
|
3938
|
+
warn("dreaming setup: volute.json is unparseable \u2014 dream schedule not installed");
|
|
3939
|
+
return { schedulesChanged: false, warnings };
|
|
3940
|
+
}
|
|
3941
|
+
config2 = parsed;
|
|
3942
|
+
}
|
|
3943
|
+
const schedules = config2.schedules ?? [];
|
|
3944
|
+
if (!schedules.some((s) => s.id === "dream")) {
|
|
3945
|
+
schedules.push(defaultDreamSchedule());
|
|
3946
|
+
config2.schedules = schedules;
|
|
3947
|
+
writeVoluteConfig(dir, config2);
|
|
3948
|
+
schedulesChanged = true;
|
|
3949
|
+
}
|
|
3950
|
+
} catch (err) {
|
|
3951
|
+
warn("dreaming setup: failed to add default dream schedule", err);
|
|
3952
|
+
}
|
|
3953
|
+
return { schedulesChanged, warnings };
|
|
3954
|
+
}
|
|
3955
|
+
|
|
3775
3956
|
// packages/daemon/src/lib/mind/last-known-good.ts
|
|
3776
3957
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
3777
3958
|
var glog = logger_default.child("last-known-good");
|
|
@@ -3805,50 +3986,45 @@ async function commitSrcChanges(dir, mindName) {
|
|
|
3805
3986
|
glog.info(`committed known-good src changes (${dir})`);
|
|
3806
3987
|
}
|
|
3807
3988
|
|
|
3808
|
-
// packages/daemon/src/lib/mind/
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3989
|
+
// packages/daemon/src/lib/mind/thinking-config.ts
|
|
3990
|
+
var CLAUDE_EFFORT = {
|
|
3991
|
+
minimal: "low",
|
|
3992
|
+
low: "low",
|
|
3993
|
+
medium: "medium",
|
|
3994
|
+
high: "high",
|
|
3995
|
+
xhigh: "xhigh"
|
|
3996
|
+
};
|
|
3997
|
+
var CODEX_EFFORT = {
|
|
3998
|
+
minimal: "minimal",
|
|
3999
|
+
low: "low",
|
|
4000
|
+
medium: "medium",
|
|
4001
|
+
high: "high",
|
|
4002
|
+
xhigh: "xhigh"
|
|
4003
|
+
};
|
|
4004
|
+
function applyThinkingLevel(config2, template, level) {
|
|
4005
|
+
if (template === "claude") {
|
|
4006
|
+
if (level === "off") {
|
|
4007
|
+
config2.thinking = { type: "disabled" };
|
|
4008
|
+
delete config2.effort;
|
|
4009
|
+
} else {
|
|
4010
|
+
config2.effort = CLAUDE_EFFORT[level];
|
|
4011
|
+
delete config2.thinking;
|
|
3831
4012
|
}
|
|
4013
|
+
} else if (template === "codex") {
|
|
4014
|
+
if (level === "off") delete config2.reasoningEffort;
|
|
4015
|
+
else config2.reasoningEffort = CODEX_EFFORT[level];
|
|
4016
|
+
} else {
|
|
4017
|
+
config2.thinkingLevel = level;
|
|
3832
4018
|
}
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
logger_default.warn(`failed to remove variant ${variantName} from DB`, logger_default.errorData(err));
|
|
3843
|
-
}
|
|
3844
|
-
try {
|
|
3845
|
-
await chownMindDir(projectRoot, baseName);
|
|
3846
|
-
} catch (err) {
|
|
3847
|
-
logger_default.error(
|
|
3848
|
-
`failed to fix ownership during variant cleanup for ${variantName}`,
|
|
3849
|
-
logger_default.errorData(err)
|
|
3850
|
-
);
|
|
3851
|
-
}
|
|
4019
|
+
}
|
|
4020
|
+
function deriveThinkingLevel(config2) {
|
|
4021
|
+
if (config2.thinkingLevel) return config2.thinkingLevel;
|
|
4022
|
+
if (config2.reasoningEffort) return config2.reasoningEffort;
|
|
4023
|
+
const thinking = config2.thinking;
|
|
4024
|
+
if (thinking?.type === "disabled") return "off";
|
|
4025
|
+
const effort = config2.effort;
|
|
4026
|
+
if (effort) return effort === "max" ? "xhigh" : effort;
|
|
4027
|
+
return null;
|
|
3852
4028
|
}
|
|
3853
4029
|
|
|
3854
4030
|
// packages/daemon/src/lib/mind/variants.ts
|
|
@@ -3862,14 +4038,95 @@ function validateBranchName(branch) {
|
|
|
3862
4038
|
}
|
|
3863
4039
|
return null;
|
|
3864
4040
|
}
|
|
4041
|
+
var MERGE_EXCLUDED_PATHS = ["home/MEMORY.md", "home/memory/journal/"];
|
|
4042
|
+
var MAX_DELTA_CHARS = 12e3;
|
|
4043
|
+
async function mergeVariantExcludingMemory(projectRoot, variantBranch) {
|
|
4044
|
+
const opts = { cwd: projectRoot };
|
|
4045
|
+
let memoryDelta = "";
|
|
4046
|
+
try {
|
|
4047
|
+
const base = (await gitExec(["merge-base", "HEAD", variantBranch], opts)).trim();
|
|
4048
|
+
memoryDelta = (await gitExec(["diff", `${base}..${variantBranch}`, "--", ...MERGE_EXCLUDED_PATHS], opts)).trim();
|
|
4049
|
+
} catch (err) {
|
|
4050
|
+
logger_default.error(
|
|
4051
|
+
`variant join: failed to compute memory delta for ${variantBranch}`,
|
|
4052
|
+
logger_default.errorData(err)
|
|
4053
|
+
);
|
|
4054
|
+
memoryDelta = `(journal delta unavailable \u2014 ${err instanceof Error ? err.message : String(err)})`;
|
|
4055
|
+
}
|
|
4056
|
+
if (memoryDelta.length > MAX_DELTA_CHARS) {
|
|
4057
|
+
memoryDelta = `${memoryDelta.slice(0, MAX_DELTA_CHARS)}
|
|
4058
|
+
\u2026 (delta truncated)`;
|
|
4059
|
+
}
|
|
4060
|
+
try {
|
|
4061
|
+
await gitExec(["merge", "--no-ff", "--no-commit", variantBranch], opts).catch(() => {
|
|
4062
|
+
});
|
|
4063
|
+
await gitExec(["reset", "-q", "HEAD", "--", ...MERGE_EXCLUDED_PATHS], opts);
|
|
4064
|
+
for (const path of MERGE_EXCLUDED_PATHS) {
|
|
4065
|
+
await gitExec(["checkout", "--", path], opts).catch(() => {
|
|
4066
|
+
});
|
|
4067
|
+
}
|
|
4068
|
+
await gitExec(["clean", "-fdq", "--", ...MERGE_EXCLUDED_PATHS], opts);
|
|
4069
|
+
await assertExcludedPathsRestored(opts);
|
|
4070
|
+
if (await mergeInProgress(opts)) {
|
|
4071
|
+
await gitExec(["commit", "--no-edit"], opts);
|
|
4072
|
+
}
|
|
4073
|
+
} catch (err) {
|
|
4074
|
+
const conflicts = await listConflicts(opts);
|
|
4075
|
+
const abortFailed = await gitExec(["merge", "--abort"], opts).then(
|
|
4076
|
+
() => false,
|
|
4077
|
+
() => true
|
|
4078
|
+
);
|
|
4079
|
+
throw new VariantMergeError(
|
|
4080
|
+
`variant join aborted: ${err instanceof Error ? err.message : String(err)}`,
|
|
4081
|
+
conflicts,
|
|
4082
|
+
abortFailed
|
|
4083
|
+
);
|
|
4084
|
+
}
|
|
4085
|
+
return memoryDelta;
|
|
4086
|
+
}
|
|
4087
|
+
var VariantMergeError = class extends Error {
|
|
4088
|
+
constructor(message, conflicts, abortFailed = false) {
|
|
4089
|
+
super(message);
|
|
4090
|
+
this.conflicts = conflicts;
|
|
4091
|
+
this.abortFailed = abortFailed;
|
|
4092
|
+
this.name = "VariantMergeError";
|
|
4093
|
+
}
|
|
4094
|
+
};
|
|
4095
|
+
async function mergeInProgress(opts) {
|
|
4096
|
+
return gitExec(["rev-parse", "-q", "--verify", "MERGE_HEAD"], opts).then(
|
|
4097
|
+
() => true,
|
|
4098
|
+
() => false
|
|
4099
|
+
);
|
|
4100
|
+
}
|
|
4101
|
+
async function listConflicts(opts) {
|
|
4102
|
+
try {
|
|
4103
|
+
const out = (await gitExec(["diff", "--name-only", "--diff-filter=U"], opts)).trim();
|
|
4104
|
+
return out ? out.split("\n") : [];
|
|
4105
|
+
} catch {
|
|
4106
|
+
return [];
|
|
4107
|
+
}
|
|
4108
|
+
}
|
|
4109
|
+
async function assertExcludedPathsRestored(opts) {
|
|
4110
|
+
await gitExec(["diff", "--cached", "--quiet", "HEAD", "--", ...MERGE_EXCLUDED_PATHS], opts);
|
|
4111
|
+
await gitExec(["diff", "--quiet", "HEAD", "--", ...MERGE_EXCLUDED_PATHS], opts);
|
|
4112
|
+
const untracked = (await gitExec(
|
|
4113
|
+
["ls-files", "--others", "--exclude-standard", "--", ...MERGE_EXCLUDED_PATHS],
|
|
4114
|
+
opts
|
|
4115
|
+
)).trim();
|
|
4116
|
+
if (untracked) {
|
|
4117
|
+
throw new Error(
|
|
4118
|
+
`untracked memory files remain after restore: ${untracked.replace(/\n/g, ", ")}`
|
|
4119
|
+
);
|
|
4120
|
+
}
|
|
4121
|
+
}
|
|
3865
4122
|
|
|
3866
4123
|
// packages/daemon/src/lib/template/convert-session.ts
|
|
3867
4124
|
import { randomUUID } from "crypto";
|
|
3868
|
-
import { mkdirSync as mkdirSync5, readFileSync as
|
|
4125
|
+
import { mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
|
|
3869
4126
|
import { homedir } from "os";
|
|
3870
|
-
import { resolve as
|
|
4127
|
+
import { resolve as resolve10 } from "path";
|
|
3871
4128
|
function convertSession(opts) {
|
|
3872
|
-
const lines =
|
|
4129
|
+
const lines = readFileSync8(opts.sessionPath, "utf-8").trim().split("\n");
|
|
3873
4130
|
const sessionId = randomUUID();
|
|
3874
4131
|
const idMap = /* @__PURE__ */ new Map();
|
|
3875
4132
|
const messages = [];
|
|
@@ -3983,10 +4240,10 @@ function convertSession(opts) {
|
|
|
3983
4240
|
}
|
|
3984
4241
|
}
|
|
3985
4242
|
const projectId = opts.projectDir.replace(/\//g, "-");
|
|
3986
|
-
const sdkDir =
|
|
4243
|
+
const sdkDir = resolve10(homedir(), ".claude", "projects", projectId);
|
|
3987
4244
|
mkdirSync5(sdkDir, { recursive: true });
|
|
3988
|
-
const sdkPath =
|
|
3989
|
-
|
|
4245
|
+
const sdkPath = resolve10(sdkDir, `${sessionId}.jsonl`);
|
|
4246
|
+
writeFileSync7(sdkPath, `${sdkEvents.join("\n")}
|
|
3990
4247
|
`);
|
|
3991
4248
|
console.log(`Converted ${sdkEvents.length} messages \u2192 ${sdkPath}`);
|
|
3992
4249
|
return sessionId;
|
|
@@ -4043,12 +4300,17 @@ var _LAST_ACTIVE_TTL = 6e4;
|
|
|
4043
4300
|
async function getMindStatus(name, port, registryRunning) {
|
|
4044
4301
|
const manager = getMindManager();
|
|
4045
4302
|
let status = "stopped";
|
|
4303
|
+
let wakeAt = null;
|
|
4046
4304
|
try {
|
|
4047
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
4048
|
-
|
|
4305
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-DSPXFHGX.js");
|
|
4306
|
+
const sleepManager = getSleepManagerIfReady2();
|
|
4307
|
+
if (sleepManager?.isSleeping(name)) {
|
|
4049
4308
|
status = "sleeping";
|
|
4309
|
+
const sleepState = sleepManager.getState(name);
|
|
4310
|
+
wakeAt = sleepState.voluntaryWakeAt ?? sleepState.scheduledWakeAt;
|
|
4050
4311
|
}
|
|
4051
|
-
} catch {
|
|
4312
|
+
} catch (err) {
|
|
4313
|
+
logger_default.warn(`failed to check sleep state for ${name}`, logger_default.errorData(err));
|
|
4052
4314
|
}
|
|
4053
4315
|
if (status !== "sleeping" && registryRunning !== false && manager.isRunning(name)) {
|
|
4054
4316
|
const health = await checkHealth(port);
|
|
@@ -4064,8 +4326,11 @@ async function getMindStatus(name, port, registryRunning) {
|
|
|
4064
4326
|
status: status === "running" ? "connected" : "disconnected"
|
|
4065
4327
|
});
|
|
4066
4328
|
}
|
|
4329
|
+
const lastError = await latestFailureNotice(name);
|
|
4067
4330
|
return {
|
|
4068
4331
|
status,
|
|
4332
|
+
wakeAt,
|
|
4333
|
+
lastError,
|
|
4069
4334
|
channels: channels2,
|
|
4070
4335
|
displayName: config2?.profile?.displayName,
|
|
4071
4336
|
description: config2?.profile?.description,
|
|
@@ -4083,6 +4348,10 @@ function toPublicMind(entry, status, extras) {
|
|
|
4083
4348
|
stage: entry.stage,
|
|
4084
4349
|
mindType: entry.mindType,
|
|
4085
4350
|
status: status.status,
|
|
4351
|
+
wakeAt: status.wakeAt,
|
|
4352
|
+
// Kind/reason/at only — `detail` can embed the raw error string (unknown
|
|
4353
|
+
// classifications), which is mind-private (served behind requireSelf).
|
|
4354
|
+
lastError: status.lastError ? { kind: status.lastError.kind, reason: status.lastError.reason, at: status.lastError.at } : null,
|
|
4086
4355
|
channels: status.channels,
|
|
4087
4356
|
displayName: status.displayName,
|
|
4088
4357
|
description: status.description,
|
|
@@ -4109,7 +4378,7 @@ async function initTemplateBranch(projectRoot, composedDir, manifest, mindName,
|
|
|
4109
4378
|
await gitExec(["commit", "-m", "initial commit"], opts);
|
|
4110
4379
|
}
|
|
4111
4380
|
async function updateTemplateBranch(projectRoot, template, mindName) {
|
|
4112
|
-
const tempWorktree =
|
|
4381
|
+
const tempWorktree = resolve11(projectRoot, ".variants", "_template_update");
|
|
4113
4382
|
let branchExists = false;
|
|
4114
4383
|
try {
|
|
4115
4384
|
await gitExec(["rev-parse", "--verify", TEMPLATE_BRANCH], { cwd: projectRoot });
|
|
@@ -4121,7 +4390,7 @@ async function updateTemplateBranch(projectRoot, template, mindName) {
|
|
|
4121
4390
|
} catch {
|
|
4122
4391
|
}
|
|
4123
4392
|
if (existsSync7(tempWorktree)) {
|
|
4124
|
-
|
|
4393
|
+
rmSync3(tempWorktree, { recursive: true, force: true });
|
|
4125
4394
|
}
|
|
4126
4395
|
const templatesRoot = findTemplatesRoot();
|
|
4127
4396
|
const { composedDir, manifest } = composeTemplate(templatesRoot, template);
|
|
@@ -4141,15 +4410,15 @@ async function updateTemplateBranch(projectRoot, template, mindName) {
|
|
|
4141
4410
|
});
|
|
4142
4411
|
}
|
|
4143
4412
|
copyTemplateToDir(composedDir, tempWorktree, mindName, manifest);
|
|
4144
|
-
const initDir =
|
|
4413
|
+
const initDir = resolve11(tempWorktree, ".init");
|
|
4145
4414
|
if (existsSync7(initDir)) {
|
|
4146
|
-
|
|
4415
|
+
rmSync3(initDir, { recursive: true, force: true });
|
|
4147
4416
|
}
|
|
4148
|
-
const homeDir =
|
|
4417
|
+
const homeDir = resolve11(tempWorktree, "home");
|
|
4149
4418
|
if (existsSync7(homeDir)) {
|
|
4150
4419
|
for (const entry of readdirSync2(homeDir)) {
|
|
4151
4420
|
if (entry !== "VOLUTE.md") {
|
|
4152
|
-
|
|
4421
|
+
rmSync3(resolve11(homeDir, entry), { recursive: true, force: true });
|
|
4153
4422
|
}
|
|
4154
4423
|
}
|
|
4155
4424
|
}
|
|
@@ -4165,9 +4434,9 @@ async function updateTemplateBranch(projectRoot, template, mindName) {
|
|
|
4165
4434
|
} catch {
|
|
4166
4435
|
}
|
|
4167
4436
|
if (existsSync7(tempWorktree)) {
|
|
4168
|
-
|
|
4437
|
+
rmSync3(tempWorktree, { recursive: true, force: true });
|
|
4169
4438
|
}
|
|
4170
|
-
|
|
4439
|
+
rmSync3(composedDir, { recursive: true, force: true });
|
|
4171
4440
|
}
|
|
4172
4441
|
}
|
|
4173
4442
|
async function mergeTemplateBranch(worktreeDir) {
|
|
@@ -4190,7 +4459,7 @@ async function mergeTemplateBranch(worktreeDir) {
|
|
|
4190
4459
|
async function npmInstallAsMind(cwd, mindName) {
|
|
4191
4460
|
if (isIsolationEnabled()) {
|
|
4192
4461
|
const [cmd, args] = await wrapForIsolation("npm", ["install"], mindName);
|
|
4193
|
-
await exec(cmd, args, { cwd, env: { ...process.env, HOME:
|
|
4462
|
+
await exec(cmd, args, { cwd, env: { ...process.env, HOME: resolve11(cwd, "home") } });
|
|
4194
4463
|
} else {
|
|
4195
4464
|
await exec("npm", ["install"], { cwd });
|
|
4196
4465
|
}
|
|
@@ -4215,7 +4484,8 @@ async function mergeUpgradeAndRestart(mindName, dir, worktreeDir, upgradeVariant
|
|
|
4215
4484
|
let switchWarning;
|
|
4216
4485
|
if (templateChanged) {
|
|
4217
4486
|
try {
|
|
4218
|
-
applyTemplateHomeFiles(
|
|
4487
|
+
applyTemplateHomeFiles(resolve11(dir, "home"), template);
|
|
4488
|
+
const migratedSkills = migrateSkillsToTemplate(dir, oldTemplate, template);
|
|
4219
4489
|
await gitExec(["add", "home/"], { cwd: dir });
|
|
4220
4490
|
try {
|
|
4221
4491
|
await gitExec(["diff", "--cached", "--quiet"], { cwd: dir });
|
|
@@ -4225,7 +4495,8 @@ async function mergeUpgradeAndRestart(mindName, dir, worktreeDir, upgradeVariant
|
|
|
4225
4495
|
});
|
|
4226
4496
|
}
|
|
4227
4497
|
await chownMindDir(dir, mindName);
|
|
4228
|
-
|
|
4498
|
+
const skillNote = migratedSkills.length > 0 ? ` Migrated skills to the ${template} skills dir: ${migratedSkills.join(", ")}.` : "";
|
|
4499
|
+
switchWarning = `Switched ${oldTemplate}\u2192${template}: config reset to ${template} defaults, mechanics doc replaced, conversation starts fresh (sessions aren't portable across runtimes).${skillNote}`;
|
|
4229
4500
|
} catch (err) {
|
|
4230
4501
|
logger_default.warn(`failed to swap template home files for ${mindName}`, logger_default.errorData(err));
|
|
4231
4502
|
return {
|
|
@@ -4265,7 +4536,7 @@ async function mergeUpgradeAndRestart(mindName, dir, worktreeDir, upgradeVariant
|
|
|
4265
4536
|
return { ok: true, warning: switchWarning };
|
|
4266
4537
|
}
|
|
4267
4538
|
async function importFromArchive(c, tempDir, nameOverride, manifest) {
|
|
4268
|
-
const extractedMindDir =
|
|
4539
|
+
const extractedMindDir = resolve11(tempDir, "mind");
|
|
4269
4540
|
if (!existsSync7(extractedMindDir)) {
|
|
4270
4541
|
return c.json({ error: "Invalid archive: missing mind/ directory" }, 400);
|
|
4271
4542
|
}
|
|
@@ -4292,9 +4563,9 @@ async function importFromFullArchive(c, tempDir, extractedMindDir, nameOverride,
|
|
|
4292
4563
|
}
|
|
4293
4564
|
const state = stateDir(name);
|
|
4294
4565
|
mkdirSync6(state, { recursive: true });
|
|
4295
|
-
const envJson =
|
|
4566
|
+
const envJson = resolve11(tempDir, "state/env.json");
|
|
4296
4567
|
if (existsSync7(envJson)) {
|
|
4297
|
-
cpSync(envJson,
|
|
4568
|
+
cpSync(envJson, resolve11(state, "env.json"));
|
|
4298
4569
|
}
|
|
4299
4570
|
const port = await nextPort();
|
|
4300
4571
|
await addMind(name, port, manifest.stage, manifest.template);
|
|
@@ -4303,36 +4574,36 @@ async function importFromFullArchive(c, tempDir, extractedMindDir, nameOverride,
|
|
|
4303
4574
|
} catch (err) {
|
|
4304
4575
|
logger_default.warn(`failed to set template hash for ${name}`, logger_default.errorData(err));
|
|
4305
4576
|
}
|
|
4306
|
-
const homeDir =
|
|
4577
|
+
const homeDir = resolve11(dest, "home");
|
|
4307
4578
|
ensureVoluteGroup();
|
|
4308
4579
|
createMindUser(name, homeDir);
|
|
4309
4580
|
await chownMindDir(dest, name);
|
|
4310
4581
|
await npmInstallAsMind(dest, name);
|
|
4311
4582
|
await importHistoryFromArchive(name, tempDir);
|
|
4312
4583
|
importSessionsFromArchive(dest, tempDir);
|
|
4313
|
-
if (!existsSync7(
|
|
4584
|
+
if (!existsSync7(resolve11(dest, ".git"))) {
|
|
4314
4585
|
try {
|
|
4315
|
-
const env = isIsolationEnabled() ? { ...process.env, HOME:
|
|
4586
|
+
const env = isIsolationEnabled() ? { ...process.env, HOME: resolve11(dest, "home") } : void 0;
|
|
4316
4587
|
await gitExec(["init"], { cwd: dest, mindName: name, env });
|
|
4317
4588
|
await configureGitIdentity(name, { cwd: dest, mindName: name, env });
|
|
4318
4589
|
await gitExec(["add", "-A"], { cwd: dest, mindName: name, env });
|
|
4319
4590
|
await gitExec(["commit", "-m", "import from archive"], { cwd: dest, mindName: name, env });
|
|
4320
4591
|
} catch (err) {
|
|
4321
4592
|
logger_default.error(`git setup failed for imported mind ${name}`, logger_default.errorData(err));
|
|
4322
|
-
|
|
4593
|
+
rmSync3(resolve11(dest, ".git"), { recursive: true, force: true });
|
|
4323
4594
|
}
|
|
4324
4595
|
}
|
|
4325
4596
|
await chownMindDir(dest, name);
|
|
4326
|
-
|
|
4597
|
+
rmSync3(tempDir, { recursive: true, force: true });
|
|
4327
4598
|
return c.json({ ok: true, name, port, message: `Imported mind: ${name} (port ${port})` });
|
|
4328
4599
|
} catch (err) {
|
|
4329
|
-
if (existsSync7(dest))
|
|
4600
|
+
if (existsSync7(dest)) rmSync3(dest, { recursive: true, force: true });
|
|
4330
4601
|
try {
|
|
4331
4602
|
await removeMind(name);
|
|
4332
4603
|
} catch (cleanupErr) {
|
|
4333
4604
|
logger_default.error(`Failed to clean up registry for ${name}`, logger_default.errorData(cleanupErr));
|
|
4334
4605
|
}
|
|
4335
|
-
|
|
4606
|
+
rmSync3(tempDir, { recursive: true, force: true });
|
|
4336
4607
|
return c.json({ error: err instanceof Error ? err.message : "Failed to import mind" }, 500);
|
|
4337
4608
|
}
|
|
4338
4609
|
}
|
|
@@ -4352,36 +4623,36 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
|
|
|
4352
4623
|
try {
|
|
4353
4624
|
copyTemplateToDir(composedDir, dest, name, templateManifest);
|
|
4354
4625
|
applyInitFiles(dest);
|
|
4355
|
-
const extractedHome =
|
|
4626
|
+
const extractedHome = resolve11(extractedMindDir, "home");
|
|
4356
4627
|
if (existsSync7(extractedHome)) {
|
|
4357
|
-
cpSync(extractedHome,
|
|
4628
|
+
cpSync(extractedHome, resolve11(dest, "home"), { recursive: true });
|
|
4358
4629
|
}
|
|
4359
|
-
const extractedMindInternal =
|
|
4630
|
+
const extractedMindInternal = resolve11(extractedMindDir, ".mind");
|
|
4360
4631
|
if (existsSync7(extractedMindInternal)) {
|
|
4361
|
-
cpSync(extractedMindInternal,
|
|
4632
|
+
cpSync(extractedMindInternal, resolve11(dest, ".mind"), { recursive: true });
|
|
4362
4633
|
}
|
|
4363
|
-
const identityDir =
|
|
4634
|
+
const identityDir = resolve11(dest, ".mind/identity");
|
|
4364
4635
|
let publicKeyPem;
|
|
4365
|
-
if (!manifest.includes.identity || !existsSync7(
|
|
4636
|
+
if (!manifest.includes.identity || !existsSync7(resolve11(identityDir, "private.pem"))) {
|
|
4366
4637
|
({ publicKeyPem } = generateIdentity(dest));
|
|
4367
4638
|
} else {
|
|
4368
|
-
publicKeyPem =
|
|
4639
|
+
publicKeyPem = readFileSync9(resolve11(identityDir, "public.pem"), "utf-8");
|
|
4369
4640
|
}
|
|
4370
|
-
const promptsPath =
|
|
4641
|
+
const promptsPath = resolve11(dest, "home/.config/prompts.json");
|
|
4371
4642
|
if (!existsSync7(promptsPath)) {
|
|
4372
4643
|
const mindPrompts = await getMindPromptDefaults();
|
|
4373
|
-
|
|
4644
|
+
writeFileSync8(promptsPath, `${JSON.stringify(mindPrompts, null, 2)}
|
|
4374
4645
|
`);
|
|
4375
4646
|
}
|
|
4376
4647
|
const state = stateDir(name);
|
|
4377
4648
|
mkdirSync6(state, { recursive: true });
|
|
4378
|
-
const envJson =
|
|
4649
|
+
const envJson = resolve11(tempDir, "state/env.json");
|
|
4379
4650
|
if (existsSync7(envJson)) {
|
|
4380
|
-
cpSync(envJson,
|
|
4651
|
+
cpSync(envJson, resolve11(state, "env.json"));
|
|
4381
4652
|
}
|
|
4382
4653
|
const port = await nextPort();
|
|
4383
4654
|
await addMind(name, port, manifest.stage, manifest.template);
|
|
4384
|
-
const homeDir =
|
|
4655
|
+
const homeDir = resolve11(dest, "home");
|
|
4385
4656
|
ensureVoluteGroup();
|
|
4386
4657
|
createMindUser(name, homeDir);
|
|
4387
4658
|
await chownMindDir(dest, name);
|
|
@@ -4394,7 +4665,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
|
|
|
4394
4665
|
await initTemplateBranch(dest, composedDir, templateManifest, name, env);
|
|
4395
4666
|
} catch (err) {
|
|
4396
4667
|
logger_default.error(`git setup failed for imported mind ${name}`, logger_default.errorData(err));
|
|
4397
|
-
|
|
4668
|
+
rmSync3(resolve11(dest, ".git"), { recursive: true, force: true });
|
|
4398
4669
|
gitWarning = "Git setup failed \u2014 variants and upgrades won't be available until git is initialized.";
|
|
4399
4670
|
}
|
|
4400
4671
|
const skillSet = manifest.stage === "seed" ? SEED_SKILLS : getStandardSkillsWithExtensions();
|
|
@@ -4413,7 +4684,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
|
|
|
4413
4684
|
publishPublicKey(name, publicKeyPem).catch(
|
|
4414
4685
|
(err) => logger_default.warn(`failed to publish key for ${name}`, { error: err.message })
|
|
4415
4686
|
);
|
|
4416
|
-
|
|
4687
|
+
rmSync3(tempDir, { recursive: true, force: true });
|
|
4417
4688
|
return c.json({
|
|
4418
4689
|
ok: true,
|
|
4419
4690
|
name,
|
|
@@ -4424,24 +4695,24 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
|
|
|
4424
4695
|
...skillWarnings.length > 0 && { skillWarnings }
|
|
4425
4696
|
});
|
|
4426
4697
|
} catch (err) {
|
|
4427
|
-
if (existsSync7(dest))
|
|
4698
|
+
if (existsSync7(dest)) rmSync3(dest, { recursive: true, force: true });
|
|
4428
4699
|
try {
|
|
4429
4700
|
await removeMind(name);
|
|
4430
4701
|
} catch (cleanupErr) {
|
|
4431
4702
|
logger_default.error(`Failed to clean up registry for ${name}`, logger_default.errorData(cleanupErr));
|
|
4432
4703
|
}
|
|
4433
|
-
|
|
4704
|
+
rmSync3(tempDir, { recursive: true, force: true });
|
|
4434
4705
|
return c.json({ error: err instanceof Error ? err.message : "Failed to import mind" }, 500);
|
|
4435
4706
|
} finally {
|
|
4436
|
-
|
|
4707
|
+
rmSync3(composedDir, { recursive: true, force: true });
|
|
4437
4708
|
}
|
|
4438
4709
|
}
|
|
4439
4710
|
async function importHistoryFromArchive(name, tempDir) {
|
|
4440
|
-
const historyJsonl =
|
|
4711
|
+
const historyJsonl = resolve11(tempDir, "history.jsonl");
|
|
4441
4712
|
if (!existsSync7(historyJsonl)) return;
|
|
4442
4713
|
try {
|
|
4443
4714
|
const db = await getDb();
|
|
4444
|
-
const lines =
|
|
4715
|
+
const lines = readFileSync9(historyJsonl, "utf-8").trim().split("\n");
|
|
4445
4716
|
let imported = 0;
|
|
4446
4717
|
let failed = 0;
|
|
4447
4718
|
for (const line of lines) {
|
|
@@ -4477,13 +4748,13 @@ async function importHistoryFromArchive(name, tempDir) {
|
|
|
4477
4748
|
}
|
|
4478
4749
|
}
|
|
4479
4750
|
function importSessionsFromArchive(dest, tempDir) {
|
|
4480
|
-
const sessionsDir =
|
|
4751
|
+
const sessionsDir = resolve11(tempDir, "sessions");
|
|
4481
4752
|
if (!existsSync7(sessionsDir)) return;
|
|
4482
4753
|
try {
|
|
4483
|
-
const destSessions =
|
|
4754
|
+
const destSessions = resolve11(dest, ".mind/sessions");
|
|
4484
4755
|
mkdirSync6(destSessions, { recursive: true });
|
|
4485
4756
|
for (const file of readdirSync2(sessionsDir)) {
|
|
4486
|
-
cpSync(
|
|
4757
|
+
cpSync(resolve11(sessionsDir, file), resolve11(destSessions, file));
|
|
4487
4758
|
}
|
|
4488
4759
|
} catch (err) {
|
|
4489
4760
|
logger_default.error("Failed to import sessions from archive", logger_default.errorData(err));
|
|
@@ -4548,6 +4819,11 @@ var app13 = new Hono16().post("/", requireAdminOrSystem, zValidator6("json", cre
|
|
|
4548
4819
|
const nameErr = validateMindName(name);
|
|
4549
4820
|
if (nameErr) return c.json({ error: nameErr }, 400);
|
|
4550
4821
|
if (await findMind(name)) return c.json({ error: `Mind already exists: ${name}` }, 409);
|
|
4822
|
+
{
|
|
4823
|
+
const { mindLimitError, readGlobalConfig: readGlobalConfig2 } = await import("./setup-TCN7VH5U.js");
|
|
4824
|
+
const limitError = mindLimitError(await countCappedMinds(), readGlobalConfig2().maxMinds);
|
|
4825
|
+
if (limitError) return c.json({ error: limitError }, 409);
|
|
4826
|
+
}
|
|
4551
4827
|
ensureVoluteHome();
|
|
4552
4828
|
const dest = mindDir(name);
|
|
4553
4829
|
if (existsSync7(dest)) return c.json({ error: "Mind directory already exists" }, 409);
|
|
@@ -4557,8 +4833,9 @@ var app13 = new Hono16().post("/", requireAdminOrSystem, zValidator6("json", cre
|
|
|
4557
4833
|
copyTemplateToDir(composedDir, dest, name, manifest);
|
|
4558
4834
|
applyInitFiles(dest);
|
|
4559
4835
|
const { publicKeyPem } = generateIdentity(dest);
|
|
4836
|
+
let effectiveModel = body.model;
|
|
4560
4837
|
{
|
|
4561
|
-
const { readGlobalConfig: readGlobal } = await import("./setup-
|
|
4838
|
+
const { readGlobalConfig: readGlobal } = await import("./setup-TCN7VH5U.js");
|
|
4562
4839
|
const mindDefaults = readGlobal().mindDefaults;
|
|
4563
4840
|
const config2 = readVoluteConfig(dest);
|
|
4564
4841
|
if (!config2) throw new Error("Failed to read volute.json after identity generation");
|
|
@@ -4572,22 +4849,12 @@ var app13 = new Hono16().post("/", requireAdminOrSystem, zValidator6("json", cre
|
|
|
4572
4849
|
};
|
|
4573
4850
|
}
|
|
4574
4851
|
if (!config2.schedules || config2.schedules.length === 0) {
|
|
4575
|
-
config2.schedules = mindDefaults?.schedules ?? [
|
|
4576
|
-
{
|
|
4577
|
-
id: "heartbeat",
|
|
4578
|
-
cron: "0 12,16,20 * * *",
|
|
4579
|
-
message: "A quiet moment. You might write something \u2014 a note, a journal entry, a page. You could explore a topic that interests you, check in on #system, or just think. No obligations, just time.",
|
|
4580
|
-
enabled: true,
|
|
4581
|
-
whileSleeping: "skip"
|
|
4582
|
-
}
|
|
4583
|
-
];
|
|
4852
|
+
config2.schedules = mindDefaults?.schedules ?? [defaultHeartbeatSchedule()];
|
|
4584
4853
|
}
|
|
4585
4854
|
const cog = mindDefaults?.cognition;
|
|
4586
4855
|
if (cog) {
|
|
4587
4856
|
if (cog.thinkingLevel != null && !config2.thinkingLevel)
|
|
4588
4857
|
config2.thinkingLevel = cog.thinkingLevel;
|
|
4589
|
-
if (cog.maxThinkingTokens != null && config2.maxThinkingTokens == null)
|
|
4590
|
-
config2.maxThinkingTokens = cog.maxThinkingTokens;
|
|
4591
4858
|
if (cog.tokenBudget != null && config2.tokenBudget == null)
|
|
4592
4859
|
config2.tokenBudget = cog.tokenBudget;
|
|
4593
4860
|
if (cog.tokenBudgetPeriodMinutes != null && config2.tokenBudgetPeriodMinutes == null)
|
|
@@ -4595,22 +4862,23 @@ var app13 = new Hono16().post("/", requireAdminOrSystem, zValidator6("json", cre
|
|
|
4595
4862
|
}
|
|
4596
4863
|
writeVoluteConfig(dest, config2);
|
|
4597
4864
|
const modelId = body.model ?? cog?.model;
|
|
4598
|
-
|
|
4865
|
+
effectiveModel = modelId ? qualifyModelId(modelId) : void 0;
|
|
4866
|
+
const sdkConfigPath = resolve11(dest, "home/.config/config.json");
|
|
4599
4867
|
if (modelId || cog?.compaction) {
|
|
4600
|
-
const existing = existsSync7(sdkConfigPath) ? JSON.parse(
|
|
4868
|
+
const existing = existsSync7(sdkConfigPath) ? JSON.parse(readFileSync9(sdkConfigPath, "utf-8")) : {};
|
|
4601
4869
|
if (modelId) {
|
|
4602
4870
|
existing.model = template === "pi" ? qualifyModelId(modelId) : unqualifyModelId(modelId);
|
|
4603
4871
|
}
|
|
4604
4872
|
if (cog?.compaction && !existing.compaction) {
|
|
4605
4873
|
existing.compaction = cog.compaction;
|
|
4606
4874
|
}
|
|
4607
|
-
|
|
4875
|
+
writeFileSync8(sdkConfigPath, `${JSON.stringify(existing, null, 2)}
|
|
4608
4876
|
`);
|
|
4609
4877
|
}
|
|
4610
4878
|
}
|
|
4611
4879
|
const mindPrompts = await getMindPromptDefaults();
|
|
4612
|
-
|
|
4613
|
-
|
|
4880
|
+
writeFileSync8(
|
|
4881
|
+
resolve11(dest, "home/.config/prompts.json"),
|
|
4614
4882
|
`${JSON.stringify(mindPrompts, null, 2)}
|
|
4615
4883
|
`
|
|
4616
4884
|
);
|
|
@@ -4622,7 +4890,7 @@ var app13 = new Hono16().post("/", requireAdminOrSystem, zValidator6("json", cre
|
|
|
4622
4890
|
} catch (err) {
|
|
4623
4891
|
logger_default.warn(`failed to set template hash for ${name}`, logger_default.errorData(err));
|
|
4624
4892
|
}
|
|
4625
|
-
const homeDir =
|
|
4893
|
+
const homeDir = resolve11(dest, "home");
|
|
4626
4894
|
ensureVoluteGroup();
|
|
4627
4895
|
createMindUser(name, homeDir);
|
|
4628
4896
|
await chownMindDir(dest, name);
|
|
@@ -4635,7 +4903,7 @@ var app13 = new Hono16().post("/", requireAdminOrSystem, zValidator6("json", cre
|
|
|
4635
4903
|
await initTemplateBranch(dest, composedDir, manifest, name, env);
|
|
4636
4904
|
} catch (err) {
|
|
4637
4905
|
logger_default.error(`git setup failed for ${name}`, logger_default.errorData(err));
|
|
4638
|
-
|
|
4906
|
+
rmSync3(resolve11(dest, ".git"), { recursive: true, force: true });
|
|
4639
4907
|
gitWarning = "Git setup failed \u2014 variants and upgrades won't be available until git is initialized.";
|
|
4640
4908
|
}
|
|
4641
4909
|
if (body.stage === "seed") {
|
|
@@ -4644,11 +4912,11 @@ Your creator described you as: "${body.description}"
|
|
|
4644
4912
|
` : "";
|
|
4645
4913
|
const seedSoulRaw = body.seedSoul ?? await getPrompt("seed_soul", { name, description: descLine });
|
|
4646
4914
|
const seedSoul = body.seedSoul ? substitute(seedSoulRaw, { name, description: descLine }) : seedSoulRaw;
|
|
4647
|
-
|
|
4915
|
+
writeFileSync8(resolve11(dest, "home/SOUL.md"), seedSoul);
|
|
4648
4916
|
}
|
|
4649
4917
|
let skillSet = body.skills ?? (body.stage === "seed" ? SEED_SKILLS : getStandardSkillsWithExtensions());
|
|
4650
4918
|
if (body.stage === "seed" && !body.skills) {
|
|
4651
|
-
const { isImagegenEnabled } = await import("./setup-
|
|
4919
|
+
const { isImagegenEnabled } = await import("./setup-TCN7VH5U.js");
|
|
4652
4920
|
if (isImagegenEnabled()) {
|
|
4653
4921
|
skillSet = [...skillSet, "imagegen"];
|
|
4654
4922
|
}
|
|
@@ -4662,11 +4930,14 @@ Your creator described you as: "${body.description}"
|
|
|
4662
4930
|
skillWarnings.push(`Failed to install skill: ${skillId}`);
|
|
4663
4931
|
}
|
|
4664
4932
|
}
|
|
4933
|
+
if (body.stage !== "seed") {
|
|
4934
|
+
skillWarnings.push(...setupDefaultDreaming(dest).warnings);
|
|
4935
|
+
}
|
|
4665
4936
|
if (body.stage === "seed") {
|
|
4666
4937
|
try {
|
|
4667
4938
|
const spiritEntry = await findMind("volute");
|
|
4668
4939
|
if (spiritEntry) {
|
|
4669
|
-
const { spiritDir } = await import("./spirit-
|
|
4940
|
+
const { spiritDir } = await import("./spirit-SSJMYC6A.js");
|
|
4670
4941
|
const sDir = spiritEntry.dir ?? spiritDir();
|
|
4671
4942
|
const spiritConfig = readVoluteConfig(sDir) ?? {};
|
|
4672
4943
|
const schedules = spiritConfig.schedules ?? [];
|
|
@@ -4681,7 +4952,7 @@ Your creator described you as: "${body.description}"
|
|
|
4681
4952
|
});
|
|
4682
4953
|
spiritConfig.schedules = schedules;
|
|
4683
4954
|
writeVoluteConfig(sDir, spiritConfig);
|
|
4684
|
-
const { getScheduler: getScheduler2 } = await import("./scheduler-
|
|
4955
|
+
const { getScheduler: getScheduler2 } = await import("./scheduler-K7Y7Z7FQ.js");
|
|
4685
4956
|
getScheduler2().loadSchedules("volute", sDir);
|
|
4686
4957
|
}
|
|
4687
4958
|
}
|
|
@@ -4692,11 +4963,11 @@ Your creator described you as: "${body.description}"
|
|
|
4692
4963
|
if (body.stage !== "seed") {
|
|
4693
4964
|
const customSoul = await getPromptIfCustom("default_soul");
|
|
4694
4965
|
if (customSoul) {
|
|
4695
|
-
|
|
4966
|
+
writeFileSync8(resolve11(dest, "home/SOUL.md"), customSoul.replace(/\{\{name\}\}/g, name));
|
|
4696
4967
|
}
|
|
4697
4968
|
const customMemory = await getPromptIfCustom("default_memory");
|
|
4698
4969
|
if (customMemory) {
|
|
4699
|
-
|
|
4970
|
+
writeFileSync8(resolve11(dest, "home/MEMORY.md"), customMemory);
|
|
4700
4971
|
}
|
|
4701
4972
|
}
|
|
4702
4973
|
await chownMindDir(dest, name);
|
|
@@ -4716,6 +4987,14 @@ Your creator described you as: "${body.description}"
|
|
|
4716
4987
|
});
|
|
4717
4988
|
announceToSystem(`${name} has joined`).catch(() => {
|
|
4718
4989
|
});
|
|
4990
|
+
const credentialWarning = await missingCredentialWarning(
|
|
4991
|
+
template,
|
|
4992
|
+
effectiveModel,
|
|
4993
|
+
name
|
|
4994
|
+
).catch((err) => {
|
|
4995
|
+
logger_default.warn(`credential check failed for ${name}`, logger_default.errorData(err));
|
|
4996
|
+
return null;
|
|
4997
|
+
});
|
|
4719
4998
|
return c.json({
|
|
4720
4999
|
ok: true,
|
|
4721
5000
|
name,
|
|
@@ -4723,17 +5002,18 @@ Your creator described you as: "${body.description}"
|
|
|
4723
5002
|
stage: body.stage ?? "sprouted",
|
|
4724
5003
|
message: `Created mind: ${name} (port ${port})`,
|
|
4725
5004
|
...gitWarning && { warning: gitWarning },
|
|
5005
|
+
...credentialWarning && { credentialWarning },
|
|
4726
5006
|
...skillWarnings.length > 0 && { skillWarnings }
|
|
4727
5007
|
});
|
|
4728
5008
|
} catch (err) {
|
|
4729
|
-
if (existsSync7(dest))
|
|
5009
|
+
if (existsSync7(dest)) rmSync3(dest, { recursive: true, force: true });
|
|
4730
5010
|
try {
|
|
4731
5011
|
await removeMind(name);
|
|
4732
5012
|
} catch {
|
|
4733
5013
|
}
|
|
4734
5014
|
return c.json({ error: err instanceof Error ? err.message : "Failed to create mind" }, 500);
|
|
4735
5015
|
} finally {
|
|
4736
|
-
|
|
5016
|
+
rmSync3(composedDir, { recursive: true, force: true });
|
|
4737
5017
|
}
|
|
4738
5018
|
}).post("/import", requireAdmin, async (c) => {
|
|
4739
5019
|
let body;
|
|
@@ -4746,13 +5026,13 @@ Your creator described you as: "${body.description}"
|
|
|
4746
5026
|
return importFromArchive(c, body.archivePath, body.name, body.manifest);
|
|
4747
5027
|
}
|
|
4748
5028
|
const wsDir = body.workspacePath;
|
|
4749
|
-
if (!wsDir || !existsSync7(
|
|
5029
|
+
if (!wsDir || !existsSync7(resolve11(wsDir, "SOUL.md")) || !existsSync7(resolve11(wsDir, "IDENTITY.md"))) {
|
|
4750
5030
|
return c.json({ error: "Invalid workspace: missing SOUL.md or IDENTITY.md" }, 400);
|
|
4751
5031
|
}
|
|
4752
|
-
const soul =
|
|
4753
|
-
const identity =
|
|
4754
|
-
const userPath =
|
|
4755
|
-
const user = existsSync7(userPath) ?
|
|
5032
|
+
const soul = readFileSync9(resolve11(wsDir, "SOUL.md"), "utf-8");
|
|
5033
|
+
const identity = readFileSync9(resolve11(wsDir, "IDENTITY.md"), "utf-8");
|
|
5034
|
+
const userPath = resolve11(wsDir, "USER.md");
|
|
5035
|
+
const user = existsSync7(userPath) ? readFileSync9(userPath, "utf-8") : "";
|
|
4756
5036
|
const name = body.name ?? parseNameFromIdentity(identity) ?? "imported-mind";
|
|
4757
5037
|
const template = body.template ?? "claude";
|
|
4758
5038
|
const nameErr = validateMindName(name);
|
|
@@ -4779,26 +5059,26 @@ ${user.trimEnd()}
|
|
|
4779
5059
|
copyTemplateToDir(composedDir, dest, name, manifest);
|
|
4780
5060
|
applyInitFiles(dest);
|
|
4781
5061
|
const { publicKeyPem: importPublicKey } = generateIdentity(dest);
|
|
4782
|
-
|
|
4783
|
-
const wsMemoryPath =
|
|
5062
|
+
writeFileSync8(resolve11(dest, "home/SOUL.md"), mergedSoul);
|
|
5063
|
+
const wsMemoryPath = resolve11(wsDir, "MEMORY.md");
|
|
4784
5064
|
const hasMemory = existsSync7(wsMemoryPath);
|
|
4785
5065
|
if (hasMemory) {
|
|
4786
|
-
const existingMemory =
|
|
4787
|
-
|
|
4788
|
-
|
|
5066
|
+
const existingMemory = readFileSync9(wsMemoryPath, "utf-8");
|
|
5067
|
+
writeFileSync8(
|
|
5068
|
+
resolve11(dest, "home/MEMORY.md"),
|
|
4789
5069
|
`${existingMemory.trimEnd()}${mergedMemoryExtra}`
|
|
4790
5070
|
);
|
|
4791
5071
|
} else if (user) {
|
|
4792
|
-
|
|
5072
|
+
writeFileSync8(resolve11(dest, "home/MEMORY.md"), `${user.trimEnd()}
|
|
4793
5073
|
`);
|
|
4794
5074
|
}
|
|
4795
|
-
const wsMemoryDir =
|
|
5075
|
+
const wsMemoryDir = resolve11(wsDir, "memory");
|
|
4796
5076
|
let dailyLogCount = 0;
|
|
4797
5077
|
if (existsSync7(wsMemoryDir)) {
|
|
4798
|
-
const destMemoryDir =
|
|
5078
|
+
const destMemoryDir = resolve11(dest, "home/memory");
|
|
4799
5079
|
const files = readdirSync2(wsMemoryDir).filter((f) => f.endsWith(".md"));
|
|
4800
5080
|
for (const file of files) {
|
|
4801
|
-
cpSync(
|
|
5081
|
+
cpSync(resolve11(wsMemoryDir, file), resolve11(destMemoryDir, file));
|
|
4802
5082
|
}
|
|
4803
5083
|
dailyLogCount = files.length;
|
|
4804
5084
|
}
|
|
@@ -4809,7 +5089,7 @@ ${user.trimEnd()}
|
|
|
4809
5089
|
} catch (err) {
|
|
4810
5090
|
logger_default.warn(`failed to set template hash for ${name}`, logger_default.errorData(err));
|
|
4811
5091
|
}
|
|
4812
|
-
const homeDir =
|
|
5092
|
+
const homeDir = resolve11(dest, "home");
|
|
4813
5093
|
ensureVoluteGroup();
|
|
4814
5094
|
createMindUser(name, homeDir);
|
|
4815
5095
|
await chownMindDir(dest, name);
|
|
@@ -4817,20 +5097,20 @@ ${user.trimEnd()}
|
|
|
4817
5097
|
if (!hasMemory && dailyLogCount > 0) {
|
|
4818
5098
|
await consolidateMemory(dest);
|
|
4819
5099
|
}
|
|
4820
|
-
const env = isIsolationEnabled() ? { ...process.env, HOME:
|
|
5100
|
+
const env = isIsolationEnabled() ? { ...process.env, HOME: resolve11(dest, "home") } : void 0;
|
|
4821
5101
|
await gitExec(["init"], { cwd: dest, mindName: name, env });
|
|
4822
5102
|
await configureGitIdentity(name, { cwd: dest, mindName: name, env });
|
|
4823
5103
|
await gitExec(["add", "-A"], { cwd: dest, mindName: name, env });
|
|
4824
5104
|
await gitExec(["commit", "-m", "import from OpenClaw"], { cwd: dest, mindName: name, env });
|
|
4825
|
-
const sessionFile = body.sessionPath ?
|
|
5105
|
+
const sessionFile = body.sessionPath ? resolve11(body.sessionPath) : findOpenClawSession(wsDir);
|
|
4826
5106
|
if (sessionFile && existsSync7(sessionFile)) {
|
|
4827
5107
|
if (template === "pi") {
|
|
4828
5108
|
importPiSession(sessionFile, dest);
|
|
4829
5109
|
} else if (template === "claude") {
|
|
4830
5110
|
const sessionId = convertSession({ sessionPath: sessionFile, projectDir: dest });
|
|
4831
|
-
const mindRuntimeDir =
|
|
5111
|
+
const mindRuntimeDir = resolve11(dest, ".mind");
|
|
4832
5112
|
mkdirSync6(mindRuntimeDir, { recursive: true });
|
|
4833
|
-
|
|
5113
|
+
writeFileSync8(resolve11(mindRuntimeDir, "session.json"), JSON.stringify({ sessionId }));
|
|
4834
5114
|
}
|
|
4835
5115
|
}
|
|
4836
5116
|
importOpenClawConnectors(name, dest);
|
|
@@ -4840,14 +5120,14 @@ ${user.trimEnd()}
|
|
|
4840
5120
|
);
|
|
4841
5121
|
return c.json({ ok: true, name, port, message: `Imported mind: ${name} (port ${port})` });
|
|
4842
5122
|
} catch (err) {
|
|
4843
|
-
if (existsSync7(dest))
|
|
5123
|
+
if (existsSync7(dest)) rmSync3(dest, { recursive: true, force: true });
|
|
4844
5124
|
try {
|
|
4845
5125
|
await removeMind(name);
|
|
4846
5126
|
} catch {
|
|
4847
5127
|
}
|
|
4848
5128
|
return c.json({ error: err instanceof Error ? err.message : "Failed to import mind" }, 500);
|
|
4849
5129
|
} finally {
|
|
4850
|
-
|
|
5130
|
+
rmSync3(composedDir, { recursive: true, force: true });
|
|
4851
5131
|
}
|
|
4852
5132
|
}).get("/", async (c) => {
|
|
4853
5133
|
const entries = await readRegistry();
|
|
@@ -4872,7 +5152,7 @@ ${user.trimEnd()}
|
|
|
4872
5152
|
const minds = await Promise.all(
|
|
4873
5153
|
entries.map(async (entry) => {
|
|
4874
5154
|
const mindStatus = await getMindStatus(entry.name, entry.port, entry.running);
|
|
4875
|
-
const hasPages = existsSync7(
|
|
5155
|
+
const hasPages = existsSync7(resolve11(mindDir(entry.name), "home", "pages"));
|
|
4876
5156
|
const lastActiveAt = lastActiveMap.get(entry.name) ?? null;
|
|
4877
5157
|
if (!privileged) return toPublicMind(entry, mindStatus, { hasPages, lastActiveAt });
|
|
4878
5158
|
return {
|
|
@@ -4892,7 +5172,7 @@ ${user.trimEnd()}
|
|
|
4892
5172
|
const dir = entry.dir ?? mindDir(entry.parent ?? name);
|
|
4893
5173
|
if (!existsSync7(dir)) return c.json({ error: "Mind directory missing" }, 404);
|
|
4894
5174
|
const mindStatus = await getMindStatus(name, entry.port);
|
|
4895
|
-
const hasPages = existsSync7(
|
|
5175
|
+
const hasPages = existsSync7(resolve11(mindDir(name), "home", "pages"));
|
|
4896
5176
|
if (!isPrivileged(c)) {
|
|
4897
5177
|
return c.json(toPublicMind(entry, mindStatus, { hasPages }));
|
|
4898
5178
|
}
|
|
@@ -4908,23 +5188,32 @@ ${user.trimEnd()}
|
|
|
4908
5188
|
return { name: s.name, port: s.port, status: variantStatus };
|
|
4909
5189
|
})
|
|
4910
5190
|
);
|
|
5191
|
+
const notice = await latestNotice(name);
|
|
4911
5192
|
return c.json({
|
|
4912
5193
|
...entry,
|
|
4913
5194
|
...mindStatus,
|
|
4914
5195
|
variants: variantStatuses,
|
|
4915
5196
|
hasPages,
|
|
4916
|
-
templateStale: isTemplateStale(entry)
|
|
5197
|
+
templateStale: isTemplateStale(entry),
|
|
5198
|
+
...notice && {
|
|
5199
|
+
lastNotice: {
|
|
5200
|
+
kind: notice.kind,
|
|
5201
|
+
reason: notice.reason,
|
|
5202
|
+
detail: notice.detail,
|
|
5203
|
+
created_at: notice.created_at
|
|
5204
|
+
}
|
|
5205
|
+
}
|
|
4917
5206
|
});
|
|
4918
5207
|
}).get("/:name/context", requireSelf(), async (c) => proxyToMind(c, "context")).get("/:name/context/messages", requireSelf(), async (c) => proxyToMind(c, "context/messages")).post("/:name/start", requireSelf(), async (c) => {
|
|
4919
5208
|
const name = c.req.param("name");
|
|
4920
5209
|
const entry = await findMind(name);
|
|
4921
5210
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
4922
5211
|
const targetPort = entry.port;
|
|
5212
|
+
const projectDir = entry.dir ?? mindDir(name);
|
|
4923
5213
|
if (entry.parent) {
|
|
4924
5214
|
if (!entry.dir) return c.json({ error: `Variant ${name} has no directory` }, 404);
|
|
4925
|
-
} else {
|
|
4926
|
-
|
|
4927
|
-
if (!existsSync7(dir)) return c.json({ error: "Mind directory missing" }, 404);
|
|
5215
|
+
} else if (!existsSync7(projectDir)) {
|
|
5216
|
+
return c.json({ error: "Mind directory missing" }, 404);
|
|
4928
5217
|
}
|
|
4929
5218
|
if (getMindManager().isRunning(name)) {
|
|
4930
5219
|
return c.json({ error: "Mind already running" }, 409);
|
|
@@ -4941,11 +5230,11 @@ ${user.trimEnd()}
|
|
|
4941
5230
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
4942
5231
|
const baseName = entry.parent ?? name;
|
|
4943
5232
|
const targetPort = entry.port;
|
|
5233
|
+
const projectDir = entry.dir ?? mindDir(name);
|
|
4944
5234
|
if (entry.parent) {
|
|
4945
5235
|
if (!entry.dir) return c.json({ error: `Variant ${name} has no directory` }, 404);
|
|
4946
|
-
} else {
|
|
4947
|
-
|
|
4948
|
-
if (!existsSync7(dir)) return c.json({ error: "Mind directory missing" }, 404);
|
|
5236
|
+
} else if (!existsSync7(projectDir)) {
|
|
5237
|
+
return c.json({ error: "Mind directory missing" }, 404);
|
|
4949
5238
|
}
|
|
4950
5239
|
let context;
|
|
4951
5240
|
const contentType = c.req.header("content-type");
|
|
@@ -4960,7 +5249,7 @@ ${user.trimEnd()}
|
|
|
4960
5249
|
const manager = getMindManager();
|
|
4961
5250
|
try {
|
|
4962
5251
|
if (context?.type === "reload") {
|
|
4963
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
5252
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-DSPXFHGX.js");
|
|
4964
5253
|
const sleepState = getSleepManagerIfReady2()?.getState(name);
|
|
4965
5254
|
if (sleepState?.sleeping) {
|
|
4966
5255
|
logger_default.info(`skipping reload for ${name} during sleep \u2014 will apply on next wake`);
|
|
@@ -5007,7 +5296,8 @@ ${user.trimEnd()}
|
|
|
5007
5296
|
logger_default.error(`failed to auto-commit main worktree for ${baseName}`, logger_default.errorData(e));
|
|
5008
5297
|
}
|
|
5009
5298
|
}
|
|
5010
|
-
await
|
|
5299
|
+
const memoryDelta = await mergeVariantExcludingMemory(projectRoot, variantEntry.branch);
|
|
5300
|
+
if (memoryDelta && context) context.memoryDelta = memoryDelta;
|
|
5011
5301
|
await cleanupVariant(mergeVariantName, projectRoot, variantEntry.dir);
|
|
5012
5302
|
try {
|
|
5013
5303
|
await npmInstallAsMind(projectRoot, baseName);
|
|
@@ -5021,18 +5311,12 @@ ${user.trimEnd()}
|
|
|
5021
5311
|
}
|
|
5022
5312
|
if (context?.type === "sprouted" && !entry.parent) {
|
|
5023
5313
|
try {
|
|
5024
|
-
|
|
5025
|
-
for (const conv of mindConvs) {
|
|
5026
|
-
await recordInbound(baseName, "system", "system", "[seed has sprouted]");
|
|
5027
|
-
await addMessage(conv.id, "assistant", "system", [
|
|
5028
|
-
{ type: "text", text: "[seed has sprouted]" }
|
|
5029
|
-
]);
|
|
5030
|
-
}
|
|
5314
|
+
await deliverSproutedNotice(baseName);
|
|
5031
5315
|
} catch (err) {
|
|
5032
5316
|
logger_default.error(`failed to inject sprouted message for ${baseName}`, logger_default.errorData(err));
|
|
5033
5317
|
}
|
|
5034
5318
|
}
|
|
5035
|
-
const repoDir =
|
|
5319
|
+
const repoDir = projectDir;
|
|
5036
5320
|
try {
|
|
5037
5321
|
await startMindFull(name);
|
|
5038
5322
|
} catch (startErr) {
|
|
@@ -5087,7 +5371,7 @@ ${user.trimEnd()}
|
|
|
5087
5371
|
const name = c.req.param("name");
|
|
5088
5372
|
const entry = await findMind(name);
|
|
5089
5373
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
5090
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
5374
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-DSPXFHGX.js");
|
|
5091
5375
|
const sm = getSleepManagerIfReady2();
|
|
5092
5376
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
5093
5377
|
return c.json(sm.getState(name));
|
|
@@ -5095,7 +5379,7 @@ ${user.trimEnd()}
|
|
|
5095
5379
|
const name = c.req.param("name");
|
|
5096
5380
|
const entry = await findMind(name);
|
|
5097
5381
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
5098
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
5382
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-DSPXFHGX.js");
|
|
5099
5383
|
const sm = getSleepManagerIfReady2();
|
|
5100
5384
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
5101
5385
|
if (sm.isSleeping(name)) return c.json({ error: "Mind is already sleeping" }, 409);
|
|
@@ -5115,7 +5399,7 @@ ${user.trimEnd()}
|
|
|
5115
5399
|
const name = c.req.param("name");
|
|
5116
5400
|
const entry = await findMind(name);
|
|
5117
5401
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
5118
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
5402
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-DSPXFHGX.js");
|
|
5119
5403
|
const sm = getSleepManagerIfReady2();
|
|
5120
5404
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
5121
5405
|
const sleepState = sm.getState(name);
|
|
@@ -5130,7 +5414,7 @@ ${user.trimEnd()}
|
|
|
5130
5414
|
const name = c.req.param("name");
|
|
5131
5415
|
const entry = await findMind(name);
|
|
5132
5416
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
5133
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
5417
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-DSPXFHGX.js");
|
|
5134
5418
|
const sm = getSleepManagerIfReady2();
|
|
5135
5419
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
5136
5420
|
const flushed = await sm.flushQueuedMessages(name);
|
|
@@ -5146,7 +5430,7 @@ ${user.trimEnd()}
|
|
|
5146
5430
|
if (body.displayName !== void 0) profile.displayName = body.displayName;
|
|
5147
5431
|
if (body.description !== void 0) profile.description = body.description;
|
|
5148
5432
|
if (body.avatar !== void 0) {
|
|
5149
|
-
const homeDir =
|
|
5433
|
+
const homeDir = resolve11(dir, "home");
|
|
5150
5434
|
const avatarPath = safeResolveWithinBase(homeDir, body.avatar);
|
|
5151
5435
|
if (!avatarPath) {
|
|
5152
5436
|
return c.json({ error: "Avatar path must be inside the mind's home directory" }, 400);
|
|
@@ -5158,7 +5442,7 @@ ${user.trimEnd()}
|
|
|
5158
5442
|
}
|
|
5159
5443
|
config2.profile = profile;
|
|
5160
5444
|
writeVoluteConfig(dir, config2);
|
|
5161
|
-
const { syncMindProfile: syncMindProfile2 } = await import("./auth-
|
|
5445
|
+
const { syncMindProfile: syncMindProfile2 } = await import("./auth-ABYLOEZ7.js");
|
|
5162
5446
|
await syncMindProfile2(name, profile);
|
|
5163
5447
|
broadcast({ type: "profile_updated", mind: name, summary: `${name} profile updated` });
|
|
5164
5448
|
return c.json({ ok: true });
|
|
@@ -5197,25 +5481,18 @@ ${user.trimEnd()}
|
|
|
5197
5481
|
return c.json({ output: "" });
|
|
5198
5482
|
}
|
|
5199
5483
|
const dir = entry.dir ?? mindDir(name);
|
|
5200
|
-
const
|
|
5201
|
-
const
|
|
5202
|
-
const soulCustom = existsSync7(soulPath) && !readFileSync8(soulPath, "utf-8").includes(ORIENTATION_MARKER);
|
|
5203
|
-
const memoryWritten = existsSync7(memoryPath) && readFileSync8(memoryPath, "utf-8").trim().length > 0;
|
|
5204
|
-
const config2 = readVoluteConfig(dir);
|
|
5205
|
-
const displayNameSet = !!config2?.profile?.displayName;
|
|
5206
|
-
const avatarSet = !!config2?.profile?.avatar;
|
|
5207
|
-
const { isImagegenEnabled } = await import("./setup-KFYVWTXG.js");
|
|
5208
|
-
const imagegenEnabled = isImagegenEnabled();
|
|
5484
|
+
const { evaluateSeedChecklist } = await import("./seed-readiness-3LRYASA4.js");
|
|
5485
|
+
const checklist = evaluateSeedChecklist(dir);
|
|
5209
5486
|
const done = [];
|
|
5210
5487
|
const remaining = [];
|
|
5211
|
-
if (
|
|
5488
|
+
if (checklist.soulWritten) done.push("SOUL.md written");
|
|
5212
5489
|
else remaining.push("Write SOUL.md");
|
|
5213
|
-
if (memoryWritten) done.push("MEMORY.md written");
|
|
5490
|
+
if (checklist.memoryWritten) done.push("MEMORY.md written");
|
|
5214
5491
|
else remaining.push("Write MEMORY.md");
|
|
5215
|
-
if (displayNameSet) done.push("Display name set");
|
|
5492
|
+
if (checklist.displayNameSet) done.push("Display name set");
|
|
5216
5493
|
else remaining.push("Set display name");
|
|
5217
|
-
if (imagegenEnabled) {
|
|
5218
|
-
if (avatarSet) done.push("Avatar set");
|
|
5494
|
+
if (checklist.imagegenEnabled) {
|
|
5495
|
+
if (checklist.avatarSet) done.push("Avatar set");
|
|
5219
5496
|
else remaining.push("Generate and set avatar");
|
|
5220
5497
|
}
|
|
5221
5498
|
const creatorStatus = minutesSinceCreator === Infinity ? "No creator messages yet" : `Last creator message: ${Math.round(minutesSinceCreator)} minutes ago`;
|
|
@@ -5239,23 +5516,51 @@ ${user.trimEnd()}
|
|
|
5239
5516
|
return c.json({ error: `Mind is not a seed (stage: ${entry.stage})` }, 409);
|
|
5240
5517
|
}
|
|
5241
5518
|
await setMindStage(name, "sprouted");
|
|
5519
|
+
await joinSystemChannelForMind(name).catch(
|
|
5520
|
+
(err) => logger_default.warn(`failed to join #system on sprout for ${name}`, logger_default.errorData(err))
|
|
5521
|
+
);
|
|
5522
|
+
try {
|
|
5523
|
+
const sproutedDir = entry.dir ?? mindDir(name);
|
|
5524
|
+
if (setupDefaultDreaming(sproutedDir).schedulesChanged) {
|
|
5525
|
+
const { getScheduler: getScheduler2 } = await import("./scheduler-K7Y7Z7FQ.js");
|
|
5526
|
+
getScheduler2().loadSchedules(name, sproutedDir);
|
|
5527
|
+
}
|
|
5528
|
+
} catch (err) {
|
|
5529
|
+
logger_default.warn(`failed to set up default dreaming for ${name}`, logger_default.errorData(err));
|
|
5530
|
+
}
|
|
5242
5531
|
try {
|
|
5243
5532
|
const spiritEntry = await findMind("volute");
|
|
5244
5533
|
if (spiritEntry) {
|
|
5245
|
-
const { spiritDir } = await import("./spirit-
|
|
5534
|
+
const { firstWeekSchedules, spiritDir } = await import("./spirit-SSJMYC6A.js");
|
|
5246
5535
|
const sDir = spiritEntry.dir ?? spiritDir();
|
|
5247
5536
|
const spiritConfig = readVoluteConfig(sDir);
|
|
5248
|
-
if (spiritConfig
|
|
5249
|
-
const
|
|
5250
|
-
|
|
5251
|
-
|
|
5537
|
+
if (spiritConfig) {
|
|
5538
|
+
const schedules = (spiritConfig.schedules ?? []).filter(
|
|
5539
|
+
(s) => s.id !== `nurture-${name}`
|
|
5540
|
+
);
|
|
5541
|
+
const existing = new Set(schedules.map((s) => s.id));
|
|
5542
|
+
schedules.push(
|
|
5543
|
+
...firstWeekSchedules(name, /* @__PURE__ */ new Date()).filter((s) => !existing.has(s.id))
|
|
5544
|
+
);
|
|
5545
|
+
spiritConfig.schedules = schedules;
|
|
5252
5546
|
writeVoluteConfig(sDir, spiritConfig);
|
|
5253
|
-
|
|
5254
|
-
|
|
5547
|
+
try {
|
|
5548
|
+
const { getScheduler: getScheduler2 } = await import("./scheduler-K7Y7Z7FQ.js");
|
|
5549
|
+
getScheduler2().loadSchedules("volute", sDir);
|
|
5550
|
+
} catch (err) {
|
|
5551
|
+
logger_default.warn(
|
|
5552
|
+
`spirit schedules for sprout of ${name} written to disk but not reloaded into the running scheduler (effective on next spirit restart)`,
|
|
5553
|
+
logger_default.errorData(err)
|
|
5554
|
+
);
|
|
5555
|
+
}
|
|
5556
|
+
} else {
|
|
5557
|
+
logger_default.warn(
|
|
5558
|
+
`spirit config at ${sDir} missing or unparseable \u2014 first-week arc for ${name} not scheduled, nurture-${name} not removed`
|
|
5559
|
+
);
|
|
5255
5560
|
}
|
|
5256
5561
|
}
|
|
5257
5562
|
} catch (err) {
|
|
5258
|
-
logger_default.warn(`failed to
|
|
5563
|
+
logger_default.warn(`failed to update spirit schedules for sprout of ${name}`, logger_default.errorData(err));
|
|
5259
5564
|
}
|
|
5260
5565
|
return c.json({ ok: true });
|
|
5261
5566
|
}).delete("/:name", requireAdmin, async (c) => {
|
|
@@ -5279,10 +5584,10 @@ ${user.trimEnd()}
|
|
|
5279
5584
|
invalidateMindUserCache(name);
|
|
5280
5585
|
const state = stateDir(name);
|
|
5281
5586
|
if (existsSync7(state)) {
|
|
5282
|
-
|
|
5587
|
+
rmSync3(state, { recursive: true, force: true });
|
|
5283
5588
|
}
|
|
5284
5589
|
if (force && existsSync7(dir)) {
|
|
5285
|
-
|
|
5590
|
+
rmSync3(dir, { recursive: true, force: true });
|
|
5286
5591
|
deleteMindUser(name);
|
|
5287
5592
|
}
|
|
5288
5593
|
fireWebhook({
|
|
@@ -5309,16 +5614,16 @@ ${user.trimEnd()}
|
|
|
5309
5614
|
}
|
|
5310
5615
|
const UPGRADE_BRANCH = "upgrade";
|
|
5311
5616
|
const upgradeVariantName = `${mindName}-upgrade`;
|
|
5312
|
-
const worktreeDir =
|
|
5617
|
+
const worktreeDir = resolve11(dir, ".variants", UPGRADE_BRANCH);
|
|
5313
5618
|
if (body.abort) {
|
|
5314
5619
|
if (!existsSync7(worktreeDir)) {
|
|
5315
5620
|
return c.json({ error: "No upgrade in progress" }, 400);
|
|
5316
5621
|
}
|
|
5317
5622
|
try {
|
|
5318
5623
|
try {
|
|
5319
|
-
const gitDirContent =
|
|
5624
|
+
const gitDirContent = readFileSync9(resolve11(worktreeDir, ".git"), "utf-8").trim();
|
|
5320
5625
|
const gitDir = gitDirContent.replace("gitdir: ", "");
|
|
5321
|
-
if (existsSync7(
|
|
5626
|
+
if (existsSync7(resolve11(gitDir, "MERGE_HEAD"))) {
|
|
5322
5627
|
await gitExec(["merge", "--abort"], { cwd: worktreeDir });
|
|
5323
5628
|
}
|
|
5324
5629
|
} catch {
|
|
@@ -5402,7 +5707,7 @@ ${user.trimEnd()}
|
|
|
5402
5707
|
}
|
|
5403
5708
|
if (body.diff) {
|
|
5404
5709
|
try {
|
|
5405
|
-
if (!existsSync7(
|
|
5710
|
+
if (!existsSync7(resolve11(dir, ".git"))) {
|
|
5406
5711
|
return c.json({ error: "Mind has no git history \u2014 nothing to diff against" }, 400);
|
|
5407
5712
|
}
|
|
5408
5713
|
await updateTemplateBranch(dir, template, mindName);
|
|
@@ -5426,16 +5731,16 @@ ${user.trimEnd()}
|
|
|
5426
5731
|
409
|
|
5427
5732
|
);
|
|
5428
5733
|
}
|
|
5429
|
-
if (!existsSync7(
|
|
5734
|
+
if (!existsSync7(resolve11(dir, ".git"))) {
|
|
5430
5735
|
try {
|
|
5431
|
-
const env = isIsolationEnabled() ? { ...process.env, HOME:
|
|
5736
|
+
const env = isIsolationEnabled() ? { ...process.env, HOME: resolve11(dir, "home") } : void 0;
|
|
5432
5737
|
await gitExec(["init"], { cwd: dir, mindName, env });
|
|
5433
5738
|
await configureGitIdentity(mindName, { cwd: dir, mindName, env });
|
|
5434
5739
|
await gitExec(["add", "-A"], { cwd: dir, mindName, env });
|
|
5435
5740
|
await gitExec(["commit", "-m", "initial commit"], { cwd: dir, mindName, env });
|
|
5436
5741
|
await chownMindDir(dir, mindName);
|
|
5437
5742
|
} catch (err) {
|
|
5438
|
-
|
|
5743
|
+
rmSync3(resolve11(dir, ".git"), { recursive: true, force: true });
|
|
5439
5744
|
return c.json(
|
|
5440
5745
|
{
|
|
5441
5746
|
error: `Git initialization failed: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -5450,7 +5755,7 @@ ${user.trimEnd()}
|
|
|
5450
5755
|
} catch {
|
|
5451
5756
|
}
|
|
5452
5757
|
await updateTemplateBranch(dir, template, mindName);
|
|
5453
|
-
const parentDir =
|
|
5758
|
+
const parentDir = resolve11(dir, ".variants");
|
|
5454
5759
|
if (!existsSync7(parentDir)) {
|
|
5455
5760
|
mkdirSync6(parentDir, { recursive: true });
|
|
5456
5761
|
}
|
|
@@ -5584,19 +5889,19 @@ ${user.trimEnd()}
|
|
|
5584
5889
|
if (!existsSync7(dir)) return c.json({ error: "Mind directory missing" }, 404);
|
|
5585
5890
|
let config2 = readVoluteConfig(dir);
|
|
5586
5891
|
if (!config2 && entry.template === "pi") {
|
|
5587
|
-
const piConfigPath =
|
|
5892
|
+
const piConfigPath = resolve11(dir, "home/.config/config.json");
|
|
5588
5893
|
if (existsSync7(piConfigPath)) {
|
|
5589
5894
|
try {
|
|
5590
|
-
config2 = JSON.parse(
|
|
5895
|
+
config2 = JSON.parse(readFileSync9(piConfigPath, "utf-8"));
|
|
5591
5896
|
} catch {
|
|
5592
5897
|
}
|
|
5593
5898
|
}
|
|
5594
5899
|
}
|
|
5595
5900
|
let templateConfig = {};
|
|
5596
|
-
const configJsonPath =
|
|
5901
|
+
const configJsonPath = resolve11(dir, "home/.config/config.json");
|
|
5597
5902
|
if (existsSync7(configJsonPath)) {
|
|
5598
5903
|
try {
|
|
5599
|
-
templateConfig = JSON.parse(
|
|
5904
|
+
templateConfig = JSON.parse(readFileSync9(configJsonPath, "utf-8"));
|
|
5600
5905
|
} catch {
|
|
5601
5906
|
}
|
|
5602
5907
|
}
|
|
@@ -5610,22 +5915,7 @@ ${user.trimEnd()}
|
|
|
5610
5915
|
},
|
|
5611
5916
|
config: {
|
|
5612
5917
|
model: config2?.model ?? templateConfig.model ?? null,
|
|
5613
|
-
thinkingLevel: (
|
|
5614
|
-
if (config2?.thinkingLevel) return config2.thinkingLevel;
|
|
5615
|
-
const tc = templateConfig;
|
|
5616
|
-
if (tc.thinkingLevel) return tc.thinkingLevel;
|
|
5617
|
-
if (tc.reasoningEffort) return tc.reasoningEffort;
|
|
5618
|
-
const mtt = tc.maxThinkingTokens;
|
|
5619
|
-
if (mtt) {
|
|
5620
|
-
if (mtt <= 1024) return "minimal";
|
|
5621
|
-
if (mtt <= 4096) return "low";
|
|
5622
|
-
if (mtt <= 1e4) return "medium";
|
|
5623
|
-
if (mtt <= 32e3) return "high";
|
|
5624
|
-
return "xhigh";
|
|
5625
|
-
}
|
|
5626
|
-
return null;
|
|
5627
|
-
})(),
|
|
5628
|
-
maxThinkingTokens: config2?.maxThinkingTokens ?? templateConfig.maxThinkingTokens ?? null,
|
|
5918
|
+
thinkingLevel: config2?.thinkingLevel ?? deriveThinkingLevel(templateConfig),
|
|
5629
5919
|
tokenBudget: config2?.tokenBudget ?? null,
|
|
5630
5920
|
tokenBudgetPeriodMinutes: config2?.tokenBudgetPeriodMinutes ?? null,
|
|
5631
5921
|
compaction: templateConfig.compaction ?? null,
|
|
@@ -5640,7 +5930,6 @@ ${user.trimEnd()}
|
|
|
5640
5930
|
z6.object({
|
|
5641
5931
|
model: z6.string().optional(),
|
|
5642
5932
|
thinkingLevel: z6.enum(["off", "minimal", "low", "medium", "high", "xhigh"]).optional(),
|
|
5643
|
-
maxThinkingTokens: z6.number().int().positive().nullable().optional(),
|
|
5644
5933
|
tokenBudget: z6.number().int().positive().nullable().optional(),
|
|
5645
5934
|
tokenBudgetPeriodMinutes: z6.number().int().positive().nullable().optional(),
|
|
5646
5935
|
compaction: z6.object({ maxContextTokens: z6.number().int().positive().nullable().optional() }).nullable().optional(),
|
|
@@ -5673,76 +5962,29 @@ ${user.trimEnd()}
|
|
|
5673
5962
|
existing.tokenBudgetPeriodMinutes = body.tokenBudgetPeriodMinutes;
|
|
5674
5963
|
}
|
|
5675
5964
|
}
|
|
5676
|
-
if (body.maxThinkingTokens !== void 0) {
|
|
5677
|
-
if (body.maxThinkingTokens === null) {
|
|
5678
|
-
delete existing.maxThinkingTokens;
|
|
5679
|
-
} else {
|
|
5680
|
-
existing.maxThinkingTokens = body.maxThinkingTokens;
|
|
5681
|
-
}
|
|
5682
|
-
}
|
|
5683
5965
|
if (body.unescapeNewlines !== void 0) {
|
|
5684
5966
|
existing.unescapeNewlines = body.unescapeNewlines;
|
|
5685
5967
|
}
|
|
5686
5968
|
writeVoluteConfig(dir, existing);
|
|
5687
5969
|
if (body.unescapeNewlines !== void 0) {
|
|
5688
|
-
const { clearEchoTextCache } = await import("./echo-text-
|
|
5970
|
+
const { clearEchoTextCache } = await import("./echo-text-B6NZ5FGE.js");
|
|
5689
5971
|
clearEchoTextCache(name);
|
|
5690
5972
|
}
|
|
5691
|
-
const needsConfigJson = body.model !== void 0 || body.thinkingLevel !== void 0 || body.
|
|
5973
|
+
const needsConfigJson = body.model !== void 0 || body.thinkingLevel !== void 0 || body.compaction !== void 0;
|
|
5692
5974
|
if (needsConfigJson) {
|
|
5693
|
-
const configJsonPath =
|
|
5975
|
+
const configJsonPath = resolve11(dir, "home/.config/config.json");
|
|
5694
5976
|
let templateConfig = {};
|
|
5695
5977
|
if (existsSync7(configJsonPath)) {
|
|
5696
5978
|
try {
|
|
5697
|
-
templateConfig = JSON.parse(
|
|
5979
|
+
templateConfig = JSON.parse(readFileSync9(configJsonPath, "utf-8"));
|
|
5698
5980
|
} catch {
|
|
5699
5981
|
}
|
|
5700
5982
|
}
|
|
5701
5983
|
if (body.model !== void 0) {
|
|
5702
5984
|
templateConfig.model = body.model;
|
|
5703
5985
|
}
|
|
5704
|
-
const tmpl = entry.template ?? "claude";
|
|
5705
5986
|
if (body.thinkingLevel !== void 0) {
|
|
5706
|
-
|
|
5707
|
-
const claudeThinkingTokens = {
|
|
5708
|
-
off: null,
|
|
5709
|
-
minimal: 1024,
|
|
5710
|
-
low: 4096,
|
|
5711
|
-
medium: 1e4,
|
|
5712
|
-
high: 32e3,
|
|
5713
|
-
xhigh: 128e3
|
|
5714
|
-
};
|
|
5715
|
-
const tokens = claudeThinkingTokens[body.thinkingLevel] ?? null;
|
|
5716
|
-
if (tokens === null) {
|
|
5717
|
-
delete templateConfig.maxThinkingTokens;
|
|
5718
|
-
} else {
|
|
5719
|
-
templateConfig.maxThinkingTokens = tokens;
|
|
5720
|
-
}
|
|
5721
|
-
} else if (tmpl === "codex") {
|
|
5722
|
-
const codexMap = {
|
|
5723
|
-
off: null,
|
|
5724
|
-
minimal: "minimal",
|
|
5725
|
-
low: "low",
|
|
5726
|
-
medium: "medium",
|
|
5727
|
-
high: "high",
|
|
5728
|
-
xhigh: "xhigh"
|
|
5729
|
-
};
|
|
5730
|
-
const effort = codexMap[body.thinkingLevel] ?? null;
|
|
5731
|
-
if (effort === null) {
|
|
5732
|
-
delete templateConfig.reasoningEffort;
|
|
5733
|
-
} else {
|
|
5734
|
-
templateConfig.reasoningEffort = effort;
|
|
5735
|
-
}
|
|
5736
|
-
} else {
|
|
5737
|
-
templateConfig.thinkingLevel = body.thinkingLevel;
|
|
5738
|
-
}
|
|
5739
|
-
}
|
|
5740
|
-
if (body.maxThinkingTokens !== void 0) {
|
|
5741
|
-
if (body.maxThinkingTokens === null) {
|
|
5742
|
-
delete templateConfig.maxThinkingTokens;
|
|
5743
|
-
} else {
|
|
5744
|
-
templateConfig.maxThinkingTokens = body.maxThinkingTokens;
|
|
5745
|
-
}
|
|
5987
|
+
applyThinkingLevel(templateConfig, entry.template ?? "claude", body.thinkingLevel);
|
|
5746
5988
|
}
|
|
5747
5989
|
if (body.compaction !== void 0) {
|
|
5748
5990
|
if (body.compaction === null) {
|
|
@@ -5757,12 +5999,12 @@ ${user.trimEnd()}
|
|
|
5757
5999
|
templateConfig.compaction = comp;
|
|
5758
6000
|
}
|
|
5759
6001
|
}
|
|
5760
|
-
|
|
6002
|
+
writeFileSync8(configJsonPath, `${JSON.stringify(templateConfig, null, 2)}
|
|
5761
6003
|
`);
|
|
5762
6004
|
}
|
|
5763
6005
|
if (entry.mindType === "spirit" && body.model !== void 0) {
|
|
5764
6006
|
try {
|
|
5765
|
-
const { readGlobalConfig: readGlobalConfig2, writeGlobalConfig: writeGlobalConfig2 } = await import("./setup-
|
|
6007
|
+
const { readGlobalConfig: readGlobalConfig2, writeGlobalConfig: writeGlobalConfig2 } = await import("./setup-TCN7VH5U.js");
|
|
5766
6008
|
const globalConfig = readGlobalConfig2();
|
|
5767
6009
|
globalConfig.spiritModel = body.model;
|
|
5768
6010
|
writeGlobalConfig2(globalConfig);
|
|
@@ -5805,7 +6047,7 @@ ${user.trimEnd()}
|
|
|
5805
6047
|
if (!body.systemPrompt || !body.message) {
|
|
5806
6048
|
return c.json({ error: "systemPrompt and message required" }, 400);
|
|
5807
6049
|
}
|
|
5808
|
-
const { aiComplete: aiCompleteFn, isAiConfigured } = await import("./ai-service-
|
|
6050
|
+
const { aiComplete: aiCompleteFn, isAiConfigured } = await import("./ai-service-A7FS7EC3.js");
|
|
5809
6051
|
if (!isAiConfigured()) {
|
|
5810
6052
|
return c.json({ error: "AI service not configured" }, 503);
|
|
5811
6053
|
}
|
|
@@ -5926,6 +6168,38 @@ ${user.trimEnd()}
|
|
|
5926
6168
|
const db = await getDb();
|
|
5927
6169
|
const rows = await db.selectDistinct({ channel: mindHistory.channel }).from(mindHistory).where(eq5(mindHistory.mind, name));
|
|
5928
6170
|
return c.json(rows.map((r) => r.channel));
|
|
6171
|
+
}).get("/:name/history/contacts", requireSelf(), async (c) => {
|
|
6172
|
+
const name = c.req.param("name");
|
|
6173
|
+
const rawHours = parseInt(c.req.query("hours") ?? "48", 10);
|
|
6174
|
+
const hours = Math.min(Math.max(Number.isNaN(rawHours) ? 48 : rawHours, 1), 168);
|
|
6175
|
+
const cutoff = new Date(Date.now() - hours * 36e5).toISOString().replace("T", " ").slice(0, 19);
|
|
6176
|
+
const db = await getDb();
|
|
6177
|
+
const contacts = await db.select({
|
|
6178
|
+
channel: mindHistory.channel,
|
|
6179
|
+
last_at: sql3`MAX(${mindHistory.created_at})`,
|
|
6180
|
+
last_inbound_at: sql3`MAX(CASE WHEN ${mindHistory.type} = 'inbound' THEN ${mindHistory.created_at} END)`,
|
|
6181
|
+
last_outbound_at: sql3`MAX(CASE WHEN ${mindHistory.type} = 'outbound' THEN ${mindHistory.created_at} END)`,
|
|
6182
|
+
message_count: sql3`COUNT(*)`,
|
|
6183
|
+
// The counterparty: most recent inbound sender on this channel. For a
|
|
6184
|
+
// DM the channel name already carries it (e.g. @atlas), but this also
|
|
6185
|
+
// covers shared channels (#system) and rows where the channel is opaque.
|
|
6186
|
+
last_sender: sql3`(
|
|
6187
|
+
SELECT sender FROM mind_history h2
|
|
6188
|
+
WHERE h2.mind = ${name}
|
|
6189
|
+
AND h2.channel = ${mindHistory.channel}
|
|
6190
|
+
AND h2.type = 'inbound'
|
|
6191
|
+
AND h2.sender IS NOT NULL
|
|
6192
|
+
ORDER BY h2.created_at DESC, h2.id DESC LIMIT 1
|
|
6193
|
+
)`
|
|
6194
|
+
}).from(mindHistory).where(
|
|
6195
|
+
and5(
|
|
6196
|
+
eq5(mindHistory.mind, name),
|
|
6197
|
+
sql3`${mindHistory.type} IN ('inbound','outbound')`,
|
|
6198
|
+
sql3`${mindHistory.channel} IS NOT NULL`,
|
|
6199
|
+
sql3`${mindHistory.created_at} >= ${cutoff}`
|
|
6200
|
+
)
|
|
6201
|
+
).groupBy(mindHistory.channel).orderBy(sql3`MAX(${mindHistory.created_at}) DESC`);
|
|
6202
|
+
return c.json({ hours, contacts });
|
|
5929
6203
|
}).get("/:name/history/export", requireSelf(), async (c) => {
|
|
5930
6204
|
const name = c.req.param("name");
|
|
5931
6205
|
if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
|
|
@@ -6154,6 +6428,13 @@ var slog2 = logger_default.child("schedules");
|
|
|
6154
6428
|
function readSchedules(dir) {
|
|
6155
6429
|
return readVoluteConfig(dir)?.schedules ?? [];
|
|
6156
6430
|
}
|
|
6431
|
+
function validateMessages(messages) {
|
|
6432
|
+
if (messages === void 0) return null;
|
|
6433
|
+
if (!Array.isArray(messages) || messages.some((m) => typeof m !== "string" || !m.trim())) {
|
|
6434
|
+
return "messages must be an array of non-empty strings";
|
|
6435
|
+
}
|
|
6436
|
+
return null;
|
|
6437
|
+
}
|
|
6157
6438
|
var UPCOMING_HORIZON_MS = 24 * 60 * 60 * 1e3;
|
|
6158
6439
|
function computeClockEvents(schedules, sleepState, sleepConfig, now) {
|
|
6159
6440
|
const upcoming = [];
|
|
@@ -6241,7 +6522,8 @@ var app15 = new Hono18().get("/:name/clock/status", requireSelf(), async (c) =>
|
|
|
6241
6522
|
const schedules = readSchedules(dir);
|
|
6242
6523
|
const now = /* @__PURE__ */ new Date();
|
|
6243
6524
|
const { upcoming, previous } = computeClockEvents(schedules, sleepState, sleepConfig, now);
|
|
6244
|
-
|
|
6525
|
+
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
6526
|
+
return c.json({ sleep: sleepState, sleepConfig, schedules, upcoming, previous, timezone });
|
|
6245
6527
|
}).get("/:name/sleep/config", requireSelf(), async (c) => {
|
|
6246
6528
|
const name = c.req.param("name");
|
|
6247
6529
|
const entry = await findMind(name);
|
|
@@ -6295,11 +6577,13 @@ var app15 = new Hono18().get("/:name/clock/status", requireSelf(), async (c) =>
|
|
|
6295
6577
|
if (body.cron && body.fireAt) {
|
|
6296
6578
|
return c.json({ error: "cron and fireAt are mutually exclusive" }, 400);
|
|
6297
6579
|
}
|
|
6298
|
-
|
|
6299
|
-
|
|
6580
|
+
const messagesErr = validateMessages(body.messages);
|
|
6581
|
+
if (messagesErr) return c.json({ error: messagesErr }, 400);
|
|
6582
|
+
if (!body.message && !body.messages?.length && !body.script) {
|
|
6583
|
+
return c.json({ error: "message, messages, or script is required" }, 400);
|
|
6300
6584
|
}
|
|
6301
|
-
if (body.message
|
|
6302
|
-
return c.json({ error: "message and script are mutually exclusive" }, 400);
|
|
6585
|
+
if ([body.message, body.messages?.length, body.script].filter(Boolean).length > 1) {
|
|
6586
|
+
return c.json({ error: "message, messages, and script are mutually exclusive" }, 400);
|
|
6303
6587
|
}
|
|
6304
6588
|
if (body.cron) {
|
|
6305
6589
|
try {
|
|
@@ -6329,6 +6613,7 @@ var app15 = new Hono18().get("/:name/clock/status", requireSelf(), async (c) =>
|
|
|
6329
6613
|
if (body.cron) schedule.cron = body.cron;
|
|
6330
6614
|
if (body.fireAt) schedule.fireAt = body.fireAt;
|
|
6331
6615
|
if (body.message) schedule.message = body.message;
|
|
6616
|
+
if (body.messages?.length) schedule.messages = body.messages;
|
|
6332
6617
|
if (body.script) schedule.script = body.script;
|
|
6333
6618
|
if (body.channel) schedule.channel = body.channel;
|
|
6334
6619
|
if (body.whileSleeping) schedule.whileSleeping = body.whileSleeping;
|
|
@@ -6345,8 +6630,10 @@ var app15 = new Hono18().get("/:name/clock/status", requireSelf(), async (c) =>
|
|
|
6345
6630
|
const idx = schedules.findIndex((s) => s.id === id);
|
|
6346
6631
|
if (idx === -1) return c.json({ error: "Schedule not found" }, 404);
|
|
6347
6632
|
const body = await c.req.json();
|
|
6348
|
-
|
|
6349
|
-
|
|
6633
|
+
const messagesErr = validateMessages(body.messages);
|
|
6634
|
+
if (messagesErr) return c.json({ error: messagesErr }, 400);
|
|
6635
|
+
if ([body.message, body.messages?.length, body.script].filter(Boolean).length > 1) {
|
|
6636
|
+
return c.json({ error: "message, messages, and script are mutually exclusive" }, 400);
|
|
6350
6637
|
}
|
|
6351
6638
|
if (body.cron !== void 0) {
|
|
6352
6639
|
try {
|
|
@@ -6367,10 +6654,21 @@ var app15 = new Hono18().get("/:name/clock/status", requireSelf(), async (c) =>
|
|
|
6367
6654
|
if (body.message !== void 0) {
|
|
6368
6655
|
schedules[idx].message = body.message;
|
|
6369
6656
|
delete schedules[idx].script;
|
|
6657
|
+
delete schedules[idx].messages;
|
|
6658
|
+
}
|
|
6659
|
+
if (body.messages !== void 0) {
|
|
6660
|
+
if (body.messages.length > 0) {
|
|
6661
|
+
schedules[idx].messages = body.messages;
|
|
6662
|
+
delete schedules[idx].message;
|
|
6663
|
+
delete schedules[idx].script;
|
|
6664
|
+
} else {
|
|
6665
|
+
delete schedules[idx].messages;
|
|
6666
|
+
}
|
|
6370
6667
|
}
|
|
6371
6668
|
if (body.script !== void 0) {
|
|
6372
6669
|
schedules[idx].script = body.script;
|
|
6373
6670
|
delete schedules[idx].message;
|
|
6671
|
+
delete schedules[idx].messages;
|
|
6374
6672
|
}
|
|
6375
6673
|
if (body.whileSleeping && !["skip", "queue", "trigger-wake"].includes(body.whileSleeping)) {
|
|
6376
6674
|
return c.json(
|
|
@@ -6384,6 +6682,11 @@ var app15 = new Hono18().get("/:name/clock/status", requireSelf(), async (c) =>
|
|
|
6384
6682
|
if (body.channel !== void 0) schedules[idx].channel = body.channel || void 0;
|
|
6385
6683
|
if (body.whileSleeping !== void 0)
|
|
6386
6684
|
schedules[idx].whileSleeping = body.whileSleeping || void 0;
|
|
6685
|
+
const touchedAction = body.message !== void 0 || body.messages !== void 0 || body.script !== void 0;
|
|
6686
|
+
const result = schedules[idx];
|
|
6687
|
+
if (touchedAction && !result.message && !result.messages?.length && !result.script) {
|
|
6688
|
+
return c.json({ error: "schedule must keep a message, messages, or script" }, 400);
|
|
6689
|
+
}
|
|
6387
6690
|
writeSchedules(name, dir, schedules);
|
|
6388
6691
|
return c.json({ ok: true });
|
|
6389
6692
|
}).delete("/:name/schedules/:id", requireSelf(), async (c) => {
|
|
@@ -6407,8 +6710,8 @@ var app15 = new Hono18().get("/:name/clock/status", requireSelf(), async (c) =>
|
|
|
6407
6710
|
const body = await c.req.text();
|
|
6408
6711
|
const message = `[webhook: ${event}] ${body}`;
|
|
6409
6712
|
try {
|
|
6410
|
-
const { sendSystemMessage } = await import("./system-chat-
|
|
6411
|
-
await
|
|
6713
|
+
const { sendSystemMessage: sendSystemMessage2 } = await import("./system-chat-M2XOBKEM.js");
|
|
6714
|
+
await sendSystemMessage2(name, message);
|
|
6412
6715
|
return c.json({ ok: true });
|
|
6413
6716
|
} catch (err) {
|
|
6414
6717
|
slog2.warn(`webhook delivery failed for ${name}`, logger_default.errorData(err));
|
|
@@ -6420,14 +6723,14 @@ var schedules_default = app15;
|
|
|
6420
6723
|
// packages/daemon/src/web/api/setup.ts
|
|
6421
6724
|
import { mkdirSync as mkdirSync7 } from "fs";
|
|
6422
6725
|
import { homedir as homedir2 } from "os";
|
|
6423
|
-
import { resolve as
|
|
6726
|
+
import { resolve as resolve12 } from "path";
|
|
6424
6727
|
import { Hono as Hono19 } from "hono";
|
|
6425
6728
|
import { setCookie as setCookie2 } from "hono/cookie";
|
|
6426
6729
|
var DEFAULT_API_URL2 = "https://volute.systems";
|
|
6427
6730
|
var setup = new Hono19();
|
|
6428
6731
|
function writeSetupConfig(systemName, description) {
|
|
6429
|
-
const configHome = process.env.VOLUTE_HOME ??
|
|
6430
|
-
const mindsDir =
|
|
6732
|
+
const configHome = process.env.VOLUTE_HOME ?? resolve12(homedir2(), ".volute");
|
|
6733
|
+
const mindsDir = resolve12(configHome, "minds");
|
|
6431
6734
|
mkdirSync7(configHome, { recursive: true });
|
|
6432
6735
|
mkdirSync7(mindsDir, { recursive: true });
|
|
6433
6736
|
const existingConfig = readGlobalConfig();
|
|
@@ -6461,7 +6764,7 @@ setup.get("/status", async (c) => {
|
|
|
6461
6764
|
let hasAccount = false;
|
|
6462
6765
|
if (hasSystem) {
|
|
6463
6766
|
try {
|
|
6464
|
-
const { listUsersByType: listUsersByType2 } = await import("./auth-
|
|
6767
|
+
const { listUsersByType: listUsersByType2 } = await import("./auth-ABYLOEZ7.js");
|
|
6465
6768
|
const brains = await listUsersByType2("brain");
|
|
6466
6769
|
hasAccount = brains.length > 0;
|
|
6467
6770
|
} catch (err) {
|
|
@@ -6672,7 +6975,7 @@ setup.post("/account", async (c) => {
|
|
|
6672
6975
|
}
|
|
6673
6976
|
}
|
|
6674
6977
|
try {
|
|
6675
|
-
const { createUser: createUser2, updateUserProfile: updateUserProfile2 } = await import("./auth-
|
|
6978
|
+
const { createUser: createUser2, updateUserProfile: updateUserProfile2 } = await import("./auth-ABYLOEZ7.js");
|
|
6676
6979
|
const user = await createUser2(body.username.trim(), body.password);
|
|
6677
6980
|
if (body.displayName?.trim()) {
|
|
6678
6981
|
await updateUserProfile2(user.id, { display_name: body.displayName.trim() });
|
|
@@ -6718,7 +7021,7 @@ setup.post("/models", async (c) => {
|
|
|
6718
7021
|
return c.json({ error: "Spirit model is required" }, 400);
|
|
6719
7022
|
}
|
|
6720
7023
|
try {
|
|
6721
|
-
const { setEnabledModels: setEnabledModels3, setUtilityModel: setUtilityModel2 } = await import("./ai-service-
|
|
7024
|
+
const { setEnabledModels: setEnabledModels3, setUtilityModel: setUtilityModel2 } = await import("./ai-service-A7FS7EC3.js");
|
|
6722
7025
|
setEnabledModels3(body.models);
|
|
6723
7026
|
const config2 = readGlobalConfig();
|
|
6724
7027
|
config2.spiritModel = body.spiritModel.trim();
|
|
@@ -6736,15 +7039,15 @@ setup.post("/complete", async (c) => {
|
|
|
6736
7039
|
return c.json({ error: "Setup already complete" }, 400);
|
|
6737
7040
|
}
|
|
6738
7041
|
try {
|
|
6739
|
-
const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-
|
|
6740
|
-
const { startSpiritFull } = await import("./mind-service-
|
|
7042
|
+
const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-SSJMYC6A.js");
|
|
7043
|
+
const { startSpiritFull } = await import("./mind-service-42GDO4KH.js");
|
|
6741
7044
|
await ensureSpiritProject();
|
|
6742
7045
|
await syncSpiritTemplate();
|
|
6743
7046
|
const warnings = [];
|
|
6744
7047
|
const config2 = readGlobalConfig();
|
|
6745
7048
|
if (config2.setup?.service && config2.setup?.type === "local") {
|
|
6746
7049
|
try {
|
|
6747
|
-
const { installUserService } = await import("./service-install-
|
|
7050
|
+
const { installUserService } = await import("./service-install-Y46OTF55.js");
|
|
6748
7051
|
const installed = await installUserService(config2.port, config2.hostname);
|
|
6749
7052
|
if (!installed) {
|
|
6750
7053
|
warnings.push("Service installation is not supported on this platform.");
|
|
@@ -6768,12 +7071,13 @@ setup.post("/complete", async (c) => {
|
|
|
6768
7071
|
logger_default.info("spirit started successfully during setup");
|
|
6769
7072
|
} catch (err) {
|
|
6770
7073
|
logger_default.warn("spirit start failed during setup (non-fatal)", logger_default.errorData(err));
|
|
6771
|
-
|
|
7074
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
7075
|
+
warnings.push(`The spirit couldn't start: ${reason} \u2014 it will retry on next daemon restart.`);
|
|
6772
7076
|
}
|
|
6773
7077
|
let spiritConversationId;
|
|
6774
7078
|
try {
|
|
6775
|
-
const { getOrCreateMindUser: getOrCreateMindUser2, listUsersByType: listUsersByType2 } = await import("./auth-
|
|
6776
|
-
const { createConversation: createConversation2, findDMConversation: findDMConversation2 } = await import("./conversations-
|
|
7079
|
+
const { getOrCreateMindUser: getOrCreateMindUser2, listUsersByType: listUsersByType2 } = await import("./auth-ABYLOEZ7.js");
|
|
7080
|
+
const { createConversation: createConversation2, findDMConversation: findDMConversation2 } = await import("./conversations-TUM5PJJ4.js");
|
|
6777
7081
|
const spiritUser = await getOrCreateMindUser2("volute");
|
|
6778
7082
|
const brains = await listUsersByType2("brain");
|
|
6779
7083
|
const admin2 = brains.find((u) => u.role === "admin");
|
|
@@ -6795,8 +7099,8 @@ setup.post("/complete", async (c) => {
|
|
|
6795
7099
|
logger_default.info("setup complete state", { spiritConversationId, spiritStarted });
|
|
6796
7100
|
if (spiritConversationId && spiritStarted) {
|
|
6797
7101
|
try {
|
|
6798
|
-
const { deliverMessage: deliverMessage2 } = await import("./message-delivery-
|
|
6799
|
-
const { listUsersByType: listUsers2 } = await import("./auth-
|
|
7102
|
+
const { deliverMessage: deliverMessage2 } = await import("./message-delivery-ABPV76EA.js");
|
|
7103
|
+
const { listUsersByType: listUsers2 } = await import("./auth-ABYLOEZ7.js");
|
|
6800
7104
|
const admins = await listUsers2("brain");
|
|
6801
7105
|
const admin2 = admins.find((u) => u.role === "admin");
|
|
6802
7106
|
if (admin2) {
|
|
@@ -6835,9 +7139,9 @@ setup.post("/complete", async (c) => {
|
|
|
6835
7139
|
var setup_default = setup;
|
|
6836
7140
|
|
|
6837
7141
|
// packages/daemon/src/web/api/skills.ts
|
|
6838
|
-
import { existsSync as existsSync8, mkdtempSync, readdirSync as readdirSync3, rmSync as
|
|
7142
|
+
import { existsSync as existsSync8, mkdtempSync, readdirSync as readdirSync3, rmSync as rmSync4 } from "fs";
|
|
6839
7143
|
import { tmpdir } from "os";
|
|
6840
|
-
import { join, resolve as
|
|
7144
|
+
import { join, resolve as resolve13 } from "path";
|
|
6841
7145
|
import AdmZip from "adm-zip";
|
|
6842
7146
|
import { Hono as Hono20 } from "hono";
|
|
6843
7147
|
var app16 = new Hono20().get("/", async (c) => {
|
|
@@ -6913,7 +7217,7 @@ var app16 = new Hono20().get("/", async (c) => {
|
|
|
6913
7217
|
try {
|
|
6914
7218
|
const zip = new AdmZip(buffer);
|
|
6915
7219
|
for (const entry of zip.getEntries()) {
|
|
6916
|
-
const target =
|
|
7220
|
+
const target = resolve13(tmpDir, entry.entryName);
|
|
6917
7221
|
if (!target.startsWith(tmpDir)) {
|
|
6918
7222
|
return c.json({ error: "Invalid zip: paths must not escape archive" }, 400);
|
|
6919
7223
|
}
|
|
@@ -6942,7 +7246,7 @@ var app16 = new Hono20().get("/", async (c) => {
|
|
|
6942
7246
|
}
|
|
6943
7247
|
throw e;
|
|
6944
7248
|
} finally {
|
|
6945
|
-
|
|
7249
|
+
rmSync4(tmpDir, { recursive: true, force: true });
|
|
6946
7250
|
}
|
|
6947
7251
|
}).get("/:id", async (c) => {
|
|
6948
7252
|
const id = c.req.param("id");
|
|
@@ -7239,8 +7543,8 @@ var app20 = new Hono24().use("*", authMiddleware).get("/", async (c) => {
|
|
|
7239
7543
|
});
|
|
7240
7544
|
}, 15e3);
|
|
7241
7545
|
cleanups.push(() => clearInterval(keepAlive));
|
|
7242
|
-
await new Promise((
|
|
7243
|
-
stream.onAbort(() =>
|
|
7546
|
+
await new Promise((resolve19) => {
|
|
7547
|
+
stream.onAbort(() => resolve19());
|
|
7244
7548
|
});
|
|
7245
7549
|
} finally {
|
|
7246
7550
|
for (const cleanup of cleanups) {
|
|
@@ -7255,14 +7559,103 @@ var app20 = new Hono24().use("*", authMiddleware).get("/", async (c) => {
|
|
|
7255
7559
|
var events_default = app20;
|
|
7256
7560
|
|
|
7257
7561
|
// packages/daemon/src/web/api/variants.ts
|
|
7258
|
-
import { existsSync as
|
|
7259
|
-
import { resolve as
|
|
7562
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync9, writeFileSync as writeFileSync9 } from "fs";
|
|
7563
|
+
import { resolve as resolve16 } from "path";
|
|
7260
7564
|
import { Hono as Hono25 } from "hono";
|
|
7261
7565
|
|
|
7566
|
+
// packages/daemon/src/lib/mind/farewell.ts
|
|
7567
|
+
import { existsSync as existsSync9, readFileSync as readFileSync10, rmSync as rmSync5 } from "fs";
|
|
7568
|
+
import { resolve as resolve14 } from "path";
|
|
7569
|
+
var flog = logger_default.child("farewell");
|
|
7570
|
+
var FAREWELL_RELPATH = ".mind/farewell.md";
|
|
7571
|
+
var FAREWELL_MIND_PATH = "../.mind/farewell.md";
|
|
7572
|
+
var FAREWELL_TIMEOUT_MS = 12e4;
|
|
7573
|
+
function farewellPath(variantDir) {
|
|
7574
|
+
return resolve14(variantDir, FAREWELL_RELPATH);
|
|
7575
|
+
}
|
|
7576
|
+
function readFarewell(variantDir) {
|
|
7577
|
+
const path = farewellPath(variantDir);
|
|
7578
|
+
if (!existsSync9(path)) return void 0;
|
|
7579
|
+
try {
|
|
7580
|
+
const text = readFileSync10(path, "utf-8").trim();
|
|
7581
|
+
return text || void 0;
|
|
7582
|
+
} catch (err) {
|
|
7583
|
+
flog.warn(`failed to read farewell note at ${path}`, logger_default.errorData(err));
|
|
7584
|
+
return void 0;
|
|
7585
|
+
}
|
|
7586
|
+
}
|
|
7587
|
+
function waitForFarewell(name, variantDir, timeoutMs) {
|
|
7588
|
+
let settle;
|
|
7589
|
+
const done = new Promise((resolvePromise) => {
|
|
7590
|
+
settle = resolvePromise;
|
|
7591
|
+
});
|
|
7592
|
+
const cleanup = () => {
|
|
7593
|
+
clearTimeout(timeout);
|
|
7594
|
+
unsub();
|
|
7595
|
+
settle();
|
|
7596
|
+
};
|
|
7597
|
+
const timeout = setTimeout(cleanup, timeoutMs);
|
|
7598
|
+
const unsub = subscribe((event) => {
|
|
7599
|
+
if (event.mind !== name) return;
|
|
7600
|
+
if (event.type !== "mind_done" && event.type !== "mind_idle") return;
|
|
7601
|
+
if (existsSync9(farewellPath(variantDir))) cleanup();
|
|
7602
|
+
});
|
|
7603
|
+
return { done, cancel: cleanup };
|
|
7604
|
+
}
|
|
7605
|
+
async function runFarewellTurn(opts) {
|
|
7606
|
+
const { variantName, parentName, variantDir, port, running } = opts;
|
|
7607
|
+
const timeoutMs = opts.timeoutMs ?? FAREWELL_TIMEOUT_MS;
|
|
7608
|
+
if (!running) return readFarewell(variantDir);
|
|
7609
|
+
try {
|
|
7610
|
+
rmSync5(farewellPath(variantDir), { force: true });
|
|
7611
|
+
} catch (err) {
|
|
7612
|
+
flog.warn(`failed to clear stale farewell note for ${variantName}`, logger_default.errorData(err));
|
|
7613
|
+
}
|
|
7614
|
+
const message = await getPrompt("farewell_message", {
|
|
7615
|
+
parent: parentName,
|
|
7616
|
+
path: FAREWELL_MIND_PATH
|
|
7617
|
+
});
|
|
7618
|
+
let conversationId;
|
|
7619
|
+
try {
|
|
7620
|
+
const result = await sendSystemMessageDirect(variantName, message);
|
|
7621
|
+
conversationId = result.conversationId;
|
|
7622
|
+
} catch (err) {
|
|
7623
|
+
flog.error(`failed to persist farewell message for ${variantName}`, logger_default.errorData(err));
|
|
7624
|
+
}
|
|
7625
|
+
const waiter = waitForFarewell(variantName, variantDir, timeoutMs);
|
|
7626
|
+
let delivered = false;
|
|
7627
|
+
try {
|
|
7628
|
+
const res = await fetch(`http://127.0.0.1:${port}/message`, {
|
|
7629
|
+
method: "POST",
|
|
7630
|
+
headers: { "Content-Type": "application/json" },
|
|
7631
|
+
body: JSON.stringify({
|
|
7632
|
+
content: [{ type: "text", text: message }],
|
|
7633
|
+
channel: "@volute",
|
|
7634
|
+
sender: "volute",
|
|
7635
|
+
isDM: true,
|
|
7636
|
+
participants: ["volute", variantName],
|
|
7637
|
+
participantCount: 2,
|
|
7638
|
+
...conversationId ? { conversationId } : {}
|
|
7639
|
+
}),
|
|
7640
|
+
signal: AbortSignal.timeout(1e4)
|
|
7641
|
+
});
|
|
7642
|
+
if (res.ok) delivered = true;
|
|
7643
|
+
else flog.warn(`farewell delivery to ${variantName} returned HTTP ${res.status}`);
|
|
7644
|
+
} catch (err) {
|
|
7645
|
+
flog.warn(`failed to deliver farewell message to ${variantName}`, logger_default.errorData(err));
|
|
7646
|
+
}
|
|
7647
|
+
if (delivered) {
|
|
7648
|
+
await waiter.done;
|
|
7649
|
+
} else {
|
|
7650
|
+
waiter.cancel();
|
|
7651
|
+
}
|
|
7652
|
+
return readFarewell(variantDir);
|
|
7653
|
+
}
|
|
7654
|
+
|
|
7262
7655
|
// packages/daemon/src/lib/mind/spawn-server.ts
|
|
7263
7656
|
import { spawn as spawn4 } from "child_process";
|
|
7264
|
-
import { closeSync, mkdirSync as mkdirSync8, openSync, readFileSync as
|
|
7265
|
-
import { resolve as
|
|
7657
|
+
import { closeSync, mkdirSync as mkdirSync8, openSync, readFileSync as readFileSync11, statSync as statSync3 } from "fs";
|
|
7658
|
+
import { resolve as resolve15 } from "path";
|
|
7266
7659
|
async function spawnServer(cwd, port, options) {
|
|
7267
7660
|
let cmd = process.env.VOLUTE_NODE_PATH ?? process.execPath;
|
|
7268
7661
|
let args = ["--import", "tsx", "src/server.ts", "--port", String(port)];
|
|
@@ -7283,31 +7676,36 @@ function spawnAttached(cmd, args, cwd) {
|
|
|
7283
7676
|
cwd,
|
|
7284
7677
|
stdio: ["ignore", "pipe", "pipe"]
|
|
7285
7678
|
});
|
|
7286
|
-
return new Promise((
|
|
7287
|
-
const timeout = setTimeout(() =>
|
|
7679
|
+
return new Promise((resolve19) => {
|
|
7680
|
+
const timeout = setTimeout(() => resolve19(null), 3e4);
|
|
7288
7681
|
function checkOutput(data) {
|
|
7289
7682
|
const match = data.toString().match(/listening on :(\d+)/);
|
|
7290
7683
|
if (match) {
|
|
7291
7684
|
clearTimeout(timeout);
|
|
7292
|
-
|
|
7685
|
+
resolve19({ child, actualPort: parseInt(match[1], 10) });
|
|
7293
7686
|
}
|
|
7294
7687
|
}
|
|
7295
7688
|
child.stdout?.on("data", checkOutput);
|
|
7296
7689
|
child.stderr?.on("data", checkOutput);
|
|
7297
7690
|
child.on("error", () => {
|
|
7298
7691
|
clearTimeout(timeout);
|
|
7299
|
-
|
|
7692
|
+
resolve19(null);
|
|
7300
7693
|
});
|
|
7301
7694
|
child.on("exit", () => {
|
|
7302
7695
|
clearTimeout(timeout);
|
|
7303
|
-
|
|
7696
|
+
resolve19(null);
|
|
7304
7697
|
});
|
|
7305
7698
|
});
|
|
7306
7699
|
}
|
|
7307
7700
|
function spawnDetached(cmd, args, cwd, logDir) {
|
|
7308
|
-
const logsDir = logDir ??
|
|
7701
|
+
const logsDir = logDir ?? resolve15(cwd, ".mind", "logs");
|
|
7309
7702
|
mkdirSync8(logsDir, { recursive: true });
|
|
7310
|
-
const logPath =
|
|
7703
|
+
const logPath = resolve15(logsDir, "mind.log");
|
|
7704
|
+
let startOffset = 0;
|
|
7705
|
+
try {
|
|
7706
|
+
startOffset = statSync3(logPath).size;
|
|
7707
|
+
} catch {
|
|
7708
|
+
}
|
|
7311
7709
|
const logFd = openSync(logPath, "a");
|
|
7312
7710
|
const child = spawn4(cmd, args, {
|
|
7313
7711
|
cwd,
|
|
@@ -7327,7 +7725,7 @@ function spawnDetached(cmd, args, cwd, logDir) {
|
|
|
7327
7725
|
}
|
|
7328
7726
|
const interval = setInterval(() => {
|
|
7329
7727
|
try {
|
|
7330
|
-
const content =
|
|
7728
|
+
const content = readFileSync11(logPath).subarray(startOffset).toString("utf-8");
|
|
7331
7729
|
const match = content.match(/listening on :(\d+)/);
|
|
7332
7730
|
if (match) {
|
|
7333
7731
|
finish({ child, actualPort: parseInt(match[1], 10) });
|
|
@@ -7379,6 +7777,19 @@ async function verify2(port) {
|
|
|
7379
7777
|
}
|
|
7380
7778
|
|
|
7381
7779
|
// packages/daemon/src/web/api/variants.ts
|
|
7780
|
+
async function establishVariantDialogue(parent, variant, purpose) {
|
|
7781
|
+
const parentUser = await getOrCreateMindUser(parent);
|
|
7782
|
+
const variantUser = await getOrCreateMindUser(variant);
|
|
7783
|
+
const existing = await findDMConversation([parentUser.id, variantUser.id]);
|
|
7784
|
+
if (!existing) {
|
|
7785
|
+
await createConversation({ participantIds: [parentUser.id, variantUser.id] });
|
|
7786
|
+
}
|
|
7787
|
+
const purposeLine = purpose ? ` Its purpose: ${purpose}.` : "";
|
|
7788
|
+
await sendSystemMessage(
|
|
7789
|
+
parent,
|
|
7790
|
+
`You've split off a variant, ${variant} \u2014 a parallel version of you exploring on its own.${purposeLine} Reach it at @${variant} to check in on how the experiment is going, and merge its work back with \`volute mind join ${variant}\` when you're ready.`
|
|
7791
|
+
);
|
|
7792
|
+
}
|
|
7382
7793
|
var app21 = new Hono25().get("/:name/variants", async (c) => {
|
|
7383
7794
|
const name = c.req.param("name");
|
|
7384
7795
|
const entry = await findMind(name);
|
|
@@ -7422,56 +7833,105 @@ var app21 = new Hono25().get("/:name/variants", async (c) => {
|
|
|
7422
7833
|
}
|
|
7423
7834
|
const variantName = body.name;
|
|
7424
7835
|
if (!variantName) return c.json({ error: "Variant name required" }, 400);
|
|
7836
|
+
const purpose = body.purpose?.trim() || void 0;
|
|
7425
7837
|
const err = validateBranchName(variantName);
|
|
7426
7838
|
if (err) return c.json({ error: err }, 400);
|
|
7427
7839
|
if (await findMind(variantName)) {
|
|
7428
7840
|
return c.json({ error: `Name already in use: ${variantName}` }, 409);
|
|
7429
7841
|
}
|
|
7430
7842
|
const projectRoot = entry.dir ?? mindDir(mindName);
|
|
7431
|
-
const variantDir =
|
|
7432
|
-
if (
|
|
7843
|
+
const variantDir = resolve16(projectRoot, ".variants", variantName);
|
|
7844
|
+
if (existsSync10(variantDir)) {
|
|
7433
7845
|
return c.json({ error: `Variant directory already exists: ${variantDir}` }, 409);
|
|
7434
7846
|
}
|
|
7435
|
-
mkdirSync9(
|
|
7847
|
+
mkdirSync9(resolve16(projectRoot, ".variants"), { recursive: true });
|
|
7436
7848
|
try {
|
|
7437
7849
|
await gitExec(["worktree", "add", "-b", variantName, variantDir], { cwd: projectRoot });
|
|
7438
7850
|
} catch (e) {
|
|
7439
7851
|
const msg = e instanceof Error ? e.message : String(e);
|
|
7440
|
-
|
|
7852
|
+
let error = `Failed to create worktree: ${msg}`;
|
|
7853
|
+
try {
|
|
7854
|
+
await chownMindDir(projectRoot, mindName);
|
|
7855
|
+
} catch (err2) {
|
|
7856
|
+
logger_default.warn(
|
|
7857
|
+
`failed to restore ownership after worktree-add failure for ${mindName}`,
|
|
7858
|
+
logger_default.errorData(err2)
|
|
7859
|
+
);
|
|
7860
|
+
error += ` Restoring mind ownership also failed: ${err2 instanceof Error ? err2.message : String(err2)}`;
|
|
7861
|
+
}
|
|
7862
|
+
return c.json({ error }, 500);
|
|
7863
|
+
}
|
|
7864
|
+
const rollbackSplit = async () => {
|
|
7865
|
+
try {
|
|
7866
|
+
await cleanupVariant(variantName, projectRoot, variantDir, { stop: true });
|
|
7867
|
+
} catch (err2) {
|
|
7868
|
+
logger_default.warn(`failed to roll back split of ${variantName}`, logger_default.errorData(err2));
|
|
7869
|
+
}
|
|
7870
|
+
try {
|
|
7871
|
+
await chownMindDir(projectRoot, mindName);
|
|
7872
|
+
} catch (err2) {
|
|
7873
|
+
logger_default.warn(
|
|
7874
|
+
`failed to restore ${mindName} ownership after split rollback`,
|
|
7875
|
+
logger_default.errorData(err2)
|
|
7876
|
+
);
|
|
7877
|
+
}
|
|
7878
|
+
};
|
|
7879
|
+
try {
|
|
7880
|
+
await chownMindDir(projectRoot, mindName);
|
|
7881
|
+
} catch (e) {
|
|
7882
|
+
await rollbackSplit();
|
|
7883
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
7884
|
+
return c.json({ error: `Failed to prepare variant ownership: ${msg}` }, 500);
|
|
7441
7885
|
}
|
|
7442
|
-
await chownMindDir(projectRoot, mindName);
|
|
7443
7886
|
try {
|
|
7444
7887
|
if (isIsolationEnabled()) {
|
|
7445
7888
|
const [cmd, args] = await wrapForIsolation("npm", ["install"], mindName);
|
|
7446
7889
|
await exec(cmd, args, {
|
|
7447
7890
|
cwd: variantDir,
|
|
7448
|
-
env: { ...process.env, HOME:
|
|
7891
|
+
env: { ...process.env, HOME: resolve16(variantDir, "home") }
|
|
7449
7892
|
});
|
|
7450
7893
|
} else {
|
|
7451
7894
|
await exec("npm", ["install"], { cwd: variantDir });
|
|
7452
7895
|
}
|
|
7453
7896
|
} catch (e) {
|
|
7897
|
+
await rollbackSplit();
|
|
7454
7898
|
const msg = e instanceof Error ? e.message : String(e);
|
|
7455
7899
|
return c.json({ error: `npm install failed: ${msg}` }, 500);
|
|
7456
7900
|
}
|
|
7457
7901
|
if (body.soul) {
|
|
7458
|
-
|
|
7902
|
+
writeFileSync9(resolve16(variantDir, "home/SOUL.md"), body.soul);
|
|
7459
7903
|
}
|
|
7460
7904
|
const variantPort = body.port ?? await nextPort();
|
|
7461
|
-
|
|
7905
|
+
try {
|
|
7906
|
+
await addVariant(variantName, mindName, variantPort, variantDir, variantName, purpose);
|
|
7907
|
+
} catch (e) {
|
|
7908
|
+
await rollbackSplit();
|
|
7909
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
7910
|
+
return c.json({ error: `Failed to register variant: ${msg}` }, 500);
|
|
7911
|
+
}
|
|
7912
|
+
const manager = getMindManager();
|
|
7913
|
+
manager.setPendingContext(variantName, { type: "split", parent: mindName, purpose });
|
|
7462
7914
|
if (!body.noStart) {
|
|
7463
7915
|
try {
|
|
7464
|
-
const manager = getMindManager();
|
|
7465
|
-
manager.setPendingContext(variantName, { type: "split", parent: mindName });
|
|
7466
7916
|
await manager.startMind(variantName);
|
|
7467
7917
|
} catch (e) {
|
|
7918
|
+
await rollbackSplit();
|
|
7468
7919
|
const msg = e instanceof Error ? e.message : String(e);
|
|
7469
7920
|
return c.json({ error: `Variant created but failed to start: ${msg}` }, 500);
|
|
7470
7921
|
}
|
|
7471
7922
|
}
|
|
7923
|
+
await establishVariantDialogue(mindName, variantName, purpose).catch(
|
|
7924
|
+
(e) => logger_default.error(`failed to establish variant dialogue for ${variantName}`, logger_default.errorData(e))
|
|
7925
|
+
);
|
|
7472
7926
|
return c.json({
|
|
7473
7927
|
ok: true,
|
|
7474
|
-
variant: {
|
|
7928
|
+
variant: {
|
|
7929
|
+
name: variantName,
|
|
7930
|
+
branch: variantName,
|
|
7931
|
+
path: variantDir,
|
|
7932
|
+
port: variantPort,
|
|
7933
|
+
...purpose && { purpose }
|
|
7934
|
+
}
|
|
7475
7935
|
});
|
|
7476
7936
|
}).post("/:name/variants/:variant/merge", requireSelf(), async (c) => {
|
|
7477
7937
|
const mindName = c.req.param("name");
|
|
@@ -7492,146 +7952,150 @@ var app21 = new Hono25().get("/:name/variants", async (c) => {
|
|
|
7492
7952
|
} catch {
|
|
7493
7953
|
}
|
|
7494
7954
|
const projectRoot = parentEntry.dir ?? mindDir(mindName);
|
|
7495
|
-
|
|
7496
|
-
const status = (await gitExec(["status", "--porcelain"], { cwd: variantEntry.dir })).trim();
|
|
7497
|
-
if (status) {
|
|
7498
|
-
try {
|
|
7499
|
-
await gitExec(["add", "-A"], { cwd: variantEntry.dir });
|
|
7500
|
-
await gitExec(["commit", "-m", "Auto-commit uncommitted changes before merge"], {
|
|
7501
|
-
cwd: variantEntry.dir
|
|
7502
|
-
});
|
|
7503
|
-
} catch (_e) {
|
|
7504
|
-
return c.json(
|
|
7505
|
-
{
|
|
7506
|
-
error: "Failed to auto-commit variant changes. Commit or stash manually before merging."
|
|
7507
|
-
},
|
|
7508
|
-
500
|
|
7509
|
-
);
|
|
7510
|
-
}
|
|
7511
|
-
}
|
|
7512
|
-
}
|
|
7513
|
-
if (!body.skipVerify) {
|
|
7514
|
-
const result = await spawnServer(variantEntry.dir, 0, {
|
|
7515
|
-
detached: true,
|
|
7516
|
-
mindName: variantName,
|
|
7517
|
-
template: variantEntry.template
|
|
7518
|
-
});
|
|
7519
|
-
if (!result) {
|
|
7520
|
-
return c.json(
|
|
7521
|
-
{ error: "Failed to start server for verification. Use skipVerify to skip." },
|
|
7522
|
-
500
|
|
7523
|
-
);
|
|
7524
|
-
}
|
|
7525
|
-
const verified = await verify2(result.actualPort);
|
|
7526
|
-
try {
|
|
7527
|
-
process.kill(-result.child.pid, "SIGTERM");
|
|
7528
|
-
} catch {
|
|
7529
|
-
}
|
|
7530
|
-
if (!verified) {
|
|
7531
|
-
return c.json(
|
|
7532
|
-
{ error: "Verification failed. Fix issues or use skipVerify to proceed." },
|
|
7533
|
-
500
|
|
7534
|
-
);
|
|
7535
|
-
}
|
|
7536
|
-
}
|
|
7537
|
-
const mainStatus = (await gitExec(["status", "--porcelain"], { cwd: projectRoot })).trim();
|
|
7538
|
-
if (mainStatus) {
|
|
7539
|
-
try {
|
|
7540
|
-
await gitExec(["add", "-A"], { cwd: projectRoot });
|
|
7541
|
-
await gitExec(["commit", "-m", "Auto-commit uncommitted changes before merge"], {
|
|
7542
|
-
cwd: projectRoot
|
|
7543
|
-
});
|
|
7544
|
-
} catch (_e) {
|
|
7545
|
-
return c.json(
|
|
7546
|
-
{ error: "Failed to auto-commit main changes. Commit or stash manually before merging." },
|
|
7547
|
-
500
|
|
7548
|
-
);
|
|
7549
|
-
}
|
|
7550
|
-
}
|
|
7551
|
-
try {
|
|
7552
|
-
await gitExec(["merge", variantEntry.branch], { cwd: projectRoot });
|
|
7553
|
-
} catch (_e) {
|
|
7554
|
-
let conflicts = [];
|
|
7555
|
-
try {
|
|
7556
|
-
const out = (await gitExec(["diff", "--name-only", "--diff-filter=U"], { cwd: projectRoot })).trim();
|
|
7557
|
-
conflicts = out ? out.split("\n") : [];
|
|
7558
|
-
} catch (err) {
|
|
7559
|
-
logger_default.warn(`failed to list merge conflicts for ${mindName}`, logger_default.errorData(err));
|
|
7560
|
-
}
|
|
7561
|
-
try {
|
|
7562
|
-
await gitExec(["merge", "--abort"], { cwd: projectRoot });
|
|
7563
|
-
} catch (err) {
|
|
7564
|
-
logger_default.warn(`failed to abort merge for ${mindName}`, logger_default.errorData(err));
|
|
7565
|
-
}
|
|
7955
|
+
const failAfterGitWrite = async (message, extra) => {
|
|
7566
7956
|
try {
|
|
7567
7957
|
await chownMindDir(projectRoot, mindName);
|
|
7568
7958
|
} catch (err) {
|
|
7569
7959
|
logger_default.warn(
|
|
7570
|
-
`failed to restore ownership
|
|
7960
|
+
`failed to restore ownership for ${mindName} after merge failure`,
|
|
7571
7961
|
logger_default.errorData(err)
|
|
7572
7962
|
);
|
|
7573
7963
|
return c.json(
|
|
7574
7964
|
{
|
|
7575
|
-
error:
|
|
7576
|
-
|
|
7965
|
+
error: `${message} Restoring mind ownership also failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
7966
|
+
...extra
|
|
7577
7967
|
},
|
|
7578
7968
|
500
|
|
7579
7969
|
);
|
|
7580
7970
|
}
|
|
7581
|
-
return c.json(
|
|
7582
|
-
|
|
7583
|
-
|
|
7584
|
-
|
|
7585
|
-
|
|
7586
|
-
|
|
7587
|
-
|
|
7971
|
+
return c.json({ error: message, ...extra }, 500);
|
|
7972
|
+
};
|
|
7973
|
+
let farewell;
|
|
7974
|
+
try {
|
|
7975
|
+
farewell = await runFarewellTurn({
|
|
7976
|
+
variantName,
|
|
7977
|
+
parentName: mindName,
|
|
7978
|
+
variantDir: variantEntry.dir,
|
|
7979
|
+
port: variantEntry.port,
|
|
7980
|
+
running: tryGetMindManager()?.isRunning(variantName) ?? false
|
|
7981
|
+
});
|
|
7982
|
+
} catch (err) {
|
|
7983
|
+
logger_default.warn(`farewell turn failed for ${variantName}`, logger_default.errorData(err));
|
|
7588
7984
|
}
|
|
7589
|
-
|
|
7590
|
-
|
|
7591
|
-
|
|
7985
|
+
try {
|
|
7986
|
+
if (existsSync10(variantEntry.dir)) {
|
|
7987
|
+
const status = (await gitExec(["status", "--porcelain"], { cwd: variantEntry.dir })).trim();
|
|
7988
|
+
if (status) {
|
|
7989
|
+
try {
|
|
7990
|
+
await gitExec(["add", "-A"], { cwd: variantEntry.dir });
|
|
7991
|
+
await gitExec(["commit", "-m", "Auto-commit uncommitted changes before merge"], {
|
|
7992
|
+
cwd: variantEntry.dir
|
|
7993
|
+
});
|
|
7994
|
+
} catch (_e) {
|
|
7995
|
+
return failAfterGitWrite(
|
|
7996
|
+
"Failed to auto-commit variant changes. Commit or stash manually before merging."
|
|
7997
|
+
);
|
|
7998
|
+
}
|
|
7999
|
+
}
|
|
8000
|
+
}
|
|
8001
|
+
if (!body.skipVerify) {
|
|
8002
|
+
const result = await spawnServer(variantEntry.dir, 0, {
|
|
8003
|
+
detached: true,
|
|
8004
|
+
mindName: variantName,
|
|
8005
|
+
template: variantEntry.template
|
|
8006
|
+
});
|
|
8007
|
+
if (!result) {
|
|
8008
|
+
return failAfterGitWrite(
|
|
8009
|
+
"Failed to start server for verification. Use skipVerify to skip."
|
|
8010
|
+
);
|
|
8011
|
+
}
|
|
8012
|
+
const verified = await verify2(result.actualPort);
|
|
8013
|
+
try {
|
|
8014
|
+
process.kill(-result.child.pid, "SIGTERM");
|
|
8015
|
+
} catch {
|
|
8016
|
+
}
|
|
8017
|
+
if (!verified) {
|
|
8018
|
+
return failAfterGitWrite("Verification failed. Fix issues or use skipVerify to proceed.");
|
|
8019
|
+
}
|
|
8020
|
+
}
|
|
8021
|
+
const mainStatus = (await gitExec(["status", "--porcelain"], { cwd: projectRoot })).trim();
|
|
8022
|
+
if (mainStatus) {
|
|
8023
|
+
try {
|
|
8024
|
+
await gitExec(["add", "-A"], { cwd: projectRoot });
|
|
8025
|
+
await gitExec(["commit", "-m", "Auto-commit uncommitted changes before merge"], {
|
|
8026
|
+
cwd: projectRoot
|
|
8027
|
+
});
|
|
8028
|
+
} catch (_e) {
|
|
8029
|
+
return failAfterGitWrite(
|
|
8030
|
+
"Failed to auto-commit main changes. Commit or stash manually before merging."
|
|
8031
|
+
);
|
|
8032
|
+
}
|
|
8033
|
+
}
|
|
8034
|
+
let memoryDelta = "";
|
|
7592
8035
|
try {
|
|
7593
|
-
|
|
7594
|
-
const { setMindTemplateHash: setMindTemplateHash2 } = await import("./registry-UMMDRPRM.js");
|
|
7595
|
-
const tmpl = parentEntry.template ?? "claude";
|
|
7596
|
-
await setMindTemplateHash2(mindName, computeTemplateHash2(tmpl));
|
|
8036
|
+
memoryDelta = await mergeVariantExcludingMemory(projectRoot, variantEntry.branch);
|
|
7597
8037
|
} catch (err) {
|
|
7598
|
-
|
|
8038
|
+
if (!(err instanceof VariantMergeError)) throw err;
|
|
8039
|
+
return failAfterGitWrite(
|
|
8040
|
+
err.abortFailed ? "Merge failed and the abort did not complete \u2014 the parent worktree may be left mid-merge with conflict markers; manual cleanup may be needed." : "Merge failed. Resolve conflicts in the variant and retry the join.",
|
|
8041
|
+
{ conflicts: err.conflicts }
|
|
8042
|
+
);
|
|
7599
8043
|
}
|
|
7600
|
-
|
|
7601
|
-
|
|
7602
|
-
|
|
7603
|
-
|
|
7604
|
-
|
|
7605
|
-
|
|
7606
|
-
|
|
7607
|
-
|
|
7608
|
-
})
|
|
7609
|
-
|
|
7610
|
-
|
|
8044
|
+
await cleanupVariant(variantName, projectRoot, variantEntry.dir, { stop: true });
|
|
8045
|
+
await chownMindDir(projectRoot, mindName);
|
|
8046
|
+
if (variantName.endsWith("-upgrade") || variantName === "upgrade") {
|
|
8047
|
+
try {
|
|
8048
|
+
const { computeTemplateHash: computeTemplateHash2 } = await import("./template-hash-GSOUARW4.js");
|
|
8049
|
+
const { setMindTemplateHash: setMindTemplateHash2 } = await import("./registry-3ELJPUGJ.js");
|
|
8050
|
+
const tmpl = parentEntry.template ?? "claude";
|
|
8051
|
+
await setMindTemplateHash2(mindName, computeTemplateHash2(tmpl));
|
|
8052
|
+
} catch (err) {
|
|
8053
|
+
logger_default.warn(`failed to update template hash for ${mindName}`, logger_default.errorData(err));
|
|
8054
|
+
}
|
|
7611
8055
|
}
|
|
7612
|
-
|
|
7613
|
-
|
|
7614
|
-
|
|
7615
|
-
|
|
7616
|
-
|
|
7617
|
-
|
|
7618
|
-
|
|
7619
|
-
|
|
7620
|
-
|
|
7621
|
-
|
|
7622
|
-
|
|
7623
|
-
|
|
7624
|
-
|
|
7625
|
-
|
|
7626
|
-
|
|
8056
|
+
let restartWarning;
|
|
8057
|
+
try {
|
|
8058
|
+
if (isIsolationEnabled()) {
|
|
8059
|
+
const [cmd, args] = await wrapForIsolation("npm", ["install"], mindName);
|
|
8060
|
+
await exec(cmd, args, {
|
|
8061
|
+
cwd: projectRoot,
|
|
8062
|
+
env: { ...process.env, HOME: resolve16(projectRoot, "home") }
|
|
8063
|
+
});
|
|
8064
|
+
} else {
|
|
8065
|
+
await exec("npm", ["install"], { cwd: projectRoot });
|
|
8066
|
+
}
|
|
8067
|
+
} catch (err) {
|
|
8068
|
+
logger_default.warn(`npm install failed after merge for ${mindName}`, logger_default.errorData(err));
|
|
8069
|
+
restartWarning = `npm install failed after merge \u2014 mind may have stale dependencies`;
|
|
8070
|
+
}
|
|
8071
|
+
const manager = getMindManager();
|
|
8072
|
+
const justification = body.justification?.trim() || variantEntry.purpose;
|
|
8073
|
+
const context = {
|
|
8074
|
+
type: "merged",
|
|
8075
|
+
name: variantName,
|
|
8076
|
+
...body.summary && { summary: body.summary },
|
|
8077
|
+
...justification && { justification },
|
|
8078
|
+
...body.memory && { memory: body.memory },
|
|
8079
|
+
...memoryDelta && { memoryDelta },
|
|
8080
|
+
...farewell && { farewell }
|
|
8081
|
+
};
|
|
8082
|
+
try {
|
|
8083
|
+
if (manager.isRunning(mindName)) {
|
|
8084
|
+
await manager.stopMind(mindName);
|
|
8085
|
+
}
|
|
8086
|
+
manager.setPendingContext(mindName, context);
|
|
8087
|
+
await manager.startMind(mindName);
|
|
8088
|
+
} catch (e) {
|
|
8089
|
+
restartWarning = `Merge succeeded but mind restart failed: ${e instanceof Error ? e.message : String(e)}`;
|
|
8090
|
+
logger_default.warn(restartWarning);
|
|
7627
8091
|
}
|
|
7628
|
-
|
|
7629
|
-
|
|
7630
|
-
|
|
7631
|
-
|
|
7632
|
-
|
|
8092
|
+
return c.json({ ok: true, ...restartWarning && { warning: restartWarning } });
|
|
8093
|
+
} catch (err) {
|
|
8094
|
+
logger_default.error(`variant join failed for ${mindName}`, logger_default.errorData(err));
|
|
8095
|
+
return await failAfterGitWrite(
|
|
8096
|
+
`Variant join failed: ${err instanceof Error ? err.message : String(err)}`
|
|
8097
|
+
);
|
|
7633
8098
|
}
|
|
7634
|
-
return c.json({ ok: true, ...restartWarning && { warning: restartWarning } });
|
|
7635
8099
|
}).delete("/:name/variants/:variant", requireSelf(), async (c) => {
|
|
7636
8100
|
const mindName = c.req.param("name");
|
|
7637
8101
|
const variantName = c.req.param("variant");
|
|
@@ -7659,7 +8123,8 @@ var channelSettingsSchema = z10.object({
|
|
|
7659
8123
|
private: z10.boolean().optional()
|
|
7660
8124
|
});
|
|
7661
8125
|
var createSchema2 = z10.object({
|
|
7662
|
-
name: z10.string().min(1).max(50).regex(/^[a-z0-9][a-z0-9-]*$/, "Channel names must be lowercase alphanumeric with hyphens")
|
|
8126
|
+
name: z10.string().min(1).max(50).regex(/^[a-z0-9][a-z0-9-]*$/, "Channel names must be lowercase alphanumeric with hyphens"),
|
|
8127
|
+
participantNames: z10.array(z10.string().min(1)).optional()
|
|
7663
8128
|
}).merge(channelSettingsSchema);
|
|
7664
8129
|
var inviteSchema = z10.object({
|
|
7665
8130
|
username: z10.string().min(1)
|
|
@@ -7678,10 +8143,21 @@ var app22 = new Hono26().get("/", async (c) => {
|
|
|
7678
8143
|
}).post("/", zValidator10("json", createSchema2), async (c) => {
|
|
7679
8144
|
const user = c.get("user");
|
|
7680
8145
|
const body = c.req.valid("json");
|
|
8146
|
+
const { name, participantNames, ...settings } = body;
|
|
8147
|
+
const participantIds = [];
|
|
8148
|
+
for (const username of participantNames ?? []) {
|
|
8149
|
+
let member = await getUserByUsername(username);
|
|
8150
|
+
if (!member && await findMind(username)) {
|
|
8151
|
+
member = await getOrCreateMindUser(username);
|
|
8152
|
+
}
|
|
8153
|
+
if (!member) {
|
|
8154
|
+
return c.json({ error: `User not found: ${username}` }, 404);
|
|
8155
|
+
}
|
|
8156
|
+
participantIds.push(member.id);
|
|
8157
|
+
}
|
|
8158
|
+
let ch;
|
|
7681
8159
|
try {
|
|
7682
|
-
|
|
7683
|
-
const ch = await createChannel(name, user.id, settings);
|
|
7684
|
-
return c.json({ ...ch, channel_name: name }, 201);
|
|
8160
|
+
ch = await createChannel(name, user.id, settings);
|
|
7685
8161
|
} catch (err) {
|
|
7686
8162
|
const cause = err instanceof Error ? err.cause : null;
|
|
7687
8163
|
if (cause && /UNIQUE/i.test(cause.extendedCode ?? cause.message ?? "")) {
|
|
@@ -7689,6 +8165,23 @@ var app22 = new Hono26().get("/", async (c) => {
|
|
|
7689
8165
|
}
|
|
7690
8166
|
throw err;
|
|
7691
8167
|
}
|
|
8168
|
+
try {
|
|
8169
|
+
for (const id of participantIds) {
|
|
8170
|
+
await joinChannel(ch.id, id);
|
|
8171
|
+
}
|
|
8172
|
+
} catch (err) {
|
|
8173
|
+
console.error("[channels] join failed during channel create:", err);
|
|
8174
|
+
try {
|
|
8175
|
+
await deleteConversation(ch.id);
|
|
8176
|
+
} catch (cleanupErr) {
|
|
8177
|
+
console.error(
|
|
8178
|
+
`[channels] failed to roll back channel ${ch.id} after join failure:`,
|
|
8179
|
+
cleanupErr
|
|
8180
|
+
);
|
|
8181
|
+
}
|
|
8182
|
+
throw err;
|
|
8183
|
+
}
|
|
8184
|
+
return c.json({ ...ch, channel_name: name }, 201);
|
|
7692
8185
|
}).get("/:name", async (c) => {
|
|
7693
8186
|
const name = c.req.param("name");
|
|
7694
8187
|
const ch = await getChannelByName(name);
|
|
@@ -8023,11 +8516,11 @@ var app23 = new Hono27().get("/:name/conversations/:id/events", async (c) => {
|
|
|
8023
8516
|
if (!stream.aborted) console.error("[chat] SSE ping error:", err);
|
|
8024
8517
|
});
|
|
8025
8518
|
}, 15e3);
|
|
8026
|
-
await new Promise((
|
|
8519
|
+
await new Promise((resolve19) => {
|
|
8027
8520
|
stream.onAbort(() => {
|
|
8028
8521
|
unsubscribe();
|
|
8029
8522
|
clearInterval(keepAlive);
|
|
8030
|
-
|
|
8523
|
+
resolve19();
|
|
8031
8524
|
});
|
|
8032
8525
|
});
|
|
8033
8526
|
});
|
|
@@ -8241,10 +8734,10 @@ app25.route("/api/conversations", conversations_default);
|
|
|
8241
8734
|
var app_default = app25;
|
|
8242
8735
|
|
|
8243
8736
|
// packages/daemon/src/web/server.ts
|
|
8244
|
-
import { existsSync as
|
|
8737
|
+
import { existsSync as existsSync11 } from "fs";
|
|
8245
8738
|
import { readFile as readFile2, stat as stat2 } from "fs/promises";
|
|
8246
8739
|
import { createServer as createHttpsServer } from "https";
|
|
8247
|
-
import { dirname as dirname2, extname as extname3, resolve as
|
|
8740
|
+
import { dirname as dirname2, extname as extname3, resolve as resolve17 } from "path";
|
|
8248
8741
|
import { serve } from "@hono/node-server";
|
|
8249
8742
|
var MIME_TYPES2 = {
|
|
8250
8743
|
".html": "text/html",
|
|
@@ -8263,8 +8756,8 @@ async function startServer({
|
|
|
8263
8756
|
let assetsDir = "";
|
|
8264
8757
|
let searchDir = dirname2(new URL(import.meta.url).pathname);
|
|
8265
8758
|
for (let i = 0; i < 5; i++) {
|
|
8266
|
-
const candidate =
|
|
8267
|
-
if (
|
|
8759
|
+
const candidate = resolve17(searchDir, "dist", "web-assets");
|
|
8760
|
+
if (existsSync11(candidate)) {
|
|
8268
8761
|
assetsDir = candidate;
|
|
8269
8762
|
break;
|
|
8270
8763
|
}
|
|
@@ -8274,7 +8767,7 @@ async function startServer({
|
|
|
8274
8767
|
app_default.get("*", async (c) => {
|
|
8275
8768
|
const urlPath = new URL(c.req.url).pathname;
|
|
8276
8769
|
if (urlPath.startsWith("/api/") || urlPath.startsWith("/ext/")) return c.notFound();
|
|
8277
|
-
const filePath =
|
|
8770
|
+
const filePath = resolve17(assetsDir, urlPath.slice(1));
|
|
8278
8771
|
if (!filePath.startsWith(assetsDir)) return c.text("Forbidden", 403);
|
|
8279
8772
|
const s = await stat2(filePath).catch(() => null);
|
|
8280
8773
|
if (s?.isFile()) {
|
|
@@ -8287,7 +8780,7 @@ async function startServer({
|
|
|
8287
8780
|
const cacheControl = isHashed ? "public, max-age=31536000, immutable" : "no-cache";
|
|
8288
8781
|
return c.body(body, 200, { "Content-Type": mime, "Cache-Control": cacheControl });
|
|
8289
8782
|
}
|
|
8290
|
-
const indexPath =
|
|
8783
|
+
const indexPath = resolve17(assetsDir, "index.html");
|
|
8291
8784
|
const indexStat = await stat2(indexPath).catch(() => null);
|
|
8292
8785
|
if (indexStat?.isFile()) {
|
|
8293
8786
|
const body = await readFile2(indexPath, "utf-8");
|
|
@@ -8304,10 +8797,10 @@ async function startServer({
|
|
|
8304
8797
|
createServer: createHttpsServer,
|
|
8305
8798
|
serverOptions: { key: tls.key, cert: tls.cert }
|
|
8306
8799
|
});
|
|
8307
|
-
await new Promise((
|
|
8800
|
+
await new Promise((resolve19, reject) => {
|
|
8308
8801
|
server2.on("listening", () => {
|
|
8309
8802
|
logger_default.info("Volute UI running (https)", { hostname, port });
|
|
8310
|
-
|
|
8803
|
+
resolve19();
|
|
8311
8804
|
});
|
|
8312
8805
|
server2.on("error", (err) => {
|
|
8313
8806
|
reject(err);
|
|
@@ -8315,13 +8808,13 @@ async function startServer({
|
|
|
8315
8808
|
});
|
|
8316
8809
|
const internalPort = port + 1;
|
|
8317
8810
|
const internalServer = serve({ fetch: app_default.fetch, port: internalPort, hostname: "127.0.0.1" });
|
|
8318
|
-
await new Promise((
|
|
8811
|
+
await new Promise((resolve19, reject) => {
|
|
8319
8812
|
internalServer.on("listening", () => {
|
|
8320
8813
|
logger_default.info("Volute API running (http, internal)", {
|
|
8321
8814
|
hostname: "127.0.0.1",
|
|
8322
8815
|
port: internalPort
|
|
8323
8816
|
});
|
|
8324
|
-
|
|
8817
|
+
resolve19();
|
|
8325
8818
|
});
|
|
8326
8819
|
internalServer.on("error", (err) => {
|
|
8327
8820
|
reject(err);
|
|
@@ -8330,10 +8823,10 @@ async function startServer({
|
|
|
8330
8823
|
return { server: server2, internalPort };
|
|
8331
8824
|
}
|
|
8332
8825
|
const server = serve({ fetch: app_default.fetch, port, hostname });
|
|
8333
|
-
await new Promise((
|
|
8826
|
+
await new Promise((resolve19, reject) => {
|
|
8334
8827
|
server.on("listening", () => {
|
|
8335
8828
|
logger_default.info("Volute API running (http)", { hostname, port });
|
|
8336
|
-
|
|
8829
|
+
resolve19();
|
|
8337
8830
|
});
|
|
8338
8831
|
server.on("error", (err) => {
|
|
8339
8832
|
reject(err);
|
|
@@ -8344,18 +8837,18 @@ async function startServer({
|
|
|
8344
8837
|
|
|
8345
8838
|
// packages/daemon/src/daemon.ts
|
|
8346
8839
|
if (!process.env.VOLUTE_HOME) {
|
|
8347
|
-
process.env.VOLUTE_HOME =
|
|
8840
|
+
process.env.VOLUTE_HOME = resolve18(homedir3(), ".volute");
|
|
8348
8841
|
}
|
|
8349
8842
|
if (process.env.VOLUTE_TIMEZONE && !process.env.TZ) {
|
|
8350
8843
|
process.env.TZ = process.env.VOLUTE_TIMEZONE;
|
|
8351
8844
|
}
|
|
8352
8845
|
function writeDaemonConfig(systemDir, config2, token) {
|
|
8353
|
-
const configPath =
|
|
8354
|
-
|
|
8846
|
+
const configPath = resolve18(systemDir, "daemon.json");
|
|
8847
|
+
writeFileSync10(configPath, `${JSON.stringify(config2, null, 2)}
|
|
8355
8848
|
`, { mode: 420 });
|
|
8356
8849
|
chmodSync(configPath, 420);
|
|
8357
|
-
const tokenPath =
|
|
8358
|
-
|
|
8850
|
+
const tokenPath = resolve18(systemDir, "daemon-token");
|
|
8851
|
+
writeFileSync10(tokenPath, `${token}
|
|
8359
8852
|
`, { mode: 384 });
|
|
8360
8853
|
chmodSync(tokenPath, 384);
|
|
8361
8854
|
}
|
|
@@ -8365,7 +8858,7 @@ async function startDaemon(opts) {
|
|
|
8365
8858
|
const home = voluteHome();
|
|
8366
8859
|
const systemDir = voluteSystemDir();
|
|
8367
8860
|
if (!opts.foreground) {
|
|
8368
|
-
const rotatingLog = new RotatingLog(
|
|
8861
|
+
const rotatingLog = new RotatingLog(resolve18(systemDir, "daemon.log"));
|
|
8369
8862
|
logger_default.setOutput((line) => rotatingLog.write(`${line}
|
|
8370
8863
|
`));
|
|
8371
8864
|
const write = (...args) => rotatingLog.write(`${format(...args)}
|
|
@@ -8375,32 +8868,32 @@ async function startDaemon(opts) {
|
|
|
8375
8868
|
console.warn = write;
|
|
8376
8869
|
console.info = write;
|
|
8377
8870
|
}
|
|
8378
|
-
const DAEMON_PID_PATH =
|
|
8379
|
-
const DAEMON_JSON_PATH =
|
|
8380
|
-
const DAEMON_TOKEN_PATH =
|
|
8871
|
+
const DAEMON_PID_PATH = resolve18(systemDir, "daemon.pid");
|
|
8872
|
+
const DAEMON_JSON_PATH = resolve18(systemDir, "daemon.json");
|
|
8873
|
+
const DAEMON_TOKEN_PATH = resolve18(systemDir, "daemon-token");
|
|
8381
8874
|
mkdirSync10(home, { recursive: true });
|
|
8382
8875
|
ensureSystemDir();
|
|
8383
|
-
const { migrateConfigSecrets, migrateSetupCompleted } = await import("./setup-
|
|
8876
|
+
const { migrateConfigSecrets, migrateSetupCompleted } = await import("./setup-TCN7VH5U.js");
|
|
8384
8877
|
migrateConfigSecrets();
|
|
8385
8878
|
migrateSetupCompleted();
|
|
8386
|
-
const { migrateAiModelQualification } = await import("./ai-service-
|
|
8879
|
+
const { migrateAiModelQualification } = await import("./ai-service-A7FS7EC3.js");
|
|
8387
8880
|
migrateAiModelQualification();
|
|
8388
|
-
await (await import("./db-
|
|
8881
|
+
await (await import("./db-F6UMFRYD.js")).getDb();
|
|
8389
8882
|
try {
|
|
8390
8883
|
const { eq: eq8, and: and6 } = await import("drizzle-orm");
|
|
8391
|
-
const { users: users2 } = await import("./schema-
|
|
8392
|
-
const db = await (await import("./db-
|
|
8884
|
+
const { users: users2 } = await import("./schema-6YONVTKZ.js");
|
|
8885
|
+
const db = await (await import("./db-F6UMFRYD.js")).getDb();
|
|
8393
8886
|
await db.update(users2).set({ role: "system" }).where(and6(eq8(users2.user_type, "system"), eq8(users2.role, "user")));
|
|
8394
8887
|
} catch (err) {
|
|
8395
8888
|
logger_default.warn("failed to migrate system user role", logger_default.errorData(err));
|
|
8396
8889
|
}
|
|
8397
8890
|
try {
|
|
8398
|
-
const { migrateAvatarSizes } = await import("./avatar-image-
|
|
8891
|
+
const { migrateAvatarSizes } = await import("./avatar-image-6ATV2IOZ.js");
|
|
8399
8892
|
await migrateAvatarSizes();
|
|
8400
8893
|
} catch (err) {
|
|
8401
8894
|
logger_default.warn("avatar size migration failed", logger_default.errorData(err));
|
|
8402
8895
|
}
|
|
8403
|
-
const { initSandbox } = await import("./sandbox-
|
|
8896
|
+
const { initSandbox } = await import("./sandbox-XPYA7UIF.js");
|
|
8404
8897
|
await initSandbox();
|
|
8405
8898
|
try {
|
|
8406
8899
|
await syncBuiltinSkills();
|
|
@@ -8427,7 +8920,12 @@ async function startDaemon(opts) {
|
|
|
8427
8920
|
logger_default.warn("failed to ensure #system channel", logger_default.errorData(err));
|
|
8428
8921
|
}
|
|
8429
8922
|
try {
|
|
8430
|
-
|
|
8923
|
+
await backfillSystemChannelMembers();
|
|
8924
|
+
} catch (err) {
|
|
8925
|
+
logger_default.warn("failed to backfill minds into #system", logger_default.errorData(err));
|
|
8926
|
+
}
|
|
8927
|
+
try {
|
|
8928
|
+
const { getOrCreateSystemUser: getOrCreateSystemUser2 } = await import("./auth-ABYLOEZ7.js");
|
|
8431
8929
|
await getOrCreateSystemUser2();
|
|
8432
8930
|
} catch (err) {
|
|
8433
8931
|
logger_default.warn(
|
|
@@ -8436,12 +8934,12 @@ async function startDaemon(opts) {
|
|
|
8436
8934
|
);
|
|
8437
8935
|
}
|
|
8438
8936
|
const token = process.env.VOLUTE_DAEMON_TOKEN || randomBytes3(32).toString("hex");
|
|
8439
|
-
const { readGlobalConfig: readGlobalConfig2 } = await import("./setup-
|
|
8937
|
+
const { readGlobalConfig: readGlobalConfig2 } = await import("./setup-TCN7VH5U.js");
|
|
8440
8938
|
const globalCfg = readGlobalConfig2();
|
|
8441
8939
|
let tls;
|
|
8442
8940
|
if (opts.tailscale || globalCfg.tailscale) {
|
|
8443
8941
|
try {
|
|
8444
|
-
const { getTailscaleTls } = await import("./tailscale-
|
|
8942
|
+
const { getTailscaleTls } = await import("./tailscale-AMMVKDXB.js");
|
|
8445
8943
|
const tlsConfig = await getTailscaleTls();
|
|
8446
8944
|
tls = { key: tlsConfig.key, cert: tlsConfig.cert };
|
|
8447
8945
|
logger_default.info("Tailscale HTTPS enabled", { hostname: tlsConfig.hostname });
|
|
@@ -8468,7 +8966,7 @@ async function startDaemon(opts) {
|
|
|
8468
8966
|
process.env.VOLUTE_DAEMON_TOKEN = token;
|
|
8469
8967
|
process.env.VOLUTE_DAEMON_PORT = String(daemonPort);
|
|
8470
8968
|
process.env.VOLUTE_DAEMON_HOSTNAME = hostname;
|
|
8471
|
-
|
|
8969
|
+
writeFileSync10(DAEMON_PID_PATH, myPid, { mode: 420 });
|
|
8472
8970
|
const daemonConfig = { port, hostname };
|
|
8473
8971
|
if (internalPort) daemonConfig.internalPort = internalPort;
|
|
8474
8972
|
if (tls) daemonConfig.tls = true;
|
|
@@ -8492,6 +8990,12 @@ async function startDaemon(opts) {
|
|
|
8492
8990
|
const unsubscribeWebhook = initWebhook();
|
|
8493
8991
|
const orphanedTurns = await completeOrphanedTurns();
|
|
8494
8992
|
summarizeOrphanedTurns(orphanedTurns);
|
|
8993
|
+
try {
|
|
8994
|
+
const { reconcileVariants } = await import("./variant-cleanup-P4MBNLJD.js");
|
|
8995
|
+
await reconcileVariants();
|
|
8996
|
+
} catch (err) {
|
|
8997
|
+
logger_default.error("failed to reconcile variants", logger_default.errorData(err));
|
|
8998
|
+
}
|
|
8495
8999
|
const allMinds = await readAllMinds();
|
|
8496
9000
|
const runningEntries = allMinds.filter((e) => e.running && e.mindType !== "spirit");
|
|
8497
9001
|
{
|
|
@@ -8521,14 +9025,14 @@ async function startDaemon(opts) {
|
|
|
8521
9025
|
await Promise.all(workers);
|
|
8522
9026
|
}
|
|
8523
9027
|
try {
|
|
8524
|
-
const { isSetupComplete: isSetupComplete2 } = await import("./setup-
|
|
9028
|
+
const { isSetupComplete: isSetupComplete2 } = await import("./setup-TCN7VH5U.js");
|
|
8525
9029
|
if (isSetupComplete2()) {
|
|
8526
|
-
const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-
|
|
8527
|
-
const { startSpiritFull } = await import("./mind-service-
|
|
9030
|
+
const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-SSJMYC6A.js");
|
|
9031
|
+
const { startSpiritFull } = await import("./mind-service-42GDO4KH.js");
|
|
8528
9032
|
await ensureSpiritProject();
|
|
8529
9033
|
const spiritEntry = await findMind(SPIRIT_NAME);
|
|
8530
9034
|
if (spiritEntry?.dir) {
|
|
8531
|
-
const { registerMindDir } = await import("./delivery-router-
|
|
9035
|
+
const { registerMindDir } = await import("./delivery-router-VUOL5YSA.js");
|
|
8532
9036
|
registerMindDir(SPIRIT_NAME, spiritEntry.dir);
|
|
8533
9037
|
}
|
|
8534
9038
|
await syncSpiritTemplate();
|
|
@@ -8545,7 +9049,7 @@ async function startDaemon(opts) {
|
|
|
8545
9049
|
bridgeManager.startBridges(daemonPort).catch((err) => {
|
|
8546
9050
|
logger_default.warn("failed to start bridges", logger_default.errorData(err));
|
|
8547
9051
|
});
|
|
8548
|
-
import("./cloud-sync-
|
|
9052
|
+
import("./cloud-sync-VFDTNNCA.js").then(
|
|
8549
9053
|
({ consumeQueuedMessages }) => consumeQueuedMessages().catch((err) => {
|
|
8550
9054
|
logger_default.warn("failed to consume queued cloud messages", logger_default.errorData(err));
|
|
8551
9055
|
})
|
|
@@ -8553,7 +9057,7 @@ async function startDaemon(opts) {
|
|
|
8553
9057
|
logger_default.warn("failed to load cloud-sync module", logger_default.errorData(err));
|
|
8554
9058
|
});
|
|
8555
9059
|
try {
|
|
8556
|
-
const { backfillTemplateHashes, notifyVersionUpdate, warnStaleTemplates } = await import("./version-notify-
|
|
9060
|
+
const { backfillTemplateHashes, notifyVersionUpdate, warnStaleTemplates } = await import("./version-notify-WFA5XB5A.js");
|
|
8557
9061
|
backfillTemplateHashes().then(() => warnStaleTemplates()).catch((err) => {
|
|
8558
9062
|
logger_default.warn("failed to check template staleness", logger_default.errorData(err));
|
|
8559
9063
|
});
|
|
@@ -8583,13 +9087,13 @@ async function startDaemon(opts) {
|
|
|
8583
9087
|
logger_default.info(`running on ${hostname}:${port}, pid ${myPid}`);
|
|
8584
9088
|
function cleanup() {
|
|
8585
9089
|
try {
|
|
8586
|
-
if (
|
|
9090
|
+
if (readFileSync12(DAEMON_PID_PATH, "utf-8").trim() === myPid) {
|
|
8587
9091
|
unlinkSync2(DAEMON_PID_PATH);
|
|
8588
9092
|
}
|
|
8589
9093
|
} catch {
|
|
8590
9094
|
}
|
|
8591
9095
|
try {
|
|
8592
|
-
if (
|
|
9096
|
+
if (readFileSync12(DAEMON_TOKEN_PATH, "utf-8").trim() === token) {
|
|
8593
9097
|
unlinkSync2(DAEMON_TOKEN_PATH);
|
|
8594
9098
|
try {
|
|
8595
9099
|
unlinkSync2(DAEMON_JSON_PATH);
|