volute 0.24.0 → 0.26.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 +15 -20
- package/dist/{activity-events-4O37J7PD.js → activity-events-ZMBAKLUF.js} +2 -2
- package/dist/api.d.ts +590 -10
- package/dist/{auth-HM2RSPY7.js → auth-4TV573WE.js} +2 -2
- package/dist/{channel-HZOSHGNF.js → channel-ZVZV42UD.js} +3 -3
- package/dist/{chunk-NOBRGACV.js → chunk-2VO7453N.js} +56 -19
- package/dist/{chunk-OOW675I3.js → chunk-3CFRE2VC.js} +931 -775
- package/dist/{chunk-PHHKNGA3.js → chunk-3TV4GLFO.js} +2 -2
- package/dist/{chunk-4TJ72QQ3.js → chunk-5Y3PBKW6.js} +3 -3
- package/dist/{chunk-BFK6SOEJ.js → chunk-J2CO4WEV.js} +1 -1
- package/dist/{chunk-TQDITGES.js → chunk-LX22GRG7.js} +10 -13
- package/dist/{chunk-E7GOKNOT.js → chunk-NWI2425I.js} +1 -1
- package/dist/{chunk-2767L2RZ.js → chunk-OZFKBXD6.js} +1 -1
- package/dist/{chunk-XLC342FO.js → chunk-SIAG3QMM.js} +14 -1
- package/dist/{chunk-RVKR2R7F.js → chunk-SSI47XP2.js} +10 -2
- package/dist/chunk-TZKJLDQN.js +78 -0
- package/dist/{chunk-P3W36ZGD.js → chunk-USNBKHYG.js} +33 -5
- package/dist/chunk-UTL75LP6.js +113 -0
- package/dist/{chunk-3AIBT4TW.js → chunk-V63B7DX3.js} +24 -1
- package/dist/{chunk-33XAVCS4.js → chunk-WBHMQ5OZ.js} +49 -0
- package/dist/{chunk-TRQEV3CD.js → chunk-WGOGUMPO.js} +22 -3
- package/dist/chunk-XOXLRRR2.js +176 -0
- package/dist/{chunk-JTDFJWI2.js → chunk-YJA7P64S.js} +1 -1
- package/dist/chunk-ZYGKG6VC.js +22 -0
- package/dist/cli.js +44 -20
- package/dist/{cloud-sync-DIU3OCPV.js → cloud-sync-NI2K3C7G.js} +11 -9
- package/dist/{connector-M6XFI6GM.js → connector-G722WXAU.js} +4 -4
- package/dist/{create-VDQJER52.js → create-4YBRTTJS.js} +1 -1
- package/dist/{daemon-client-JOVQZ52X.js → daemon-client-Z7FAJ6JW.js} +1 -1
- package/dist/{daemon-restart-YMPEATQH.js → daemon-restart-BJZ3O4U4.js} +6 -5
- package/dist/daemon.js +982 -340
- package/dist/{delete-2MRR4JX5.js → delete-27OYNK25.js} +1 -1
- package/dist/{down-674SX2IZ.js → down-7UKFMJJZ.js} +4 -4
- package/dist/{env-2FPOZK37.js → env-M336ONDP.js} +4 -4
- package/dist/{export-IKFAPRAO.js → export-HP4G5DQC.js} +1 -1
- package/dist/{file-KT3UIQM3.js → file-HUDKTRAS.js} +3 -3
- package/dist/{history-46WZN5CN.js → history-B64GTFTD.js} +3 -3
- package/dist/{import-FRDPQPJ2.js → import-XIB7UV4S.js} +2 -2
- package/dist/{log-6SGSSR3D.js → log-PBFNILJ4.js} +3 -3
- package/dist/{login-UO6AOVEA.js → login-6U7U6BNG.js} +1 -1
- package/dist/login-B5E7N7MY.js +46 -0
- package/dist/logout-XSJRYS3U.js +39 -0
- package/dist/{logs-HRBONI5I.js → logs-3CART7O7.js} +3 -3
- package/dist/{merge-KSFJKX6T.js → merge-VK2HSKMA.js} +3 -3
- package/dist/{message-delivery-S7BCNV6Y.js → message-delivery-MS5JYPZX.js} +11 -9
- package/dist/{mind-KPLCRKQA.js → mind-HZ3QSDDJ.js} +17 -17
- package/dist/{mind-activity-tracker-NMDDEV3K.js → mind-activity-tracker-4G6FURY2.js} +3 -3
- package/dist/{mind-manager-ZNRIYEK3.js → mind-manager-VVK67AY3.js} +6 -4
- package/dist/{mind-sleep-GHPTSAYN.js → mind-sleep-DTV7L44D.js} +3 -3
- package/dist/{mind-wake-BJDJFMDF.js → mind-wake-PFN4FN3T.js} +3 -3
- package/dist/notes-37FW2UR2.js +230 -0
- package/dist/{package-S5YF25XV.js → package-VZWLXPHV.js} +3 -1
- package/dist/{pages-TWR6U7DS.js → pages-DIIT5HMQ.js} +1 -1
- package/dist/{publish-BZNHKUUK.js → publish-HQV7YREB.js} +4 -4
- package/dist/{pull-D32SPFVU.js → pull-2MB4SK3C.js} +3 -3
- package/dist/{register-U2UO6TC4.js → register-EFND67FQ.js} +1 -1
- package/dist/{restart-5BMNV7KU.js → restart-CCK7D6TV.js} +3 -3
- package/dist/sandbox-EHGFF52K.js +19 -0
- package/dist/{schedule-YEFDLVMJ.js → schedule-6F7ELB2M.js} +3 -3
- package/dist/{seed-6FEKB3YC.js → seed-E5OQGWX3.js} +1 -1
- package/dist/{send-IISDYFCL.js → send-IH6XZKPC.js} +6 -20
- package/dist/service-LLBV3R7M.js +122 -0
- package/dist/setup-F6TWFYGQ.js +371 -0
- package/dist/setup-YGAAIKKZ.js +17 -0
- package/dist/{shared-LWMNTTZN.js → shared-UMO4S7CC.js} +4 -4
- package/dist/{skill-BQOFACEI.js → skill-42LGFBQC.js} +13 -5
- package/dist/skills/dreaming/SKILL.md +68 -0
- package/dist/skills/dreaming/references/INSTALL.md +56 -0
- package/dist/skills/dreaming/scripts/dream.ts +289 -0
- package/dist/skills/dreaming/scripts/wake-context-dreams.sh +30 -0
- package/dist/skills/imagegen/SKILL.md +37 -0
- package/dist/skills/imagegen/references/INSTALL.md +13 -0
- package/dist/skills/imagegen/scripts/imagegen.ts +136 -0
- package/dist/skills/notes/SKILL.md +34 -0
- package/dist/skills/resonance/SKILL.md +73 -0
- package/dist/skills/resonance/assets/default-config.json +21 -0
- package/dist/skills/resonance/references/INSTALL.md +23 -0
- package/dist/skills/resonance/scripts/resonance.ts +1250 -0
- package/dist/skills/volute-mind/SKILL.md +23 -3
- package/dist/{sleep-manager-XXSWQQLE.js → sleep-manager-EE4NRN2Q.js} +11 -9
- package/dist/{sprout-CGSW4CF5.js → sprout-QL74KR2X.js} +5 -5
- package/dist/{start-C7XITZ5O.js → start-O5JQASRC.js} +3 -3
- package/dist/{status-SIRPLEZC.js → status-FZBEBM7Q.js} +3 -3
- package/dist/{status-LYS4NUOZ.js → status-WXD4HXRL.js} +3 -3
- package/dist/{stop-CVKBSLXY.js → stop-2SOG5NYF.js} +3 -3
- package/dist/up-SDMCSVI3.js +17 -0
- package/dist/{update-7XCZMYBT.js → update-5VUDAI3D.js} +6 -6
- package/dist/{upgrade-7RUIXGOO.js → upgrade-QCCO33BK.js} +1 -1
- package/dist/{variant-UGREB4G5.js → variant-WWLDY6D5.js} +4 -4
- package/dist/{version-notify-SZ75QRGO.js → version-notify-USFZBWMG.js} +11 -9
- package/dist/web-assets/assets/index-CUQ31ieL.js +69 -0
- package/dist/web-assets/assets/index-CW8NSl1o.css +1 -0
- package/dist/web-assets/favicon.png +0 -0
- package/dist/web-assets/index.html +5 -4
- package/dist/web-assets/logo.png +0 -0
- package/drizzle/0015_notes.sql +23 -0
- package/drizzle/0016_note_reactions_and_replies.sql +15 -0
- package/drizzle/meta/_journal.json +14 -0
- package/package.json +3 -1
- package/templates/_base/.init/.config/hooks/wake-context.sh +7 -0
- package/templates/_base/home/public/.gitkeep +0 -0
- package/templates/_base/src/lib/startup.ts +8 -0
- package/templates/claude/src/agent.ts +51 -1
- package/templates/claude/src/server.ts +1 -0
- package/templates/pi/package.json.tmpl +1 -0
- package/templates/pi/src/agent.ts +48 -1
- package/templates/pi/src/lib/subagents.ts +150 -0
- package/templates/pi/src/server.ts +1 -0
- package/dist/chunk-NWPT4ASZ.js +0 -89
- package/dist/service-FASYWLTC.js +0 -247
- package/dist/setup-BMLM2UTK.js +0 -230
- package/dist/up-OMHACRJL.js +0 -15
- package/dist/web-assets/assets/index-Bx9WDoaQ.js +0 -69
- package/dist/web-assets/assets/index-Clz8OhmJ.css +0 -1
package/dist/daemon.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
sharedMerge,
|
|
8
8
|
sharedPull,
|
|
9
9
|
sharedStatus
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-3TV4GLFO.js";
|
|
11
11
|
import {
|
|
12
12
|
applyInitFiles,
|
|
13
13
|
composeTemplate,
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
} from "./chunk-AKPFNL7L.js";
|
|
19
19
|
import {
|
|
20
20
|
addMessage,
|
|
21
|
+
announceToSystem,
|
|
21
22
|
approveUser,
|
|
22
23
|
changePassword,
|
|
23
24
|
countAdmins,
|
|
@@ -28,6 +29,7 @@ import {
|
|
|
28
29
|
deleteMindUser as deleteMindUser2,
|
|
29
30
|
deleteUser,
|
|
30
31
|
deliverMessage,
|
|
32
|
+
ensureSystemChannel,
|
|
31
33
|
extractTextContent,
|
|
32
34
|
findDMConversation,
|
|
33
35
|
fireWebhook,
|
|
@@ -58,6 +60,7 @@ import {
|
|
|
58
60
|
isParticipant,
|
|
59
61
|
isParticipantOrOwner,
|
|
60
62
|
joinChannel,
|
|
63
|
+
joinSystemChannel,
|
|
61
64
|
leaveChannel,
|
|
62
65
|
listChannels,
|
|
63
66
|
listConversationsForUser,
|
|
@@ -66,6 +69,7 @@ import {
|
|
|
66
69
|
listUsers,
|
|
67
70
|
listUsersByType,
|
|
68
71
|
markConversationRead,
|
|
72
|
+
migrateMindRoles,
|
|
69
73
|
publish,
|
|
70
74
|
publish2,
|
|
71
75
|
publishTypingForChannels,
|
|
@@ -78,19 +82,21 @@ import {
|
|
|
78
82
|
subscribe2 as subscribe3,
|
|
79
83
|
updateUserProfile,
|
|
80
84
|
verifyUser
|
|
81
|
-
} from "./chunk-
|
|
85
|
+
} from "./chunk-3CFRE2VC.js";
|
|
82
86
|
import {
|
|
83
|
-
|
|
87
|
+
deleteSystemsConfig,
|
|
88
|
+
readSystemsConfig,
|
|
89
|
+
writeSystemsConfig
|
|
84
90
|
} from "./chunk-HFCBO2GL.js";
|
|
85
91
|
import {
|
|
86
92
|
getActiveMinds,
|
|
87
93
|
onMindEvent,
|
|
88
94
|
stopAll
|
|
89
|
-
} from "./chunk-
|
|
95
|
+
} from "./chunk-NWI2425I.js";
|
|
90
96
|
import {
|
|
91
97
|
broadcast,
|
|
92
98
|
subscribe
|
|
93
|
-
} from "./chunk-
|
|
99
|
+
} from "./chunk-J2CO4WEV.js";
|
|
94
100
|
import {
|
|
95
101
|
PROMPT_DEFAULTS,
|
|
96
102
|
PROMPT_KEYS,
|
|
@@ -100,28 +106,10 @@ import {
|
|
|
100
106
|
getPrompt,
|
|
101
107
|
getPromptIfCustom,
|
|
102
108
|
initMindManager,
|
|
109
|
+
resolveMindToken,
|
|
103
110
|
substitute
|
|
104
|
-
} from "./chunk-
|
|
105
|
-
import
|
|
106
|
-
findOpenClawSession,
|
|
107
|
-
importOpenClawConnectors,
|
|
108
|
-
importPiSession,
|
|
109
|
-
parseNameFromIdentity
|
|
110
|
-
} from "./chunk-4TJ72QQ3.js";
|
|
111
|
-
import {
|
|
112
|
-
readVoluteConfig,
|
|
113
|
-
writeVoluteConfig
|
|
114
|
-
} from "./chunk-XLC342FO.js";
|
|
115
|
-
import {
|
|
116
|
-
loadMergedEnv,
|
|
117
|
-
mindEnvPath,
|
|
118
|
-
readEnv,
|
|
119
|
-
sharedEnvPath,
|
|
120
|
-
writeEnv
|
|
121
|
-
} from "./chunk-PHU4DEAJ.js";
|
|
122
|
-
import {
|
|
123
|
-
isHomeOnlyArchive
|
|
124
|
-
} from "./chunk-KTJGZ7M7.js";
|
|
111
|
+
} from "./chunk-2VO7453N.js";
|
|
112
|
+
import "./chunk-UTL75LP6.js";
|
|
125
113
|
import {
|
|
126
114
|
SEED_SKILLS,
|
|
127
115
|
STANDARD_SKILLS,
|
|
@@ -137,25 +125,53 @@ import {
|
|
|
137
125
|
syncBuiltinSkills,
|
|
138
126
|
uninstallSkill,
|
|
139
127
|
updateSkill
|
|
140
|
-
} from "./chunk-
|
|
128
|
+
} from "./chunk-USNBKHYG.js";
|
|
141
129
|
import {
|
|
142
130
|
activity,
|
|
143
131
|
conversations,
|
|
144
132
|
getDb,
|
|
145
133
|
mindHistory,
|
|
134
|
+
noteComments,
|
|
135
|
+
noteReactions,
|
|
136
|
+
notes,
|
|
146
137
|
sessions,
|
|
147
|
-
systemPrompts
|
|
148
|
-
|
|
138
|
+
systemPrompts,
|
|
139
|
+
users
|
|
140
|
+
} from "./chunk-WBHMQ5OZ.js";
|
|
149
141
|
import {
|
|
150
142
|
logBuffer,
|
|
151
143
|
logger_default
|
|
152
144
|
} from "./chunk-YUIHSKR6.js";
|
|
153
|
-
import
|
|
145
|
+
import {
|
|
146
|
+
checkForUpdate,
|
|
147
|
+
checkForUpdateCached,
|
|
148
|
+
getCurrentVersion
|
|
149
|
+
} from "./chunk-ON3FF5JA.js";
|
|
150
|
+
import {
|
|
151
|
+
findOpenClawSession,
|
|
152
|
+
importOpenClawConnectors,
|
|
153
|
+
importPiSession,
|
|
154
|
+
parseNameFromIdentity
|
|
155
|
+
} from "./chunk-5Y3PBKW6.js";
|
|
156
|
+
import {
|
|
157
|
+
readVoluteConfig,
|
|
158
|
+
writeVoluteConfig
|
|
159
|
+
} from "./chunk-SIAG3QMM.js";
|
|
160
|
+
import {
|
|
161
|
+
loadMergedEnv,
|
|
162
|
+
mindEnvPath,
|
|
163
|
+
readEnv,
|
|
164
|
+
sharedEnvPath,
|
|
165
|
+
writeEnv
|
|
166
|
+
} from "./chunk-PHU4DEAJ.js";
|
|
167
|
+
import {
|
|
168
|
+
isHomeOnlyArchive
|
|
169
|
+
} from "./chunk-KTJGZ7M7.js";
|
|
154
170
|
import {
|
|
155
171
|
exec,
|
|
156
172
|
gitExec,
|
|
157
173
|
resolveVoluteBin
|
|
158
|
-
} from "./chunk-
|
|
174
|
+
} from "./chunk-YJA7P64S.js";
|
|
159
175
|
import {
|
|
160
176
|
chownMindDir,
|
|
161
177
|
createMindUser,
|
|
@@ -163,12 +179,8 @@ import {
|
|
|
163
179
|
ensureVoluteGroup,
|
|
164
180
|
isIsolationEnabled,
|
|
165
181
|
wrapForIsolation
|
|
166
|
-
} from "./chunk-
|
|
167
|
-
import
|
|
168
|
-
checkForUpdate,
|
|
169
|
-
checkForUpdateCached,
|
|
170
|
-
getCurrentVersion
|
|
171
|
-
} from "./chunk-ON3FF5JA.js";
|
|
182
|
+
} from "./chunk-XOXLRRR2.js";
|
|
183
|
+
import "./chunk-D424ZQGI.js";
|
|
172
184
|
import {
|
|
173
185
|
buildVoluteSlug,
|
|
174
186
|
resolveChannelId,
|
|
@@ -176,6 +188,7 @@ import {
|
|
|
176
188
|
splitMessage,
|
|
177
189
|
writeChannelEntry
|
|
178
190
|
} from "./chunk-WSLPZF72.js";
|
|
191
|
+
import "./chunk-TZKJLDQN.js";
|
|
179
192
|
import {
|
|
180
193
|
addMind,
|
|
181
194
|
addVariant,
|
|
@@ -210,7 +223,7 @@ import {
|
|
|
210
223
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
211
224
|
import { mkdirSync as mkdirSync9, readFileSync as readFileSync11, unlinkSync, writeFileSync as writeFileSync9 } from "fs";
|
|
212
225
|
import { homedir as homedir2 } from "os";
|
|
213
|
-
import { resolve as
|
|
226
|
+
import { resolve as resolve19 } from "path";
|
|
214
227
|
import { format } from "util";
|
|
215
228
|
|
|
216
229
|
// src/lib/migrate-agents-to-minds.ts
|
|
@@ -360,6 +373,17 @@ function migrateDotVoluteDir(name) {
|
|
|
360
373
|
console.warn(`[migrate] both .volute/ and .mind/ exist for ${name}, skipping rename`);
|
|
361
374
|
}
|
|
362
375
|
}
|
|
376
|
+
function migratePagesDirToPublic(name) {
|
|
377
|
+
const dir = mindDir(name);
|
|
378
|
+
const oldPagesDir = resolve2(dir, "home", "pages");
|
|
379
|
+
const newPublicDir = resolve2(dir, "home", "public");
|
|
380
|
+
const newPagesDir = resolve2(newPublicDir, "pages");
|
|
381
|
+
if (existsSync2(oldPagesDir) && !existsSync2(newPagesDir)) {
|
|
382
|
+
mkdirSync(newPublicDir, { recursive: true });
|
|
383
|
+
renameSync2(oldPagesDir, newPagesDir);
|
|
384
|
+
logger_default.info(`migrated pages/ \u2192 public/pages/ for ${name}`);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
363
387
|
function migrateMindState(name) {
|
|
364
388
|
const src = resolve2(mindDir(name), ".mind");
|
|
365
389
|
if (!existsSync2(src)) return;
|
|
@@ -396,7 +420,7 @@ function isValidDaemonToken(token) {
|
|
|
396
420
|
if (!expected || token.length !== expected.length) return false;
|
|
397
421
|
return timingSafeEqual(Buffer.from(token), Buffer.from(expected));
|
|
398
422
|
}
|
|
399
|
-
var SESSION_MAX_AGE =
|
|
423
|
+
var SESSION_MAX_AGE = 365 * 24 * 60 * 60 * 1e3;
|
|
400
424
|
var SESSION_CACHE_TTL = 5 * 60 * 1e3;
|
|
401
425
|
var sessionCache = /* @__PURE__ */ new Map();
|
|
402
426
|
function invalidateSessionCache(sessionId) {
|
|
@@ -435,6 +459,24 @@ var requireAdmin = createMiddleware(async (c, next) => {
|
|
|
435
459
|
}
|
|
436
460
|
await next();
|
|
437
461
|
});
|
|
462
|
+
async function resolveSession(sessionId) {
|
|
463
|
+
const cached = sessionCache.get(sessionId);
|
|
464
|
+
if (cached && cached.expires > Date.now()) {
|
|
465
|
+
return cached.user;
|
|
466
|
+
}
|
|
467
|
+
const userId = await getSessionUserId(sessionId);
|
|
468
|
+
if (userId == null) {
|
|
469
|
+
sessionCache.delete(sessionId);
|
|
470
|
+
return null;
|
|
471
|
+
}
|
|
472
|
+
const user = await getUser(userId);
|
|
473
|
+
if (!user) {
|
|
474
|
+
sessionCache.delete(sessionId);
|
|
475
|
+
return null;
|
|
476
|
+
}
|
|
477
|
+
sessionCache.set(sessionId, { userId, user, expires: Date.now() + SESSION_CACHE_TTL });
|
|
478
|
+
return user;
|
|
479
|
+
}
|
|
438
480
|
var authMiddleware = createMiddleware(async (c, next) => {
|
|
439
481
|
const authHeader = c.req.header("Authorization");
|
|
440
482
|
if (authHeader?.startsWith("Bearer ")) {
|
|
@@ -442,7 +484,7 @@ var authMiddleware = createMiddleware(async (c, next) => {
|
|
|
442
484
|
if (token && isValidDaemonToken(token)) {
|
|
443
485
|
c.set("user", {
|
|
444
486
|
id: 0,
|
|
445
|
-
username: "
|
|
487
|
+
username: "daemon",
|
|
446
488
|
role: "admin",
|
|
447
489
|
user_type: "brain",
|
|
448
490
|
display_name: null,
|
|
@@ -452,41 +494,52 @@ var authMiddleware = createMiddleware(async (c, next) => {
|
|
|
452
494
|
await next();
|
|
453
495
|
return;
|
|
454
496
|
}
|
|
497
|
+
const mindName = resolveMindToken(token);
|
|
498
|
+
if (mindName) {
|
|
499
|
+
const mindUser = await getOrCreateMindUser(mindName);
|
|
500
|
+
c.set("user", mindUser);
|
|
501
|
+
await next();
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
if (token) {
|
|
505
|
+
const user2 = await resolveSession(token);
|
|
506
|
+
if (user2) {
|
|
507
|
+
if (user2.role === "pending") return c.json({ error: "Account pending approval" }, 403);
|
|
508
|
+
c.set("user", user2);
|
|
509
|
+
await next();
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
455
513
|
}
|
|
456
514
|
const sessionId = getCookie(c, "volute_session");
|
|
457
515
|
if (!sessionId) return c.json({ error: "Unauthorized" }, 401);
|
|
458
|
-
const
|
|
459
|
-
if (
|
|
460
|
-
if (cached.user.role === "pending") return c.json({ error: "Account pending approval" }, 403);
|
|
461
|
-
c.set("user", cached.user);
|
|
462
|
-
await next();
|
|
463
|
-
return;
|
|
464
|
-
}
|
|
465
|
-
const userId = await getSessionUserId(sessionId);
|
|
466
|
-
if (userId == null) {
|
|
467
|
-
sessionCache.delete(sessionId);
|
|
468
|
-
return c.json({ error: "Unauthorized" }, 401);
|
|
469
|
-
}
|
|
470
|
-
const user = await getUser(userId);
|
|
471
|
-
if (!user) {
|
|
472
|
-
sessionCache.delete(sessionId);
|
|
473
|
-
return c.json({ error: "Unauthorized" }, 401);
|
|
474
|
-
}
|
|
516
|
+
const user = await resolveSession(sessionId);
|
|
517
|
+
if (!user) return c.json({ error: "Unauthorized" }, 401);
|
|
475
518
|
if (user.role === "pending") return c.json({ error: "Account pending approval" }, 403);
|
|
476
|
-
sessionCache.set(sessionId, { userId, user, expires: Date.now() + SESSION_CACHE_TTL });
|
|
477
519
|
c.set("user", user);
|
|
478
520
|
await next();
|
|
479
521
|
});
|
|
522
|
+
var requireSelf = (paramName = "name") => createMiddleware(async (c, next) => {
|
|
523
|
+
const user = c.get("user");
|
|
524
|
+
if (user.role !== "admin") {
|
|
525
|
+
const target = c.req.param(paramName) ?? "";
|
|
526
|
+
const [baseName] = target.split("@", 2);
|
|
527
|
+
if (user.username !== baseName) {
|
|
528
|
+
return c.json({ error: "Forbidden" }, 403);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
await next();
|
|
532
|
+
});
|
|
480
533
|
|
|
481
534
|
// src/web/server.ts
|
|
482
535
|
import { existsSync as existsSync13 } from "fs";
|
|
483
|
-
import { readFile as
|
|
536
|
+
import { readFile as readFile4, stat as stat4 } from "fs/promises";
|
|
484
537
|
import { createServer as createHttpsServer } from "https";
|
|
485
|
-
import { dirname, extname as
|
|
538
|
+
import { dirname, extname as extname5, resolve as resolve18 } from "path";
|
|
486
539
|
import { serve } from "@hono/node-server";
|
|
487
540
|
|
|
488
541
|
// src/web/app.ts
|
|
489
|
-
import { Hono as
|
|
542
|
+
import { Hono as Hono30 } from "hono";
|
|
490
543
|
import { bodyLimit } from "hono/body-limit";
|
|
491
544
|
import { csrf } from "hono/csrf";
|
|
492
545
|
import { HTTPException } from "hono/http-exception";
|
|
@@ -538,7 +591,7 @@ var app = new Hono().get("/events", async (c) => {
|
|
|
538
591
|
});
|
|
539
592
|
cleanups.push(unsubActivity);
|
|
540
593
|
for (const conv of conversations2) {
|
|
541
|
-
const unsubConv =
|
|
594
|
+
const unsubConv = subscribe2(conv.id, (event) => {
|
|
542
595
|
stream.writeSSE({
|
|
543
596
|
data: JSON.stringify({ event: "conversation", conversationId: conv.id, ...event })
|
|
544
597
|
}).catch((err) => {
|
|
@@ -553,8 +606,8 @@ var app = new Hono().get("/events", async (c) => {
|
|
|
553
606
|
});
|
|
554
607
|
}, 15e3);
|
|
555
608
|
cleanups.push(() => clearInterval(keepAlive));
|
|
556
|
-
await new Promise((
|
|
557
|
-
stream.onAbort(() =>
|
|
609
|
+
await new Promise((resolve20) => {
|
|
610
|
+
stream.onAbort(() => resolve20());
|
|
558
611
|
});
|
|
559
612
|
} finally {
|
|
560
613
|
for (const cleanup of cleanups) {
|
|
@@ -575,6 +628,17 @@ import { zValidator } from "@hono/zod-validator";
|
|
|
575
628
|
import { Hono as Hono2 } from "hono";
|
|
576
629
|
import { deleteCookie, getCookie as getCookie2, setCookie } from "hono/cookie";
|
|
577
630
|
import { z } from "zod";
|
|
631
|
+
function tryJoinSystem(userId) {
|
|
632
|
+
if (!process.env.VOLUTE_DAEMON_TOKEN) return;
|
|
633
|
+
joinSystemChannel(userId).catch(() => {
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
var SESSION_COOKIE_OPTIONS = {
|
|
637
|
+
path: "/",
|
|
638
|
+
httpOnly: true,
|
|
639
|
+
sameSite: "Lax",
|
|
640
|
+
maxAge: Math.floor(SESSION_MAX_AGE / 1e3)
|
|
641
|
+
};
|
|
578
642
|
var credentialsSchema = z.object({
|
|
579
643
|
username: z.string().min(1),
|
|
580
644
|
password: z.string().min(1)
|
|
@@ -610,6 +674,11 @@ var authenticated = new Hono2().use(authMiddleware).post("/change-password", zVa
|
|
|
610
674
|
await updateUserProfile(user.id, body);
|
|
611
675
|
const sessionId = getCookie2(c, "volute_session");
|
|
612
676
|
if (sessionId) invalidateSessionCache(sessionId);
|
|
677
|
+
broadcast({
|
|
678
|
+
type: "profile_updated",
|
|
679
|
+
mind: user.username,
|
|
680
|
+
summary: `${user.username} profile updated`
|
|
681
|
+
});
|
|
613
682
|
return c.json({ ok: true });
|
|
614
683
|
}).post("/avatar", async (c) => {
|
|
615
684
|
const user = c.get("user");
|
|
@@ -637,6 +706,11 @@ var authenticated = new Hono2().use(authMiddleware).post("/change-password", zVa
|
|
|
637
706
|
await updateUserProfile(user.id, { avatar: filename });
|
|
638
707
|
const sessionId = getCookie2(c, "volute_session");
|
|
639
708
|
if (sessionId) invalidateSessionCache(sessionId);
|
|
709
|
+
broadcast({
|
|
710
|
+
type: "profile_updated",
|
|
711
|
+
mind: user.username,
|
|
712
|
+
summary: `${user.username} avatar updated`
|
|
713
|
+
});
|
|
640
714
|
return c.json({ ok: true, avatar: filename });
|
|
641
715
|
}).delete("/avatar", async (c) => {
|
|
642
716
|
const user = c.get("user");
|
|
@@ -647,6 +721,11 @@ var authenticated = new Hono2().use(authMiddleware).post("/change-password", zVa
|
|
|
647
721
|
await updateUserProfile(user.id, { avatar: null });
|
|
648
722
|
const sessionId = getCookie2(c, "volute_session");
|
|
649
723
|
if (sessionId) invalidateSessionCache(sessionId);
|
|
724
|
+
broadcast({
|
|
725
|
+
type: "profile_updated",
|
|
726
|
+
mind: user.username,
|
|
727
|
+
summary: `${user.username} avatar removed`
|
|
728
|
+
});
|
|
650
729
|
return c.json({ ok: true });
|
|
651
730
|
});
|
|
652
731
|
var admin = new Hono2().use(authMiddleware).get("/users", async (c) => {
|
|
@@ -700,6 +779,14 @@ var admin = new Hono2().use(authMiddleware).get("/users", async (c) => {
|
|
|
700
779
|
if (Number.isNaN(id)) return c.json({ error: "Invalid user ID" }, 400);
|
|
701
780
|
const body = c.req.valid("json");
|
|
702
781
|
await updateUserProfile(id, body);
|
|
782
|
+
const updatedUser = await getUser(id);
|
|
783
|
+
if (updatedUser) {
|
|
784
|
+
broadcast({
|
|
785
|
+
type: "profile_updated",
|
|
786
|
+
mind: updatedUser.username,
|
|
787
|
+
summary: `${updatedUser.username} profile updated`
|
|
788
|
+
});
|
|
789
|
+
}
|
|
703
790
|
return c.json({ ok: true });
|
|
704
791
|
}).delete("/users/:id", async (c) => {
|
|
705
792
|
const user = c.get("user");
|
|
@@ -728,8 +815,9 @@ var app2 = new Hono2().post("/register", zValidator("json", credentialsSchema),
|
|
|
728
815
|
const user = await createUser(username, password);
|
|
729
816
|
if (user.role === "admin") {
|
|
730
817
|
const sessionId = await createSession(user.id);
|
|
731
|
-
setCookie(c, "volute_session", sessionId,
|
|
818
|
+
setCookie(c, "volute_session", sessionId, SESSION_COOKIE_OPTIONS);
|
|
732
819
|
}
|
|
820
|
+
tryJoinSystem(user.id);
|
|
733
821
|
return c.json({ id: user.id, username: user.username, role: user.role });
|
|
734
822
|
}).post("/login", zValidator("json", credentialsSchema), async (c) => {
|
|
735
823
|
const { username, password } = c.req.valid("json");
|
|
@@ -738,13 +826,22 @@ var app2 = new Hono2().post("/register", zValidator("json", credentialsSchema),
|
|
|
738
826
|
return c.json({ error: "Invalid credentials" }, 401);
|
|
739
827
|
}
|
|
740
828
|
const sessionId = await createSession(user.id);
|
|
741
|
-
setCookie(c, "volute_session", sessionId,
|
|
742
|
-
|
|
829
|
+
setCookie(c, "volute_session", sessionId, SESSION_COOKIE_OPTIONS);
|
|
830
|
+
tryJoinSystem(user.id);
|
|
831
|
+
return c.json({ id: user.id, username: user.username, role: user.role, sessionId });
|
|
743
832
|
}).post("/logout", async (c) => {
|
|
744
|
-
const
|
|
745
|
-
if (
|
|
746
|
-
await deleteSession(
|
|
833
|
+
const cookieSession = getCookie2(c, "volute_session");
|
|
834
|
+
if (cookieSession) {
|
|
835
|
+
await deleteSession(cookieSession);
|
|
747
836
|
deleteCookie(c, "volute_session", { path: "/" });
|
|
837
|
+
return c.json({ ok: true });
|
|
838
|
+
}
|
|
839
|
+
const authHeader = c.req.header("Authorization");
|
|
840
|
+
if (authHeader?.startsWith("Bearer ")) {
|
|
841
|
+
const token = authHeader.slice(7);
|
|
842
|
+
if (token) {
|
|
843
|
+
await deleteSession(token);
|
|
844
|
+
}
|
|
748
845
|
}
|
|
749
846
|
return c.json({ ok: true });
|
|
750
847
|
}).get("/me", async (c) => {
|
|
@@ -1053,8 +1150,8 @@ async function listConversations2(env) {
|
|
|
1053
1150
|
const userMap = /* @__PURE__ */ new Map();
|
|
1054
1151
|
const imChannels = data.channels.filter((ch) => ch.is_im && ch.user);
|
|
1055
1152
|
if (imChannels.length > 0) {
|
|
1056
|
-
const
|
|
1057
|
-
for (const u of
|
|
1153
|
+
const users2 = await listUsers3(env);
|
|
1154
|
+
for (const u of users2) {
|
|
1058
1155
|
userMap.set(u.id, u.username);
|
|
1059
1156
|
}
|
|
1060
1157
|
}
|
|
@@ -1446,7 +1543,7 @@ function resolveChannelId2(env, slug) {
|
|
|
1446
1543
|
function buildEnv(name) {
|
|
1447
1544
|
return { ...loadMergedEnv(name), VOLUTE_MIND: name, VOLUTE_MIND_DIR: mindDir(name) };
|
|
1448
1545
|
}
|
|
1449
|
-
var app3 = new Hono3().post("/:name/channels/send",
|
|
1546
|
+
var app3 = new Hono3().post("/:name/channels/send", requireSelf(), async (c) => {
|
|
1450
1547
|
const name = c.req.param("name");
|
|
1451
1548
|
if (!findMind(name)) return c.json({ error: "Mind not found" }, 404);
|
|
1452
1549
|
const { platform, uri, message, images, sender } = await c.req.json();
|
|
@@ -1512,12 +1609,12 @@ var app3 = new Hono3().post("/:name/channels/send", requireAdmin, async (c) => {
|
|
|
1512
1609
|
return c.json({ error: `Platform ${platform} does not support listing users` }, 400);
|
|
1513
1610
|
const env = buildEnv(name);
|
|
1514
1611
|
try {
|
|
1515
|
-
const
|
|
1516
|
-
return c.json(
|
|
1612
|
+
const users2 = await driver.listUsers(env);
|
|
1613
|
+
return c.json(users2);
|
|
1517
1614
|
} catch (err) {
|
|
1518
1615
|
return c.json({ error: err instanceof Error ? err.message : String(err) }, 500);
|
|
1519
1616
|
}
|
|
1520
|
-
}).post("/:name/channels/create",
|
|
1617
|
+
}).post("/:name/channels/create", requireSelf(), async (c) => {
|
|
1521
1618
|
const name = c.req.param("name");
|
|
1522
1619
|
if (!findMind(name)) return c.json({ error: "Mind not found" }, 404);
|
|
1523
1620
|
const {
|
|
@@ -1872,7 +1969,7 @@ async function notifyMind(port, message, channel, sender) {
|
|
|
1872
1969
|
console.warn(`[file-sharing] notify mind on port ${port} failed:`, err);
|
|
1873
1970
|
}
|
|
1874
1971
|
}
|
|
1875
|
-
var app6 = new Hono6().post("/:name/files/send", async (c) => {
|
|
1972
|
+
var app6 = new Hono6().post("/:name/files/send", requireSelf(), async (c) => {
|
|
1876
1973
|
const senderName = c.req.param("name");
|
|
1877
1974
|
const senderEntry = findMind(senderName);
|
|
1878
1975
|
if (!senderEntry) return c.json({ error: "Sender mind not found" }, 404);
|
|
@@ -1886,13 +1983,13 @@ var app6 = new Hono6().post("/:name/files/send", async (c) => {
|
|
|
1886
1983
|
if (pathErr) return c.json({ error: pathErr }, 400);
|
|
1887
1984
|
const senderDir = mindDir(senderName);
|
|
1888
1985
|
const filePath = resolve6(senderDir, "home", body.filePath);
|
|
1889
|
-
const
|
|
1890
|
-
const
|
|
1891
|
-
if (!
|
|
1892
|
-
if (
|
|
1986
|
+
const MAX_FILE_SIZE2 = 50 * 1024 * 1024;
|
|
1987
|
+
const stat5 = statSync(filePath, { throwIfNoEntry: false });
|
|
1988
|
+
if (!stat5) return c.json({ error: `File not found: ${body.filePath}` }, 404);
|
|
1989
|
+
if (stat5.size > MAX_FILE_SIZE2) {
|
|
1893
1990
|
return c.json(
|
|
1894
1991
|
{
|
|
1895
|
-
error: `File too large (${formatFileSize(
|
|
1992
|
+
error: `File too large (${formatFileSize(stat5.size)}, max ${formatFileSize(MAX_FILE_SIZE2)})`
|
|
1896
1993
|
},
|
|
1897
1994
|
413
|
|
1898
1995
|
);
|
|
@@ -2020,12 +2117,12 @@ var app7 = new Hono7().get("/:name/avatar", async (c) => {
|
|
|
2020
2117
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
2021
2118
|
const dir = mindDir(name);
|
|
2022
2119
|
const config = readVoluteConfig(dir);
|
|
2023
|
-
if (!config?.avatar) return c.json({ error: "No avatar configured" }, 404);
|
|
2024
|
-
const ext = extname2(config.avatar).toLowerCase();
|
|
2120
|
+
if (!config?.profile?.avatar) return c.json({ error: "No avatar configured" }, 404);
|
|
2121
|
+
const ext = extname2(config.profile.avatar).toLowerCase();
|
|
2025
2122
|
const mime = AVATAR_MIME2[ext];
|
|
2026
2123
|
if (!mime) return c.json({ error: "Invalid avatar extension" }, 400);
|
|
2027
2124
|
const homeDir = resolve7(dir, "home");
|
|
2028
|
-
const avatarPath = resolve7(homeDir, config.avatar);
|
|
2125
|
+
const avatarPath = resolve7(homeDir, config.profile.avatar);
|
|
2029
2126
|
if (!avatarPath.startsWith(`${homeDir}/`)) return c.json({ error: "Invalid avatar path" }, 400);
|
|
2030
2127
|
let realAvatarPath;
|
|
2031
2128
|
try {
|
|
@@ -2197,9 +2294,9 @@ var app9 = new Hono9().get("/:name/logs", async (c) => {
|
|
|
2197
2294
|
stream.onAbort(() => {
|
|
2198
2295
|
tail.kill();
|
|
2199
2296
|
});
|
|
2200
|
-
await new Promise((
|
|
2201
|
-
tail.on("exit",
|
|
2202
|
-
stream.onAbort(
|
|
2297
|
+
await new Promise((resolve20) => {
|
|
2298
|
+
tail.on("exit", resolve20);
|
|
2299
|
+
stream.onAbort(resolve20);
|
|
2203
2300
|
});
|
|
2204
2301
|
});
|
|
2205
2302
|
}).get("/:name/logs/tail", async (c) => {
|
|
@@ -2217,8 +2314,8 @@ var app9 = new Hono9().get("/:name/logs", async (c) => {
|
|
|
2217
2314
|
tail.stdout.on("data", (data) => {
|
|
2218
2315
|
output += data.toString();
|
|
2219
2316
|
});
|
|
2220
|
-
await new Promise((
|
|
2221
|
-
tail.on("exit",
|
|
2317
|
+
await new Promise((resolve20) => {
|
|
2318
|
+
tail.on("exit", resolve20);
|
|
2222
2319
|
});
|
|
2223
2320
|
return c.text(output);
|
|
2224
2321
|
});
|
|
@@ -2246,12 +2343,12 @@ var app10 = new Hono10().get("/:name/skills", async (c) => {
|
|
|
2246
2343
|
const { skillId } = c.req.valid("json");
|
|
2247
2344
|
const dir = mindDir(name);
|
|
2248
2345
|
try {
|
|
2249
|
-
await installSkill(name, dir, skillId);
|
|
2346
|
+
const result = await installSkill(name, dir, skillId);
|
|
2347
|
+
return c.json({ ok: true, ...result });
|
|
2250
2348
|
} catch (e) {
|
|
2251
2349
|
const msg = e instanceof Error ? e.message : String(e);
|
|
2252
2350
|
return c.json({ error: msg }, 400);
|
|
2253
2351
|
}
|
|
2254
|
-
return c.json({ ok: true });
|
|
2255
2352
|
}
|
|
2256
2353
|
).post(
|
|
2257
2354
|
"/:name/skills/update",
|
|
@@ -2606,7 +2703,7 @@ async function getMindStatus(name, port) {
|
|
|
2606
2703
|
const manager = getMindManager();
|
|
2607
2704
|
let status = "stopped";
|
|
2608
2705
|
try {
|
|
2609
|
-
const { getSleepManagerIfReady } = await import("./sleep-manager-
|
|
2706
|
+
const { getSleepManagerIfReady } = await import("./sleep-manager-EE4NRN2Q.js");
|
|
2610
2707
|
if (getSleepManagerIfReady()?.isSleeping(name)) {
|
|
2611
2708
|
status = "sleeping";
|
|
2612
2709
|
}
|
|
@@ -2641,9 +2738,9 @@ async function getMindStatus(name, port) {
|
|
|
2641
2738
|
return {
|
|
2642
2739
|
status,
|
|
2643
2740
|
channels,
|
|
2644
|
-
displayName: config?.displayName,
|
|
2645
|
-
description: config?.description,
|
|
2646
|
-
avatar: config?.avatar
|
|
2741
|
+
displayName: config?.profile?.displayName,
|
|
2742
|
+
description: config?.profile?.description,
|
|
2743
|
+
avatar: config?.profile?.avatar
|
|
2647
2744
|
};
|
|
2648
2745
|
}
|
|
2649
2746
|
var TEMPLATE_BRANCH = "volute/template";
|
|
@@ -3004,11 +3101,30 @@ var app11 = new Hono11().post("/", requireAdmin, zValidator3("json", createMindS
|
|
|
3004
3101
|
copyTemplateToDir(composedDir, dest, name, manifest);
|
|
3005
3102
|
applyInitFiles(dest);
|
|
3006
3103
|
const { publicKeyPem } = generateIdentity(dest);
|
|
3007
|
-
|
|
3008
|
-
const
|
|
3009
|
-
if (!
|
|
3010
|
-
|
|
3011
|
-
|
|
3104
|
+
{
|
|
3105
|
+
const config = readVoluteConfig(dest);
|
|
3106
|
+
if (!config) throw new Error("Failed to read volute.json after identity generation");
|
|
3107
|
+
if (body.description) {
|
|
3108
|
+
config.profile = { ...config.profile, description: body.description };
|
|
3109
|
+
}
|
|
3110
|
+
if (!config.sleep) {
|
|
3111
|
+
config.sleep = {
|
|
3112
|
+
enabled: true,
|
|
3113
|
+
schedule: { sleep: "0 0 * * *", wake: "0 8 * * *" }
|
|
3114
|
+
};
|
|
3115
|
+
}
|
|
3116
|
+
if (!config.schedules || config.schedules.length === 0) {
|
|
3117
|
+
config.schedules = [
|
|
3118
|
+
{
|
|
3119
|
+
id: "heartbeat",
|
|
3120
|
+
cron: "0 12,16,20 * * *",
|
|
3121
|
+
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.",
|
|
3122
|
+
enabled: true,
|
|
3123
|
+
skipWhenSleeping: true
|
|
3124
|
+
}
|
|
3125
|
+
];
|
|
3126
|
+
}
|
|
3127
|
+
writeVoluteConfig(dest, config);
|
|
3012
3128
|
}
|
|
3013
3129
|
if (body.model) {
|
|
3014
3130
|
const configPath2 = resolve12(dest, "home/.config/config.json");
|
|
@@ -3094,6 +3210,8 @@ The human who planted you described you as: "${body.description}"
|
|
|
3094
3210
|
description: body.description
|
|
3095
3211
|
}
|
|
3096
3212
|
});
|
|
3213
|
+
announceToSystem(`${name} has joined`).catch(() => {
|
|
3214
|
+
});
|
|
3097
3215
|
return c.json({
|
|
3098
3216
|
ok: true,
|
|
3099
3217
|
name,
|
|
@@ -3247,7 +3365,7 @@ ${user.trimEnd()}
|
|
|
3247
3365
|
const minds = await Promise.all(
|
|
3248
3366
|
entries.map(async (entry) => {
|
|
3249
3367
|
const mindStatus = await getMindStatus(entry.name, entry.port);
|
|
3250
|
-
const hasPages = existsSync10(resolve12(mindDir(entry.name), "home", "pages"));
|
|
3368
|
+
const hasPages = existsSync10(resolve12(mindDir(entry.name), "home", "public", "pages"));
|
|
3251
3369
|
return {
|
|
3252
3370
|
...entry,
|
|
3253
3371
|
...mindStatus,
|
|
@@ -3280,7 +3398,7 @@ ${user.trimEnd()}
|
|
|
3280
3398
|
return { name: v.name, port: v.port, status: variantStatus };
|
|
3281
3399
|
})
|
|
3282
3400
|
);
|
|
3283
|
-
const hasPages = existsSync10(resolve12(mindDir(name), "home", "pages"));
|
|
3401
|
+
const hasPages = existsSync10(resolve12(mindDir(name), "home", "public", "pages"));
|
|
3284
3402
|
return c.json({ ...entry, ...mindStatus, variants: variantStatuses, hasPages });
|
|
3285
3403
|
}).post("/:name/start", requireAdmin, async (c) => {
|
|
3286
3404
|
const name = c.req.param("name");
|
|
@@ -3305,7 +3423,7 @@ ${user.trimEnd()}
|
|
|
3305
3423
|
} catch (err) {
|
|
3306
3424
|
return c.json({ error: err instanceof Error ? err.message : "Failed to start mind" }, 500);
|
|
3307
3425
|
}
|
|
3308
|
-
}).post("/:name/restart",
|
|
3426
|
+
}).post("/:name/restart", requireSelf(), async (c) => {
|
|
3309
3427
|
const name = c.req.param("name");
|
|
3310
3428
|
const [baseName, variantName] = name.split("@", 2);
|
|
3311
3429
|
const entry = findMind(baseName);
|
|
@@ -3331,6 +3449,14 @@ ${user.trimEnd()}
|
|
|
3331
3449
|
}
|
|
3332
3450
|
const manager = getMindManager();
|
|
3333
3451
|
try {
|
|
3452
|
+
if (context?.type === "reload") {
|
|
3453
|
+
const { getSleepManagerIfReady } = await import("./sleep-manager-EE4NRN2Q.js");
|
|
3454
|
+
const sleepState = getSleepManagerIfReady()?.getState(name);
|
|
3455
|
+
if (sleepState?.sleeping) {
|
|
3456
|
+
logger_default.info(`skipping reload for ${name} during sleep \u2014 will apply on next wake`);
|
|
3457
|
+
return c.json({ ok: true, deferred: true, port: targetPort });
|
|
3458
|
+
}
|
|
3459
|
+
}
|
|
3334
3460
|
if (manager.isRunning(name)) {
|
|
3335
3461
|
await stopMindFull(name);
|
|
3336
3462
|
}
|
|
@@ -3380,7 +3506,7 @@ ${user.trimEnd()}
|
|
|
3380
3506
|
}
|
|
3381
3507
|
}
|
|
3382
3508
|
}
|
|
3383
|
-
if (context) {
|
|
3509
|
+
if (context && context.type !== "reload") {
|
|
3384
3510
|
manager.setPendingContext(name, context);
|
|
3385
3511
|
}
|
|
3386
3512
|
if (context?.type === "sprouted" && !variantName) {
|
|
@@ -3424,7 +3550,7 @@ ${user.trimEnd()}
|
|
|
3424
3550
|
const name = c.req.param("name");
|
|
3425
3551
|
const entry = findMind(name);
|
|
3426
3552
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
3427
|
-
const { getSleepManagerIfReady } = await import("./sleep-manager-
|
|
3553
|
+
const { getSleepManagerIfReady } = await import("./sleep-manager-EE4NRN2Q.js");
|
|
3428
3554
|
const sm = getSleepManagerIfReady();
|
|
3429
3555
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
3430
3556
|
return c.json(sm.getState(name));
|
|
@@ -3432,7 +3558,7 @@ ${user.trimEnd()}
|
|
|
3432
3558
|
const name = c.req.param("name");
|
|
3433
3559
|
const entry = findMind(name);
|
|
3434
3560
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
3435
|
-
const { getSleepManagerIfReady } = await import("./sleep-manager-
|
|
3561
|
+
const { getSleepManagerIfReady } = await import("./sleep-manager-EE4NRN2Q.js");
|
|
3436
3562
|
const sm = getSleepManagerIfReady();
|
|
3437
3563
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
3438
3564
|
if (sm.isSleeping(name)) return c.json({ error: "Mind is already sleeping" }, 409);
|
|
@@ -3452,17 +3578,22 @@ ${user.trimEnd()}
|
|
|
3452
3578
|
const name = c.req.param("name");
|
|
3453
3579
|
const entry = findMind(name);
|
|
3454
3580
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
3455
|
-
const { getSleepManagerIfReady } = await import("./sleep-manager-
|
|
3581
|
+
const { getSleepManagerIfReady } = await import("./sleep-manager-EE4NRN2Q.js");
|
|
3456
3582
|
const sm = getSleepManagerIfReady();
|
|
3457
3583
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
3458
|
-
|
|
3459
|
-
|
|
3584
|
+
const sleepState = sm.getState(name);
|
|
3585
|
+
if (!sleepState.sleeping) return c.json({ error: "Mind is not sleeping" }, 409);
|
|
3586
|
+
if (sleepState.wokenByTrigger) {
|
|
3587
|
+
sm.convertTriggerToFullWake(name);
|
|
3588
|
+
} else {
|
|
3589
|
+
sm.initiateWake(name).catch((err) => logger_default.error(`failed to wake ${name}`, logger_default.errorData(err)));
|
|
3590
|
+
}
|
|
3460
3591
|
return c.json({ ok: true });
|
|
3461
3592
|
}).post("/:name/sleep/messages", requireAdmin, async (c) => {
|
|
3462
3593
|
const name = c.req.param("name");
|
|
3463
3594
|
const entry = findMind(name);
|
|
3464
3595
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
3465
|
-
const { getSleepManagerIfReady } = await import("./sleep-manager-
|
|
3596
|
+
const { getSleepManagerIfReady } = await import("./sleep-manager-EE4NRN2Q.js");
|
|
3466
3597
|
const sm = getSleepManagerIfReady();
|
|
3467
3598
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
3468
3599
|
const flushed = await sm.flushQueuedMessages(name);
|
|
@@ -3670,7 +3801,7 @@ ${user.trimEnd()}
|
|
|
3670
3801
|
500
|
|
3671
3802
|
);
|
|
3672
3803
|
}
|
|
3673
|
-
}).post("/:name/message", async (c) => {
|
|
3804
|
+
}).post("/:name/message", requireSelf(), async (c) => {
|
|
3674
3805
|
const name = c.req.param("name");
|
|
3675
3806
|
const [baseName, variantName] = name.split("@", 2);
|
|
3676
3807
|
const entry = findMind(baseName);
|
|
@@ -3680,7 +3811,7 @@ ${user.trimEnd()}
|
|
|
3680
3811
|
if (!variant) return c.json({ error: `Unknown variant: ${variantName}` }, 404);
|
|
3681
3812
|
}
|
|
3682
3813
|
try {
|
|
3683
|
-
const { getSleepManagerIfReady } = await import("./sleep-manager-
|
|
3814
|
+
const { getSleepManagerIfReady } = await import("./sleep-manager-EE4NRN2Q.js");
|
|
3684
3815
|
const sm = getSleepManagerIfReady();
|
|
3685
3816
|
if (sm?.isSleeping(baseName)) {
|
|
3686
3817
|
const body2 = await c.req.text();
|
|
@@ -3903,7 +4034,7 @@ ${user.trimEnd()}
|
|
|
3903
4034
|
logger_default.error(`failed to get pending deliveries for ${baseName}`, logger_default.errorData(err));
|
|
3904
4035
|
return c.json({ error: "Failed to retrieve pending messages" }, 500);
|
|
3905
4036
|
}
|
|
3906
|
-
}).post("/:name/events", async (c) => {
|
|
4037
|
+
}).post("/:name/events", requireSelf(), async (c) => {
|
|
3907
4038
|
const name = c.req.param("name");
|
|
3908
4039
|
const [baseName] = name.split("@", 2);
|
|
3909
4040
|
let body;
|
|
@@ -3929,7 +4060,7 @@ ${user.trimEnd()}
|
|
|
3929
4060
|
} catch (err) {
|
|
3930
4061
|
logger_default.error(`failed to persist event for ${baseName}`, logger_default.errorData(err));
|
|
3931
4062
|
}
|
|
3932
|
-
|
|
4063
|
+
publish2(baseName, {
|
|
3933
4064
|
mind: baseName,
|
|
3934
4065
|
type: body.type,
|
|
3935
4066
|
session: body.session,
|
|
@@ -3988,7 +4119,7 @@ ${user.trimEnd()}
|
|
|
3988
4119
|
unsubscribe?.();
|
|
3989
4120
|
}
|
|
3990
4121
|
}, 15e3);
|
|
3991
|
-
unsubscribe =
|
|
4122
|
+
unsubscribe = subscribe3(baseName, (event) => {
|
|
3992
4123
|
if (typeFilter && !typeFilter.includes(event.type)) return;
|
|
3993
4124
|
if (sessionFilter && event.session !== sessionFilter) return;
|
|
3994
4125
|
if (channelFilter && event.channel !== channelFilter) return;
|
|
@@ -4016,7 +4147,7 @@ ${user.trimEnd()}
|
|
|
4016
4147
|
Connection: "keep-alive"
|
|
4017
4148
|
}
|
|
4018
4149
|
});
|
|
4019
|
-
}).post("/:name/history", async (c) => {
|
|
4150
|
+
}).post("/:name/history", requireSelf(), async (c) => {
|
|
4020
4151
|
const name = c.req.param("name");
|
|
4021
4152
|
const [baseName] = name.split("@", 2);
|
|
4022
4153
|
let body;
|
|
@@ -4087,10 +4218,330 @@ ${user.trimEnd()}
|
|
|
4087
4218
|
});
|
|
4088
4219
|
var minds_default = app11;
|
|
4089
4220
|
|
|
4221
|
+
// src/web/api/notes.ts
|
|
4222
|
+
import { zValidator as zValidator4 } from "@hono/zod-validator";
|
|
4223
|
+
import { Hono as Hono12 } from "hono";
|
|
4224
|
+
import { z as z4 } from "zod";
|
|
4225
|
+
|
|
4226
|
+
// src/lib/notes.ts
|
|
4227
|
+
import { and as and2, count, desc as desc3, eq as eq3, inArray, sql as sql2 } from "drizzle-orm";
|
|
4228
|
+
async function createNote(authorId, title, content, replyToId) {
|
|
4229
|
+
const db = await getDb();
|
|
4230
|
+
let slug = slugify(title) || "untitled";
|
|
4231
|
+
const existing = await db.select({ slug: notes.slug }).from(notes).where(eq3(notes.author_id, authorId)).all();
|
|
4232
|
+
const existingSlugs = new Set(existing.map((r) => r.slug));
|
|
4233
|
+
if (existingSlugs.has(slug)) {
|
|
4234
|
+
let i = 2;
|
|
4235
|
+
while (existingSlugs.has(`${slug}-${i}`)) i++;
|
|
4236
|
+
slug = `${slug}-${i}`;
|
|
4237
|
+
}
|
|
4238
|
+
const [row] = await db.insert(notes).values({ author_id: authorId, title, slug, content, reply_to_id: replyToId ?? null }).returning();
|
|
4239
|
+
const author = await db.select().from(users).where(eq3(users.id, authorId)).get();
|
|
4240
|
+
return {
|
|
4241
|
+
...row,
|
|
4242
|
+
author_username: author?.username ?? "unknown",
|
|
4243
|
+
author_display_name: author?.display_name ?? null,
|
|
4244
|
+
comment_count: 0
|
|
4245
|
+
};
|
|
4246
|
+
}
|
|
4247
|
+
async function getNote(authorUsername, slug) {
|
|
4248
|
+
const db = await getDb();
|
|
4249
|
+
const row = await db.select({
|
|
4250
|
+
id: notes.id,
|
|
4251
|
+
author_id: notes.author_id,
|
|
4252
|
+
title: notes.title,
|
|
4253
|
+
slug: notes.slug,
|
|
4254
|
+
content: notes.content,
|
|
4255
|
+
reply_to_id: notes.reply_to_id,
|
|
4256
|
+
created_at: notes.created_at,
|
|
4257
|
+
updated_at: notes.updated_at,
|
|
4258
|
+
author_username: users.username,
|
|
4259
|
+
author_display_name: users.display_name
|
|
4260
|
+
}).from(notes).innerJoin(users, eq3(notes.author_id, users.id)).where(and2(eq3(users.username, authorUsername), eq3(notes.slug, slug))).get();
|
|
4261
|
+
if (!row) return null;
|
|
4262
|
+
const comments = await getComments(row.id);
|
|
4263
|
+
const reactions = await getReactions(row.id);
|
|
4264
|
+
let reply_to = null;
|
|
4265
|
+
if (row.reply_to_id) {
|
|
4266
|
+
const parent = await db.select({
|
|
4267
|
+
title: notes.title,
|
|
4268
|
+
slug: notes.slug,
|
|
4269
|
+
author_username: users.username
|
|
4270
|
+
}).from(notes).innerJoin(users, eq3(notes.author_id, users.id)).where(eq3(notes.id, row.reply_to_id)).get();
|
|
4271
|
+
if (parent) {
|
|
4272
|
+
reply_to = parent;
|
|
4273
|
+
}
|
|
4274
|
+
}
|
|
4275
|
+
const replies = await db.select({
|
|
4276
|
+
author_username: users.username,
|
|
4277
|
+
slug: notes.slug,
|
|
4278
|
+
title: notes.title,
|
|
4279
|
+
created_at: notes.created_at
|
|
4280
|
+
}).from(notes).innerJoin(users, eq3(notes.author_id, users.id)).where(eq3(notes.reply_to_id, row.id)).orderBy(notes.created_at).all();
|
|
4281
|
+
return { ...row, comment_count: comments.length, comments, reactions, reply_to, replies };
|
|
4282
|
+
}
|
|
4283
|
+
async function listNotes(opts) {
|
|
4284
|
+
const db = await getDb();
|
|
4285
|
+
const limit = opts?.limit ?? 50;
|
|
4286
|
+
const offset = opts?.offset ?? 0;
|
|
4287
|
+
const conditions = [];
|
|
4288
|
+
if (opts?.authorUsername) {
|
|
4289
|
+
conditions.push(eq3(users.username, opts.authorUsername));
|
|
4290
|
+
}
|
|
4291
|
+
const rows = await db.select({
|
|
4292
|
+
id: notes.id,
|
|
4293
|
+
author_id: notes.author_id,
|
|
4294
|
+
title: notes.title,
|
|
4295
|
+
slug: notes.slug,
|
|
4296
|
+
content: notes.content,
|
|
4297
|
+
reply_to_id: notes.reply_to_id,
|
|
4298
|
+
created_at: notes.created_at,
|
|
4299
|
+
updated_at: notes.updated_at,
|
|
4300
|
+
author_username: users.username,
|
|
4301
|
+
author_display_name: users.display_name
|
|
4302
|
+
}).from(notes).innerJoin(users, eq3(notes.author_id, users.id)).where(conditions.length > 0 ? and2(...conditions) : void 0).orderBy(desc3(notes.created_at)).limit(limit).offset(offset).all();
|
|
4303
|
+
const noteIds = rows.map((r) => r.id);
|
|
4304
|
+
if (noteIds.length === 0) return [];
|
|
4305
|
+
const commentCounts = await db.select({
|
|
4306
|
+
note_id: noteComments.note_id,
|
|
4307
|
+
count: count()
|
|
4308
|
+
}).from(noteComments).where(inArray(noteComments.note_id, noteIds)).groupBy(noteComments.note_id).all();
|
|
4309
|
+
const countMap = new Map(commentCounts.map((r) => [r.note_id, r.count]));
|
|
4310
|
+
const allReactions = await db.select({
|
|
4311
|
+
note_id: noteReactions.note_id,
|
|
4312
|
+
emoji: noteReactions.emoji,
|
|
4313
|
+
count: count()
|
|
4314
|
+
}).from(noteReactions).where(inArray(noteReactions.note_id, noteIds)).groupBy(noteReactions.note_id, noteReactions.emoji).all();
|
|
4315
|
+
const reactionMap = /* @__PURE__ */ new Map();
|
|
4316
|
+
for (const r of allReactions) {
|
|
4317
|
+
if (!reactionMap.has(r.note_id)) reactionMap.set(r.note_id, []);
|
|
4318
|
+
reactionMap.get(r.note_id).push({ emoji: r.emoji, count: r.count });
|
|
4319
|
+
}
|
|
4320
|
+
const replyToIds = [...new Set(rows.filter((r) => r.reply_to_id).map((r) => r.reply_to_id))];
|
|
4321
|
+
const replyToMap = /* @__PURE__ */ new Map();
|
|
4322
|
+
if (replyToIds.length > 0) {
|
|
4323
|
+
const parents = await db.select({
|
|
4324
|
+
id: notes.id,
|
|
4325
|
+
title: notes.title,
|
|
4326
|
+
slug: notes.slug,
|
|
4327
|
+
author_username: users.username
|
|
4328
|
+
}).from(notes).innerJoin(users, eq3(notes.author_id, users.id)).where(inArray(notes.id, replyToIds)).all();
|
|
4329
|
+
for (const parent of parents) {
|
|
4330
|
+
replyToMap.set(parent.id, {
|
|
4331
|
+
author_username: parent.author_username,
|
|
4332
|
+
slug: parent.slug,
|
|
4333
|
+
title: parent.title
|
|
4334
|
+
});
|
|
4335
|
+
}
|
|
4336
|
+
}
|
|
4337
|
+
return rows.map((r) => {
|
|
4338
|
+
const reactions = reactionMap.get(r.id);
|
|
4339
|
+
const topReactions = reactions ? reactions.sort((a, b) => b.count - a.count).slice(0, 3).map((rx) => ({ ...rx, usernames: [] })) : void 0;
|
|
4340
|
+
return {
|
|
4341
|
+
...r,
|
|
4342
|
+
comment_count: countMap.get(r.id) ?? 0,
|
|
4343
|
+
reactions: topReactions,
|
|
4344
|
+
reply_to: r.reply_to_id ? replyToMap.get(r.reply_to_id) ?? null : null
|
|
4345
|
+
};
|
|
4346
|
+
});
|
|
4347
|
+
}
|
|
4348
|
+
async function updateNote(authorUsername, slug, updates) {
|
|
4349
|
+
const db = await getDb();
|
|
4350
|
+
const existing = await db.select({ id: notes.id, author_id: notes.author_id }).from(notes).innerJoin(users, eq3(notes.author_id, users.id)).where(and2(eq3(users.username, authorUsername), eq3(notes.slug, slug))).get();
|
|
4351
|
+
if (!existing) return null;
|
|
4352
|
+
const set = { updated_at: sql2`(datetime('now'))` };
|
|
4353
|
+
if (updates.title !== void 0) set.title = updates.title;
|
|
4354
|
+
if (updates.content !== void 0) set.content = updates.content;
|
|
4355
|
+
await db.update(notes).set(set).where(eq3(notes.id, existing.id));
|
|
4356
|
+
return getNote(authorUsername, slug).then(
|
|
4357
|
+
(n) => n ? { ...n, comments: void 0 } : null
|
|
4358
|
+
);
|
|
4359
|
+
}
|
|
4360
|
+
async function deleteNote(authorUsername, slug, authorId) {
|
|
4361
|
+
const db = await getDb();
|
|
4362
|
+
const existing = await db.select({ id: notes.id, author_id: notes.author_id }).from(notes).innerJoin(users, eq3(notes.author_id, users.id)).where(and2(eq3(users.username, authorUsername), eq3(notes.slug, slug))).get();
|
|
4363
|
+
if (!existing || existing.author_id !== authorId) return false;
|
|
4364
|
+
await db.delete(notes).where(eq3(notes.id, existing.id));
|
|
4365
|
+
return true;
|
|
4366
|
+
}
|
|
4367
|
+
async function addComment(noteId, authorId, content) {
|
|
4368
|
+
const db = await getDb();
|
|
4369
|
+
const [row] = await db.insert(noteComments).values({ note_id: noteId, author_id: authorId, content }).returning();
|
|
4370
|
+
const author = await db.select().from(users).where(eq3(users.id, authorId)).get();
|
|
4371
|
+
return {
|
|
4372
|
+
...row,
|
|
4373
|
+
author_username: author?.username ?? "unknown",
|
|
4374
|
+
author_display_name: author?.display_name ?? null
|
|
4375
|
+
};
|
|
4376
|
+
}
|
|
4377
|
+
async function getComments(noteId) {
|
|
4378
|
+
const db = await getDb();
|
|
4379
|
+
return db.select({
|
|
4380
|
+
id: noteComments.id,
|
|
4381
|
+
note_id: noteComments.note_id,
|
|
4382
|
+
author_id: noteComments.author_id,
|
|
4383
|
+
content: noteComments.content,
|
|
4384
|
+
created_at: noteComments.created_at,
|
|
4385
|
+
author_username: users.username,
|
|
4386
|
+
author_display_name: users.display_name
|
|
4387
|
+
}).from(noteComments).innerJoin(users, eq3(noteComments.author_id, users.id)).where(eq3(noteComments.note_id, noteId)).orderBy(noteComments.created_at).all();
|
|
4388
|
+
}
|
|
4389
|
+
async function deleteComment(commentId, authorId) {
|
|
4390
|
+
const db = await getDb();
|
|
4391
|
+
const existing = await db.select({ id: noteComments.id, author_id: noteComments.author_id }).from(noteComments).where(eq3(noteComments.id, commentId)).get();
|
|
4392
|
+
if (!existing || existing.author_id !== authorId) return false;
|
|
4393
|
+
await db.delete(noteComments).where(eq3(noteComments.id, commentId));
|
|
4394
|
+
return true;
|
|
4395
|
+
}
|
|
4396
|
+
async function toggleReaction(noteId, userId, emoji) {
|
|
4397
|
+
const db = await getDb();
|
|
4398
|
+
const existing = await db.select({ id: noteReactions.id }).from(noteReactions).where(
|
|
4399
|
+
and2(
|
|
4400
|
+
eq3(noteReactions.note_id, noteId),
|
|
4401
|
+
eq3(noteReactions.user_id, userId),
|
|
4402
|
+
eq3(noteReactions.emoji, emoji)
|
|
4403
|
+
)
|
|
4404
|
+
).get();
|
|
4405
|
+
if (existing) {
|
|
4406
|
+
await db.delete(noteReactions).where(eq3(noteReactions.id, existing.id));
|
|
4407
|
+
return { added: false };
|
|
4408
|
+
}
|
|
4409
|
+
await db.insert(noteReactions).values({ note_id: noteId, user_id: userId, emoji });
|
|
4410
|
+
return { added: true };
|
|
4411
|
+
}
|
|
4412
|
+
async function getReactions(noteId) {
|
|
4413
|
+
const db = await getDb();
|
|
4414
|
+
const rows = await db.select({
|
|
4415
|
+
emoji: noteReactions.emoji,
|
|
4416
|
+
username: users.username
|
|
4417
|
+
}).from(noteReactions).innerJoin(users, eq3(noteReactions.user_id, users.id)).where(eq3(noteReactions.note_id, noteId)).orderBy(noteReactions.emoji).all();
|
|
4418
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
4419
|
+
for (const r of rows) {
|
|
4420
|
+
if (!grouped.has(r.emoji)) grouped.set(r.emoji, []);
|
|
4421
|
+
grouped.get(r.emoji).push(r.username);
|
|
4422
|
+
}
|
|
4423
|
+
return [...grouped.entries()].map(([emoji, usernames]) => ({
|
|
4424
|
+
emoji,
|
|
4425
|
+
count: usernames.length,
|
|
4426
|
+
usernames
|
|
4427
|
+
}));
|
|
4428
|
+
}
|
|
4429
|
+
async function resolveNoteId(authorSlug) {
|
|
4430
|
+
const [author, slug] = authorSlug.split("/", 2);
|
|
4431
|
+
if (!author || !slug) return null;
|
|
4432
|
+
const db = await getDb();
|
|
4433
|
+
const row = await db.select({ id: notes.id }).from(notes).innerJoin(users, eq3(notes.author_id, users.id)).where(and2(eq3(users.username, author), eq3(notes.slug, slug))).get();
|
|
4434
|
+
return row?.id ?? null;
|
|
4435
|
+
}
|
|
4436
|
+
|
|
4437
|
+
// src/web/api/notes.ts
|
|
4438
|
+
var createSchema = z4.object({
|
|
4439
|
+
title: z4.string().min(1).max(200),
|
|
4440
|
+
content: z4.string().min(1),
|
|
4441
|
+
reply_to: z4.string().optional()
|
|
4442
|
+
});
|
|
4443
|
+
var updateSchema = z4.object({
|
|
4444
|
+
title: z4.string().min(1).max(200).optional(),
|
|
4445
|
+
content: z4.string().min(1).optional()
|
|
4446
|
+
});
|
|
4447
|
+
var commentSchema = z4.object({
|
|
4448
|
+
content: z4.string().min(1)
|
|
4449
|
+
});
|
|
4450
|
+
var reactionSchema = z4.object({
|
|
4451
|
+
emoji: z4.string().min(1).max(32)
|
|
4452
|
+
});
|
|
4453
|
+
async function resolveUserId(c) {
|
|
4454
|
+
const user = c.get("user");
|
|
4455
|
+
if (user.id === 0) {
|
|
4456
|
+
const asUser = c.req.query("as");
|
|
4457
|
+
if (!asUser) return null;
|
|
4458
|
+
try {
|
|
4459
|
+
const mindUser = await getOrCreateMindUser(asUser);
|
|
4460
|
+
return { id: mindUser.id, username: mindUser.username };
|
|
4461
|
+
} catch {
|
|
4462
|
+
const brainUser = await getUserByUsername(asUser);
|
|
4463
|
+
if (brainUser) return { id: brainUser.id, username: brainUser.username };
|
|
4464
|
+
return null;
|
|
4465
|
+
}
|
|
4466
|
+
}
|
|
4467
|
+
return { id: user.id, username: user.username };
|
|
4468
|
+
}
|
|
4469
|
+
var app12 = new Hono12().get("/", async (c) => {
|
|
4470
|
+
const author = c.req.query("author");
|
|
4471
|
+
const limit = c.req.query("limit") ? parseInt(c.req.query("limit"), 10) : void 0;
|
|
4472
|
+
const offset = c.req.query("offset") ? parseInt(c.req.query("offset"), 10) : void 0;
|
|
4473
|
+
const result = await listNotes({ authorUsername: author, limit, offset });
|
|
4474
|
+
return c.json(result);
|
|
4475
|
+
}).post("/", zValidator4("json", createSchema), async (c) => {
|
|
4476
|
+
const actor = await resolveUserId(c);
|
|
4477
|
+
if (!actor) return c.json({ error: "Missing ?as=<username> for CLI requests" }, 400);
|
|
4478
|
+
const { title, content, reply_to } = c.req.valid("json");
|
|
4479
|
+
let replyToId;
|
|
4480
|
+
if (reply_to) {
|
|
4481
|
+
const id = await resolveNoteId(reply_to);
|
|
4482
|
+
if (id === null) return c.json({ error: `Reply target not found: ${reply_to}` }, 404);
|
|
4483
|
+
replyToId = id;
|
|
4484
|
+
}
|
|
4485
|
+
const note = await createNote(actor.id, title, content, replyToId);
|
|
4486
|
+
const replyInfo = reply_to ? ` (in reply to ${reply_to})` : "";
|
|
4487
|
+
announceToSystem(`${actor.username} published a note: ${title}${replyInfo}`).catch(() => {
|
|
4488
|
+
});
|
|
4489
|
+
return c.json(note, 201);
|
|
4490
|
+
}).get("/:author/:slug", async (c) => {
|
|
4491
|
+
const { author, slug } = c.req.param();
|
|
4492
|
+
const note = await getNote(author, slug);
|
|
4493
|
+
if (!note) return c.json({ error: "Note not found" }, 404);
|
|
4494
|
+
return c.json(note);
|
|
4495
|
+
}).put("/:author/:slug", zValidator4("json", updateSchema), async (c) => {
|
|
4496
|
+
const actor = await resolveUserId(c);
|
|
4497
|
+
if (!actor) return c.json({ error: "Missing ?as=<username> for CLI requests" }, 400);
|
|
4498
|
+
const { author, slug } = c.req.param();
|
|
4499
|
+
if (actor.username !== author) return c.json({ error: "Forbidden" }, 403);
|
|
4500
|
+
const updates = c.req.valid("json");
|
|
4501
|
+
const note = await updateNote(author, slug, updates);
|
|
4502
|
+
if (!note) return c.json({ error: "Note not found" }, 404);
|
|
4503
|
+
return c.json(note);
|
|
4504
|
+
}).delete("/:author/:slug", async (c) => {
|
|
4505
|
+
const actor = await resolveUserId(c);
|
|
4506
|
+
if (!actor) return c.json({ error: "Missing ?as=<username> for CLI requests" }, 400);
|
|
4507
|
+
const { author, slug } = c.req.param();
|
|
4508
|
+
const deleted = await deleteNote(author, slug, actor.id);
|
|
4509
|
+
if (!deleted) return c.json({ error: "Note not found or not authorized" }, 404);
|
|
4510
|
+
return c.json({ ok: true });
|
|
4511
|
+
}).post("/:author/:slug/reactions", zValidator4("json", reactionSchema), async (c) => {
|
|
4512
|
+
const actor = await resolveUserId(c);
|
|
4513
|
+
if (!actor) return c.json({ error: "Missing ?as=<username> for CLI requests" }, 400);
|
|
4514
|
+
const { author, slug } = c.req.param();
|
|
4515
|
+
const note = await getNote(author, slug);
|
|
4516
|
+
if (!note) return c.json({ error: "Note not found" }, 404);
|
|
4517
|
+
const { emoji } = c.req.valid("json");
|
|
4518
|
+
const result = await toggleReaction(note.id, actor.id, emoji);
|
|
4519
|
+
const reactions = await getReactions(note.id);
|
|
4520
|
+
return c.json({ ...result, reactions });
|
|
4521
|
+
}).post("/:author/:slug/comments", zValidator4("json", commentSchema), async (c) => {
|
|
4522
|
+
const actor = await resolveUserId(c);
|
|
4523
|
+
if (!actor) return c.json({ error: "Missing ?as=<username> for CLI requests" }, 400);
|
|
4524
|
+
const { author, slug } = c.req.param();
|
|
4525
|
+
const note = await getNote(author, slug);
|
|
4526
|
+
if (!note) return c.json({ error: "Note not found" }, 404);
|
|
4527
|
+
const { content } = c.req.valid("json");
|
|
4528
|
+
const comment = await addComment(note.id, actor.id, content);
|
|
4529
|
+
return c.json(comment, 201);
|
|
4530
|
+
}).delete("/:author/:slug/comments/:id", async (c) => {
|
|
4531
|
+
const actor = await resolveUserId(c);
|
|
4532
|
+
if (!actor) return c.json({ error: "Missing ?as=<username> for CLI requests" }, 400);
|
|
4533
|
+
const commentId = parseInt(c.req.param("id"), 10);
|
|
4534
|
+
if (Number.isNaN(commentId)) return c.json({ error: "Invalid comment ID" }, 400);
|
|
4535
|
+
const deleted = await deleteComment(commentId, actor.id);
|
|
4536
|
+
if (!deleted) return c.json({ error: "Comment not found or not authorized" }, 404);
|
|
4537
|
+
return c.json({ ok: true });
|
|
4538
|
+
});
|
|
4539
|
+
var notes_default = app12;
|
|
4540
|
+
|
|
4090
4541
|
// src/web/api/pages.ts
|
|
4091
4542
|
import { readFile as readFile2, stat as stat2 } from "fs/promises";
|
|
4092
4543
|
import { extname as extname3, resolve as resolve13 } from "path";
|
|
4093
|
-
import { Hono as
|
|
4544
|
+
import { Hono as Hono13 } from "hono";
|
|
4094
4545
|
var MIME_TYPES = {
|
|
4095
4546
|
".html": "text/html",
|
|
4096
4547
|
".js": "application/javascript",
|
|
@@ -4107,14 +4558,14 @@ var MIME_TYPES = {
|
|
|
4107
4558
|
".txt": "text/plain",
|
|
4108
4559
|
".xml": "application/xml"
|
|
4109
4560
|
};
|
|
4110
|
-
var
|
|
4561
|
+
var app13 = new Hono13().get("/:name/*", async (c) => {
|
|
4111
4562
|
const name = c.req.param("name");
|
|
4112
4563
|
let pagesRoot;
|
|
4113
4564
|
if (name === "_system") {
|
|
4114
4565
|
pagesRoot = resolve13(voluteHome(), "shared", "pages");
|
|
4115
4566
|
} else {
|
|
4116
4567
|
if (!findMind(name)) return c.text("Not found", 404);
|
|
4117
|
-
pagesRoot = resolve13(mindDir(name), "home", "pages");
|
|
4568
|
+
pagesRoot = resolve13(mindDir(name), "home", "public", "pages");
|
|
4118
4569
|
}
|
|
4119
4570
|
const wildcard = c.req.path.replace(`/pages/${name}`, "") || "/";
|
|
4120
4571
|
const requestedPath = resolve13(pagesRoot, wildcard.slice(1));
|
|
@@ -4137,14 +4588,14 @@ var app12 = new Hono12().get("/:name/*", async (c) => {
|
|
|
4137
4588
|
}
|
|
4138
4589
|
return c.text("Not found", 404);
|
|
4139
4590
|
});
|
|
4140
|
-
var pages_default =
|
|
4591
|
+
var pages_default = app13;
|
|
4141
4592
|
|
|
4142
4593
|
// src/web/api/prompts.ts
|
|
4143
|
-
import { zValidator as
|
|
4144
|
-
import { eq as
|
|
4145
|
-
import { Hono as
|
|
4146
|
-
import { z as
|
|
4147
|
-
var
|
|
4594
|
+
import { zValidator as zValidator5 } from "@hono/zod-validator";
|
|
4595
|
+
import { eq as eq4, sql as sql3 } from "drizzle-orm";
|
|
4596
|
+
import { Hono as Hono14 } from "hono";
|
|
4597
|
+
import { z as z5 } from "zod";
|
|
4598
|
+
var app14 = new Hono14().get("/", async (c) => {
|
|
4148
4599
|
let rows;
|
|
4149
4600
|
try {
|
|
4150
4601
|
const db = await getDb();
|
|
@@ -4167,16 +4618,16 @@ var app13 = new Hono13().get("/", async (c) => {
|
|
|
4167
4618
|
};
|
|
4168
4619
|
});
|
|
4169
4620
|
return c.json(prompts);
|
|
4170
|
-
}).put("/:key", requireAdmin,
|
|
4621
|
+
}).put("/:key", requireAdmin, zValidator5("json", z5.object({ content: z5.string() })), async (c) => {
|
|
4171
4622
|
const key = c.req.param("key");
|
|
4172
4623
|
if (!PROMPT_KEYS.includes(key)) {
|
|
4173
4624
|
return c.json({ error: "Unknown prompt key" }, 404);
|
|
4174
4625
|
}
|
|
4175
4626
|
const { content } = c.req.valid("json");
|
|
4176
4627
|
const db = await getDb();
|
|
4177
|
-
await db.insert(systemPrompts).values({ key, content, updated_at:
|
|
4628
|
+
await db.insert(systemPrompts).values({ key, content, updated_at: sql3`(datetime('now'))` }).onConflictDoUpdate({
|
|
4178
4629
|
target: systemPrompts.key,
|
|
4179
|
-
set: { content, updated_at:
|
|
4630
|
+
set: { content, updated_at: sql3`(datetime('now'))` }
|
|
4180
4631
|
});
|
|
4181
4632
|
return c.json({ ok: true });
|
|
4182
4633
|
}).delete("/:key", requireAdmin, async (c) => {
|
|
@@ -4185,14 +4636,103 @@ var app13 = new Hono13().get("/", async (c) => {
|
|
|
4185
4636
|
return c.json({ error: "Unknown prompt key" }, 404);
|
|
4186
4637
|
}
|
|
4187
4638
|
const db = await getDb();
|
|
4188
|
-
await db.delete(systemPrompts).where(
|
|
4639
|
+
await db.delete(systemPrompts).where(eq4(systemPrompts.key, key));
|
|
4189
4640
|
return c.json({ ok: true });
|
|
4190
4641
|
});
|
|
4191
|
-
var prompts_default =
|
|
4642
|
+
var prompts_default = app14;
|
|
4643
|
+
|
|
4644
|
+
// src/web/api/public-files.ts
|
|
4645
|
+
import { readdir as readdir2, readFile as readFile3, stat as stat3 } from "fs/promises";
|
|
4646
|
+
import { extname as extname4, resolve as resolve14 } from "path";
|
|
4647
|
+
import { Hono as Hono15 } from "hono";
|
|
4648
|
+
var MAX_FILE_SIZE = 50 * 1024 * 1024;
|
|
4649
|
+
function resolvePublicRoot(name) {
|
|
4650
|
+
if (name === "_system") return resolve14(voluteHome(), "shared");
|
|
4651
|
+
if (!findMind(name)) return null;
|
|
4652
|
+
return resolve14(mindDir(name), "home", "public");
|
|
4653
|
+
}
|
|
4654
|
+
function hasDotSegment(relativePath) {
|
|
4655
|
+
return relativePath.split("/").some((seg) => seg.startsWith("."));
|
|
4656
|
+
}
|
|
4657
|
+
var MIME_TYPES2 = {
|
|
4658
|
+
".html": "text/html",
|
|
4659
|
+
".js": "application/javascript",
|
|
4660
|
+
".css": "text/css",
|
|
4661
|
+
".json": "application/json",
|
|
4662
|
+
".svg": "image/svg+xml",
|
|
4663
|
+
".png": "image/png",
|
|
4664
|
+
".jpg": "image/jpeg",
|
|
4665
|
+
".jpeg": "image/jpeg",
|
|
4666
|
+
".gif": "image/gif",
|
|
4667
|
+
".ico": "image/x-icon",
|
|
4668
|
+
".woff": "font/woff",
|
|
4669
|
+
".woff2": "font/woff2",
|
|
4670
|
+
".txt": "text/plain",
|
|
4671
|
+
".xml": "application/xml",
|
|
4672
|
+
".md": "text/markdown",
|
|
4673
|
+
".webp": "image/webp"
|
|
4674
|
+
};
|
|
4675
|
+
async function listDir(dirPath) {
|
|
4676
|
+
let entries;
|
|
4677
|
+
try {
|
|
4678
|
+
entries = await readdir2(dirPath, { withFileTypes: true });
|
|
4679
|
+
} catch (err) {
|
|
4680
|
+
if (err?.code === "ENOENT") return [];
|
|
4681
|
+
throw err;
|
|
4682
|
+
}
|
|
4683
|
+
return entries.filter((e) => !e.name.startsWith(".")).map((e) => ({
|
|
4684
|
+
name: e.name,
|
|
4685
|
+
type: e.isDirectory() ? "directory" : "file"
|
|
4686
|
+
}));
|
|
4687
|
+
}
|
|
4688
|
+
var app15 = new Hono15().get("/:name/", async (c) => {
|
|
4689
|
+
const name = c.req.param("name");
|
|
4690
|
+
const publicRoot = resolvePublicRoot(name);
|
|
4691
|
+
if (!publicRoot) return c.json({ error: "Not found" }, 404);
|
|
4692
|
+
return c.json(await listDir(publicRoot));
|
|
4693
|
+
}).get("/:name/*", async (c) => {
|
|
4694
|
+
const name = c.req.param("name");
|
|
4695
|
+
const publicRoot = resolvePublicRoot(name);
|
|
4696
|
+
if (!publicRoot) return c.text("Not found", 404);
|
|
4697
|
+
const wildcard = c.req.path.replace(`/public/${name}`, "") || "/";
|
|
4698
|
+
const relativePath = wildcard.slice(1);
|
|
4699
|
+
const requestedPath = resolve14(publicRoot, relativePath);
|
|
4700
|
+
if (!requestedPath.startsWith(publicRoot)) return c.text("Forbidden", 403);
|
|
4701
|
+
if (hasDotSegment(relativePath)) return c.text("Forbidden", 403);
|
|
4702
|
+
let fileStat;
|
|
4703
|
+
try {
|
|
4704
|
+
fileStat = await stat3(requestedPath);
|
|
4705
|
+
} catch (err) {
|
|
4706
|
+
if (err?.code === "ENOENT") return c.text("Not found", 404);
|
|
4707
|
+
if (err?.code === "EACCES") return c.text("Forbidden", 403);
|
|
4708
|
+
return c.text("Internal server error", 500);
|
|
4709
|
+
}
|
|
4710
|
+
if (fileStat.isDirectory()) {
|
|
4711
|
+
if (wildcard.endsWith("/")) {
|
|
4712
|
+
return c.json(await listDir(requestedPath));
|
|
4713
|
+
}
|
|
4714
|
+
return c.text("Not found", 404);
|
|
4715
|
+
}
|
|
4716
|
+
if (fileStat.isFile()) {
|
|
4717
|
+
if (fileStat.size > MAX_FILE_SIZE) return c.text("File too large", 413);
|
|
4718
|
+
const ext = extname4(requestedPath);
|
|
4719
|
+
const mime = MIME_TYPES2[ext] || "application/octet-stream";
|
|
4720
|
+
try {
|
|
4721
|
+
const body = await readFile3(requestedPath);
|
|
4722
|
+
return c.body(body, 200, { "Content-Type": mime });
|
|
4723
|
+
} catch (err) {
|
|
4724
|
+
if (err?.code === "ENOENT") return c.text("Not found", 404);
|
|
4725
|
+
if (err?.code === "EACCES") return c.text("Forbidden", 403);
|
|
4726
|
+
return c.text("Failed to read file", 500);
|
|
4727
|
+
}
|
|
4728
|
+
}
|
|
4729
|
+
return c.text("Not found", 404);
|
|
4730
|
+
});
|
|
4731
|
+
var public_files_default = app15;
|
|
4192
4732
|
|
|
4193
4733
|
// src/web/api/schedules.ts
|
|
4194
4734
|
import { CronExpressionParser } from "cron-parser";
|
|
4195
|
-
import { Hono as
|
|
4735
|
+
import { Hono as Hono16 } from "hono";
|
|
4196
4736
|
var slog = logger_default.child("schedules");
|
|
4197
4737
|
function readSchedules(name) {
|
|
4198
4738
|
return readVoluteConfig(mindDir(name))?.schedules ?? [];
|
|
@@ -4209,7 +4749,7 @@ function writeSchedules(name, schedules) {
|
|
|
4209
4749
|
data: { schedules }
|
|
4210
4750
|
});
|
|
4211
4751
|
}
|
|
4212
|
-
var
|
|
4752
|
+
var app16 = new Hono16().get("/:name/schedules", (c) => {
|
|
4213
4753
|
const name = c.req.param("name");
|
|
4214
4754
|
if (!findMind(name)) return c.json({ error: "Mind not found" }, 404);
|
|
4215
4755
|
return c.json(readSchedules(name));
|
|
@@ -4242,6 +4782,7 @@ var app14 = new Hono14().get("/:name/schedules", (c) => {
|
|
|
4242
4782
|
const schedule = { id, cron: body.cron, enabled: body.enabled ?? true };
|
|
4243
4783
|
if (body.message) schedule.message = body.message;
|
|
4244
4784
|
if (body.script) schedule.script = body.script;
|
|
4785
|
+
if (body.channel) schedule.channel = body.channel;
|
|
4245
4786
|
schedules.push(schedule);
|
|
4246
4787
|
writeSchedules(name, schedules);
|
|
4247
4788
|
return c.json({ ok: true, id }, 201);
|
|
@@ -4273,6 +4814,7 @@ var app14 = new Hono14().get("/:name/schedules", (c) => {
|
|
|
4273
4814
|
delete schedules[idx].message;
|
|
4274
4815
|
}
|
|
4275
4816
|
if (body.enabled !== void 0) schedules[idx].enabled = body.enabled;
|
|
4817
|
+
if (body.channel !== void 0) schedules[idx].channel = body.channel || void 0;
|
|
4276
4818
|
writeSchedules(name, schedules);
|
|
4277
4819
|
return c.json({ ok: true });
|
|
4278
4820
|
}).delete("/:name/schedules/:id", requireAdmin, (c) => {
|
|
@@ -4312,11 +4854,11 @@ var app14 = new Hono14().get("/:name/schedules", (c) => {
|
|
|
4312
4854
|
return c.json({ error: "Failed to reach mind" }, 502);
|
|
4313
4855
|
}
|
|
4314
4856
|
});
|
|
4315
|
-
var schedules_default =
|
|
4857
|
+
var schedules_default = app16;
|
|
4316
4858
|
|
|
4317
4859
|
// src/web/api/shared.ts
|
|
4318
|
-
import { Hono as
|
|
4319
|
-
var
|
|
4860
|
+
import { Hono as Hono17 } from "hono";
|
|
4861
|
+
var app17 = new Hono17().post("/:name/shared/merge", requireAdmin, async (c) => {
|
|
4320
4862
|
const name = c.req.param("name");
|
|
4321
4863
|
const entry = findMind(name);
|
|
4322
4864
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
@@ -4365,15 +4907,15 @@ var app15 = new Hono15().post("/:name/shared/merge", requireAdmin, async (c) =>
|
|
|
4365
4907
|
return c.json({ error: err instanceof Error ? err.message : "Failed to get status" }, 500);
|
|
4366
4908
|
}
|
|
4367
4909
|
});
|
|
4368
|
-
var shared_default =
|
|
4910
|
+
var shared_default = app17;
|
|
4369
4911
|
|
|
4370
4912
|
// src/web/api/skills.ts
|
|
4371
4913
|
import { existsSync as existsSync11, mkdtempSync, readdirSync as readdirSync5, rmSync as rmSync5 } from "fs";
|
|
4372
4914
|
import { tmpdir } from "os";
|
|
4373
|
-
import { join as join2, resolve as
|
|
4915
|
+
import { join as join2, resolve as resolve15 } from "path";
|
|
4374
4916
|
import AdmZip from "adm-zip";
|
|
4375
|
-
import { Hono as
|
|
4376
|
-
var
|
|
4917
|
+
import { Hono as Hono18 } from "hono";
|
|
4918
|
+
var app18 = new Hono18().get("/", async (c) => {
|
|
4377
4919
|
const skills = await listSharedSkills();
|
|
4378
4920
|
return c.json(skills);
|
|
4379
4921
|
}).get("/:id", async (c) => {
|
|
@@ -4397,7 +4939,7 @@ var app16 = new Hono16().get("/", async (c) => {
|
|
|
4397
4939
|
try {
|
|
4398
4940
|
const zip = new AdmZip(buffer2);
|
|
4399
4941
|
for (const entry of zip.getEntries()) {
|
|
4400
|
-
const target =
|
|
4942
|
+
const target = resolve15(tmpDir, entry.entryName);
|
|
4401
4943
|
if (!target.startsWith(tmpDir)) {
|
|
4402
4944
|
return c.json({ error: "Invalid zip: paths must not escape archive" }, 400);
|
|
4403
4945
|
}
|
|
@@ -4438,12 +4980,15 @@ var app16 = new Hono16().get("/", async (c) => {
|
|
|
4438
4980
|
}
|
|
4439
4981
|
return c.json({ ok: true });
|
|
4440
4982
|
});
|
|
4441
|
-
var skills_default =
|
|
4983
|
+
var skills_default = app18;
|
|
4442
4984
|
|
|
4443
4985
|
// src/web/api/system.ts
|
|
4444
|
-
import {
|
|
4986
|
+
import { zValidator as zValidator6 } from "@hono/zod-validator";
|
|
4987
|
+
import { Hono as Hono19 } from "hono";
|
|
4445
4988
|
import { streamSSE as streamSSE3 } from "hono/streaming";
|
|
4446
|
-
|
|
4989
|
+
import { z as z6 } from "zod";
|
|
4990
|
+
var DEFAULT_API_URL = "https://volute.systems";
|
|
4991
|
+
var app19 = new Hono19().post("/restart", requireAdmin, (c) => {
|
|
4447
4992
|
setTimeout(() => process.exit(1), 200);
|
|
4448
4993
|
return c.json({ ok: true });
|
|
4449
4994
|
}).post("/stop", requireAdmin, (c) => {
|
|
@@ -4460,29 +5005,107 @@ var app17 = new Hono17().post("/restart", requireAdmin, (c) => {
|
|
|
4460
5005
|
stream.writeSSE({ data: JSON.stringify(entry) }).catch(() => {
|
|
4461
5006
|
});
|
|
4462
5007
|
});
|
|
4463
|
-
await new Promise((
|
|
5008
|
+
await new Promise((resolve20) => {
|
|
4464
5009
|
stream.onAbort(() => {
|
|
4465
5010
|
unsubscribe();
|
|
4466
|
-
|
|
5011
|
+
resolve20();
|
|
4467
5012
|
});
|
|
4468
5013
|
});
|
|
4469
5014
|
});
|
|
4470
5015
|
}).get("/info", (c) => {
|
|
4471
5016
|
const config = readSystemsConfig();
|
|
4472
5017
|
return c.json({ system: config?.system ?? null });
|
|
5018
|
+
}).post(
|
|
5019
|
+
"/register",
|
|
5020
|
+
requireAdmin,
|
|
5021
|
+
zValidator6("json", z6.object({ name: z6.string().min(1) })),
|
|
5022
|
+
async (c) => {
|
|
5023
|
+
const existing = readSystemsConfig();
|
|
5024
|
+
if (existing) {
|
|
5025
|
+
return c.json({ error: `Already registered as "${existing.system}"` }, 400);
|
|
5026
|
+
}
|
|
5027
|
+
const { name } = c.req.valid("json");
|
|
5028
|
+
const apiUrl = process.env.VOLUTE_SYSTEMS_URL || DEFAULT_API_URL;
|
|
5029
|
+
let apiKey;
|
|
5030
|
+
let system;
|
|
5031
|
+
try {
|
|
5032
|
+
const res = await fetch(`${apiUrl}/api/register`, {
|
|
5033
|
+
method: "POST",
|
|
5034
|
+
headers: { "Content-Type": "application/json" },
|
|
5035
|
+
body: JSON.stringify({ name: name.trim() })
|
|
5036
|
+
});
|
|
5037
|
+
if (!res.ok) {
|
|
5038
|
+
const err = await res.json().catch(() => ({ error: `HTTP ${res.status}` }));
|
|
5039
|
+
return c.json({ error: err.error }, 502);
|
|
5040
|
+
}
|
|
5041
|
+
({ apiKey, system } = await res.json());
|
|
5042
|
+
} catch (err) {
|
|
5043
|
+
return c.json({ error: `Connection failed: ${err.message}` }, 502);
|
|
5044
|
+
}
|
|
5045
|
+
try {
|
|
5046
|
+
writeSystemsConfig({ apiKey, system, apiUrl });
|
|
5047
|
+
} catch (err) {
|
|
5048
|
+
return c.json(
|
|
5049
|
+
{
|
|
5050
|
+
error: `Registered as "${system}" but failed to save config: ${err.message}`
|
|
5051
|
+
},
|
|
5052
|
+
500
|
|
5053
|
+
);
|
|
5054
|
+
}
|
|
5055
|
+
return c.json({ system });
|
|
5056
|
+
}
|
|
5057
|
+
).post(
|
|
5058
|
+
"/login",
|
|
5059
|
+
requireAdmin,
|
|
5060
|
+
zValidator6("json", z6.object({ key: z6.string().min(1) })),
|
|
5061
|
+
async (c) => {
|
|
5062
|
+
const existing = readSystemsConfig();
|
|
5063
|
+
if (existing) {
|
|
5064
|
+
return c.json({ error: `Already logged in as "${existing.system}"` }, 400);
|
|
5065
|
+
}
|
|
5066
|
+
const { key } = c.req.valid("json");
|
|
5067
|
+
const apiUrl = process.env.VOLUTE_SYSTEMS_URL || DEFAULT_API_URL;
|
|
5068
|
+
let system;
|
|
5069
|
+
try {
|
|
5070
|
+
const res = await fetch(`${apiUrl}/api/whoami`, {
|
|
5071
|
+
headers: { Authorization: `Bearer ${key.trim()}` }
|
|
5072
|
+
});
|
|
5073
|
+
if (!res.ok) {
|
|
5074
|
+
const err = await res.json().catch(() => ({ error: `HTTP ${res.status}` }));
|
|
5075
|
+
return c.json({ error: err.error }, 502);
|
|
5076
|
+
}
|
|
5077
|
+
({ system } = await res.json());
|
|
5078
|
+
} catch (err) {
|
|
5079
|
+
return c.json({ error: `Connection failed: ${err.message}` }, 502);
|
|
5080
|
+
}
|
|
5081
|
+
try {
|
|
5082
|
+
writeSystemsConfig({ apiKey: key.trim(), system, apiUrl });
|
|
5083
|
+
} catch (err) {
|
|
5084
|
+
return c.json(
|
|
5085
|
+
{
|
|
5086
|
+
error: `Logged in as "${system}" but failed to save config: ${err.message}`
|
|
5087
|
+
},
|
|
5088
|
+
500
|
|
5089
|
+
);
|
|
5090
|
+
}
|
|
5091
|
+
return c.json({ system });
|
|
5092
|
+
}
|
|
5093
|
+
).post("/logout", requireAdmin, (c) => {
|
|
5094
|
+
deleteSystemsConfig();
|
|
5095
|
+
return c.json({ ok: true });
|
|
4473
5096
|
});
|
|
4474
|
-
var system_default =
|
|
5097
|
+
var system_default = app19;
|
|
4475
5098
|
|
|
4476
5099
|
// src/web/api/typing.ts
|
|
4477
|
-
import { zValidator as
|
|
4478
|
-
import { Hono as
|
|
4479
|
-
import { z as
|
|
4480
|
-
var typingSchema =
|
|
4481
|
-
channel:
|
|
4482
|
-
sender:
|
|
4483
|
-
active:
|
|
5100
|
+
import { zValidator as zValidator7 } from "@hono/zod-validator";
|
|
5101
|
+
import { Hono as Hono20 } from "hono";
|
|
5102
|
+
import { z as z7 } from "zod";
|
|
5103
|
+
var typingSchema = z7.object({
|
|
5104
|
+
channel: z7.string().min(1),
|
|
5105
|
+
sender: z7.string().min(1),
|
|
5106
|
+
active: z7.boolean()
|
|
4484
5107
|
});
|
|
4485
|
-
var
|
|
5108
|
+
var app20 = new Hono20().post("/:name/typing", zValidator7("json", typingSchema), (c) => {
|
|
4486
5109
|
const { channel, sender, active } = c.req.valid("json");
|
|
4487
5110
|
const map = getTypingMap();
|
|
4488
5111
|
if (active) {
|
|
@@ -4493,7 +5116,7 @@ var app18 = new Hono18().post("/:name/typing", zValidator5("json", typingSchema)
|
|
|
4493
5116
|
const volutePrefix = "volute:";
|
|
4494
5117
|
if (channel.startsWith(volutePrefix)) {
|
|
4495
5118
|
const conversationId = channel.slice(volutePrefix.length);
|
|
4496
|
-
|
|
5119
|
+
publish(conversationId, { type: "typing", senders: map.get(channel) });
|
|
4497
5120
|
}
|
|
4498
5121
|
return c.json({ ok: true });
|
|
4499
5122
|
}).get("/:name/typing", (c) => {
|
|
@@ -4504,13 +5127,13 @@ var app18 = new Hono18().post("/:name/typing", zValidator5("json", typingSchema)
|
|
|
4504
5127
|
const map = getTypingMap();
|
|
4505
5128
|
return c.json({ typing: map.get(channel) });
|
|
4506
5129
|
});
|
|
4507
|
-
var typing_default =
|
|
5130
|
+
var typing_default = app20;
|
|
4508
5131
|
|
|
4509
5132
|
// src/web/api/update.ts
|
|
4510
5133
|
import { spawn as spawn2 } from "child_process";
|
|
4511
|
-
import { Hono as
|
|
5134
|
+
import { Hono as Hono21 } from "hono";
|
|
4512
5135
|
var bin;
|
|
4513
|
-
var
|
|
5136
|
+
var app21 = new Hono21().get("/update", async (c) => {
|
|
4514
5137
|
const result = await checkForUpdate();
|
|
4515
5138
|
return c.json(result);
|
|
4516
5139
|
}).post("/update", requireAdmin, async (c) => {
|
|
@@ -4525,21 +5148,21 @@ var app19 = new Hono19().get("/update", async (c) => {
|
|
|
4525
5148
|
child.unref();
|
|
4526
5149
|
return c.json({ ok: true, message: "Updating..." });
|
|
4527
5150
|
});
|
|
4528
|
-
var update_default =
|
|
5151
|
+
var update_default = app21;
|
|
4529
5152
|
|
|
4530
5153
|
// src/web/api/v1/chat.ts
|
|
4531
|
-
import { zValidator as
|
|
4532
|
-
import { Hono as
|
|
5154
|
+
import { zValidator as zValidator8 } from "@hono/zod-validator";
|
|
5155
|
+
import { Hono as Hono22 } from "hono";
|
|
4533
5156
|
import { streamSSE as streamSSE4 } from "hono/streaming";
|
|
4534
|
-
import { z as
|
|
5157
|
+
import { z as z8 } from "zod";
|
|
4535
5158
|
async function fanOutToMinds(opts) {
|
|
4536
5159
|
const participants = await getParticipants(opts.conversationId);
|
|
4537
5160
|
const mindParticipants = participants.filter((p) => p.userType === "mind");
|
|
4538
5161
|
const participantNames = participants.map((p) => p.username);
|
|
4539
5162
|
const isDM = opts.isDM ?? participants.length === 2;
|
|
4540
5163
|
const channelEntryType = opts.channelEntryType ?? (isDM ? "dm" : "group");
|
|
4541
|
-
const { getMindManager: getMindManager2 } = await import("./mind-manager-
|
|
4542
|
-
const { getSleepManagerIfReady } = await import("./sleep-manager-
|
|
5164
|
+
const { getMindManager: getMindManager2 } = await import("./mind-manager-VVK67AY3.js");
|
|
5165
|
+
const { getSleepManagerIfReady } = await import("./sleep-manager-EE4NRN2Q.js");
|
|
4543
5166
|
const manager = getMindManager2();
|
|
4544
5167
|
const sm = getSleepManagerIfReady();
|
|
4545
5168
|
const targetMinds = mindParticipants.map((ap) => {
|
|
@@ -4588,18 +5211,18 @@ async function fanOutToMinds(opts) {
|
|
|
4588
5211
|
});
|
|
4589
5212
|
}
|
|
4590
5213
|
}
|
|
4591
|
-
var mindChatSchema =
|
|
4592
|
-
message:
|
|
4593
|
-
conversationId:
|
|
4594
|
-
sender:
|
|
4595
|
-
images:
|
|
5214
|
+
var mindChatSchema = z8.object({
|
|
5215
|
+
message: z8.string().optional(),
|
|
5216
|
+
conversationId: z8.string().optional(),
|
|
5217
|
+
sender: z8.string().optional(),
|
|
5218
|
+
images: z8.array(z8.object({ media_type: z8.string(), data: z8.string() })).optional()
|
|
4596
5219
|
});
|
|
4597
|
-
var unifiedChatSchema =
|
|
4598
|
-
message:
|
|
4599
|
-
conversationId:
|
|
4600
|
-
images:
|
|
5220
|
+
var unifiedChatSchema = z8.object({
|
|
5221
|
+
message: z8.string().optional(),
|
|
5222
|
+
conversationId: z8.string(),
|
|
5223
|
+
images: z8.array(z8.object({ media_type: z8.string(), data: z8.string() })).optional()
|
|
4601
5224
|
});
|
|
4602
|
-
var
|
|
5225
|
+
var app22 = new Hono22().use("*", authMiddleware).post("/minds/:name/chat", zValidator8("json", mindChatSchema), async (c) => {
|
|
4603
5226
|
const name = c.req.param("name");
|
|
4604
5227
|
const [baseName] = name.split("@", 2);
|
|
4605
5228
|
const entry = findMind(baseName);
|
|
@@ -4672,7 +5295,7 @@ var app20 = new Hono20().use("*", authMiddleware).post("/minds/:name/chat", zVal
|
|
|
4672
5295
|
return c.json({ error: "Conversation not found" }, 404);
|
|
4673
5296
|
}
|
|
4674
5297
|
return streamSSE4(c, async (stream) => {
|
|
4675
|
-
const unsubscribe =
|
|
5298
|
+
const unsubscribe = subscribe2(conversationId, (event) => {
|
|
4676
5299
|
stream.writeSSE({ data: JSON.stringify(event) }).catch((err) => {
|
|
4677
5300
|
if (!stream.aborted) logger_default.error("[v1-chat] SSE write error:", logger_default.errorData(err));
|
|
4678
5301
|
});
|
|
@@ -4682,15 +5305,15 @@ var app20 = new Hono20().use("*", authMiddleware).post("/minds/:name/chat", zVal
|
|
|
4682
5305
|
if (!stream.aborted) logger_default.error("[v1-chat] SSE ping error:", logger_default.errorData(err));
|
|
4683
5306
|
});
|
|
4684
5307
|
}, 15e3);
|
|
4685
|
-
await new Promise((
|
|
5308
|
+
await new Promise((resolve20) => {
|
|
4686
5309
|
stream.onAbort(() => {
|
|
4687
5310
|
unsubscribe();
|
|
4688
5311
|
clearInterval(keepAlive);
|
|
4689
|
-
|
|
5312
|
+
resolve20();
|
|
4690
5313
|
});
|
|
4691
5314
|
});
|
|
4692
5315
|
});
|
|
4693
|
-
}).post("/chat",
|
|
5316
|
+
}).post("/chat", zValidator8("json", unifiedChatSchema), async (c) => {
|
|
4694
5317
|
const user = c.get("user");
|
|
4695
5318
|
const body = c.req.valid("json");
|
|
4696
5319
|
if (!body.message && (!body.images || body.images.length === 0)) {
|
|
@@ -4722,17 +5345,17 @@ var app20 = new Hono20().use("*", authMiddleware).post("/minds/:name/chat", zVal
|
|
|
4722
5345
|
});
|
|
4723
5346
|
return c.json({ ok: true, conversationId: body.conversationId });
|
|
4724
5347
|
});
|
|
4725
|
-
var chat_default =
|
|
5348
|
+
var chat_default = app22;
|
|
4726
5349
|
|
|
4727
5350
|
// src/web/api/v1/conversations.ts
|
|
4728
|
-
import { zValidator as
|
|
4729
|
-
import { Hono as
|
|
4730
|
-
import { z as
|
|
4731
|
-
var
|
|
4732
|
-
title:
|
|
4733
|
-
participantNames:
|
|
5351
|
+
import { zValidator as zValidator9 } from "@hono/zod-validator";
|
|
5352
|
+
import { Hono as Hono23 } from "hono";
|
|
5353
|
+
import { z as z9 } from "zod";
|
|
5354
|
+
var createSchema2 = z9.object({
|
|
5355
|
+
title: z9.string().optional(),
|
|
5356
|
+
participantNames: z9.array(z9.string()).min(1)
|
|
4734
5357
|
});
|
|
4735
|
-
var
|
|
5358
|
+
var app23 = new Hono23().use("*", authMiddleware).get("/", async (c) => {
|
|
4736
5359
|
const user = c.get("user");
|
|
4737
5360
|
const convs = await listConversationsWithParticipants(user.id);
|
|
4738
5361
|
return c.json(convs);
|
|
@@ -4763,7 +5386,7 @@ var app21 = new Hono21().use("*", authMiddleware).get("/", async (c) => {
|
|
|
4763
5386
|
}
|
|
4764
5387
|
const participants = await getParticipants(id);
|
|
4765
5388
|
return c.json(participants);
|
|
4766
|
-
}).post("/",
|
|
5389
|
+
}).post("/", zValidator9("json", createSchema2), async (c) => {
|
|
4767
5390
|
const user = c.get("user");
|
|
4768
5391
|
const body = c.req.valid("json");
|
|
4769
5392
|
const participantIds = /* @__PURE__ */ new Set();
|
|
@@ -4809,30 +5432,30 @@ var app21 = new Hono21().use("*", authMiddleware).get("/", async (c) => {
|
|
|
4809
5432
|
if (!deleted) return c.json({ error: "Conversation not found" }, 404);
|
|
4810
5433
|
return c.json({ ok: true });
|
|
4811
5434
|
});
|
|
4812
|
-
var conversations_default =
|
|
5435
|
+
var conversations_default = app23;
|
|
4813
5436
|
|
|
4814
5437
|
// src/web/api/v1/events.ts
|
|
4815
|
-
import { desc as
|
|
4816
|
-
import { Hono as
|
|
5438
|
+
import { desc as desc4 } from "drizzle-orm";
|
|
5439
|
+
import { Hono as Hono24 } from "hono";
|
|
4817
5440
|
import { streamSSE as streamSSE5 } from "hono/streaming";
|
|
4818
5441
|
|
|
4819
5442
|
// src/lib/events/brain-presence.ts
|
|
4820
5443
|
var connections = /* @__PURE__ */ new Map();
|
|
4821
5444
|
function addConnection(username) {
|
|
4822
|
-
const
|
|
4823
|
-
connections.set(username,
|
|
4824
|
-
if (
|
|
5445
|
+
const count2 = connections.get(username) ?? 0;
|
|
5446
|
+
connections.set(username, count2 + 1);
|
|
5447
|
+
if (count2 === 0) {
|
|
4825
5448
|
broadcast({ type: "brain_online", mind: username, summary: `${username} connected` });
|
|
4826
5449
|
}
|
|
4827
5450
|
}
|
|
4828
5451
|
function removeConnection(username) {
|
|
4829
|
-
const
|
|
4830
|
-
if (
|
|
4831
|
-
if (
|
|
5452
|
+
const count2 = connections.get(username);
|
|
5453
|
+
if (count2 == null) return;
|
|
5454
|
+
if (count2 <= 1) {
|
|
4832
5455
|
connections.delete(username);
|
|
4833
5456
|
broadcast({ type: "brain_offline", mind: username, summary: `${username} disconnected` });
|
|
4834
5457
|
} else {
|
|
4835
|
-
connections.set(username,
|
|
5458
|
+
connections.set(username, count2 - 1);
|
|
4836
5459
|
}
|
|
4837
5460
|
}
|
|
4838
5461
|
function getOnlineBrains() {
|
|
@@ -4860,7 +5483,7 @@ function getEventsSince(sinceId) {
|
|
|
4860
5483
|
}
|
|
4861
5484
|
|
|
4862
5485
|
// src/web/api/v1/events.ts
|
|
4863
|
-
var
|
|
5486
|
+
var app24 = new Hono24().use("*", authMiddleware).get("/", async (c) => {
|
|
4864
5487
|
const user = c.get("user");
|
|
4865
5488
|
const since = c.req.query("since");
|
|
4866
5489
|
const sinceId = since ? Number(since) : 0;
|
|
@@ -4883,7 +5506,7 @@ var app22 = new Hono22().use("*", authMiddleware).get("/", async (c) => {
|
|
|
4883
5506
|
let recentActivity = [];
|
|
4884
5507
|
try {
|
|
4885
5508
|
const db = await getDb();
|
|
4886
|
-
recentActivity = await db.select().from(activity).orderBy(
|
|
5509
|
+
recentActivity = await db.select().from(activity).orderBy(desc4(activity.created_at)).limit(50);
|
|
4887
5510
|
recentActivity = recentActivity.map((row) => ({
|
|
4888
5511
|
...row,
|
|
4889
5512
|
metadata: row.metadata ? JSON.parse(row.metadata) : null
|
|
@@ -4936,7 +5559,7 @@ var app22 = new Hono22().use("*", authMiddleware).get("/", async (c) => {
|
|
|
4936
5559
|
});
|
|
4937
5560
|
cleanups.push(unsubActivity);
|
|
4938
5561
|
for (const conv of conversations2) {
|
|
4939
|
-
const unsubConv =
|
|
5562
|
+
const unsubConv = subscribe2(conv.id, (event) => {
|
|
4940
5563
|
const data = { event: "conversation", conversationId: conv.id, ...event };
|
|
4941
5564
|
const eventId = bufferEvent(data);
|
|
4942
5565
|
stream.writeSSE({
|
|
@@ -4954,8 +5577,8 @@ var app22 = new Hono22().use("*", authMiddleware).get("/", async (c) => {
|
|
|
4954
5577
|
});
|
|
4955
5578
|
}, 15e3);
|
|
4956
5579
|
cleanups.push(() => clearInterval(keepAlive));
|
|
4957
|
-
await new Promise((
|
|
4958
|
-
stream.onAbort(() =>
|
|
5580
|
+
await new Promise((resolve20) => {
|
|
5581
|
+
stream.onAbort(() => resolve20());
|
|
4959
5582
|
});
|
|
4960
5583
|
} finally {
|
|
4961
5584
|
for (const cleanup of cleanups) {
|
|
@@ -4967,19 +5590,19 @@ var app22 = new Hono22().use("*", authMiddleware).get("/", async (c) => {
|
|
|
4967
5590
|
}
|
|
4968
5591
|
});
|
|
4969
5592
|
});
|
|
4970
|
-
var events_default =
|
|
5593
|
+
var events_default = app24;
|
|
4971
5594
|
|
|
4972
5595
|
// src/web/api/variants.ts
|
|
4973
5596
|
import { existsSync as existsSync12, mkdirSync as mkdirSync8, writeFileSync as writeFileSync8 } from "fs";
|
|
4974
|
-
import { resolve as
|
|
4975
|
-
import { Hono as
|
|
5597
|
+
import { resolve as resolve17 } from "path";
|
|
5598
|
+
import { Hono as Hono25 } from "hono";
|
|
4976
5599
|
|
|
4977
5600
|
// src/lib/spawn-server.ts
|
|
4978
5601
|
import { spawn as spawn3 } from "child_process";
|
|
4979
5602
|
import { closeSync, mkdirSync as mkdirSync7, openSync, readFileSync as readFileSync10 } from "fs";
|
|
4980
|
-
import { resolve as
|
|
5603
|
+
import { resolve as resolve16 } from "path";
|
|
4981
5604
|
function tsxBin(cwd) {
|
|
4982
|
-
return
|
|
5605
|
+
return resolve16(cwd, "node_modules", ".bin", "tsx");
|
|
4983
5606
|
}
|
|
4984
5607
|
function spawnServer(cwd, port, options) {
|
|
4985
5608
|
if (options?.detached) {
|
|
@@ -4992,31 +5615,31 @@ function spawnAttached(cwd, port) {
|
|
|
4992
5615
|
cwd,
|
|
4993
5616
|
stdio: ["ignore", "pipe", "pipe"]
|
|
4994
5617
|
});
|
|
4995
|
-
return new Promise((
|
|
4996
|
-
const timeout = setTimeout(() =>
|
|
5618
|
+
return new Promise((resolve20) => {
|
|
5619
|
+
const timeout = setTimeout(() => resolve20(null), 3e4);
|
|
4997
5620
|
function checkOutput(data) {
|
|
4998
5621
|
const match = data.toString().match(/listening on :(\d+)/);
|
|
4999
5622
|
if (match) {
|
|
5000
5623
|
clearTimeout(timeout);
|
|
5001
|
-
|
|
5624
|
+
resolve20({ child, actualPort: parseInt(match[1], 10) });
|
|
5002
5625
|
}
|
|
5003
5626
|
}
|
|
5004
5627
|
child.stdout?.on("data", checkOutput);
|
|
5005
5628
|
child.stderr?.on("data", checkOutput);
|
|
5006
5629
|
child.on("error", () => {
|
|
5007
5630
|
clearTimeout(timeout);
|
|
5008
|
-
|
|
5631
|
+
resolve20(null);
|
|
5009
5632
|
});
|
|
5010
5633
|
child.on("exit", () => {
|
|
5011
5634
|
clearTimeout(timeout);
|
|
5012
|
-
|
|
5635
|
+
resolve20(null);
|
|
5013
5636
|
});
|
|
5014
5637
|
});
|
|
5015
5638
|
}
|
|
5016
5639
|
function spawnDetached(cwd, port, logDir) {
|
|
5017
|
-
const logsDir = logDir ??
|
|
5640
|
+
const logsDir = logDir ?? resolve16(cwd, ".mind", "logs");
|
|
5018
5641
|
mkdirSync7(logsDir, { recursive: true });
|
|
5019
|
-
const logPath =
|
|
5642
|
+
const logPath = resolve16(logsDir, "mind.log");
|
|
5020
5643
|
const logFd = openSync(logPath, "a");
|
|
5021
5644
|
const child = spawn3(tsxBin(cwd), ["src/server.ts", "--port", String(port)], {
|
|
5022
5645
|
cwd,
|
|
@@ -5088,7 +5711,7 @@ async function verify2(port) {
|
|
|
5088
5711
|
}
|
|
5089
5712
|
|
|
5090
5713
|
// src/web/api/variants.ts
|
|
5091
|
-
var
|
|
5714
|
+
var app25 = new Hono25().get("/:name/variants", async (c) => {
|
|
5092
5715
|
const name = c.req.param("name");
|
|
5093
5716
|
const entry = findMind(name);
|
|
5094
5717
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
@@ -5128,11 +5751,11 @@ var app23 = new Hono23().get("/:name/variants", async (c) => {
|
|
|
5128
5751
|
const err = validateBranchName(variantName);
|
|
5129
5752
|
if (err) return c.json({ error: err }, 400);
|
|
5130
5753
|
const projectRoot = mindDir(mindName);
|
|
5131
|
-
const variantDir =
|
|
5754
|
+
const variantDir = resolve17(projectRoot, ".variants", variantName);
|
|
5132
5755
|
if (existsSync12(variantDir)) {
|
|
5133
5756
|
return c.json({ error: `Variant directory already exists: ${variantDir}` }, 409);
|
|
5134
5757
|
}
|
|
5135
|
-
mkdirSync8(
|
|
5758
|
+
mkdirSync8(resolve17(projectRoot, ".variants"), { recursive: true });
|
|
5136
5759
|
try {
|
|
5137
5760
|
await gitExec(["worktree", "add", "-b", variantName, variantDir], { cwd: projectRoot });
|
|
5138
5761
|
} catch (e) {
|
|
@@ -5145,7 +5768,7 @@ var app23 = new Hono23().get("/:name/variants", async (c) => {
|
|
|
5145
5768
|
const [cmd, args] = wrapForIsolation("npm", ["install"], mindName);
|
|
5146
5769
|
await exec(cmd, args, {
|
|
5147
5770
|
cwd: variantDir,
|
|
5148
|
-
env: { ...process.env, HOME:
|
|
5771
|
+
env: { ...process.env, HOME: resolve17(variantDir, "home") }
|
|
5149
5772
|
});
|
|
5150
5773
|
} else {
|
|
5151
5774
|
await exec("npm", ["install"], { cwd: variantDir });
|
|
@@ -5155,7 +5778,7 @@ var app23 = new Hono23().get("/:name/variants", async (c) => {
|
|
|
5155
5778
|
return c.json({ error: `npm install failed: ${msg}` }, 500);
|
|
5156
5779
|
}
|
|
5157
5780
|
if (body.soul) {
|
|
5158
|
-
writeFileSync8(
|
|
5781
|
+
writeFileSync8(resolve17(variantDir, "home/SOUL.md"), body.soul);
|
|
5159
5782
|
}
|
|
5160
5783
|
const variantPort = body.port ?? nextPort();
|
|
5161
5784
|
const variant = {
|
|
@@ -5266,7 +5889,7 @@ var app23 = new Hono23().get("/:name/variants", async (c) => {
|
|
|
5266
5889
|
const [cmd, args] = wrapForIsolation("npm", ["install"], mindName);
|
|
5267
5890
|
await exec(cmd, args, {
|
|
5268
5891
|
cwd: projectRoot,
|
|
5269
|
-
env: { ...process.env, HOME:
|
|
5892
|
+
env: { ...process.env, HOME: resolve17(projectRoot, "home") }
|
|
5270
5893
|
});
|
|
5271
5894
|
} else {
|
|
5272
5895
|
await exec("npm", ["install"], { cwd: projectRoot });
|
|
@@ -5304,19 +5927,19 @@ var app23 = new Hono23().get("/:name/variants", async (c) => {
|
|
|
5304
5927
|
await cleanupVariant(mindName, variantName, projectRoot, variant.path, { stop: true });
|
|
5305
5928
|
return c.json({ ok: true });
|
|
5306
5929
|
});
|
|
5307
|
-
var variants_default =
|
|
5930
|
+
var variants_default = app25;
|
|
5308
5931
|
|
|
5309
5932
|
// src/web/api/volute/channels.ts
|
|
5310
|
-
import { zValidator as
|
|
5311
|
-
import { Hono as
|
|
5312
|
-
import { z as
|
|
5313
|
-
var
|
|
5314
|
-
name:
|
|
5933
|
+
import { zValidator as zValidator10 } from "@hono/zod-validator";
|
|
5934
|
+
import { Hono as Hono26 } from "hono";
|
|
5935
|
+
import { z as z10 } from "zod";
|
|
5936
|
+
var createSchema3 = z10.object({
|
|
5937
|
+
name: z10.string().min(1).max(50).regex(/^[a-z0-9][a-z0-9-]*$/, "Channel names must be lowercase alphanumeric with hyphens")
|
|
5315
5938
|
});
|
|
5316
|
-
var inviteSchema =
|
|
5317
|
-
username:
|
|
5939
|
+
var inviteSchema = z10.object({
|
|
5940
|
+
username: z10.string().min(1)
|
|
5318
5941
|
});
|
|
5319
|
-
var
|
|
5942
|
+
var app26 = new Hono26().get("/", async (c) => {
|
|
5320
5943
|
const user = c.get("user");
|
|
5321
5944
|
const channels = await listChannels();
|
|
5322
5945
|
const results = await Promise.all(
|
|
@@ -5327,7 +5950,7 @@ var app24 = new Hono24().get("/", async (c) => {
|
|
|
5327
5950
|
})
|
|
5328
5951
|
);
|
|
5329
5952
|
return c.json(results);
|
|
5330
|
-
}).post("/",
|
|
5953
|
+
}).post("/", zValidator10("json", createSchema3), async (c) => {
|
|
5331
5954
|
const user = c.get("user");
|
|
5332
5955
|
const body = c.req.valid("json");
|
|
5333
5956
|
try {
|
|
@@ -5360,7 +5983,7 @@ var app24 = new Hono24().get("/", async (c) => {
|
|
|
5360
5983
|
if (!ch) return c.json({ error: "Channel not found" }, 404);
|
|
5361
5984
|
const participants = await getParticipants(ch.id);
|
|
5362
5985
|
return c.json(participants);
|
|
5363
|
-
}).post("/:name/invite",
|
|
5986
|
+
}).post("/:name/invite", zValidator10("json", inviteSchema), async (c) => {
|
|
5364
5987
|
const name = c.req.param("name");
|
|
5365
5988
|
const inviter = c.get("user");
|
|
5366
5989
|
const { username } = c.req.valid("json");
|
|
@@ -5380,21 +6003,21 @@ var app24 = new Hono24().get("/", async (c) => {
|
|
|
5380
6003
|
]);
|
|
5381
6004
|
return c.json({ ok: true });
|
|
5382
6005
|
});
|
|
5383
|
-
var channels_default2 =
|
|
6006
|
+
var channels_default2 = app26;
|
|
5384
6007
|
|
|
5385
6008
|
// src/web/api/volute/chat.ts
|
|
5386
|
-
import { zValidator as
|
|
5387
|
-
import { Hono as
|
|
6009
|
+
import { zValidator as zValidator11 } from "@hono/zod-validator";
|
|
6010
|
+
import { Hono as Hono27 } from "hono";
|
|
5388
6011
|
import { streamSSE as streamSSE6 } from "hono/streaming";
|
|
5389
|
-
import { z as
|
|
6012
|
+
import { z as z11 } from "zod";
|
|
5390
6013
|
async function fanOutToMinds2(opts) {
|
|
5391
6014
|
const participants = await getParticipants(opts.conversationId);
|
|
5392
6015
|
const mindParticipants = participants.filter((p) => p.userType === "mind");
|
|
5393
6016
|
const participantNames = participants.map((p) => p.username);
|
|
5394
6017
|
const isDM = opts.isDM ?? participants.length === 2;
|
|
5395
6018
|
const channelEntryType = opts.channelEntryType ?? (isDM ? "dm" : "group");
|
|
5396
|
-
const { getMindManager: getMindManager2 } = await import("./mind-manager-
|
|
5397
|
-
const { getSleepManagerIfReady } = await import("./sleep-manager-
|
|
6019
|
+
const { getMindManager: getMindManager2 } = await import("./mind-manager-VVK67AY3.js");
|
|
6020
|
+
const { getSleepManagerIfReady } = await import("./sleep-manager-EE4NRN2Q.js");
|
|
5398
6021
|
const manager = getMindManager2();
|
|
5399
6022
|
const sm = getSleepManagerIfReady();
|
|
5400
6023
|
const targetMinds = mindParticipants.map((ap) => {
|
|
@@ -5442,18 +6065,18 @@ async function fanOutToMinds2(opts) {
|
|
|
5442
6065
|
});
|
|
5443
6066
|
}
|
|
5444
6067
|
}
|
|
5445
|
-
var chatSchema =
|
|
5446
|
-
message:
|
|
5447
|
-
conversationId:
|
|
5448
|
-
sender:
|
|
5449
|
-
images:
|
|
5450
|
-
|
|
5451
|
-
media_type:
|
|
5452
|
-
data:
|
|
6068
|
+
var chatSchema = z11.object({
|
|
6069
|
+
message: z11.string().optional(),
|
|
6070
|
+
conversationId: z11.string().optional(),
|
|
6071
|
+
sender: z11.string().optional(),
|
|
6072
|
+
images: z11.array(
|
|
6073
|
+
z11.object({
|
|
6074
|
+
media_type: z11.string(),
|
|
6075
|
+
data: z11.string()
|
|
5453
6076
|
})
|
|
5454
6077
|
).optional()
|
|
5455
6078
|
});
|
|
5456
|
-
var
|
|
6079
|
+
var app27 = new Hono27().post("/:name/chat", zValidator11("json", chatSchema), async (c) => {
|
|
5457
6080
|
const name = c.req.param("name");
|
|
5458
6081
|
const [baseName] = name.split("@", 2);
|
|
5459
6082
|
const entry = findMind(baseName);
|
|
@@ -5530,7 +6153,7 @@ var app25 = new Hono25().post("/:name/chat", zValidator9("json", chatSchema), as
|
|
|
5530
6153
|
return c.json({ error: "Conversation not found" }, 404);
|
|
5531
6154
|
}
|
|
5532
6155
|
return streamSSE6(c, async (stream) => {
|
|
5533
|
-
const unsubscribe =
|
|
6156
|
+
const unsubscribe = subscribe2(conversationId, (event) => {
|
|
5534
6157
|
stream.writeSSE({ data: JSON.stringify(event) }).catch((err) => {
|
|
5535
6158
|
if (!stream.aborted) console.error("[chat] SSE write error:", err);
|
|
5536
6159
|
});
|
|
@@ -5540,23 +6163,23 @@ var app25 = new Hono25().post("/:name/chat", zValidator9("json", chatSchema), as
|
|
|
5540
6163
|
if (!stream.aborted) console.error("[chat] SSE ping error:", err);
|
|
5541
6164
|
});
|
|
5542
6165
|
}, 15e3);
|
|
5543
|
-
await new Promise((
|
|
6166
|
+
await new Promise((resolve20) => {
|
|
5544
6167
|
stream.onAbort(() => {
|
|
5545
6168
|
unsubscribe();
|
|
5546
6169
|
clearInterval(keepAlive);
|
|
5547
|
-
|
|
6170
|
+
resolve20();
|
|
5548
6171
|
});
|
|
5549
6172
|
});
|
|
5550
6173
|
});
|
|
5551
6174
|
});
|
|
5552
|
-
var unifiedChatSchema2 =
|
|
5553
|
-
message:
|
|
5554
|
-
conversationId:
|
|
5555
|
-
images:
|
|
6175
|
+
var unifiedChatSchema2 = z11.object({
|
|
6176
|
+
message: z11.string().optional(),
|
|
6177
|
+
conversationId: z11.string(),
|
|
6178
|
+
images: z11.array(z11.object({ media_type: z11.string(), data: z11.string() })).optional()
|
|
5556
6179
|
});
|
|
5557
|
-
var unifiedChatApp = new
|
|
6180
|
+
var unifiedChatApp = new Hono27().post(
|
|
5558
6181
|
"/chat",
|
|
5559
|
-
|
|
6182
|
+
zValidator11("json", unifiedChatSchema2),
|
|
5560
6183
|
async (c) => {
|
|
5561
6184
|
const user = c.get("user");
|
|
5562
6185
|
const body = c.req.valid("json");
|
|
@@ -5590,18 +6213,18 @@ var unifiedChatApp = new Hono25().post(
|
|
|
5590
6213
|
return c.json({ ok: true, conversationId: body.conversationId });
|
|
5591
6214
|
}
|
|
5592
6215
|
);
|
|
5593
|
-
var chat_default2 =
|
|
6216
|
+
var chat_default2 = app27;
|
|
5594
6217
|
|
|
5595
6218
|
// src/web/api/volute/conversations.ts
|
|
5596
|
-
import { zValidator as
|
|
5597
|
-
import { Hono as
|
|
5598
|
-
import { z as
|
|
5599
|
-
var createConvSchema =
|
|
5600
|
-
title:
|
|
5601
|
-
participantIds:
|
|
5602
|
-
participantNames:
|
|
6219
|
+
import { zValidator as zValidator12 } from "@hono/zod-validator";
|
|
6220
|
+
import { Hono as Hono28 } from "hono";
|
|
6221
|
+
import { z as z12 } from "zod";
|
|
6222
|
+
var createConvSchema = z12.object({
|
|
6223
|
+
title: z12.string().optional(),
|
|
6224
|
+
participantIds: z12.array(z12.number()).optional(),
|
|
6225
|
+
participantNames: z12.array(z12.string()).optional()
|
|
5603
6226
|
});
|
|
5604
|
-
var
|
|
6227
|
+
var app28 = new Hono28().get("/:name/conversations", async (c) => {
|
|
5605
6228
|
const name = c.req.param("name");
|
|
5606
6229
|
const user = c.get("user");
|
|
5607
6230
|
let lookupId = user.id;
|
|
@@ -5612,7 +6235,7 @@ var app26 = new Hono26().get("/:name/conversations", async (c) => {
|
|
|
5612
6235
|
const all = await listConversationsForUser(lookupId);
|
|
5613
6236
|
const convs = all.filter((c2) => c2.mind_name === name || c2.type === "channel");
|
|
5614
6237
|
return c.json(convs);
|
|
5615
|
-
}).post("/:name/conversations",
|
|
6238
|
+
}).post("/:name/conversations", zValidator12("json", createConvSchema), async (c) => {
|
|
5616
6239
|
const name = c.req.param("name");
|
|
5617
6240
|
const user = c.get("user");
|
|
5618
6241
|
const body = c.req.valid("json");
|
|
@@ -5686,18 +6309,18 @@ var app26 = new Hono26().get("/:name/conversations", async (c) => {
|
|
|
5686
6309
|
if (!deleted) return c.json({ error: "Conversation not found" }, 404);
|
|
5687
6310
|
return c.json({ ok: true });
|
|
5688
6311
|
});
|
|
5689
|
-
var conversations_default2 =
|
|
6312
|
+
var conversations_default2 = app28;
|
|
5690
6313
|
|
|
5691
6314
|
// src/web/api/volute/user-conversations.ts
|
|
5692
|
-
import { zValidator as
|
|
5693
|
-
import { Hono as
|
|
6315
|
+
import { zValidator as zValidator13 } from "@hono/zod-validator";
|
|
6316
|
+
import { Hono as Hono29 } from "hono";
|
|
5694
6317
|
import { streamSSE as streamSSE7 } from "hono/streaming";
|
|
5695
|
-
import { z as
|
|
5696
|
-
var
|
|
5697
|
-
title:
|
|
5698
|
-
participantNames:
|
|
6318
|
+
import { z as z13 } from "zod";
|
|
6319
|
+
var createSchema4 = z13.object({
|
|
6320
|
+
title: z13.string().optional(),
|
|
6321
|
+
participantNames: z13.array(z13.string()).min(1)
|
|
5699
6322
|
});
|
|
5700
|
-
var
|
|
6323
|
+
var app29 = new Hono29().use("*", authMiddleware).get("/", async (c) => {
|
|
5701
6324
|
const user = c.get("user");
|
|
5702
6325
|
const convs = await listConversationsWithParticipants(user.id);
|
|
5703
6326
|
return c.json(convs);
|
|
@@ -5709,7 +6332,7 @@ var app27 = new Hono27().use("*", authMiddleware).get("/", async (c) => {
|
|
|
5709
6332
|
}
|
|
5710
6333
|
const msgs = await getMessages(id);
|
|
5711
6334
|
return c.json(msgs);
|
|
5712
|
-
}).post("/",
|
|
6335
|
+
}).post("/", zValidator13("json", createSchema4), async (c) => {
|
|
5713
6336
|
const user = c.get("user");
|
|
5714
6337
|
const body = c.req.valid("json");
|
|
5715
6338
|
const participantIds = /* @__PURE__ */ new Set();
|
|
@@ -5746,7 +6369,7 @@ var app27 = new Hono27().use("*", authMiddleware).get("/", async (c) => {
|
|
|
5746
6369
|
return c.json({ error: "Conversation not found" }, 404);
|
|
5747
6370
|
}
|
|
5748
6371
|
return streamSSE7(c, async (stream) => {
|
|
5749
|
-
const unsubscribe =
|
|
6372
|
+
const unsubscribe = subscribe2(conversationId, (event) => {
|
|
5750
6373
|
stream.writeSSE({ data: JSON.stringify(event) }).catch((err) => {
|
|
5751
6374
|
if (!stream.aborted) console.error("[chat] SSE write error:", err);
|
|
5752
6375
|
});
|
|
@@ -5756,11 +6379,11 @@ var app27 = new Hono27().use("*", authMiddleware).get("/", async (c) => {
|
|
|
5756
6379
|
if (!stream.aborted) console.error("[chat] SSE ping error:", err);
|
|
5757
6380
|
});
|
|
5758
6381
|
}, 15e3);
|
|
5759
|
-
await new Promise((
|
|
6382
|
+
await new Promise((resolve20) => {
|
|
5760
6383
|
stream.onAbort(() => {
|
|
5761
6384
|
unsubscribe();
|
|
5762
6385
|
clearInterval(keepAlive);
|
|
5763
|
-
|
|
6386
|
+
resolve20();
|
|
5764
6387
|
});
|
|
5765
6388
|
});
|
|
5766
6389
|
});
|
|
@@ -5771,12 +6394,12 @@ var app27 = new Hono27().use("*", authMiddleware).get("/", async (c) => {
|
|
|
5771
6394
|
if (!deleted) return c.json({ error: "Conversation not found" }, 404);
|
|
5772
6395
|
return c.json({ ok: true });
|
|
5773
6396
|
});
|
|
5774
|
-
var user_conversations_default =
|
|
6397
|
+
var user_conversations_default = app29;
|
|
5775
6398
|
|
|
5776
6399
|
// src/web/app.ts
|
|
5777
6400
|
var httpLog = logger_default.child("http");
|
|
5778
|
-
var
|
|
5779
|
-
|
|
6401
|
+
var app30 = new Hono30();
|
|
6402
|
+
app30.onError((err, c) => {
|
|
5780
6403
|
if (err instanceof HTTPException) {
|
|
5781
6404
|
return err.getResponse();
|
|
5782
6405
|
}
|
|
@@ -5787,10 +6410,10 @@ app28.onError((err, c) => {
|
|
|
5787
6410
|
});
|
|
5788
6411
|
return c.json({ error: "Internal server error" }, 500);
|
|
5789
6412
|
});
|
|
5790
|
-
|
|
6413
|
+
app30.notFound((c) => {
|
|
5791
6414
|
return c.json({ error: "Not found" }, 404);
|
|
5792
6415
|
});
|
|
5793
|
-
|
|
6416
|
+
app30.use("*", async (c, next) => {
|
|
5794
6417
|
const start = Date.now();
|
|
5795
6418
|
await next();
|
|
5796
6419
|
const duration = Date.now() - start;
|
|
@@ -5801,7 +6424,7 @@ app28.use("*", async (c, next) => {
|
|
|
5801
6424
|
httpLog.debug("request", data);
|
|
5802
6425
|
}
|
|
5803
6426
|
});
|
|
5804
|
-
|
|
6427
|
+
app30.get("/api/health", (c) => {
|
|
5805
6428
|
let version = "unknown";
|
|
5806
6429
|
let cached = null;
|
|
5807
6430
|
try {
|
|
@@ -5816,38 +6439,40 @@ app28.get("/api/health", (c) => {
|
|
|
5816
6439
|
...cached?.updateAvailable ? { updateAvailable: true, latest: cached.latest } : {}
|
|
5817
6440
|
});
|
|
5818
6441
|
});
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
5825
|
-
|
|
5826
|
-
|
|
5827
|
-
|
|
5828
|
-
|
|
5829
|
-
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
|
|
5836
|
-
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
6442
|
+
app30.use("/api/*", bodyLimit({ maxSize: 10 * 1024 * 1024 }));
|
|
6443
|
+
app30.use("/api/*", csrf());
|
|
6444
|
+
app30.use("/api/activity/*", authMiddleware);
|
|
6445
|
+
app30.use("/api/minds/*", authMiddleware);
|
|
6446
|
+
app30.use("/api/conversations/*", authMiddleware);
|
|
6447
|
+
app30.use("/api/volute/*", authMiddleware);
|
|
6448
|
+
app30.use("/api/system/*", authMiddleware);
|
|
6449
|
+
app30.use("/api/env/*", authMiddleware);
|
|
6450
|
+
app30.use("/api/prompts/*", authMiddleware);
|
|
6451
|
+
app30.use("/api/skills/*", authMiddleware);
|
|
6452
|
+
app30.use("/api/notes/*", authMiddleware);
|
|
6453
|
+
app30.use("/api/v1/*", authMiddleware);
|
|
6454
|
+
app30.route("/pages", pages_default);
|
|
6455
|
+
app30.route("/public", public_files_default);
|
|
6456
|
+
var routes = app30.route("/api/activity", activity_default).route("/api/keys", keys_default).route("/api/auth", auth_default).route("/api/system", system_default).route("/api/system", update_default).route("/api/minds", minds_default).route("/api/minds", chat_default2).route("/api/minds", connectors_default).route("/api/minds", schedules_default).route("/api/minds", logs_default).route("/api/minds", typing_default).route("/api/minds", variants_default).route("/api/minds", file_sharing_default).route("/api/minds", files_default).route("/api/minds", channels_default).route("/api/minds", shared_default).route("/api/minds", env_default).route("/api/minds", mind_skills_default).route("/api/minds", conversations_default2).route("/api/env", sharedEnvApp).route("/api/notes", notes_default).route("/api/prompts", prompts_default).route("/api/skills", skills_default).route("/api/conversations", user_conversations_default).route("/api/volute/channels", channels_default2).route("/api/volute", unifiedChatApp).route("/api/v1/conversations", conversations_default).route("/api/v1/events", events_default).route("/api/v1", chat_default);
|
|
6457
|
+
app30.route("/api/v1/minds", minds_default);
|
|
6458
|
+
app30.route("/api/v1/minds", typing_default);
|
|
6459
|
+
app30.route("/api/v1/minds", variants_default);
|
|
6460
|
+
app30.route("/api/v1/minds", files_default);
|
|
6461
|
+
app30.route("/api/v1/minds", env_default);
|
|
6462
|
+
app30.route("/api/v1/minds", mind_skills_default);
|
|
6463
|
+
app30.route("/api/v1/minds", connectors_default);
|
|
6464
|
+
app30.route("/api/v1/minds", schedules_default);
|
|
6465
|
+
app30.route("/api/v1/minds", logs_default);
|
|
6466
|
+
app30.route("/api/v1/system", system_default);
|
|
6467
|
+
app30.route("/api/v1/system", update_default);
|
|
6468
|
+
app30.route("/api/v1/prompts", prompts_default);
|
|
6469
|
+
app30.route("/api/v1/skills", skills_default);
|
|
6470
|
+
app30.route("/api/v1/env", sharedEnvApp);
|
|
6471
|
+
app30.route("/api/v1/channels", channels_default2);
|
|
6472
|
+
var app_default = app30;
|
|
5848
6473
|
|
|
5849
6474
|
// src/web/server.ts
|
|
5850
|
-
var
|
|
6475
|
+
var MIME_TYPES3 = {
|
|
5851
6476
|
".html": "text/html",
|
|
5852
6477
|
".js": "application/javascript",
|
|
5853
6478
|
".css": "text/css",
|
|
@@ -5864,7 +6489,7 @@ async function startServer({
|
|
|
5864
6489
|
let assetsDir = "";
|
|
5865
6490
|
let searchDir = dirname(new URL(import.meta.url).pathname);
|
|
5866
6491
|
for (let i = 0; i < 5; i++) {
|
|
5867
|
-
const candidate =
|
|
6492
|
+
const candidate = resolve18(searchDir, "dist", "web-assets");
|
|
5868
6493
|
if (existsSync13(candidate)) {
|
|
5869
6494
|
assetsDir = candidate;
|
|
5870
6495
|
break;
|
|
@@ -5875,19 +6500,19 @@ async function startServer({
|
|
|
5875
6500
|
app_default.get("*", async (c) => {
|
|
5876
6501
|
const urlPath = new URL(c.req.url).pathname;
|
|
5877
6502
|
if (urlPath.startsWith("/api/")) return c.notFound();
|
|
5878
|
-
const filePath =
|
|
6503
|
+
const filePath = resolve18(assetsDir, urlPath.slice(1));
|
|
5879
6504
|
if (!filePath.startsWith(assetsDir)) return c.text("Forbidden", 403);
|
|
5880
|
-
const s = await
|
|
6505
|
+
const s = await stat4(filePath).catch(() => null);
|
|
5881
6506
|
if (s?.isFile()) {
|
|
5882
|
-
const ext =
|
|
5883
|
-
const mime =
|
|
5884
|
-
const body = await
|
|
6507
|
+
const ext = extname5(filePath);
|
|
6508
|
+
const mime = MIME_TYPES3[ext] || "application/octet-stream";
|
|
6509
|
+
const body = await readFile4(filePath);
|
|
5885
6510
|
return c.body(body, 200, { "Content-Type": mime });
|
|
5886
6511
|
}
|
|
5887
|
-
const indexPath =
|
|
5888
|
-
const indexStat = await
|
|
6512
|
+
const indexPath = resolve18(assetsDir, "index.html");
|
|
6513
|
+
const indexStat = await stat4(indexPath).catch(() => null);
|
|
5889
6514
|
if (indexStat?.isFile()) {
|
|
5890
|
-
const body = await
|
|
6515
|
+
const body = await readFile4(indexPath, "utf-8");
|
|
5891
6516
|
return c.html(body);
|
|
5892
6517
|
}
|
|
5893
6518
|
return c.text("Not found", 404);
|
|
@@ -5901,10 +6526,10 @@ async function startServer({
|
|
|
5901
6526
|
createServer: createHttpsServer,
|
|
5902
6527
|
serverOptions: { key: tls.key, cert: tls.cert }
|
|
5903
6528
|
});
|
|
5904
|
-
await new Promise((
|
|
6529
|
+
await new Promise((resolve20, reject) => {
|
|
5905
6530
|
server2.on("listening", () => {
|
|
5906
6531
|
logger_default.info("Volute UI running (https)", { hostname, port });
|
|
5907
|
-
|
|
6532
|
+
resolve20();
|
|
5908
6533
|
});
|
|
5909
6534
|
server2.on("error", (err) => {
|
|
5910
6535
|
reject(err);
|
|
@@ -5912,13 +6537,13 @@ async function startServer({
|
|
|
5912
6537
|
});
|
|
5913
6538
|
const internalPort = port + 1;
|
|
5914
6539
|
const internalServer = serve({ fetch: app_default.fetch, port: internalPort, hostname: "127.0.0.1" });
|
|
5915
|
-
await new Promise((
|
|
6540
|
+
await new Promise((resolve20, reject) => {
|
|
5916
6541
|
internalServer.on("listening", () => {
|
|
5917
6542
|
logger_default.info("Volute API running (http, internal)", {
|
|
5918
6543
|
hostname: "127.0.0.1",
|
|
5919
6544
|
port: internalPort
|
|
5920
6545
|
});
|
|
5921
|
-
|
|
6546
|
+
resolve20();
|
|
5922
6547
|
});
|
|
5923
6548
|
internalServer.on("error", (err) => {
|
|
5924
6549
|
reject(err);
|
|
@@ -5927,10 +6552,10 @@ async function startServer({
|
|
|
5927
6552
|
return { server: server2, internalPort };
|
|
5928
6553
|
}
|
|
5929
6554
|
const server = serve({ fetch: app_default.fetch, port, hostname });
|
|
5930
|
-
await new Promise((
|
|
6555
|
+
await new Promise((resolve20, reject) => {
|
|
5931
6556
|
server.on("listening", () => {
|
|
5932
6557
|
logger_default.info("Volute API running (http)", { hostname, port });
|
|
5933
|
-
|
|
6558
|
+
resolve20();
|
|
5934
6559
|
});
|
|
5935
6560
|
server.on("error", (err) => {
|
|
5936
6561
|
reject(err);
|
|
@@ -5941,7 +6566,7 @@ async function startServer({
|
|
|
5941
6566
|
|
|
5942
6567
|
// src/daemon.ts
|
|
5943
6568
|
if (!process.env.VOLUTE_HOME) {
|
|
5944
|
-
process.env.VOLUTE_HOME =
|
|
6569
|
+
process.env.VOLUTE_HOME = resolve19(homedir2(), ".volute");
|
|
5945
6570
|
}
|
|
5946
6571
|
if (process.env.VOLUTE_TIMEZONE && !process.env.TZ) {
|
|
5947
6572
|
process.env.TZ = process.env.VOLUTE_TIMEZONE;
|
|
@@ -5951,7 +6576,7 @@ async function startDaemon(opts) {
|
|
|
5951
6576
|
const myPid = String(process.pid);
|
|
5952
6577
|
const home = voluteHome();
|
|
5953
6578
|
if (!opts.foreground) {
|
|
5954
|
-
const rotatingLog = new RotatingLog(
|
|
6579
|
+
const rotatingLog = new RotatingLog(resolve19(home, "daemon.log"));
|
|
5955
6580
|
logger_default.setOutput((line) => rotatingLog.write(`${line}
|
|
5956
6581
|
`));
|
|
5957
6582
|
const write = (...args) => rotatingLog.write(`${format(...args)}
|
|
@@ -5961,8 +6586,8 @@ async function startDaemon(opts) {
|
|
|
5961
6586
|
console.warn = write;
|
|
5962
6587
|
console.info = write;
|
|
5963
6588
|
}
|
|
5964
|
-
const DAEMON_PID_PATH =
|
|
5965
|
-
const DAEMON_JSON_PATH =
|
|
6589
|
+
const DAEMON_PID_PATH = resolve19(home, "daemon.pid");
|
|
6590
|
+
const DAEMON_JSON_PATH = resolve19(home, "daemon.json");
|
|
5966
6591
|
mkdirSync9(home, { recursive: true });
|
|
5967
6592
|
migrateAgentsToMinds();
|
|
5968
6593
|
try {
|
|
@@ -5971,11 +6596,18 @@ async function startDaemon(opts) {
|
|
|
5971
6596
|
logger_default.warn("failed to initialize shared repo", logger_default.errorData(err));
|
|
5972
6597
|
}
|
|
5973
6598
|
initRegistryCache();
|
|
6599
|
+
const { initSandbox } = await import("./sandbox-EHGFF52K.js");
|
|
6600
|
+
await initSandbox();
|
|
5974
6601
|
try {
|
|
5975
6602
|
await syncBuiltinSkills();
|
|
5976
6603
|
} catch (err) {
|
|
5977
6604
|
logger_default.error("failed to sync built-in skills", logger_default.errorData(err));
|
|
5978
6605
|
}
|
|
6606
|
+
try {
|
|
6607
|
+
await ensureSystemChannel();
|
|
6608
|
+
} catch (err) {
|
|
6609
|
+
logger_default.warn("failed to ensure #system channel", logger_default.errorData(err));
|
|
6610
|
+
}
|
|
5979
6611
|
const token = process.env.VOLUTE_DAEMON_TOKEN || randomBytes2(32).toString("hex");
|
|
5980
6612
|
let tls;
|
|
5981
6613
|
if (opts.tailscale) {
|
|
@@ -6021,11 +6653,12 @@ async function startDaemon(opts) {
|
|
|
6021
6653
|
const unsubscribeWebhook = initWebhook();
|
|
6022
6654
|
const registry = readRegistry();
|
|
6023
6655
|
for (const entry of registry) {
|
|
6024
|
-
|
|
6025
|
-
|
|
6026
|
-
|
|
6027
|
-
|
|
6028
|
-
|
|
6656
|
+
for (const migrate of [migrateDotVoluteDir, migrateMindState, migratePagesDirToPublic]) {
|
|
6657
|
+
try {
|
|
6658
|
+
migrate(entry.name);
|
|
6659
|
+
} catch (err) {
|
|
6660
|
+
logger_default.warn(`failed to migrate state for ${entry.name}`, logger_default.errorData(err));
|
|
6661
|
+
}
|
|
6029
6662
|
}
|
|
6030
6663
|
}
|
|
6031
6664
|
const runningEntries = registry.filter((e) => e.running);
|
|
@@ -6075,7 +6708,7 @@ async function startDaemon(opts) {
|
|
|
6075
6708
|
});
|
|
6076
6709
|
await Promise.all(workers);
|
|
6077
6710
|
}
|
|
6078
|
-
import("./cloud-sync-
|
|
6711
|
+
import("./cloud-sync-NI2K3C7G.js").then(
|
|
6079
6712
|
({ consumeQueuedMessages }) => consumeQueuedMessages().catch((err) => {
|
|
6080
6713
|
logger_default.warn("failed to consume queued cloud messages", logger_default.errorData(err));
|
|
6081
6714
|
})
|
|
@@ -6083,7 +6716,7 @@ async function startDaemon(opts) {
|
|
|
6083
6716
|
logger_default.warn("failed to load cloud-sync module", logger_default.errorData(err));
|
|
6084
6717
|
});
|
|
6085
6718
|
try {
|
|
6086
|
-
const { backfillTemplateHashes, notifyVersionUpdate } = await import("./version-notify-
|
|
6719
|
+
const { backfillTemplateHashes, notifyVersionUpdate } = await import("./version-notify-USFZBWMG.js");
|
|
6087
6720
|
backfillTemplateHashes();
|
|
6088
6721
|
notifyVersionUpdate().catch((err) => {
|
|
6089
6722
|
logger_default.warn("failed to send version update notifications", logger_default.errorData(err));
|
|
@@ -6097,6 +6730,9 @@ async function startDaemon(opts) {
|
|
|
6097
6730
|
cleanExpiredSessions().catch((err) => {
|
|
6098
6731
|
logger_default.warn("failed to clean expired sessions", logger_default.errorData(err));
|
|
6099
6732
|
});
|
|
6733
|
+
migrateMindRoles().catch((err) => {
|
|
6734
|
+
logger_default.warn("failed to migrate mind roles", logger_default.errorData(err));
|
|
6735
|
+
});
|
|
6100
6736
|
logger_default.info(`running on ${hostname}:${port}, pid ${myPid}`);
|
|
6101
6737
|
function cleanup() {
|
|
6102
6738
|
try {
|
|
@@ -6158,6 +6794,7 @@ if (import.meta.url === `file://${process.argv[1]}` || process.argv[1]?.endsWith
|
|
|
6158
6794
|
let hostname = "127.0.0.1";
|
|
6159
6795
|
let foreground = false;
|
|
6160
6796
|
let tailscale = false;
|
|
6797
|
+
let noSandbox = false;
|
|
6161
6798
|
for (let i = 2; i < process.argv.length; i++) {
|
|
6162
6799
|
if (process.argv[i] === "--port" && process.argv[i + 1]) {
|
|
6163
6800
|
port = parseInt(process.argv[i + 1], 10);
|
|
@@ -6169,8 +6806,13 @@ if (import.meta.url === `file://${process.argv[1]}` || process.argv[1]?.endsWith
|
|
|
6169
6806
|
foreground = true;
|
|
6170
6807
|
} else if (process.argv[i] === "--tailscale") {
|
|
6171
6808
|
tailscale = true;
|
|
6809
|
+
} else if (process.argv[i] === "--no-sandbox") {
|
|
6810
|
+
noSandbox = true;
|
|
6172
6811
|
}
|
|
6173
6812
|
}
|
|
6813
|
+
if (noSandbox) {
|
|
6814
|
+
process.env.VOLUTE_SANDBOX = "0";
|
|
6815
|
+
}
|
|
6174
6816
|
startDaemon({ port, hostname, foreground, tailscale });
|
|
6175
6817
|
}
|
|
6176
6818
|
export {
|