volute 0.28.0 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +127 -18
- package/dist/{accept-666DIZX2.js → accept-E3PAH3QJ.js} +2 -2
- package/dist/{activity-events-BBIEA2F4.js → activity-events-BKBPPUBP.js} +2 -2
- package/dist/ai-service-VAJT5UBS.js +29 -0
- package/dist/api.d.ts +586 -529
- package/dist/{archive-UA4BDFXQ.js → archive-WWDBWYN2.js} +2 -2
- package/dist/{bridge-FQHZL3MC.js → bridge-RO37CUFM.js} +2 -2
- package/dist/{chat-M4SX42JD.js → chat-TCUNPFGO.js} +8 -8
- package/dist/{chunk-IAYBDWVG.js → chunk-2C2VXEBB.js} +147 -2
- package/dist/chunk-2NDZC3S7.js +1330 -0
- package/dist/{chunk-IKRVFPWU.js → chunk-7D47T4RB.js} +3 -2
- package/dist/chunk-A6TUJJ3L.js +19 -0
- package/dist/{chunk-AW7PFDVN.js → chunk-CVH6Y2YG.js} +1 -1
- package/dist/{chunk-XBLSAVJF.js → chunk-DTC6EH5I.js} +1 -1
- package/dist/chunk-EFP3PE6C.js +232 -0
- package/dist/{chunk-JGFVMROS.js → chunk-EFVHR7KH.js} +1 -1
- package/dist/{chunk-K5NAC55T.js → chunk-FSM45XD5.js} +2 -2
- package/dist/{chunk-LAC664WU.js → chunk-FXHXHI2A.js} +42 -24
- package/dist/{chunk-RKQEHRBB.js → chunk-G3GBKZGG.js} +1 -1
- package/dist/{chunk-H7OZRFJB.js → chunk-HHTXM4JT.js} +0 -49
- package/dist/{chunk-J4IBNXGJ.js → chunk-IKHDUZRH.js} +4 -3
- package/dist/{chunk-MD4C26II.js → chunk-JGFRDMR6.js} +1 -1
- package/dist/{chunk-POSXWWTA.js → chunk-LIRWLNAK.js} +26 -12
- package/dist/{chunk-NI5FFCCS.js → chunk-MDPCSXZ4.js} +35 -11
- package/dist/chunk-NSBFETWP.js +188 -0
- package/dist/{chunk-VIVMW2H2.js → chunk-P27RV5WM.js} +1 -1
- package/dist/{chunk-EHYDTZTF.js → chunk-P7VFDSSG.js} +2 -2
- package/dist/{chunk-AAPXKR5V.js → chunk-QVAQ5454.js} +181 -544
- package/dist/{chunk-HDN7MNGD.js → chunk-S5LR3XYJ.js} +1 -1
- package/dist/{chunk-2YP2TVDT.js → chunk-UPA6COHU.js} +5 -5
- package/dist/{chunk-AKPFNL7L.js → chunk-VGWJSNHS.js} +1 -1
- package/dist/{chunk-SGVNFZHW.js → chunk-W5OOPLNP.js} +3 -3
- package/dist/{chunk-2WPW7OT6.js → chunk-ZWKTUQEL.js} +1 -1
- package/dist/cli.js +25 -26
- package/dist/clock-G3ALCMLJ.js +263 -0
- package/dist/{cloud-sync-HDL6PHZI.js → cloud-sync-JV4LJOK3.js} +14 -12
- package/dist/connectors/discord-bridge.js +1 -1
- package/dist/connectors/slack-bridge.js +1 -1
- package/dist/connectors/telegram-bridge.js +1 -1
- package/dist/{conversations-M2K4253F.js → conversations-7KVQV7EZ.js} +9 -3
- package/dist/create-JTLS7GX3.js +70 -0
- package/dist/{create-QWV73WXD.js → create-VQSQHJQW.js} +1 -1
- package/dist/{daemon-client-I42FK2BF.js → daemon-client-BCTFGVCZ.js} +2 -2
- package/dist/{daemon-restart-G4B2OYAB.js → daemon-restart-4JGBHEJ4.js} +7 -7
- package/dist/daemon.js +1474 -1124
- package/dist/{db-IC4J52XQ.js → db-HMFPIRO2.js} +1 -1
- package/dist/{delete-4JYGD4VN.js → delete-JESHKE7F.js} +1 -1
- package/dist/down-NGBMGORS.js +14 -0
- package/dist/{env-YJMUMFIY.js → env-CLXXT7M2.js} +2 -2
- package/dist/{export-BOJQWBMA.js → export-EGA5M5PB.js} +3 -3
- package/dist/extension-WZ4SUPJB.js +174 -0
- package/dist/extensions-ECO4RPFQ.js +27 -0
- package/dist/{files-M546TKVN.js → files-4VEJDASH.js} +3 -3
- package/dist/{history-ALPTNB3I.js → history-EJMMLXDO.js} +17 -2
- package/dist/{import-SRTQXBGH.js → import-YCGPMBSI.js} +3 -3
- package/dist/{join-J4QU42DL.js → join-2GBJKZEN.js} +1 -1
- package/dist/{list-R73GENNL.js → list-Q6O7FGAN.js} +2 -2
- package/dist/{login-3QZNR2DF.js → login-RET5WESK.js} +2 -2
- package/dist/{login-BKP3AFWN.js → login-RL6AU2SM.js} +3 -3
- package/dist/{logout-T53VKCPU.js → logout-CGAGJN3L.js} +2 -2
- package/dist/{logout-IQK7FNEK.js → logout-JRPBEMMR.js} +3 -3
- package/dist/message-delivery-6YMVNOEC.js +28 -0
- package/dist/{migrate-registry-to-db-XC7T5B7P.js → migrate-registry-to-db-FK35IPEH.js} +1 -1
- package/dist/{mind-S5V6CK5W.js → mind-LUWRQUQ5.js} +17 -17
- package/dist/{mind-activity-tracker-EN6XNXPF.js → mind-activity-tracker-VYN2ZZ2M.js} +3 -3
- package/dist/{mind-list-UPJ75GPI.js → mind-list-V5WW5DUA.js} +2 -2
- package/dist/{mind-manager-S6ILZVX3.js → mind-manager-YFCOIAAX.js} +6 -6
- package/dist/{mind-sleep-BTSWQNAC.js → mind-sleep-R6PTNNW4.js} +2 -2
- package/dist/{mind-status-TK5AETEM.js → mind-status-I4ISFJ6I.js} +2 -2
- package/dist/{mind-wake-SBAKIDVP.js → mind-wake-67ZQEWAV.js} +2 -2
- package/dist/{package-CG4RWUGP.js → package-S2OAA5ZA.js} +11 -5
- package/dist/pages-watcher-Z3PKNROC.js +21 -0
- package/dist/{read-36UFXN3G.js → read-WQMPTSN2.js} +2 -2
- package/dist/{register-CHREOMJ3.js → register-NZDSTLP3.js} +3 -3
- package/dist/{registry-NDNOOYG4.js → registry-ODSALQQL.js} +1 -1
- package/dist/{reject-LXIZFJ4Q.js → reject-2HZOJEIJ.js} +2 -2
- package/dist/{restart-6ESL3NBO.js → restart-QHS3NT64.js} +2 -2
- package/dist/{sandbox-5BW5HPXM.js → sandbox-O5FUSF43.js} +3 -3
- package/dist/{seed-SSUCYYDF.js → seed-WUQMPLDM.js} +1 -1
- package/dist/{send-TAOEZ4NH.js → send-OAN3RYYY.js} +20 -6
- package/dist/{setup-JHL5ZEST.js → setup-QMDK5RZX.js} +2 -2
- package/dist/{setup-RXYVGGT7.js → setup-XJH3E7YM.js} +45 -14
- package/dist/{skill-AUAQTSP5.js → skill-FZIN4W4Q.js} +65 -3
- package/dist/skills/dreaming/references/INSTALL.md +3 -17
- package/dist/skills/volute-mind/SKILL.md +45 -27
- package/dist/sleep-manager-O7YQFCV5.js +30 -0
- package/dist/{split-TKJ5OT3P.js → split-EXYGGGQN.js} +1 -1
- package/dist/{sprout-UNT7LKKE.js → sprout-AXQ6H5DB.js} +8 -7
- package/dist/{start-EUJSS5R4.js → start-MTOVL6SY.js} +2 -2
- package/dist/{status-NQJYR4BG.js → status-ZRO37MWR.js} +5 -5
- package/dist/{stop-3XAITBBF.js → stop-OK5WEPVC.js} +2 -2
- package/dist/{systems-SMEFSHTA.js → systems-W3BBMSOZ.js} +5 -5
- package/dist/{tailscale-NY5MUMY3.js → tailscale-BM72RXCJ.js} +1 -1
- package/dist/{template-hash-BIMA4ILT.js → template-hash-3HOR4UAJ.js} +1 -1
- package/dist/up-BXUAIDXB.js +17 -0
- package/dist/{update-PTSH22AZ.js → update-PLPHMMZ2.js} +5 -5
- package/dist/{update-check-64FWC4Y2.js → update-check-CVCN7MF6.js} +2 -2
- package/dist/{upgrade-HA47CS4C.js → upgrade-I6NPCYUU.js} +1 -1
- package/dist/{version-notify-JDUF4HQJ.js → version-notify-2NTWVEHL.js} +18 -16
- package/dist/web-assets/assets/index--kREqKl9.js +72 -0
- package/dist/web-assets/assets/index-BXYTG0nJ.css +1 -0
- package/dist/web-assets/ext-theme.css +111 -0
- package/dist/web-assets/index.html +2 -2
- package/package.json +11 -5
- package/packages/extensions/notes/dist/ui/assets/index-DgawVO5g.css +1 -0
- package/packages/extensions/notes/dist/ui/assets/index-qUWoeC4c.js +2 -0
- package/packages/extensions/notes/dist/ui/index.html +14 -0
- package/packages/extensions/notes/skills/notes/SKILL.md +62 -0
- package/packages/extensions/notes/skills/notes/scripts/notes.mjs +185 -0
- package/packages/extensions/pages/dist/ui/assets/index-D0HyS-xQ.css +1 -0
- package/packages/extensions/pages/dist/ui/assets/index-tLTROSk5.js +2 -0
- package/packages/extensions/pages/dist/ui/index.html +14 -0
- package/packages/extensions/pages/skills/pages/SKILL.md +58 -0
- package/templates/_base/home/VOLUTE.md +1 -1
- package/templates/_base/src/lib/logger.ts +10 -49
- package/templates/_base/src/lib/router.ts +1 -9
- package/templates/claude/src/lib/stream-consumer.ts +1 -4
- package/templates/pi/src/lib/event-handler.ts +1 -14
- package/dist/chunk-P72MVS4R.js +0 -188
- package/dist/chunk-T6HKBWXZ.js +0 -23
- package/dist/chunk-ZYGKG6VC.js +0 -22
- package/dist/create-D7J73A6H.js +0 -45
- package/dist/down-LVBXEULC.js +0 -14
- package/dist/message-delivery-HV3S6HZV.js +0 -24
- package/dist/notes-XCER3I7M.js +0 -220
- package/dist/pages-KJDJX4TA.js +0 -36
- package/dist/publish-ZZB33WP4.js +0 -86
- package/dist/schedule-QTJMFATP.js +0 -154
- package/dist/skills/notes/SKILL.md +0 -34
- package/dist/sleep-manager-WMVG2VCL.js +0 -28
- package/dist/status-S7UUPNRW.js +0 -38
- package/dist/up-GM2JOH2Y.js +0 -17
- package/dist/web-assets/assets/index-BZGvToHi.css +0 -1
- package/dist/web-assets/assets/index-Cz4TrpzB.js +0 -75
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
markIdle
|
|
4
|
+
} from "./chunk-FSM45XD5.js";
|
|
5
|
+
import {
|
|
6
|
+
clearJsonMap,
|
|
7
|
+
getMindManager,
|
|
8
|
+
getPrompt,
|
|
9
|
+
loadJsonMap,
|
|
10
|
+
saveJsonMap
|
|
11
|
+
} from "./chunk-LIRWLNAK.js";
|
|
2
12
|
import {
|
|
3
13
|
addMessage,
|
|
4
14
|
createChannel,
|
|
@@ -6,28 +16,24 @@ import {
|
|
|
6
16
|
getParticipants,
|
|
7
17
|
joinChannel,
|
|
8
18
|
publish as publish2
|
|
9
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-2C2VXEBB.js";
|
|
10
20
|
import {
|
|
11
|
-
|
|
12
|
-
|
|
21
|
+
getOrCreateMindUser,
|
|
22
|
+
notifyExtensionsMindStart,
|
|
23
|
+
notifyExtensionsMindStop,
|
|
24
|
+
readSystemsConfig,
|
|
25
|
+
syncMindProfile
|
|
26
|
+
} from "./chunk-2NDZC3S7.js";
|
|
13
27
|
import {
|
|
14
|
-
broadcast,
|
|
15
28
|
publish,
|
|
16
29
|
subscribe
|
|
17
|
-
} from "./chunk-
|
|
18
|
-
import {
|
|
19
|
-
clearJsonMap,
|
|
20
|
-
getMindManager,
|
|
21
|
-
getPrompt,
|
|
22
|
-
loadJsonMap,
|
|
23
|
-
saveJsonMap
|
|
24
|
-
} from "./chunk-POSXWWTA.js";
|
|
30
|
+
} from "./chunk-P27RV5WM.js";
|
|
25
31
|
import {
|
|
26
32
|
logger_default
|
|
27
33
|
} from "./chunk-YUIHSKR6.js";
|
|
28
34
|
import {
|
|
29
35
|
exec
|
|
30
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-CVH6Y2YG.js";
|
|
31
37
|
import {
|
|
32
38
|
deliveryQueue,
|
|
33
39
|
findMind,
|
|
@@ -37,27 +43,25 @@ import {
|
|
|
37
43
|
mindHistory,
|
|
38
44
|
readRegistry,
|
|
39
45
|
stateDir,
|
|
40
|
-
users,
|
|
41
46
|
voluteHome,
|
|
42
|
-
voluteSystemDir
|
|
43
|
-
|
|
44
|
-
} from "./chunk-H7OZRFJB.js";
|
|
47
|
+
voluteSystemDir
|
|
48
|
+
} from "./chunk-HHTXM4JT.js";
|
|
45
49
|
|
|
46
50
|
// src/lib/daemon/sleep-manager.ts
|
|
47
51
|
import { execFile, spawn as spawnChild } from "child_process";
|
|
48
52
|
import {
|
|
49
|
-
existsSync as
|
|
50
|
-
mkdirSync as
|
|
51
|
-
readdirSync
|
|
52
|
-
readFileSync as
|
|
53
|
+
existsSync as existsSync4,
|
|
54
|
+
mkdirSync as mkdirSync4,
|
|
55
|
+
readdirSync,
|
|
56
|
+
readFileSync as readFileSync5,
|
|
53
57
|
readlinkSync,
|
|
54
|
-
renameSync
|
|
55
|
-
writeFileSync as
|
|
58
|
+
renameSync,
|
|
59
|
+
writeFileSync as writeFileSync4
|
|
56
60
|
} from "fs";
|
|
57
|
-
import { resolve as
|
|
61
|
+
import { resolve as resolve7 } from "path";
|
|
58
62
|
import { promisify } from "util";
|
|
59
63
|
import { CronExpressionParser as CronExpressionParser2 } from "cron-parser";
|
|
60
|
-
import { and as
|
|
64
|
+
import { and as and2, eq as eq2, inArray } from "drizzle-orm";
|
|
61
65
|
|
|
62
66
|
// src/lib/volute-config.ts
|
|
63
67
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
@@ -95,344 +99,9 @@ function writeVoluteConfig(mindDir2, config) {
|
|
|
95
99
|
`);
|
|
96
100
|
}
|
|
97
101
|
|
|
98
|
-
// src/lib/auth.ts
|
|
99
|
-
import { compareSync, hashSync } from "bcryptjs";
|
|
100
|
-
import { and, count, eq, inArray } from "drizzle-orm";
|
|
101
|
-
var userSelectFields = {
|
|
102
|
-
id: users.id,
|
|
103
|
-
username: users.username,
|
|
104
|
-
role: users.role,
|
|
105
|
-
user_type: users.user_type,
|
|
106
|
-
display_name: users.display_name,
|
|
107
|
-
description: users.description,
|
|
108
|
-
avatar: users.avatar,
|
|
109
|
-
created_at: users.created_at
|
|
110
|
-
};
|
|
111
|
-
async function createUser(username, password) {
|
|
112
|
-
const db = await getDb();
|
|
113
|
-
const hash = hashSync(password, 10);
|
|
114
|
-
const [{ value }] = await db.select({ value: count() }).from(users).where(eq(users.user_type, "brain"));
|
|
115
|
-
const role = value === 0 ? "admin" : "pending";
|
|
116
|
-
const [result] = await db.insert(users).values({ username, password_hash: hash, role }).returning(userSelectFields);
|
|
117
|
-
return result;
|
|
118
|
-
}
|
|
119
|
-
async function verifyUser(username, password) {
|
|
120
|
-
const db = await getDb();
|
|
121
|
-
const row = await db.select().from(users).where(eq(users.username, username)).get();
|
|
122
|
-
if (!row) return null;
|
|
123
|
-
if (row.user_type === "mind") return null;
|
|
124
|
-
if (!compareSync(password, row.password_hash)) return null;
|
|
125
|
-
const { password_hash: _, ...user } = row;
|
|
126
|
-
return user;
|
|
127
|
-
}
|
|
128
|
-
async function getUser(id) {
|
|
129
|
-
const db = await getDb();
|
|
130
|
-
const row = await db.select(userSelectFields).from(users).where(eq(users.id, id)).get();
|
|
131
|
-
return row ?? null;
|
|
132
|
-
}
|
|
133
|
-
async function getUserByUsername(username) {
|
|
134
|
-
const db = await getDb();
|
|
135
|
-
const row = await db.select(userSelectFields).from(users).where(eq(users.username, username)).get();
|
|
136
|
-
return row ?? null;
|
|
137
|
-
}
|
|
138
|
-
async function listUsers() {
|
|
139
|
-
const db = await getDb();
|
|
140
|
-
return db.select(userSelectFields).from(users).orderBy(users.created_at).all();
|
|
141
|
-
}
|
|
142
|
-
async function listPendingUsers() {
|
|
143
|
-
const db = await getDb();
|
|
144
|
-
return db.select(userSelectFields).from(users).where(eq(users.role, "pending")).orderBy(users.created_at).all();
|
|
145
|
-
}
|
|
146
|
-
async function listUsersByType(userType) {
|
|
147
|
-
const db = await getDb();
|
|
148
|
-
return db.select(userSelectFields).from(users).where(eq(users.user_type, userType)).orderBy(users.created_at).all();
|
|
149
|
-
}
|
|
150
|
-
async function getOrCreateMindUser(mindName) {
|
|
151
|
-
const db = await getDb();
|
|
152
|
-
const existing = await db.select(userSelectFields).from(users).where(and(eq(users.username, mindName), eq(users.user_type, "mind"))).get();
|
|
153
|
-
if (existing) return existing;
|
|
154
|
-
try {
|
|
155
|
-
const [result] = await db.insert(users).values({
|
|
156
|
-
username: mindName,
|
|
157
|
-
password_hash: "!mind",
|
|
158
|
-
role: "user",
|
|
159
|
-
user_type: "mind"
|
|
160
|
-
}).returning(userSelectFields);
|
|
161
|
-
return result;
|
|
162
|
-
} catch (err) {
|
|
163
|
-
if (err instanceof Error && err.message.includes("UNIQUE constraint")) {
|
|
164
|
-
const retried = await db.select(userSelectFields).from(users).where(and(eq(users.username, mindName), eq(users.user_type, "mind"))).get();
|
|
165
|
-
if (retried) return retried;
|
|
166
|
-
}
|
|
167
|
-
throw err;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
async function deleteMindUser(mindName) {
|
|
171
|
-
const db = await getDb();
|
|
172
|
-
await db.delete(users).where(and(eq(users.username, mindName), eq(users.user_type, "mind")));
|
|
173
|
-
}
|
|
174
|
-
async function changePassword(userId, currentPassword, newPassword) {
|
|
175
|
-
const db = await getDb();
|
|
176
|
-
const row = await db.select().from(users).where(eq(users.id, userId)).get();
|
|
177
|
-
if (!row) return false;
|
|
178
|
-
if (!compareSync(currentPassword, row.password_hash)) return false;
|
|
179
|
-
const hash = hashSync(newPassword, 10);
|
|
180
|
-
await db.update(users).set({ password_hash: hash }).where(eq(users.id, userId));
|
|
181
|
-
return true;
|
|
182
|
-
}
|
|
183
|
-
async function approveUser(id) {
|
|
184
|
-
const db = await getDb();
|
|
185
|
-
await db.update(users).set({ role: "user" }).where(and(eq(users.id, id), eq(users.role, "pending")));
|
|
186
|
-
}
|
|
187
|
-
async function countAdmins() {
|
|
188
|
-
const db = await getDb();
|
|
189
|
-
const [{ value }] = await db.select({ value: count() }).from(users).where(eq(users.role, "admin"));
|
|
190
|
-
return value;
|
|
191
|
-
}
|
|
192
|
-
async function setUserRole(id, role) {
|
|
193
|
-
const db = await getDb();
|
|
194
|
-
const target = await db.select({ id: users.id }).from(users).where(eq(users.id, id)).get();
|
|
195
|
-
if (!target) throw new Error("User not found");
|
|
196
|
-
await db.update(users).set({ role }).where(eq(users.id, id));
|
|
197
|
-
}
|
|
198
|
-
async function deleteUser(id) {
|
|
199
|
-
const db = await getDb();
|
|
200
|
-
const target = await db.select({ id: users.id }).from(users).where(and(eq(users.id, id), eq(users.user_type, "brain"))).get();
|
|
201
|
-
if (!target) throw new Error("User not found");
|
|
202
|
-
await db.delete(users).where(and(eq(users.id, id), eq(users.user_type, "brain")));
|
|
203
|
-
}
|
|
204
|
-
async function updateUserProfile(userId, profile) {
|
|
205
|
-
const db = await getDb();
|
|
206
|
-
const target = await db.select({ id: users.id }).from(users).where(eq(users.id, userId)).get();
|
|
207
|
-
if (!target) throw new Error("User not found");
|
|
208
|
-
await db.update(users).set(profile).where(eq(users.id, userId));
|
|
209
|
-
}
|
|
210
|
-
async function syncMindProfile(mindName, config) {
|
|
211
|
-
const user = await getOrCreateMindUser(mindName);
|
|
212
|
-
const newProfile = {
|
|
213
|
-
display_name: config.displayName ?? null,
|
|
214
|
-
description: config.description ?? null,
|
|
215
|
-
avatar: config.avatar ?? null
|
|
216
|
-
};
|
|
217
|
-
const changed = user.display_name !== newProfile.display_name || user.description !== newProfile.description || user.avatar !== newProfile.avatar;
|
|
218
|
-
if (!changed) return;
|
|
219
|
-
const db = await getDb();
|
|
220
|
-
await db.update(users).set(newProfile).where(eq(users.id, user.id));
|
|
221
|
-
broadcast({ type: "profile_updated", mind: mindName, summary: `${mindName} profile updated` });
|
|
222
|
-
}
|
|
223
|
-
async function migrateMindRoles() {
|
|
224
|
-
const db = await getDb();
|
|
225
|
-
await db.update(users).set({ role: "user" }).where(and(eq(users.user_type, "mind"), inArray(users.role, ["mind", "agent"])));
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// src/lib/pages-watcher.ts
|
|
229
|
-
import { existsSync as existsSync2, readdirSync, statSync, watch } from "fs";
|
|
230
|
-
import { join, resolve as resolve2 } from "path";
|
|
231
|
-
var watchers = /* @__PURE__ */ new Map();
|
|
232
|
-
var homeWatchers = /* @__PURE__ */ new Map();
|
|
233
|
-
var debounceTimers = /* @__PURE__ */ new Map();
|
|
234
|
-
var sitesCache = null;
|
|
235
|
-
var recentPagesCache = null;
|
|
236
|
-
function startPagesWatcher(mindName, pagesDir) {
|
|
237
|
-
try {
|
|
238
|
-
const watcher = watch(pagesDir, { recursive: true }, (_eventType, filename) => {
|
|
239
|
-
if (!filename || !filename.endsWith(".html")) return;
|
|
240
|
-
const key = `${mindName}:${filename}`;
|
|
241
|
-
const existing = debounceTimers.get(key);
|
|
242
|
-
if (existing) clearTimeout(existing);
|
|
243
|
-
debounceTimers.set(
|
|
244
|
-
key,
|
|
245
|
-
setTimeout(() => {
|
|
246
|
-
debounceTimers.delete(key);
|
|
247
|
-
invalidateCache();
|
|
248
|
-
publish({
|
|
249
|
-
type: "page_updated",
|
|
250
|
-
mind: mindName,
|
|
251
|
-
summary: `${mindName} updated ${filename}`,
|
|
252
|
-
metadata: { file: filename }
|
|
253
|
-
}).catch(
|
|
254
|
-
(err) => logger_default.error("failed to publish page_updated activity", logger_default.errorData(err))
|
|
255
|
-
);
|
|
256
|
-
}, 100)
|
|
257
|
-
);
|
|
258
|
-
});
|
|
259
|
-
watchers.set(mindName, watcher);
|
|
260
|
-
} catch (err) {
|
|
261
|
-
logger_default.warn(`failed to start pages watcher for ${mindName}`, logger_default.errorData(err));
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
function startWatcher(mindName) {
|
|
265
|
-
if (watchers.has(mindName)) return;
|
|
266
|
-
const pagesDir = resolve2(mindDir(mindName), "home", "public", "pages");
|
|
267
|
-
if (existsSync2(pagesDir)) {
|
|
268
|
-
startPagesWatcher(mindName, pagesDir);
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
if (homeWatchers.has(mindName)) return;
|
|
272
|
-
const publicDir = resolve2(mindDir(mindName), "home", "public");
|
|
273
|
-
if (!existsSync2(publicDir)) return;
|
|
274
|
-
try {
|
|
275
|
-
const hw = watch(publicDir, (_eventType, filename) => {
|
|
276
|
-
if (filename !== "pages") return;
|
|
277
|
-
if (!existsSync2(pagesDir)) return;
|
|
278
|
-
hw.close();
|
|
279
|
-
homeWatchers.delete(mindName);
|
|
280
|
-
invalidateCache();
|
|
281
|
-
startPagesWatcher(mindName, pagesDir);
|
|
282
|
-
});
|
|
283
|
-
homeWatchers.set(mindName, hw);
|
|
284
|
-
} catch (err) {
|
|
285
|
-
logger_default.warn(`failed to start home watcher for ${mindName}`, logger_default.errorData(err));
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
function stopWatcher(mindName) {
|
|
289
|
-
const watcher = watchers.get(mindName);
|
|
290
|
-
if (watcher) {
|
|
291
|
-
watcher.close();
|
|
292
|
-
watchers.delete(mindName);
|
|
293
|
-
}
|
|
294
|
-
const hw = homeWatchers.get(mindName);
|
|
295
|
-
if (hw) {
|
|
296
|
-
hw.close();
|
|
297
|
-
homeWatchers.delete(mindName);
|
|
298
|
-
}
|
|
299
|
-
for (const [key, timer] of debounceTimers) {
|
|
300
|
-
if (key.startsWith(`${mindName}:`)) {
|
|
301
|
-
clearTimeout(timer);
|
|
302
|
-
debounceTimers.delete(key);
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
function stopAllWatchers() {
|
|
307
|
-
for (const [, watcher] of watchers) {
|
|
308
|
-
watcher.close();
|
|
309
|
-
}
|
|
310
|
-
watchers.clear();
|
|
311
|
-
for (const [, hw] of homeWatchers) {
|
|
312
|
-
hw.close();
|
|
313
|
-
}
|
|
314
|
-
homeWatchers.clear();
|
|
315
|
-
for (const [, timer] of debounceTimers) {
|
|
316
|
-
clearTimeout(timer);
|
|
317
|
-
}
|
|
318
|
-
debounceTimers.clear();
|
|
319
|
-
invalidateCache();
|
|
320
|
-
}
|
|
321
|
-
function invalidateCache() {
|
|
322
|
-
sitesCache = null;
|
|
323
|
-
recentPagesCache = null;
|
|
324
|
-
}
|
|
325
|
-
function scanPagesDir(dir, urlPrefix) {
|
|
326
|
-
const pages = [];
|
|
327
|
-
let items;
|
|
328
|
-
try {
|
|
329
|
-
items = readdirSync(dir);
|
|
330
|
-
} catch {
|
|
331
|
-
return pages;
|
|
332
|
-
}
|
|
333
|
-
for (const item of items) {
|
|
334
|
-
if (item.startsWith(".")) continue;
|
|
335
|
-
const fullPath = resolve2(dir, item);
|
|
336
|
-
try {
|
|
337
|
-
const s = statSync(fullPath);
|
|
338
|
-
if (s.isFile() && item.endsWith(".html")) {
|
|
339
|
-
pages.push({
|
|
340
|
-
file: item,
|
|
341
|
-
modified: s.mtime.toISOString(),
|
|
342
|
-
url: `${urlPrefix}/${item}`
|
|
343
|
-
});
|
|
344
|
-
} else if (s.isDirectory()) {
|
|
345
|
-
const indexPath = resolve2(fullPath, "index.html");
|
|
346
|
-
if (existsSync2(indexPath)) {
|
|
347
|
-
const indexStat = statSync(indexPath);
|
|
348
|
-
pages.push({
|
|
349
|
-
file: join(item, "index.html"),
|
|
350
|
-
modified: indexStat.mtime.toISOString(),
|
|
351
|
-
url: `${urlPrefix}/${item}/`
|
|
352
|
-
});
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
} catch {
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
pages.sort((a, b) => new Date(b.modified).getTime() - new Date(a.modified).getTime());
|
|
359
|
-
return pages;
|
|
360
|
-
}
|
|
361
|
-
async function buildSites() {
|
|
362
|
-
const sites = [];
|
|
363
|
-
const systemPagesDir = resolve2(voluteHome(), "shared", "pages");
|
|
364
|
-
if (existsSync2(systemPagesDir)) {
|
|
365
|
-
const systemPages = scanPagesDir(systemPagesDir, "/pages/_system");
|
|
366
|
-
if (systemPages.length > 0) {
|
|
367
|
-
sites.push({ name: "_system", label: "System", pages: systemPages });
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
const entries = await readRegistry();
|
|
371
|
-
for (const entry of [...entries].sort((a, b) => a.name.localeCompare(b.name))) {
|
|
372
|
-
const pagesDir = resolve2(mindDir(entry.name), "home", "public", "pages");
|
|
373
|
-
if (!existsSync2(pagesDir)) continue;
|
|
374
|
-
const mindPages = scanPagesDir(pagesDir, `/pages/${entry.name}`);
|
|
375
|
-
if (mindPages.length > 0) {
|
|
376
|
-
sites.push({ name: entry.name, label: entry.name, pages: mindPages });
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
return sites;
|
|
380
|
-
}
|
|
381
|
-
async function buildRecentPages() {
|
|
382
|
-
const entries = await readRegistry();
|
|
383
|
-
const pages = [];
|
|
384
|
-
for (const entry of entries) {
|
|
385
|
-
const pagesDir = resolve2(mindDir(entry.name), "home", "public", "pages");
|
|
386
|
-
if (!existsSync2(pagesDir)) continue;
|
|
387
|
-
let items;
|
|
388
|
-
try {
|
|
389
|
-
items = readdirSync(pagesDir);
|
|
390
|
-
} catch {
|
|
391
|
-
continue;
|
|
392
|
-
}
|
|
393
|
-
for (const item of items) {
|
|
394
|
-
if (item.startsWith(".")) continue;
|
|
395
|
-
const fullPath = resolve2(pagesDir, item);
|
|
396
|
-
try {
|
|
397
|
-
const s = statSync(fullPath);
|
|
398
|
-
if (s.isFile() && item.endsWith(".html")) {
|
|
399
|
-
pages.push({
|
|
400
|
-
mind: entry.name,
|
|
401
|
-
file: item,
|
|
402
|
-
modified: s.mtime.toISOString(),
|
|
403
|
-
url: `/pages/${entry.name}/${item}`
|
|
404
|
-
});
|
|
405
|
-
} else if (s.isDirectory()) {
|
|
406
|
-
const indexPath = resolve2(fullPath, "index.html");
|
|
407
|
-
if (existsSync2(indexPath)) {
|
|
408
|
-
const indexStat = statSync(indexPath);
|
|
409
|
-
pages.push({
|
|
410
|
-
mind: entry.name,
|
|
411
|
-
file: join(item, "index.html"),
|
|
412
|
-
modified: indexStat.mtime.toISOString(),
|
|
413
|
-
url: `/pages/${entry.name}/${item}/`
|
|
414
|
-
});
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
} catch {
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
pages.sort((a, b) => new Date(b.modified).getTime() - new Date(a.modified).getTime());
|
|
422
|
-
return pages.slice(0, 10);
|
|
423
|
-
}
|
|
424
|
-
async function getCachedSites() {
|
|
425
|
-
if (!sitesCache) sitesCache = await buildSites();
|
|
426
|
-
return sitesCache;
|
|
427
|
-
}
|
|
428
|
-
async function getCachedRecentPages() {
|
|
429
|
-
if (!recentPagesCache) recentPagesCache = await buildRecentPages();
|
|
430
|
-
return recentPagesCache;
|
|
431
|
-
}
|
|
432
|
-
|
|
433
102
|
// src/connectors/sdk.ts
|
|
434
|
-
import { existsSync as
|
|
435
|
-
import { join
|
|
103
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
104
|
+
import { join, resolve as resolve2 } from "path";
|
|
436
105
|
function splitMessage(text, maxLength) {
|
|
437
106
|
const chunks = [];
|
|
438
107
|
while (text.length > maxLength) {
|
|
@@ -445,8 +114,8 @@ function splitMessage(text, maxLength) {
|
|
|
445
114
|
return chunks;
|
|
446
115
|
}
|
|
447
116
|
function readChannelMap(mindName) {
|
|
448
|
-
const filePath =
|
|
449
|
-
if (!
|
|
117
|
+
const filePath = join(stateDir(mindName), "channels.json");
|
|
118
|
+
if (!existsSync2(filePath)) return {};
|
|
450
119
|
try {
|
|
451
120
|
return JSON.parse(readFileSync2(filePath, "utf-8"));
|
|
452
121
|
} catch (err) {
|
|
@@ -457,10 +126,11 @@ function readChannelMap(mindName) {
|
|
|
457
126
|
function writeChannelEntry(mindName, slug, entry) {
|
|
458
127
|
const dir = stateDir(mindName);
|
|
459
128
|
mkdirSync2(dir, { recursive: true });
|
|
460
|
-
const filePath =
|
|
129
|
+
const filePath = join(dir, "channels.json");
|
|
461
130
|
const map = readChannelMap(mindName);
|
|
462
131
|
map[slug] = entry;
|
|
463
|
-
writeFileSync2(filePath, JSON.stringify(map, null, 2)
|
|
132
|
+
writeFileSync2(filePath, `${JSON.stringify(map, null, 2)}
|
|
133
|
+
`);
|
|
464
134
|
}
|
|
465
135
|
function resolveChannelId(mindName, slug) {
|
|
466
136
|
const map = readChannelMap(mindName);
|
|
@@ -501,8 +171,8 @@ function publish3(mind, event) {
|
|
|
501
171
|
|
|
502
172
|
// src/lib/delivery/delivery-manager.ts
|
|
503
173
|
import { readFile, realpath } from "fs/promises";
|
|
504
|
-
import { extname, resolve as
|
|
505
|
-
import { and
|
|
174
|
+
import { extname, resolve as resolve4 } from "path";
|
|
175
|
+
import { and, eq, sql } from "drizzle-orm";
|
|
506
176
|
|
|
507
177
|
// src/lib/typing.ts
|
|
508
178
|
var DEFAULT_TTL_MS = 1e4;
|
|
@@ -595,8 +265,8 @@ function publishTypingForChannels(channels, map) {
|
|
|
595
265
|
}
|
|
596
266
|
|
|
597
267
|
// src/lib/delivery/delivery-router.ts
|
|
598
|
-
import { readFileSync as readFileSync3, statSync
|
|
599
|
-
import { resolve as
|
|
268
|
+
import { readFileSync as readFileSync3, statSync } from "fs";
|
|
269
|
+
import { resolve as resolve3 } from "path";
|
|
600
270
|
function extractTextContent(content) {
|
|
601
271
|
if (typeof content === "string") return content;
|
|
602
272
|
if (Array.isArray(content)) {
|
|
@@ -609,7 +279,7 @@ var statCheckCache = /* @__PURE__ */ new Map();
|
|
|
609
279
|
var STAT_TTL_MS = 5e3;
|
|
610
280
|
var dlog = logger_default.child("delivery-router");
|
|
611
281
|
function configPath(mindName) {
|
|
612
|
-
return
|
|
282
|
+
return resolve3(mindDir(mindName), "home/.config/routes.json");
|
|
613
283
|
}
|
|
614
284
|
function getRoutingConfig(mindName) {
|
|
615
285
|
const path = configPath(mindName);
|
|
@@ -621,7 +291,7 @@ function getRoutingConfig(mindName) {
|
|
|
621
291
|
}
|
|
622
292
|
let mtime;
|
|
623
293
|
try {
|
|
624
|
-
mtime =
|
|
294
|
+
mtime = statSync(path).mtimeMs;
|
|
625
295
|
} catch {
|
|
626
296
|
configCache.delete(mindName);
|
|
627
297
|
statCheckCache.delete(mindName);
|
|
@@ -868,7 +538,7 @@ var DeliveryManager = class {
|
|
|
868
538
|
async restoreFromDb() {
|
|
869
539
|
try {
|
|
870
540
|
const db = await getDb();
|
|
871
|
-
const rows = await db.select().from(deliveryQueue).where(
|
|
541
|
+
const rows = await db.select().from(deliveryQueue).where(eq(deliveryQueue.status, "pending"));
|
|
872
542
|
for (const row of rows) {
|
|
873
543
|
let payload;
|
|
874
544
|
try {
|
|
@@ -886,7 +556,7 @@ var DeliveryManager = class {
|
|
|
886
556
|
this.addToBatchBuffer(row.mind, row.session, payload, sessionConfig);
|
|
887
557
|
} else {
|
|
888
558
|
try {
|
|
889
|
-
await db.delete(deliveryQueue).where(
|
|
559
|
+
await db.delete(deliveryQueue).where(eq(deliveryQueue.id, row.id));
|
|
890
560
|
} catch (err) {
|
|
891
561
|
dlog2.warn(`failed to delete queue row ${row.id} for ${row.mind}`, logger_default.errorData(err));
|
|
892
562
|
}
|
|
@@ -907,7 +577,7 @@ var DeliveryManager = class {
|
|
|
907
577
|
*/
|
|
908
578
|
async getPending(mindName) {
|
|
909
579
|
const db = await getDb();
|
|
910
|
-
const rows = await db.select().from(deliveryQueue).where(
|
|
580
|
+
const rows = await db.select().from(deliveryQueue).where(and(eq(deliveryQueue.mind, mindName), eq(deliveryQueue.status, "gated")));
|
|
911
581
|
const byChannel = /* @__PURE__ */ new Map();
|
|
912
582
|
for (const row of rows) {
|
|
913
583
|
const ch = row.channel ?? "unknown";
|
|
@@ -1068,10 +738,10 @@ var DeliveryManager = class {
|
|
|
1068
738
|
try {
|
|
1069
739
|
const db = await getDb();
|
|
1070
740
|
await db.delete(deliveryQueue).where(
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
741
|
+
and(
|
|
742
|
+
eq(deliveryQueue.mind, baseName),
|
|
743
|
+
eq(deliveryQueue.session, session),
|
|
744
|
+
eq(deliveryQueue.status, "pending")
|
|
1075
745
|
)
|
|
1076
746
|
);
|
|
1077
747
|
} catch (err) {
|
|
@@ -1197,14 +867,14 @@ var DeliveryManager = class {
|
|
|
1197
867
|
await this.persistToQueue(baseName, session, payload, "gated");
|
|
1198
868
|
try {
|
|
1199
869
|
const db = await getDb();
|
|
1200
|
-
const
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
870
|
+
const count = await db.select({ count: sql`count(*)` }).from(deliveryQueue).where(
|
|
871
|
+
and(
|
|
872
|
+
eq(deliveryQueue.mind, baseName),
|
|
873
|
+
eq(deliveryQueue.channel, payload.channel),
|
|
874
|
+
eq(deliveryQueue.status, "gated")
|
|
1205
875
|
)
|
|
1206
876
|
);
|
|
1207
|
-
if ((
|
|
877
|
+
if ((count[0]?.count ?? 0) <= 1) {
|
|
1208
878
|
await this.sendInviteNotification(mindName, payload);
|
|
1209
879
|
}
|
|
1210
880
|
} catch (err) {
|
|
@@ -1294,8 +964,8 @@ var DeliveryManager = class {
|
|
|
1294
964
|
const dir = mindDir(p.username);
|
|
1295
965
|
const config = readVoluteConfig(dir);
|
|
1296
966
|
if (!config?.profile?.avatar) continue;
|
|
1297
|
-
filePath =
|
|
1298
|
-
const homeDir =
|
|
967
|
+
filePath = resolve4(dir, "home", config.profile.avatar);
|
|
968
|
+
const homeDir = resolve4(dir, "home");
|
|
1299
969
|
if (!filePath.startsWith(`${homeDir}/`)) {
|
|
1300
970
|
dlog2.warn(`avatar path for ${p.username} escapes home directory, skipping`);
|
|
1301
971
|
continue;
|
|
@@ -1314,7 +984,7 @@ var DeliveryManager = class {
|
|
|
1314
984
|
throw err;
|
|
1315
985
|
}
|
|
1316
986
|
} else {
|
|
1317
|
-
filePath =
|
|
987
|
+
filePath = resolve4(voluteHome(), "avatars", p.avatar);
|
|
1318
988
|
}
|
|
1319
989
|
const ext = extname(filePath).toLowerCase();
|
|
1320
990
|
const mimeMap = {
|
|
@@ -1410,6 +1080,12 @@ async function recordInbound(mind, channel, sender, content) {
|
|
|
1410
1080
|
content: content ?? void 0
|
|
1411
1081
|
});
|
|
1412
1082
|
}
|
|
1083
|
+
function resolveSleepAction(sleepBehavior, wokenByTrigger, wakeTriggerMatches) {
|
|
1084
|
+
if (sleepBehavior === "skip") return "skip";
|
|
1085
|
+
if (sleepBehavior === "trigger-wake" && !wokenByTrigger) return "queue-and-wake";
|
|
1086
|
+
if (!sleepBehavior && wakeTriggerMatches) return "queue-and-wake";
|
|
1087
|
+
return "queue";
|
|
1088
|
+
}
|
|
1413
1089
|
async function deliverMessage(mindName, payload) {
|
|
1414
1090
|
try {
|
|
1415
1091
|
const baseName = await getBaseName(mindName);
|
|
@@ -1422,11 +1098,21 @@ async function deliverMessage(mindName, payload) {
|
|
|
1422
1098
|
await recordInbound(baseName, payload.channel, payload.sender ?? null, textContent);
|
|
1423
1099
|
const sleepManager = getSleepManagerIfReady();
|
|
1424
1100
|
if (sleepManager?.isSleeping(baseName)) {
|
|
1425
|
-
|
|
1426
|
-
|
|
1101
|
+
const sleepState = sleepManager.getState(baseName);
|
|
1102
|
+
const action = resolveSleepAction(
|
|
1103
|
+
payload.whileSleeping,
|
|
1104
|
+
sleepState.wokenByTrigger,
|
|
1105
|
+
sleepManager.checkWakeTrigger(baseName, payload)
|
|
1106
|
+
);
|
|
1107
|
+
if (action === "skip") {
|
|
1108
|
+
dlog3.info(
|
|
1109
|
+
`skipped delivery to ${baseName} (sleeping, whileSleeping=skip, channel=${payload.channel})`
|
|
1110
|
+
);
|
|
1111
|
+
return;
|
|
1112
|
+
}
|
|
1113
|
+
await sleepManager.queueSleepMessage(baseName, payload);
|
|
1114
|
+
if (action === "queue-and-wake") {
|
|
1427
1115
|
sleepManager.initiateWake(baseName, { trigger: { channel: payload.channel } }).catch((err) => dlog3.warn(`failed to trigger-wake ${baseName}`, logger_default.errorData(err)));
|
|
1428
|
-
} else {
|
|
1429
|
-
await sleepManager.queueSleepMessage(baseName, payload);
|
|
1430
1116
|
}
|
|
1431
1117
|
return;
|
|
1432
1118
|
}
|
|
@@ -1472,7 +1158,7 @@ async function announceToSystem(text) {
|
|
|
1472
1158
|
platformId: channelId,
|
|
1473
1159
|
platform: "volute",
|
|
1474
1160
|
name: SYSTEM_CHANNEL_NAME,
|
|
1475
|
-
type: "
|
|
1161
|
+
type: "channel"
|
|
1476
1162
|
});
|
|
1477
1163
|
} catch (err) {
|
|
1478
1164
|
logger_default.warn(`failed to write channel entry for ${mind.username}`, logger_default.errorData(err));
|
|
@@ -1491,74 +1177,6 @@ async function announceToSystem(text) {
|
|
|
1491
1177
|
}
|
|
1492
1178
|
}
|
|
1493
1179
|
|
|
1494
|
-
// src/lib/systems-config.ts
|
|
1495
|
-
import {
|
|
1496
|
-
existsSync as existsSync4,
|
|
1497
|
-
mkdirSync as mkdirSync3,
|
|
1498
|
-
readFileSync as readFileSync4,
|
|
1499
|
-
renameSync,
|
|
1500
|
-
unlinkSync,
|
|
1501
|
-
writeFileSync as writeFileSync3
|
|
1502
|
-
} from "fs";
|
|
1503
|
-
import { resolve as resolve6 } from "path";
|
|
1504
|
-
var DEFAULT_API_URL = "https://volute.systems";
|
|
1505
|
-
function configPath2() {
|
|
1506
|
-
return resolve6(voluteSystemDir(), "systems.json");
|
|
1507
|
-
}
|
|
1508
|
-
function migrateIfNeeded() {
|
|
1509
|
-
const target = configPath2();
|
|
1510
|
-
if (existsSync4(target)) return;
|
|
1511
|
-
const oldPaths = [
|
|
1512
|
-
resolve6(voluteUserHome(), "systems.json"),
|
|
1513
|
-
resolve6(voluteHome(), "systems.json")
|
|
1514
|
-
];
|
|
1515
|
-
for (const old of oldPaths) {
|
|
1516
|
-
if (old !== target && existsSync4(old)) {
|
|
1517
|
-
try {
|
|
1518
|
-
mkdirSync3(voluteSystemDir(), { recursive: true });
|
|
1519
|
-
renameSync(old, target);
|
|
1520
|
-
} catch {
|
|
1521
|
-
}
|
|
1522
|
-
return;
|
|
1523
|
-
}
|
|
1524
|
-
}
|
|
1525
|
-
}
|
|
1526
|
-
function readSystemsConfig() {
|
|
1527
|
-
migrateIfNeeded();
|
|
1528
|
-
const path = configPath2();
|
|
1529
|
-
if (!existsSync4(path)) return null;
|
|
1530
|
-
const raw = readFileSync4(path, "utf-8");
|
|
1531
|
-
let data;
|
|
1532
|
-
try {
|
|
1533
|
-
data = JSON.parse(raw);
|
|
1534
|
-
} catch {
|
|
1535
|
-
console.error(
|
|
1536
|
-
`Warning: ${path} contains invalid JSON. Run "volute systems logout" and re-login.`
|
|
1537
|
-
);
|
|
1538
|
-
return null;
|
|
1539
|
-
}
|
|
1540
|
-
if (!data.apiKey || !data.system) return null;
|
|
1541
|
-
return {
|
|
1542
|
-
apiKey: data.apiKey,
|
|
1543
|
-
system: data.system,
|
|
1544
|
-
apiUrl: data.apiUrl || DEFAULT_API_URL
|
|
1545
|
-
};
|
|
1546
|
-
}
|
|
1547
|
-
function writeSystemsConfig(config) {
|
|
1548
|
-
mkdirSync3(voluteSystemDir(), { recursive: true });
|
|
1549
|
-
writeFileSync3(configPath2(), `${JSON.stringify(config, null, 2)}
|
|
1550
|
-
`, { mode: 384 });
|
|
1551
|
-
}
|
|
1552
|
-
function deleteSystemsConfig() {
|
|
1553
|
-
try {
|
|
1554
|
-
unlinkSync(configPath2());
|
|
1555
|
-
return true;
|
|
1556
|
-
} catch (err) {
|
|
1557
|
-
if (err.code === "ENOENT") return false;
|
|
1558
|
-
throw err;
|
|
1559
|
-
}
|
|
1560
|
-
}
|
|
1561
|
-
|
|
1562
1180
|
// src/lib/daemon/mail-poller.ts
|
|
1563
1181
|
var mlog = logger_default.child("mail");
|
|
1564
1182
|
function formatEmailContent(email) {
|
|
@@ -1793,7 +1411,7 @@ async function ensureMailAddress(mindName) {
|
|
|
1793
1411
|
}
|
|
1794
1412
|
|
|
1795
1413
|
// src/lib/daemon/scheduler.ts
|
|
1796
|
-
import { resolve as
|
|
1414
|
+
import { resolve as resolve5 } from "path";
|
|
1797
1415
|
import { CronExpressionParser } from "cron-parser";
|
|
1798
1416
|
var slog = logger_default.child("scheduler");
|
|
1799
1417
|
var Scheduler = class {
|
|
@@ -1802,7 +1420,7 @@ var Scheduler = class {
|
|
|
1802
1420
|
lastFired = /* @__PURE__ */ new Map();
|
|
1803
1421
|
// "mind:scheduleId" → epoch minute
|
|
1804
1422
|
get statePath() {
|
|
1805
|
-
return
|
|
1423
|
+
return resolve5(voluteSystemDir(), "scheduler-state.json");
|
|
1806
1424
|
}
|
|
1807
1425
|
start() {
|
|
1808
1426
|
this.loadState();
|
|
@@ -1853,6 +1471,15 @@ var Scheduler = class {
|
|
|
1853
1471
|
shouldFire(schedule, epochMinute, mind, cronCache) {
|
|
1854
1472
|
const key = `${mind}:${schedule.id}`;
|
|
1855
1473
|
if (this.lastFired.get(key) === epochMinute) return false;
|
|
1474
|
+
if (schedule.fireAt) {
|
|
1475
|
+
const fireTime = Math.floor(new Date(schedule.fireAt).getTime() / 6e4);
|
|
1476
|
+
if (epochMinute >= fireTime) {
|
|
1477
|
+
this.lastFired.set(key, epochMinute);
|
|
1478
|
+
return true;
|
|
1479
|
+
}
|
|
1480
|
+
return false;
|
|
1481
|
+
}
|
|
1482
|
+
if (!schedule.cron) return false;
|
|
1856
1483
|
let prevMinute = cronCache.get(schedule.cron);
|
|
1857
1484
|
if (prevMinute === void 0) {
|
|
1858
1485
|
try {
|
|
@@ -1872,22 +1499,10 @@ var Scheduler = class {
|
|
|
1872
1499
|
return false;
|
|
1873
1500
|
}
|
|
1874
1501
|
async fire(mindName, schedule) {
|
|
1875
|
-
const sleepManager = getSleepManagerIfReady();
|
|
1876
|
-
const sleepState = sleepManager?.getState(mindName);
|
|
1877
|
-
if (sleepState?.sleeping) {
|
|
1878
|
-
if (schedule.skipWhenSleeping) {
|
|
1879
|
-
slog.info(`skipped "${schedule.id}" for ${mindName} (sleeping)`);
|
|
1880
|
-
return;
|
|
1881
|
-
}
|
|
1882
|
-
if (sleepState.wokenByTrigger) {
|
|
1883
|
-
slog.info(`skipped "${schedule.id}" for ${mindName} (trigger-woken)`);
|
|
1884
|
-
return;
|
|
1885
|
-
}
|
|
1886
|
-
}
|
|
1887
1502
|
try {
|
|
1888
1503
|
let text;
|
|
1889
1504
|
if (schedule.script) {
|
|
1890
|
-
const homeDir =
|
|
1505
|
+
const homeDir = resolve5(mindDir(mindName), "home");
|
|
1891
1506
|
try {
|
|
1892
1507
|
const output = await this.runScript(schedule.script, homeDir, mindName);
|
|
1893
1508
|
if (!output.trim()) {
|
|
@@ -1907,16 +1522,46 @@ ${stderr}` : ""}`;
|
|
|
1907
1522
|
slog.warn(`schedule "${schedule.id}" for ${mindName} has no message or script`);
|
|
1908
1523
|
return;
|
|
1909
1524
|
}
|
|
1525
|
+
const whileSleeping = schedule.whileSleeping ?? (schedule.skipWhenSleeping ? "skip" : void 0);
|
|
1910
1526
|
await this.deliver(mindName, {
|
|
1911
1527
|
content: [{ type: "text", text }],
|
|
1912
1528
|
channel: schedule.channel ?? "system:scheduler",
|
|
1913
|
-
sender: schedule.id
|
|
1529
|
+
sender: schedule.id,
|
|
1530
|
+
whileSleeping
|
|
1914
1531
|
});
|
|
1915
1532
|
slog.info(`fired "${schedule.id}" for ${mindName}`);
|
|
1533
|
+
if (schedule.fireAt) {
|
|
1534
|
+
this.removeSchedule(mindName, schedule.id);
|
|
1535
|
+
}
|
|
1916
1536
|
} catch (err) {
|
|
1917
1537
|
slog.warn(`failed to fire "${schedule.id}" for ${mindName}`, logger_default.errorData(err));
|
|
1918
1538
|
}
|
|
1919
1539
|
}
|
|
1540
|
+
removeSchedule(mindName, scheduleId) {
|
|
1541
|
+
const memSchedules = this.schedules.get(mindName);
|
|
1542
|
+
if (memSchedules) {
|
|
1543
|
+
const filtered = memSchedules.filter((s) => s.id !== scheduleId);
|
|
1544
|
+
if (filtered.length > 0) {
|
|
1545
|
+
this.schedules.set(mindName, filtered);
|
|
1546
|
+
} else {
|
|
1547
|
+
this.schedules.delete(mindName);
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
try {
|
|
1551
|
+
const dir = mindDir(mindName);
|
|
1552
|
+
const config = readVoluteConfig(dir);
|
|
1553
|
+
if (!config?.schedules) return;
|
|
1554
|
+
config.schedules = config.schedules.filter((s) => s.id !== scheduleId);
|
|
1555
|
+
if (config.schedules.length === 0) config.schedules = void 0;
|
|
1556
|
+
writeVoluteConfig(dir, config);
|
|
1557
|
+
slog.info(`removed one-time schedule "${scheduleId}" for ${mindName}`);
|
|
1558
|
+
} catch (err) {
|
|
1559
|
+
slog.error(
|
|
1560
|
+
`failed to persist removal of schedule "${scheduleId}" for ${mindName} (removed from memory)`,
|
|
1561
|
+
logger_default.errorData(err)
|
|
1562
|
+
);
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1920
1565
|
runScript(script, cwd, mindName) {
|
|
1921
1566
|
return exec("bash", ["-c", script], { cwd, mindName });
|
|
1922
1567
|
}
|
|
@@ -1936,8 +1581,8 @@ function getScheduler() {
|
|
|
1936
1581
|
}
|
|
1937
1582
|
|
|
1938
1583
|
// src/lib/daemon/token-budget.ts
|
|
1939
|
-
import { existsSync as
|
|
1940
|
-
import { resolve as
|
|
1584
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
1585
|
+
import { resolve as resolve6 } from "path";
|
|
1941
1586
|
var tlog = logger_default.child("token-budget");
|
|
1942
1587
|
var DEFAULT_BUDGET_PERIOD_MINUTES = 60;
|
|
1943
1588
|
var MAX_QUEUE_SIZE = 100;
|
|
@@ -2055,19 +1700,19 @@ var TokenBudget = class {
|
|
|
2055
1700
|
this.dirty.clear();
|
|
2056
1701
|
}
|
|
2057
1702
|
budgetStatePath(mind) {
|
|
2058
|
-
return
|
|
1703
|
+
return resolve6(stateDir(mind), "budget.json");
|
|
2059
1704
|
}
|
|
2060
1705
|
saveBudgetState(mind, state) {
|
|
2061
1706
|
try {
|
|
2062
1707
|
const dir = stateDir(mind);
|
|
2063
|
-
|
|
1708
|
+
mkdirSync3(dir, { recursive: true });
|
|
2064
1709
|
const data = {
|
|
2065
1710
|
periodStart: state.periodStart,
|
|
2066
1711
|
tokensUsed: state.tokensUsed,
|
|
2067
1712
|
warningInjected: state.warningInjected,
|
|
2068
1713
|
queue: state.queue
|
|
2069
1714
|
};
|
|
2070
|
-
|
|
1715
|
+
writeFileSync3(this.budgetStatePath(mind), `${JSON.stringify(data)}
|
|
2071
1716
|
`);
|
|
2072
1717
|
} catch (err) {
|
|
2073
1718
|
tlog.warn(`failed to save budget state for ${mind}`, logger_default.errorData(err));
|
|
@@ -2076,8 +1721,8 @@ var TokenBudget = class {
|
|
|
2076
1721
|
loadBudgetState(mind) {
|
|
2077
1722
|
try {
|
|
2078
1723
|
const path = this.budgetStatePath(mind);
|
|
2079
|
-
if (!
|
|
2080
|
-
const data = JSON.parse(
|
|
1724
|
+
if (!existsSync3(path)) return null;
|
|
1725
|
+
const data = JSON.parse(readFileSync4(path, "utf-8"));
|
|
2081
1726
|
if (typeof data.periodStart !== "number" || typeof data.tokensUsed !== "number") return null;
|
|
2082
1727
|
return {
|
|
2083
1728
|
periodStart: data.periodStart,
|
|
@@ -2146,6 +1791,7 @@ async function startMindFull(name) {
|
|
|
2146
1791
|
if (!entry || entry.stage === "seed") return;
|
|
2147
1792
|
const dir = mindDir(baseName);
|
|
2148
1793
|
getScheduler().loadSchedules(baseName);
|
|
1794
|
+
getSleepManagerIfReady()?.loadSleepConfig(baseName);
|
|
2149
1795
|
ensureMailAddress(baseName).catch(
|
|
2150
1796
|
(err) => logger_default.error(`failed to ensure mail address for ${baseName}`, logger_default.errorData(err))
|
|
2151
1797
|
);
|
|
@@ -2165,7 +1811,7 @@ async function startMindFull(name) {
|
|
|
2165
1811
|
config.tokenBudgetPeriodMinutes ?? DEFAULT_BUDGET_PERIOD_MINUTES
|
|
2166
1812
|
);
|
|
2167
1813
|
}
|
|
2168
|
-
|
|
1814
|
+
notifyExtensionsMindStart(baseName);
|
|
2169
1815
|
}
|
|
2170
1816
|
async function sleepMind(name) {
|
|
2171
1817
|
markIdle(name);
|
|
@@ -2188,7 +1834,7 @@ async function stopMindFull(name) {
|
|
|
2188
1834
|
const baseName = await getBaseName(name);
|
|
2189
1835
|
const isBase = baseName === name;
|
|
2190
1836
|
if (isBase) {
|
|
2191
|
-
|
|
1837
|
+
notifyExtensionsMindStop(baseName);
|
|
2192
1838
|
markIdle(baseName);
|
|
2193
1839
|
getScheduler().unloadSchedules(baseName);
|
|
2194
1840
|
getTokenBudget().removeBudget(baseName);
|
|
@@ -2238,8 +1884,9 @@ var SleepManager = class {
|
|
|
2238
1884
|
interval = null;
|
|
2239
1885
|
unsubActivity = null;
|
|
2240
1886
|
transitioning = /* @__PURE__ */ new Set();
|
|
1887
|
+
sleepConfigs = /* @__PURE__ */ new Map();
|
|
2241
1888
|
get statePath() {
|
|
2242
|
-
return
|
|
1889
|
+
return resolve7(voluteSystemDir(), "sleep-state.json");
|
|
2243
1890
|
}
|
|
2244
1891
|
start() {
|
|
2245
1892
|
this.loadState();
|
|
@@ -2255,8 +1902,8 @@ var SleepManager = class {
|
|
|
2255
1902
|
// --- State persistence ---
|
|
2256
1903
|
loadState() {
|
|
2257
1904
|
try {
|
|
2258
|
-
if (
|
|
2259
|
-
const data = JSON.parse(
|
|
1905
|
+
if (existsSync4(this.statePath)) {
|
|
1906
|
+
const data = JSON.parse(readFileSync5(this.statePath, "utf-8"));
|
|
2260
1907
|
for (const [name, state] of Object.entries(data)) {
|
|
2261
1908
|
state.triggerWakeHistory ??= [];
|
|
2262
1909
|
this.states.set(name, state);
|
|
@@ -2272,7 +1919,7 @@ var SleepManager = class {
|
|
|
2272
1919
|
if (state.sleeping) data[name] = state;
|
|
2273
1920
|
}
|
|
2274
1921
|
try {
|
|
2275
|
-
|
|
1922
|
+
writeFileSync4(this.statePath, `${JSON.stringify(data, null, 2)}
|
|
2276
1923
|
`);
|
|
2277
1924
|
} catch (err) {
|
|
2278
1925
|
slog2.error("failed to save sleep state", logger_default.errorData(err));
|
|
@@ -2299,9 +1946,21 @@ var SleepManager = class {
|
|
|
2299
1946
|
slog2.info(`${name} trigger-wake converted to full wake`);
|
|
2300
1947
|
}
|
|
2301
1948
|
getSleepConfig(name) {
|
|
1949
|
+
if (this.sleepConfigs.has(name)) {
|
|
1950
|
+
return this.sleepConfigs.get(name) ?? null;
|
|
1951
|
+
}
|
|
1952
|
+
const config = this.loadSleepConfig(name);
|
|
1953
|
+
return config;
|
|
1954
|
+
}
|
|
1955
|
+
loadSleepConfig(name) {
|
|
2302
1956
|
const dir = mindDir(name);
|
|
2303
1957
|
const config = readVoluteConfig(dir);
|
|
2304
|
-
|
|
1958
|
+
const sleepConfig = config?.sleep ?? null;
|
|
1959
|
+
this.sleepConfigs.set(name, sleepConfig);
|
|
1960
|
+
return sleepConfig;
|
|
1961
|
+
}
|
|
1962
|
+
invalidateSleepConfig(name) {
|
|
1963
|
+
this.sleepConfigs.delete(name);
|
|
2305
1964
|
}
|
|
2306
1965
|
/**
|
|
2307
1966
|
* Put a mind to sleep. Sends pre-sleep message, waits for completion,
|
|
@@ -2321,8 +1980,7 @@ var SleepManager = class {
|
|
|
2321
1980
|
if (!entry) return;
|
|
2322
1981
|
const sleepConfig = this.getSleepConfig(name);
|
|
2323
1982
|
const wakeTime = opts?.voluntaryWakeAt ?? this.getNextWakeTime(sleepConfig) ?? "scheduled time";
|
|
2324
|
-
const
|
|
2325
|
-
const preSleepMsg = await getPrompt("pre_sleep", { wakeTime, queuedInfo });
|
|
1983
|
+
const preSleepMsg = await getPrompt("pre_sleep", { wakeTime });
|
|
2326
1984
|
try {
|
|
2327
1985
|
const db = await getDb();
|
|
2328
1986
|
await db.insert(mindHistory).values({
|
|
@@ -2490,9 +2148,9 @@ var SleepManager = class {
|
|
|
2490
2148
|
async flushQueuedMessages(name) {
|
|
2491
2149
|
try {
|
|
2492
2150
|
const db = await getDb();
|
|
2493
|
-
const rows = await db.select().from(deliveryQueue).where(
|
|
2151
|
+
const rows = await db.select().from(deliveryQueue).where(and2(eq2(deliveryQueue.mind, name), eq2(deliveryQueue.status, "sleep-queued"))).all();
|
|
2494
2152
|
if (rows.length === 0) return 0;
|
|
2495
|
-
const { deliverMessage: deliverMessage2 } = await import("./message-delivery-
|
|
2153
|
+
const { deliverMessage: deliverMessage2 } = await import("./message-delivery-6YMVNOEC.js");
|
|
2496
2154
|
const delivered = [];
|
|
2497
2155
|
for (const row of rows) {
|
|
2498
2156
|
try {
|
|
@@ -2503,7 +2161,7 @@ var SleepManager = class {
|
|
|
2503
2161
|
}
|
|
2504
2162
|
}
|
|
2505
2163
|
if (delivered.length > 0) {
|
|
2506
|
-
await db.delete(deliveryQueue).where(
|
|
2164
|
+
await db.delete(deliveryQueue).where(inArray(deliveryQueue.id, delivered));
|
|
2507
2165
|
}
|
|
2508
2166
|
const state = this.states.get(name);
|
|
2509
2167
|
if (state) {
|
|
@@ -2592,17 +2250,17 @@ var SleepManager = class {
|
|
|
2592
2250
|
}
|
|
2593
2251
|
}
|
|
2594
2252
|
async waitForIdle(name, timeoutMs) {
|
|
2595
|
-
return new Promise((
|
|
2253
|
+
return new Promise((resolve8) => {
|
|
2596
2254
|
const timeout = setTimeout(() => {
|
|
2597
2255
|
unsub();
|
|
2598
|
-
|
|
2256
|
+
resolve8();
|
|
2599
2257
|
}, timeoutMs);
|
|
2600
2258
|
const unsub = subscribe((event) => {
|
|
2601
2259
|
if (event.mind !== name) return;
|
|
2602
2260
|
if (event.type === "mind_done" || event.type === "mind_idle") {
|
|
2603
2261
|
clearTimeout(timeout);
|
|
2604
2262
|
unsub();
|
|
2605
|
-
|
|
2263
|
+
resolve8();
|
|
2606
2264
|
}
|
|
2607
2265
|
});
|
|
2608
2266
|
});
|
|
@@ -2610,32 +2268,32 @@ var SleepManager = class {
|
|
|
2610
2268
|
async archiveSessions(name) {
|
|
2611
2269
|
const dir = mindDir(name);
|
|
2612
2270
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 16);
|
|
2613
|
-
const sessionsDir =
|
|
2614
|
-
if (
|
|
2615
|
-
const archiveDir =
|
|
2616
|
-
|
|
2617
|
-
for (const file of
|
|
2271
|
+
const sessionsDir = resolve7(dir, ".mind", "sessions");
|
|
2272
|
+
if (existsSync4(sessionsDir)) {
|
|
2273
|
+
const archiveDir = resolve7(sessionsDir, "archive");
|
|
2274
|
+
mkdirSync4(archiveDir, { recursive: true });
|
|
2275
|
+
for (const file of readdirSync(sessionsDir)) {
|
|
2618
2276
|
if (file === "archive" || !file.endsWith(".json")) continue;
|
|
2619
|
-
const src =
|
|
2277
|
+
const src = resolve7(sessionsDir, file);
|
|
2620
2278
|
const base = file.replace(/\.json$/, "");
|
|
2621
|
-
const dest =
|
|
2279
|
+
const dest = resolve7(archiveDir, `${base}-${timestamp}.json`);
|
|
2622
2280
|
try {
|
|
2623
|
-
|
|
2281
|
+
renameSync(src, dest);
|
|
2624
2282
|
} catch (err) {
|
|
2625
2283
|
slog2.warn(`failed to archive session ${file} for ${name}`, logger_default.errorData(err));
|
|
2626
2284
|
}
|
|
2627
2285
|
}
|
|
2628
2286
|
}
|
|
2629
|
-
const piSessionsDir =
|
|
2630
|
-
if (
|
|
2631
|
-
const archiveDir =
|
|
2632
|
-
|
|
2633
|
-
for (const entry of
|
|
2287
|
+
const piSessionsDir = resolve7(dir, ".mind", "pi-sessions");
|
|
2288
|
+
if (existsSync4(piSessionsDir)) {
|
|
2289
|
+
const archiveDir = resolve7(piSessionsDir, "archive");
|
|
2290
|
+
mkdirSync4(archiveDir, { recursive: true });
|
|
2291
|
+
for (const entry of readdirSync(piSessionsDir, { withFileTypes: true })) {
|
|
2634
2292
|
if (entry.name === "archive" || !entry.isDirectory()) continue;
|
|
2635
|
-
const src =
|
|
2636
|
-
const dest =
|
|
2293
|
+
const src = resolve7(piSessionsDir, entry.name);
|
|
2294
|
+
const dest = resolve7(archiveDir, `${entry.name}-${timestamp}`);
|
|
2637
2295
|
try {
|
|
2638
|
-
|
|
2296
|
+
renameSync(src, dest);
|
|
2639
2297
|
} catch (err) {
|
|
2640
2298
|
slog2.warn(`failed to archive pi-session ${entry.name} for ${name}`, logger_default.errorData(err));
|
|
2641
2299
|
}
|
|
@@ -2643,8 +2301,8 @@ var SleepManager = class {
|
|
|
2643
2301
|
}
|
|
2644
2302
|
}
|
|
2645
2303
|
async runWakeContextScript(name, sleepingSince, duration) {
|
|
2646
|
-
const scriptPath =
|
|
2647
|
-
if (!
|
|
2304
|
+
const scriptPath = resolve7(mindDir(name), "home", ".config", "hooks", "wake-context.sh");
|
|
2305
|
+
if (!existsSync4(scriptPath)) return "";
|
|
2648
2306
|
const input = JSON.stringify({
|
|
2649
2307
|
sleepingSince,
|
|
2650
2308
|
duration,
|
|
@@ -2694,7 +2352,7 @@ var SleepManager = class {
|
|
|
2694
2352
|
async buildQueuedSummary(name) {
|
|
2695
2353
|
try {
|
|
2696
2354
|
const db = await getDb();
|
|
2697
|
-
const rows = await db.select({ channel: deliveryQueue.channel, sender: deliveryQueue.sender }).from(deliveryQueue).where(
|
|
2355
|
+
const rows = await db.select({ channel: deliveryQueue.channel, sender: deliveryQueue.sender }).from(deliveryQueue).where(and2(eq2(deliveryQueue.mind, name), eq2(deliveryQueue.status, "sleep-queued"))).all();
|
|
2698
2356
|
if (rows.length === 0) return "No messages arrived while you slept.";
|
|
2699
2357
|
const channelCounts = /* @__PURE__ */ new Map();
|
|
2700
2358
|
const senders = /* @__PURE__ */ new Set();
|
|
@@ -2703,7 +2361,7 @@ var SleepManager = class {
|
|
|
2703
2361
|
channelCounts.set(ch, (channelCounts.get(ch) ?? 0) + 1);
|
|
2704
2362
|
if (row.sender) senders.add(row.sender);
|
|
2705
2363
|
}
|
|
2706
|
-
const parts = [...channelCounts.entries()].map(([ch,
|
|
2364
|
+
const parts = [...channelCounts.entries()].map(([ch, count]) => `${count} on ${ch}`);
|
|
2707
2365
|
const senderNote = senders.size > 0 ? ` from ${[...senders].join(", ")}` : "";
|
|
2708
2366
|
return `${rows.length} message${rows.length === 1 ? "" : "s"} arrived while you slept${senderNote} (${parts.join(", ")}). They'll be delivered to your normal channels now.`;
|
|
2709
2367
|
} catch (err) {
|
|
@@ -2741,16 +2399,16 @@ var SleepManager = class {
|
|
|
2741
2399
|
} catch {
|
|
2742
2400
|
try {
|
|
2743
2401
|
const portHex = port.toString(16).toUpperCase().padStart(4, "0");
|
|
2744
|
-
const tcp6 =
|
|
2402
|
+
const tcp6 = readFileSync5("/proc/net/tcp6", "utf-8");
|
|
2745
2403
|
for (const line of tcp6.split("\n")) {
|
|
2746
2404
|
if (!line.includes(`:${portHex} `)) continue;
|
|
2747
2405
|
const fields = line.trim().split(/\s+/);
|
|
2748
2406
|
if (fields[3] !== "0A") continue;
|
|
2749
2407
|
const inode = parseInt(fields[9], 10);
|
|
2750
2408
|
if (!inode) continue;
|
|
2751
|
-
for (const pidDir of
|
|
2409
|
+
for (const pidDir of readdirSync("/proc").filter((f) => /^\d+$/.test(f))) {
|
|
2752
2410
|
try {
|
|
2753
|
-
const fds =
|
|
2411
|
+
const fds = readdirSync(`/proc/${pidDir}/fd`);
|
|
2754
2412
|
for (const fd of fds) {
|
|
2755
2413
|
try {
|
|
2756
2414
|
const link = readlinkSync(`/proc/${pidDir}/fd/${fd}`);
|
|
@@ -2808,27 +2466,8 @@ function getSleepManagerIfReady() {
|
|
|
2808
2466
|
}
|
|
2809
2467
|
|
|
2810
2468
|
export {
|
|
2811
|
-
createUser,
|
|
2812
|
-
verifyUser,
|
|
2813
|
-
getUser,
|
|
2814
|
-
getUserByUsername,
|
|
2815
|
-
listUsers,
|
|
2816
|
-
listPendingUsers,
|
|
2817
|
-
listUsersByType,
|
|
2818
|
-
getOrCreateMindUser,
|
|
2819
|
-
deleteMindUser,
|
|
2820
|
-
changePassword,
|
|
2821
|
-
approveUser,
|
|
2822
|
-
countAdmins,
|
|
2823
|
-
setUserRole,
|
|
2824
|
-
deleteUser,
|
|
2825
|
-
updateUserProfile,
|
|
2826
|
-
migrateMindRoles,
|
|
2827
2469
|
readVoluteConfig,
|
|
2828
2470
|
writeVoluteConfig,
|
|
2829
|
-
stopAllWatchers,
|
|
2830
|
-
getCachedSites,
|
|
2831
|
-
getCachedRecentPages,
|
|
2832
2471
|
splitMessage,
|
|
2833
2472
|
writeChannelEntry,
|
|
2834
2473
|
resolveChannelId,
|
|
@@ -2854,9 +2493,7 @@ export {
|
|
|
2854
2493
|
initDeliveryManager,
|
|
2855
2494
|
getDeliveryManager,
|
|
2856
2495
|
recordInbound,
|
|
2496
|
+
resolveSleepAction,
|
|
2857
2497
|
deliverMessage,
|
|
2858
|
-
readSystemsConfig,
|
|
2859
|
-
writeSystemsConfig,
|
|
2860
|
-
deleteSystemsConfig,
|
|
2861
2498
|
initMailPoller
|
|
2862
2499
|
};
|