volute 0.42.1 → 0.43.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{activity-events-KFKVRZE3.js → activity-events-UISXY4Y5.js} +3 -3
- package/dist/{ai-service-3GBOJGUA.js → ai-service-FBCCEYF5.js} +8 -4
- package/dist/{archive-4PPYF7LL.js → archive-I32WHCJB.js} +3 -3
- package/dist/{auth-EAASQPTN.js → auth-3RIG6GKN.js} +4 -4
- package/dist/{chat-LYL5CWJV.js → chat-SDSX5TVV.js} +2 -2
- package/dist/{chunk-M2YMNTHK.js → chunk-2HIVYZDB.js} +1 -1
- package/dist/{chunk-6G7R2LPI.js → chunk-4SVONUTC.js} +569 -379
- package/dist/{chunk-QPTQKGGF.js → chunk-57LBQ7JX.js} +1 -1
- package/dist/{chunk-K547QOCM.js → chunk-7G2EEYXX.js} +2 -2
- package/dist/{chunk-E7XWFDZL.js → chunk-AD46AYZQ.js} +1 -1
- package/dist/{chunk-P6DPB33N.js → chunk-ATEX3ZPG.js} +2 -2
- package/dist/{chunk-WR7CSHQ4.js → chunk-C6ESRQZ7.js} +1 -1
- package/dist/{chunk-JU5GWO77.js → chunk-DE4EKQFH.js} +3 -3
- package/dist/{chunk-JPDN6ZRJ.js → chunk-DMUPZQWP.js} +8 -8
- package/dist/{chunk-BR6KFHVT.js → chunk-EE7SZJON.js} +2 -2
- package/dist/{chunk-5QG36RV7.js → chunk-EFL5UOY4.js} +3 -3
- package/dist/{chunk-OEJFZHZZ.js → chunk-GZJFC76M.js} +1 -1
- package/dist/{chunk-KUJVCDWT.js → chunk-JVK6QQIC.js} +2 -2
- package/dist/{chunk-YLTIEEVL.js → chunk-K4EHBHPE.js} +3 -3
- package/dist/{chunk-M3T3Y6FT.js → chunk-KINGVQSR.js} +5 -5
- package/dist/{chunk-MHFAWJCC.js → chunk-L3IV23ZG.js} +12 -1
- package/dist/{chunk-VBKRZCR7.js → chunk-M3PGGSBH.js} +1 -1
- package/dist/{chunk-NSGIVS5T.js → chunk-O3DXAR44.js} +2 -2
- package/dist/{chunk-KZAL7HEQ.js → chunk-ONM2AZFL.js} +1 -1
- package/dist/chunk-PGPLCJPK.js +272 -0
- package/dist/{chunk-43K5UWF7.js → chunk-QQ2MVQU3.js} +13 -12
- package/dist/{chunk-62ICA2FF.js → chunk-SPLPKBES.js} +17 -1
- package/dist/{chunk-BLOAJLGI.js → chunk-XDTKJNW5.js} +1 -1
- package/dist/{chunk-BWZHX46O.js → chunk-Y6O35D7K.js} +21 -6
- package/dist/{chunk-IUZ3EV2D.js → chunk-YAMO2U3N.js} +1 -1
- package/dist/{chunk-4ZSUUBG5.js → chunk-YSV3VJQK.js} +55 -2
- package/dist/cli.js +15 -15
- package/dist/{cloud-sync-E3ZL5SZ4.js → cloud-sync-KOEBUFW5.js} +16 -17
- package/dist/{conversations-FSZCUUYO.js → conversations-67WWW25C.js} +4 -4
- package/dist/{create-ERNQYOCK.js → create-7W3LV3J7.js} +1 -1
- package/dist/{daemon-restart-QBZOMILO.js → daemon-restart-EZXB73W7.js} +7 -8
- package/dist/daemon.js +524 -433
- package/dist/{db-ZJJJYJBU.js → db-CZV62BOQ.js} +2 -2
- package/dist/delivery-manager-OMCCJEVP.js +32 -0
- package/dist/{delivery-router-FDBZJQOY.js → delivery-router-HXBHYHJM.js} +7 -5
- package/dist/down-RRNYIGCU.js +16 -0
- package/dist/echo-text-KFSUCZXL.js +32 -0
- package/dist/{exec-ZDD7NEB4.js → exec-SJK7VUTC.js} +3 -4
- package/dist/{export-WYI3E3TB.js → export-7LPCIB5J.js} +3 -3
- package/dist/{extensions-X6MKBJLB.js → extensions-PQPZT6LS.js} +16 -17
- package/dist/{files-COTCVBGO.js → files-LV7VAKPC.js} +3 -3
- package/dist/{import-EPK6WCYS.js → import-XPECCRQ7.js} +6 -6
- package/dist/{isolation-F7ABZMDA.js → isolation-5AYEY55P.js} +5 -3
- package/dist/{login-GPBXOU4D.js → login-KJSN4JDA.js} +2 -2
- package/dist/{logout-DNNTIYE2.js → logout-TMJGUQV2.js} +2 -2
- package/dist/message-delivery-HRGFNWO7.js +34 -0
- package/dist/{mind-K5PZHPBR.js → mind-G3WEWDM4.js} +5 -5
- package/dist/{mind-activity-tracker-HWVFE533.js → mind-activity-tracker-JHAT6456.js} +4 -4
- package/dist/mind-service-2C7AINBO.js +36 -0
- package/dist/{package-G4AM2IDZ.js → package-24TL6DTF.js} +1 -1
- package/dist/{registry-MDLNPTTI.js → registry-7LQHTRUX.js} +2 -2
- package/dist/{sandbox-E7RWZDVG.js → sandbox-SJH3XOPZ.js} +4 -4
- package/dist/scheduler-VON2TB52.js +30 -0
- package/dist/{schema-HHYJDG7B.js → schema-LRUTFOER.js} +1 -1
- package/dist/{seed-SKVSWF7S.js → seed-HHSJTM6O.js} +1 -1
- package/dist/{seed-cmd-D2H5VLB4.js → seed-cmd-UPQY35RG.js} +2 -2
- package/dist/{seed-create-BUSKQ5II.js → seed-create-MDRMXMSB.js} +3 -3
- package/dist/{seed-sprout-R2IZPITD.js → seed-sprout-4M7IQWMH.js} +7 -8
- package/dist/{send-SLDFRSTL.js → send-ZBZWIH5V.js} +3 -3
- package/dist/{service-ZDOSEQCE.js → service-4EVZKGSA.js} +4 -5
- package/dist/{service-install-2HZPDHEY.js → service-install-SHRM6NHK.js} +5 -6
- package/dist/{setup-IYOUPCIK.js → setup-25MMJ3J4.js} +3 -3
- package/dist/{setup-IU3ID7II.js → setup-ZD2LMBR3.js} +8 -10
- package/dist/{skills-P6RGGASV.js → skills-4KQ377GN.js} +5 -6
- package/dist/sleep-manager-AG5SQDDI.js +34 -0
- package/dist/{spirit-XLD6QXSS.js → spirit-53DQPRNR.js} +7 -8
- package/dist/{sprout-QV2RJM5W.js → sprout-YKM2M6D6.js} +1 -1
- package/dist/{src-TJ7RBU5H.js → src-3EGHHKZK.js} +8 -8
- package/dist/{status-QAKAPDYW.js → status-WTU2WHXY.js} +5 -6
- package/dist/system-chat-54RN3K67.js +34 -0
- package/dist/{tailscale-TIRTGM6E.js → tailscale-Q7ZJL7S7.js} +2 -2
- package/dist/{turn-tracker-Z5UJC4BY.js → turn-tracker-JZYERJ76.js} +7 -5
- package/dist/up-LFZA4GI5.js +18 -0
- package/dist/{update-NY3XBVAJ.js → update-EJ7Q77ZX.js} +5 -6
- package/dist/{update-check-OG27AL4Z.js → update-check-ACYYNKTW.js} +3 -3
- package/dist/{version-notify-P7UAR7HZ.js → version-notify-SRQJCQEK.js} +17 -18
- package/dist/web-assets/assets/{index-CrFAsfiE.js → index-DTDmEfPU.js} +2 -2
- package/dist/web-assets/index.html +1 -1
- package/drizzle/0010_delivery_queue_redrive.sql +10 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +1 -1
- package/templates/claude/src/agent.ts +8 -1
- package/templates/claude/src/lib/message-channel.ts +28 -6
- package/templates/codex/src/server.ts +2 -8
- package/dist/chunk-GN52E2DP.js +0 -59
- package/dist/chunk-TFBQESH7.js +0 -179
- package/dist/delivery-manager-J3BRHXUF.js +0 -33
- package/dist/down-DSRWSCXY.js +0 -17
- package/dist/echo-text-3HBQUWPX.js +0 -33
- package/dist/message-delivery-4ZWFDBAY.js +0 -41
- package/dist/mind-manager-6DSY5IJW.js +0 -33
- package/dist/mind-service-R2FM53KD.js +0 -37
- package/dist/scheduler-2FKQCRCM.js +0 -31
- package/dist/sleep-manager-F5KWIFOX.js +0 -35
- package/dist/system-chat-EQIH6JVL.js +0 -35
- package/dist/up-ZTPGC7HB.js +0 -19
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
} from "./chunk-T2TP6ZC6.js";
|
|
5
5
|
import {
|
|
6
6
|
getDb
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-YAMO2U3N.js";
|
|
8
8
|
import {
|
|
9
9
|
activity
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-L3IV23ZG.js";
|
|
11
11
|
|
|
12
12
|
// packages/daemon/src/lib/events/activity-events.ts
|
|
13
13
|
var subscribers = /* @__PURE__ */ new Set();
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import {
|
|
3
3
|
exec,
|
|
4
4
|
execInherit
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-PGPLCJPK.js";
|
|
6
6
|
import {
|
|
7
7
|
voluteSystemDir
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-YAMO2U3N.js";
|
|
9
9
|
|
|
10
10
|
// packages/daemon/src/lib/config/service-mode.ts
|
|
11
11
|
import { execFileSync } from "child_process";
|
|
@@ -7,14 +7,14 @@ import {
|
|
|
7
7
|
modeLabel,
|
|
8
8
|
pollHealth,
|
|
9
9
|
startService
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-ATEX3ZPG.js";
|
|
11
11
|
import {
|
|
12
12
|
readGlobalConfig
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-ONM2AZFL.js";
|
|
14
14
|
import {
|
|
15
15
|
voluteHome,
|
|
16
16
|
voluteSystemDir
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-YAMO2U3N.js";
|
|
18
18
|
|
|
19
19
|
// src/commands/up.ts
|
|
20
20
|
import { spawn } from "child_process";
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
import {
|
|
3
3
|
findBridgeForChannel,
|
|
4
4
|
getBridgeConfig
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-57LBQ7JX.js";
|
|
6
6
|
import {
|
|
7
7
|
recordOutbound
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-4SVONUTC.js";
|
|
9
9
|
import {
|
|
10
10
|
readEnv,
|
|
11
11
|
sharedEnvPath
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-XDTKJNW5.js";
|
|
13
13
|
import {
|
|
14
14
|
addMessage,
|
|
15
15
|
findDMConversation,
|
|
@@ -17,21 +17,21 @@ import {
|
|
|
17
17
|
getChannelName,
|
|
18
18
|
getConversation,
|
|
19
19
|
getParticipants
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-K4EHBHPE.js";
|
|
21
21
|
import {
|
|
22
22
|
readVoluteConfig
|
|
23
23
|
} from "./chunk-CU6OFXMM.js";
|
|
24
24
|
import {
|
|
25
25
|
publish
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-Y6O35D7K.js";
|
|
27
27
|
import {
|
|
28
28
|
daemonConfigPath,
|
|
29
29
|
readDaemonToken
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-ATEX3ZPG.js";
|
|
31
31
|
import {
|
|
32
32
|
getOrCreateMindUser,
|
|
33
33
|
getUserByUsername
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-EFL5UOY4.js";
|
|
35
35
|
import {
|
|
36
36
|
logger_default
|
|
37
37
|
} from "./chunk-T2TP6ZC6.js";
|
|
@@ -40,7 +40,7 @@ import {
|
|
|
40
40
|
} from "./chunk-LGNUFVMR.js";
|
|
41
41
|
import {
|
|
42
42
|
mindDir
|
|
43
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-YAMO2U3N.js";
|
|
44
44
|
import {
|
|
45
45
|
__export
|
|
46
46
|
} from "./chunk-K3NQKI34.js";
|
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
LAUNCHD_PLIST_LABEL,
|
|
4
4
|
LAUNCHD_PLIST_PATH,
|
|
5
5
|
USER_SYSTEMD_UNIT
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-ATEX3ZPG.js";
|
|
7
7
|
import {
|
|
8
8
|
resolveVoluteBin
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-PGPLCJPK.js";
|
|
10
10
|
|
|
11
11
|
// packages/daemon/src/lib/config/service-install.ts
|
|
12
12
|
import { execFile } from "child_process";
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
broadcast
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-7G2EEYXX.js";
|
|
5
5
|
import {
|
|
6
6
|
getDb
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-YAMO2U3N.js";
|
|
8
8
|
import {
|
|
9
9
|
users
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-L3IV23ZG.js";
|
|
11
11
|
|
|
12
12
|
// packages/daemon/src/lib/auth.ts
|
|
13
13
|
import { compareSync, hashSync } from "bcryptjs";
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
setBridgeConfig
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-57LBQ7JX.js";
|
|
5
5
|
import {
|
|
6
6
|
readEnv,
|
|
7
7
|
sharedEnvPath,
|
|
8
8
|
writeEnv
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-XDTKJNW5.js";
|
|
10
10
|
|
|
11
11
|
// packages/daemon/src/lib/template/import-utils.ts
|
|
12
12
|
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from "fs";
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
subscribe
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-7G2EEYXX.js";
|
|
5
5
|
import {
|
|
6
6
|
logger_default
|
|
7
7
|
} from "./chunk-T2TP6ZC6.js";
|
|
8
8
|
import {
|
|
9
9
|
getDb
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-YAMO2U3N.js";
|
|
11
11
|
import {
|
|
12
12
|
channels,
|
|
13
13
|
conversationParticipants,
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
conversations,
|
|
16
16
|
messages,
|
|
17
17
|
users
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-L3IV23ZG.js";
|
|
19
19
|
|
|
20
20
|
// packages/daemon/src/lib/events/conversations.ts
|
|
21
21
|
import { randomUUID } from "crypto";
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
import {
|
|
3
3
|
exec,
|
|
4
4
|
gitExec
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-PGPLCJPK.js";
|
|
6
6
|
import {
|
|
7
7
|
readGlobalConfig,
|
|
8
8
|
writeGlobalConfig
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-ONM2AZFL.js";
|
|
10
10
|
import {
|
|
11
11
|
logger_default
|
|
12
12
|
} from "./chunk-T2TP6ZC6.js";
|
|
@@ -15,10 +15,10 @@ import {
|
|
|
15
15
|
mindDir,
|
|
16
16
|
readRegistry,
|
|
17
17
|
voluteHome
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-YAMO2U3N.js";
|
|
19
19
|
import {
|
|
20
20
|
sharedSkills
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-L3IV23ZG.js";
|
|
22
22
|
|
|
23
23
|
// packages/daemon/src/lib/skills.ts
|
|
24
24
|
import { createHash } from "crypto";
|
|
@@ -46,7 +46,7 @@ async function initDefaultSkills() {
|
|
|
46
46
|
const config = readGlobalConfig();
|
|
47
47
|
let extensionSkills = [];
|
|
48
48
|
try {
|
|
49
|
-
const { getExtensionStandardSkills } = await import("./extensions-
|
|
49
|
+
const { getExtensionStandardSkills } = await import("./extensions-PQPZT6LS.js");
|
|
50
50
|
extensionSkills = getExtensionStandardSkills();
|
|
51
51
|
} catch (err) {
|
|
52
52
|
logger_default.warn("failed to load extension standard skills during init", logger_default.errorData(err));
|
|
@@ -150,18 +150,29 @@ var deliveryQueue = sqliteTable(
|
|
|
150
150
|
"delivery_queue",
|
|
151
151
|
{
|
|
152
152
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
153
|
+
// Keying/cleanup/dedup key: always the base (parent) name, so an insert under a
|
|
154
|
+
// variant name and the id-scoped cleanup share a key.
|
|
153
155
|
mind: text("mind").notNull(),
|
|
156
|
+
// Original delivery target — may be a variant name. Used to resolve the port on
|
|
157
|
+
// redrive so a variant's stranded message is re-delivered to the variant, not the
|
|
158
|
+
// parent. Null on legacy rows → callers fall back to `mind`.
|
|
159
|
+
target_mind: text("target_mind"),
|
|
154
160
|
session: text("session").notNull(),
|
|
155
161
|
channel: text("channel"),
|
|
156
162
|
sender: text("sender"),
|
|
157
163
|
status: text("status").notNull().default("pending"),
|
|
158
164
|
payload: text("payload").notNull(),
|
|
165
|
+
// Redrive bookkeeping: how many delivery attempts have been made, and the
|
|
166
|
+
// earliest time the row is eligible for a retry (null = deliver ASAP).
|
|
167
|
+
attempts: integer("attempts").notNull().default(0),
|
|
168
|
+
next_attempt_at: text("next_attempt_at"),
|
|
159
169
|
created_at: text("created_at").notNull().default(sql`(datetime('now'))`)
|
|
160
170
|
},
|
|
161
171
|
(table) => [
|
|
162
172
|
index("idx_delivery_queue_mind_session").on(table.mind, table.session),
|
|
163
173
|
index("idx_delivery_queue_mind_status").on(table.mind, table.status),
|
|
164
|
-
index("idx_delivery_queue_status").on(table.status)
|
|
174
|
+
index("idx_delivery_queue_status").on(table.status),
|
|
175
|
+
index("idx_delivery_queue_status_next").on(table.status, table.next_attempt_at)
|
|
165
176
|
]
|
|
166
177
|
);
|
|
167
178
|
var activity = sqliteTable(
|
|
@@ -8,10 +8,10 @@ import {
|
|
|
8
8
|
pollHealthDown,
|
|
9
9
|
readDaemonConfig,
|
|
10
10
|
stopService
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-ATEX3ZPG.js";
|
|
12
12
|
import {
|
|
13
13
|
voluteSystemDir
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-YAMO2U3N.js";
|
|
15
15
|
|
|
16
16
|
// src/commands/down.ts
|
|
17
17
|
import { existsSync, readFileSync, unlinkSync } from "fs";
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
getBaseName,
|
|
4
|
+
validateMindName
|
|
5
|
+
} from "./chunk-YAMO2U3N.js";
|
|
6
|
+
|
|
7
|
+
// packages/daemon/src/lib/mind/isolation.ts
|
|
8
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
9
|
+
import { existsSync, readdirSync, statSync } from "fs";
|
|
10
|
+
import { resolve } from "path";
|
|
11
|
+
|
|
12
|
+
// packages/daemon/src/lib/util/exec.ts
|
|
13
|
+
import { execFile as execFileCb, execFileSync, spawn } from "child_process";
|
|
14
|
+
async function exec(cmd, args, options) {
|
|
15
|
+
const [wrappedCmd, wrappedArgs] = options?.mindName ? await wrapForIsolation(cmd, args, options.mindName) : [cmd, args];
|
|
16
|
+
return new Promise((resolve2, reject) => {
|
|
17
|
+
execFileCb(
|
|
18
|
+
wrappedCmd,
|
|
19
|
+
wrappedArgs,
|
|
20
|
+
{ cwd: options?.cwd, env: options?.env },
|
|
21
|
+
(err, stdout, stderr) => {
|
|
22
|
+
if (err) {
|
|
23
|
+
err.stderr = stderr;
|
|
24
|
+
err.stdout = stdout;
|
|
25
|
+
reject(err);
|
|
26
|
+
} else {
|
|
27
|
+
resolve2(stdout);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
function gitExec(args, options) {
|
|
34
|
+
const fullArgs = process.env.VOLUTE_ISOLATION === "user" ? ["-c", "safe.directory=*", ...args] : args;
|
|
35
|
+
return exec("git", fullArgs, options);
|
|
36
|
+
}
|
|
37
|
+
function resolveVoluteBin() {
|
|
38
|
+
try {
|
|
39
|
+
return execFileSync("which", ["volute"], { encoding: "utf-8" }).trim();
|
|
40
|
+
} catch {
|
|
41
|
+
throw new Error("Could not find volute binary on PATH");
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function execInherit(cmd, args, options) {
|
|
45
|
+
const [wrappedCmd, wrappedArgs] = options?.mindName ? await wrapForIsolation(cmd, args, options.mindName) : [cmd, args];
|
|
46
|
+
return new Promise((resolve2, reject) => {
|
|
47
|
+
const child = spawn(wrappedCmd, wrappedArgs, {
|
|
48
|
+
cwd: options?.cwd,
|
|
49
|
+
env: options?.env,
|
|
50
|
+
stdio: "inherit"
|
|
51
|
+
});
|
|
52
|
+
child.on("error", reject);
|
|
53
|
+
child.on("close", (code) => {
|
|
54
|
+
if (code === 0) resolve2();
|
|
55
|
+
else reject(new Error(`${cmd} ${args.join(" ")} exited with code ${code}`));
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// packages/daemon/src/lib/mind/isolation.ts
|
|
61
|
+
function isIsolationEnabled() {
|
|
62
|
+
return process.env.VOLUTE_ISOLATION === "user";
|
|
63
|
+
}
|
|
64
|
+
function mindUserName(mindName) {
|
|
65
|
+
const err = validateMindName(mindName);
|
|
66
|
+
if (err && mindName !== "volute") {
|
|
67
|
+
throw new Error(`Invalid mind name for isolation: ${err}`);
|
|
68
|
+
}
|
|
69
|
+
const prefix = process.env.VOLUTE_USER_PREFIX ?? "mind-";
|
|
70
|
+
return `${prefix}${mindName}`;
|
|
71
|
+
}
|
|
72
|
+
function findNextMacId(type) {
|
|
73
|
+
const idField = type === "Users" ? "UniqueID" : "PrimaryGroupID";
|
|
74
|
+
let output;
|
|
75
|
+
try {
|
|
76
|
+
output = execFileSync2("dscl", [".", "-list", `/${type}`, idField], { encoding: "utf-8" });
|
|
77
|
+
} catch (err) {
|
|
78
|
+
throw new Error(
|
|
79
|
+
`Failed to query ${type} via dscl: ${err instanceof Error ? err.message : err}`
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
const ids = /* @__PURE__ */ new Set();
|
|
83
|
+
for (const line of output.split("\n")) {
|
|
84
|
+
const parts = line.trim().split(/\s+/);
|
|
85
|
+
const id = parseInt(parts[parts.length - 1], 10);
|
|
86
|
+
if (!Number.isNaN(id)) ids.add(id);
|
|
87
|
+
}
|
|
88
|
+
let next = 401;
|
|
89
|
+
while (ids.has(next)) next++;
|
|
90
|
+
return next;
|
|
91
|
+
}
|
|
92
|
+
function getVoluteGroupGid() {
|
|
93
|
+
if (process.platform === "darwin") {
|
|
94
|
+
const output2 = execFileSync2("dscl", [".", "-read", "/Groups/volute", "PrimaryGroupID"], {
|
|
95
|
+
encoding: "utf-8"
|
|
96
|
+
});
|
|
97
|
+
const match = output2.match(/PrimaryGroupID:\s*(\d+)/);
|
|
98
|
+
if (!match) throw new Error("Could not read volute group GID");
|
|
99
|
+
return parseInt(match[1], 10);
|
|
100
|
+
}
|
|
101
|
+
const output = execFileSync2("getent", ["group", "volute"], { encoding: "utf-8" });
|
|
102
|
+
const gid = parseInt(output.split(":")[2], 10);
|
|
103
|
+
if (Number.isNaN(gid)) throw new Error("Could not read volute group GID");
|
|
104
|
+
return gid;
|
|
105
|
+
}
|
|
106
|
+
function ensureVoluteGroup(opts) {
|
|
107
|
+
if (!opts?.force && !isIsolationEnabled()) return;
|
|
108
|
+
if (process.platform === "darwin") {
|
|
109
|
+
try {
|
|
110
|
+
execFileSync2("dscl", [".", "-read", "/Groups/volute"], { stdio: "ignore" });
|
|
111
|
+
return;
|
|
112
|
+
} catch {
|
|
113
|
+
}
|
|
114
|
+
const gid = findNextMacId("Groups");
|
|
115
|
+
try {
|
|
116
|
+
execFileSync2("dscl", [".", "-create", "/Groups/volute"]);
|
|
117
|
+
execFileSync2("dscl", [".", "-create", "/Groups/volute", "PrimaryGroupID", String(gid)]);
|
|
118
|
+
execFileSync2("dscl", [".", "-create", "/Groups/volute", "Password", "*"]);
|
|
119
|
+
} catch (err) {
|
|
120
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
121
|
+
throw new Error(`Failed to create volute group on macOS: ${msg}`);
|
|
122
|
+
}
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
execFileSync2("getent", ["group", "volute"], { stdio: "ignore" });
|
|
127
|
+
} catch {
|
|
128
|
+
try {
|
|
129
|
+
execFileSync2("groupadd", ["volute"], { stdio: ["ignore", "ignore", "pipe"] });
|
|
130
|
+
} catch (err) {
|
|
131
|
+
const stderr = err?.stderr?.toString().trim();
|
|
132
|
+
throw new Error(`Failed to create volute group${stderr ? `: ${stderr}` : ""}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function createMindUser(name, homeDir) {
|
|
137
|
+
if (!isIsolationEnabled()) return;
|
|
138
|
+
const user = mindUserName(name);
|
|
139
|
+
try {
|
|
140
|
+
execFileSync2("id", [user], { stdio: "ignore" });
|
|
141
|
+
return;
|
|
142
|
+
} catch {
|
|
143
|
+
}
|
|
144
|
+
if (process.platform === "darwin") {
|
|
145
|
+
const uid = findNextMacId("Users");
|
|
146
|
+
const gid = getVoluteGroupGid();
|
|
147
|
+
const home = homeDir ?? "/var/empty";
|
|
148
|
+
try {
|
|
149
|
+
execFileSync2("dscl", [".", "-create", `/Users/${user}`]);
|
|
150
|
+
execFileSync2("dscl", [".", "-create", `/Users/${user}`, "UniqueID", String(uid)]);
|
|
151
|
+
execFileSync2("dscl", [".", "-create", `/Users/${user}`, "PrimaryGroupID", String(gid)]);
|
|
152
|
+
execFileSync2("dscl", [".", "-create", `/Users/${user}`, "UserShell", "/usr/bin/false"]);
|
|
153
|
+
execFileSync2("dscl", [".", "-create", `/Users/${user}`, "NFSHomeDirectory", home]);
|
|
154
|
+
execFileSync2("dscl", [".", "-create", `/Users/${user}`, "RealName", `Volute Mind: ${name}`]);
|
|
155
|
+
execFileSync2("dscl", [".", "-create", `/Users/${user}`, "IsHidden", "1"]);
|
|
156
|
+
execFileSync2("dscl", [".", "-append", "/Groups/volute", "GroupMembership", user]);
|
|
157
|
+
} catch (err) {
|
|
158
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
159
|
+
throw new Error(`Failed to create user ${user} on macOS: ${msg}`);
|
|
160
|
+
}
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
try {
|
|
164
|
+
const args = ["-r", "-M", "-G", "volute", "-s", "/usr/sbin/nologin"];
|
|
165
|
+
if (homeDir) args.push("-d", homeDir);
|
|
166
|
+
args.push(user);
|
|
167
|
+
execFileSync2("useradd", args, {
|
|
168
|
+
stdio: ["ignore", "ignore", "pipe"]
|
|
169
|
+
});
|
|
170
|
+
} catch (err) {
|
|
171
|
+
const stderr = err?.stderr?.toString().trim();
|
|
172
|
+
throw new Error(`Failed to create user ${user}${stderr ? `: ${stderr}` : ""}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function deleteMindUser(name) {
|
|
176
|
+
if (!isIsolationEnabled()) return;
|
|
177
|
+
const user = mindUserName(name);
|
|
178
|
+
if (process.platform === "darwin") {
|
|
179
|
+
try {
|
|
180
|
+
execFileSync2("dscl", [".", "-delete", `/Users/${user}`], { stdio: "ignore" });
|
|
181
|
+
} catch {
|
|
182
|
+
}
|
|
183
|
+
try {
|
|
184
|
+
execFileSync2("dscl", [".", "-delete", "/Groups/volute", "GroupMembership", user], {
|
|
185
|
+
stdio: "ignore"
|
|
186
|
+
});
|
|
187
|
+
} catch {
|
|
188
|
+
}
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
try {
|
|
192
|
+
execFileSync2("userdel", [user], { stdio: "ignore" });
|
|
193
|
+
} catch {
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
async function wrapForIsolation(cmd, args, mindName) {
|
|
197
|
+
if (!isIsolationEnabled()) return [cmd, args];
|
|
198
|
+
const baseName = await getBaseName(mindName);
|
|
199
|
+
const user = mindUserName(baseName);
|
|
200
|
+
if (process.platform === "darwin") {
|
|
201
|
+
return ["sudo", ["-u", user, "--", cmd, ...args]];
|
|
202
|
+
}
|
|
203
|
+
return ["runuser", ["-u", user, "--", cmd, ...args]];
|
|
204
|
+
}
|
|
205
|
+
async function userUid(user) {
|
|
206
|
+
try {
|
|
207
|
+
const uid = parseInt((await exec("id", ["-u", user])).trim(), 10);
|
|
208
|
+
return Number.isNaN(uid) ? null : uid;
|
|
209
|
+
} catch {
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
function ownedBy(path, uid) {
|
|
214
|
+
try {
|
|
215
|
+
return statSync(path).uid === uid;
|
|
216
|
+
} catch {
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
async function chownTargets(dir, user) {
|
|
221
|
+
const nodeModules = resolve(dir, "node_modules");
|
|
222
|
+
const home = resolve(dir, "home");
|
|
223
|
+
if (existsSync(nodeModules) && existsSync(home)) {
|
|
224
|
+
const uid = await userUid(user);
|
|
225
|
+
if (uid !== null && ownedBy(nodeModules, uid)) {
|
|
226
|
+
return readdirSync(dir).filter((entry) => entry !== "node_modules").map((entry) => resolve(dir, entry));
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return [dir];
|
|
230
|
+
}
|
|
231
|
+
async function chownMindDir(dir, name) {
|
|
232
|
+
if (!isIsolationEnabled()) return;
|
|
233
|
+
const user = mindUserName(name);
|
|
234
|
+
const group = process.platform === "darwin" ? "volute" : user;
|
|
235
|
+
for (const target of await chownTargets(dir, user)) {
|
|
236
|
+
try {
|
|
237
|
+
await exec("chown", ["-R", `${user}:${group}`, target]);
|
|
238
|
+
} catch (err) {
|
|
239
|
+
const stderr = String(err?.stderr ?? "").trim();
|
|
240
|
+
throw new Error(
|
|
241
|
+
`Failed to chown ${target} to ${user}:${group}${stderr ? `: ${stderr}` : ""}`
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
try {
|
|
246
|
+
await exec("chown", [`${user}:${group}`, dir]);
|
|
247
|
+
} catch (err) {
|
|
248
|
+
const stderr = String(err?.stderr ?? "").trim();
|
|
249
|
+
throw new Error(`Failed to chown ${dir} to ${user}:${group}${stderr ? `: ${stderr}` : ""}`);
|
|
250
|
+
}
|
|
251
|
+
try {
|
|
252
|
+
await exec("chmod", ["700", dir]);
|
|
253
|
+
} catch (err) {
|
|
254
|
+
const stderr = String(err?.stderr ?? "").trim();
|
|
255
|
+
throw new Error(`Failed to chmod ${dir}${stderr ? `: ${stderr}` : ""}`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export {
|
|
260
|
+
isIsolationEnabled,
|
|
261
|
+
mindUserName,
|
|
262
|
+
ensureVoluteGroup,
|
|
263
|
+
createMindUser,
|
|
264
|
+
deleteMindUser,
|
|
265
|
+
wrapForIsolation,
|
|
266
|
+
chownTargets,
|
|
267
|
+
chownMindDir,
|
|
268
|
+
exec,
|
|
269
|
+
gitExec,
|
|
270
|
+
resolveVoluteBin,
|
|
271
|
+
execInherit
|
|
272
|
+
};
|
|
@@ -10,21 +10,22 @@ import {
|
|
|
10
10
|
getSharedSkill,
|
|
11
11
|
installSkill,
|
|
12
12
|
mindSkillsDir
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-KINGVQSR.js";
|
|
14
14
|
import {
|
|
15
15
|
readVoluteConfig,
|
|
16
16
|
writeVoluteConfig
|
|
17
17
|
} from "./chunk-CU6OFXMM.js";
|
|
18
18
|
import {
|
|
19
19
|
exec
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-PGPLCJPK.js";
|
|
21
21
|
import {
|
|
22
22
|
qualifyModelId,
|
|
23
|
-
resolveTemplate
|
|
24
|
-
|
|
23
|
+
resolveTemplate,
|
|
24
|
+
unqualifyModelId
|
|
25
|
+
} from "./chunk-YSV3VJQK.js";
|
|
25
26
|
import {
|
|
26
27
|
readGlobalConfig
|
|
27
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-ONM2AZFL.js";
|
|
28
29
|
import {
|
|
29
30
|
logger_default
|
|
30
31
|
} from "./chunk-T2TP6ZC6.js";
|
|
@@ -33,7 +34,7 @@ import {
|
|
|
33
34
|
findMind,
|
|
34
35
|
nextPort,
|
|
35
36
|
voluteSystemDir
|
|
36
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-YAMO2U3N.js";
|
|
37
38
|
|
|
38
39
|
// packages/daemon/src/lib/mind/spirit.ts
|
|
39
40
|
import { cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
@@ -99,7 +100,7 @@ async function ensureSpiritProject() {
|
|
|
99
100
|
writeFileSync(routesPath, `${JSON.stringify(routesContent, null, 2)}
|
|
100
101
|
`);
|
|
101
102
|
if (spiritModel) {
|
|
102
|
-
const modelForConfig = template === "pi" ? qualifyModelId(spiritModel) : spiritModel;
|
|
103
|
+
const modelForConfig = template === "pi" ? qualifyModelId(spiritModel) : unqualifyModelId(spiritModel);
|
|
103
104
|
const configPath = resolve(dir, "home/.config/config.json");
|
|
104
105
|
const mindConfig = existsSync(configPath) ? JSON.parse(readFileSync(configPath, "utf-8")) : {};
|
|
105
106
|
mindConfig.model = modelForConfig;
|
|
@@ -129,10 +130,10 @@ async function ensureSpiritProject() {
|
|
|
129
130
|
} catch (err) {
|
|
130
131
|
slog.warn("failed to add tending schedule to spirit config", logger_default.errorData(err));
|
|
131
132
|
}
|
|
132
|
-
const { createMindUser, chownMindDir, ensureVoluteGroup } = await import("./isolation-
|
|
133
|
+
const { createMindUser, chownMindDir, ensureVoluteGroup } = await import("./isolation-5AYEY55P.js");
|
|
133
134
|
ensureVoluteGroup();
|
|
134
135
|
createMindUser("volute", resolve(dir, "home"));
|
|
135
|
-
chownMindDir(dir, "volute");
|
|
136
|
+
await chownMindDir(dir, "volute");
|
|
136
137
|
const port = await nextPort();
|
|
137
138
|
await addSpirit("volute", port, template, dir);
|
|
138
139
|
slog.info("spirit project created");
|
|
@@ -163,8 +164,8 @@ async function syncSpiritTemplate() {
|
|
|
163
164
|
cpSync(newPkg, resolve(dir, "package.json"));
|
|
164
165
|
await exec("npm", ["install"], { cwd: dir, env: npmEnv() });
|
|
165
166
|
}
|
|
166
|
-
const db = await (await import("./db-
|
|
167
|
-
const { minds } = await import("./schema-
|
|
167
|
+
const db = await (await import("./db-CZV62BOQ.js")).getDb();
|
|
168
|
+
const { minds } = await import("./schema-LRUTFOER.js");
|
|
168
169
|
const { eq } = await import("drizzle-orm");
|
|
169
170
|
await db.update(minds).set({ template: expectedTemplate }).where(eq(minds.name, "volute"));
|
|
170
171
|
}
|
|
@@ -190,7 +191,7 @@ async function syncSpiritTemplate() {
|
|
|
190
191
|
writeFileSync(resolve(dir, "home/SOUL.md"), getSpiritSoul(systemName, config.description));
|
|
191
192
|
const spiritModel = config.spiritModel;
|
|
192
193
|
if (spiritModel) {
|
|
193
|
-
const modelForConfig = template === "pi" ? qualifyModelId(spiritModel) : spiritModel;
|
|
194
|
+
const modelForConfig = template === "pi" ? qualifyModelId(spiritModel) : unqualifyModelId(spiritModel);
|
|
194
195
|
const mindConfigPath = resolve(dir, "home/.config/config.json");
|
|
195
196
|
const mindConfig = existsSync(mindConfigPath) ? JSON.parse(readFileSync(mindConfigPath, "utf-8")) : {};
|
|
196
197
|
if (mindConfig.model !== modelForConfig) {
|