volute 0.26.0 → 0.27.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 +13 -13
- package/dist/{activity-events-ZMBAKLUF.js → activity-events-BBIEA2F4.js} +2 -3
- package/dist/api.d.ts +363 -168
- package/dist/{archive-4ZQYK5MN.js → archive-UA4BDFXQ.js} +2 -2
- package/dist/{auth-4TV573WE.js → auth-D3OT2ARB.js} +3 -3
- package/dist/bridge-FQHZL3MC.js +206 -0
- package/dist/chat-MHJ3L6JQ.js +58 -0
- package/dist/{chunk-PHU4DEAJ.js → chunk-2WPW7OT6.js} +3 -3
- package/dist/{chunk-5Y3PBKW6.js → chunk-2YP2TVDT.js} +138 -56
- package/dist/{chunk-USNBKHYG.js → chunk-4WXYUOAK.js} +4 -6
- package/dist/{chunk-YJA7P64S.js → chunk-AW7PFDVN.js} +5 -5
- package/dist/{chunk-OZFKBXD6.js → chunk-EHYDTZTF.js} +6 -6
- package/dist/{chunk-LX22GRG7.js → chunk-GIE6CSN5.js} +11 -8
- package/dist/{chunk-WBHMQ5OZ.js → chunk-H7OZRFJB.js} +192 -12
- package/dist/{chunk-ON3FF5JA.js → chunk-HDN7MNGD.js} +3 -3
- package/dist/chunk-IAYBDWVG.js +477 -0
- package/dist/{chunk-TZKJLDQN.js → chunk-IKRVFPWU.js} +14 -9
- package/dist/{chunk-WGOGUMPO.js → chunk-JGFVMROS.js} +13 -6
- package/dist/{chunk-3TV4GLFO.js → chunk-JKOWNZ4P.js} +3 -3
- package/dist/{chunk-NWI2425I.js → chunk-K5NAC55T.js} +1 -1
- package/dist/{chunk-HFCBO2GL.js → chunk-KDGS53OS.js} +4 -4
- package/dist/chunk-KTLFDYPT.js +61 -0
- package/dist/{chunk-V63B7DX3.js → chunk-LAC664WU.js} +7 -4
- package/dist/{chunk-3CFRE2VC.js → chunk-OQZH4PBB.js} +337 -1061
- package/dist/{chunk-2VO7453N.js → chunk-PHSAT7YL.js} +30 -54
- package/dist/{chunk-XOXLRRR2.js → chunk-RKQEHRBB.js} +4 -3
- package/dist/chunk-T6HKBWXZ.js +23 -0
- package/dist/{chunk-UTL75LP6.js → chunk-USUXRNVD.js} +22 -22
- package/dist/{chunk-J2CO4WEV.js → chunk-VIVMW2H2.js} +4 -4
- package/dist/{chunk-KTJGZ7M7.js → chunk-XBLSAVJF.js} +1 -1
- package/dist/cli.js +31 -36
- package/dist/{cloud-sync-NI2K3C7G.js → cloud-sync-T7M3ESC3.js} +15 -14
- package/dist/connectors/discord-bridge.js +158 -0
- package/dist/connectors/slack-bridge.js +119 -0
- package/dist/connectors/telegram-bridge.js +133 -0
- package/dist/conversations-M2K4253F.js +55 -0
- package/dist/create-D7J73A6H.js +45 -0
- package/dist/{create-4YBRTTJS.js → create-QWV73WXD.js} +1 -1
- package/dist/{daemon-client-Z7FAJ6JW.js → daemon-client-I42FK2BF.js} +2 -2
- package/dist/{daemon-restart-BJZ3O4U4.js → daemon-restart-M2QTYMEG.js} +7 -7
- package/dist/daemon.js +1758 -1024
- package/dist/db-IC4J52XQ.js +8 -0
- package/dist/{delete-27OYNK25.js → delete-4JYGD4VN.js} +1 -1
- package/dist/down-LVBXEULC.js +14 -0
- package/dist/{env-M336ONDP.js → env-YJMUMFIY.js} +2 -2
- package/dist/{export-HP4G5DQC.js → export-BOJQWBMA.js} +4 -4
- package/dist/{file-HUDKTRAS.js → file-CR36YUPD.js} +4 -4
- package/dist/{history-B64GTFTD.js → history-XKRTAFS2.js} +5 -5
- package/dist/{import-XIB7UV4S.js → import-SRTQXBGH.js} +4 -4
- package/dist/join-J4QU42DL.js +66 -0
- package/dist/list-R73GENNL.js +40 -0
- package/dist/{log-PBFNILJ4.js → log-ABYNVYJ3.js} +4 -4
- package/dist/{login-B5E7N7MY.js → login-3QZNR2DF.js} +4 -4
- package/dist/{login-6U7U6BNG.js → login-XX37I52P.js} +2 -2
- package/dist/{logout-XSJRYS3U.js → logout-T53VKCPU.js} +4 -4
- package/dist/{logout-UKD5LA37.js → logout-W4KOOBIT.js} +2 -2
- package/dist/{logs-3CART7O7.js → logs-U35JR2KE.js} +5 -5
- package/dist/{merge-VK2HSKMA.js → merge-LNSMSAOF.js} +4 -4
- package/dist/message-delivery-LDXLGERA.js +25 -0
- package/dist/migrate-registry-to-db-XC7T5B7P.js +110 -0
- package/dist/{mind-HZ3QSDDJ.js → mind-DI33C74K.js} +25 -25
- package/dist/{mind-activity-tracker-4G6FURY2.js → mind-activity-tracker-EN6XNXPF.js} +3 -4
- package/dist/mind-manager-M6EMUW5I.js +18 -0
- package/dist/{mind-sleep-DTV7L44D.js → mind-sleep-BTSWQNAC.js} +4 -4
- package/dist/{mind-wake-PFN4FN3T.js → mind-wake-SBAKIDVP.js} +4 -4
- package/dist/{notes-37FW2UR2.js → notes-XCER3I7M.js} +11 -21
- package/dist/{package-VZWLXPHV.js → package-7WY6VKU3.js} +1 -1
- package/dist/{pages-DIIT5HMQ.js → pages-6EBS6CBR.js} +2 -2
- package/dist/{publish-HQV7YREB.js → publish-66UB2ZFY.js} +5 -5
- package/dist/{pull-2MB4SK3C.js → pull-XCHJTM5M.js} +4 -4
- package/dist/read-36UFXN3G.js +46 -0
- package/dist/{register-EFND67FQ.js → register-6B2CXTYM.js} +2 -2
- package/dist/{registry-D2BSQ2X5.js → registry-NDNOOYG4.js} +15 -9
- package/dist/{restart-CCK7D6TV.js → restart-6ESL3NBO.js} +5 -5
- package/dist/{sandbox-EHGFF52K.js → sandbox-TGBX22DS.js} +3 -3
- package/dist/{schedule-6F7ELB2M.js → schedule-QTJMFATP.js} +5 -5
- package/dist/{seed-E5OQGWX3.js → seed-SSUCYYDF.js} +2 -2
- package/dist/{send-IH6XZKPC.js → send-ZNCJDSRP.js} +25 -19
- package/dist/{service-LLBV3R7M.js → service-6LIN3F3K.js} +4 -4
- package/dist/{setup-F6TWFYGQ.js → setup-JG4QAEBV.js} +12 -12
- package/dist/{setup-YGAAIKKZ.js → setup-JHL5ZEST.js} +2 -2
- package/dist/{shared-UMO4S7CC.js → shared-ML5I4Q2A.js} +4 -4
- package/dist/{skill-42LGFBQC.js → skill-AUAQTSP5.js} +5 -5
- package/dist/skills/dreaming/references/INSTALL.md +2 -2
- package/dist/skills/orientation/SKILL.md +3 -3
- package/dist/skills/volute-mind/SKILL.md +32 -30
- package/dist/sleep-manager-MWYHM5HV.js +29 -0
- package/dist/split-TKJ5OT3P.js +63 -0
- package/dist/{sprout-QL74KR2X.js → sprout-IJVVKSJ2.js} +6 -7
- package/dist/{start-O5JQASRC.js → start-EUJSS5R4.js} +2 -2
- package/dist/{status-FZBEBM7Q.js → status-77YEPHMW.js} +5 -5
- package/dist/{status-WXD4HXRL.js → status-7GA4SM4Y.js} +4 -4
- package/dist/{status-LV34BG6G.js → status-THLOBLWG.js} +2 -2
- package/dist/{stop-2SOG5NYF.js → stop-3XAITBBF.js} +5 -5
- package/dist/{tailscale-AJ4VL5XK.js → tailscale-NY5MUMY3.js} +1 -1
- package/dist/up-NKSMXBWR.js +17 -0
- package/dist/{update-5VUDAI3D.js → update-PTSH22AZ.js} +9 -9
- package/dist/{update-check-F5Z3ALXX.js → update-check-64FWC4Y2.js} +2 -2
- package/dist/{upgrade-QCCO33BK.js → upgrade-HA47CS4C.js} +12 -5
- package/dist/variant-7TGZHOU3.js +41 -0
- package/dist/{version-notify-USFZBWMG.js → version-notify-5Z4MNR6M.js} +26 -30
- package/dist/web-assets/assets/index-CI5wgghI.css +1 -0
- package/dist/web-assets/assets/index-is5CvJWH.js +75 -0
- package/dist/web-assets/favicon.png +0 -0
- package/dist/web-assets/index.html +2 -2
- package/drizzle/0017_minds.sql +16 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +1 -1
- package/templates/_base/.init/.config/prompts.json +2 -2
- package/templates/_base/home/VOLUTE.md +5 -5
- package/templates/_base/src/lib/startup.ts +2 -2
- package/dist/channel-ZVZV42UD.js +0 -260
- package/dist/chunk-B2CPS4QU.js +0 -283
- package/dist/chunk-SIAG3QMM.js +0 -42
- package/dist/chunk-WSLPZF72.js +0 -173
- package/dist/connector-G722WXAU.js +0 -147
- package/dist/connectors/discord.js +0 -177
- package/dist/connectors/slack.js +0 -181
- package/dist/connectors/telegram.js +0 -187
- package/dist/down-7UKFMJJZ.js +0 -14
- package/dist/message-delivery-MS5JYPZX.js +0 -25
- package/dist/mind-manager-VVK67AY3.js +0 -19
- package/dist/sleep-manager-EE4NRN2Q.js +0 -29
- package/dist/up-SDMCSVI3.js +0 -17
- package/dist/variant-WWLDY6D5.js +0 -207
- package/dist/web-assets/assets/index-CUQ31ieL.js +0 -69
- package/dist/web-assets/assets/index-CW8NSl1o.css +0 -1
|
Binary file
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
9
9
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
10
10
|
<link href="https://fonts.googleapis.com/css2?family=Averia+Serif+Libre:wght@300;400;700&family=Fira+Code:wght@300;400;500;600&family=Averia+Sans+Libre:ital,wght@0,300;0,400;0,500;0,600;1,400&display=swap" rel="stylesheet" />
|
|
11
|
-
<script type="module" crossorigin src="/assets/index-
|
|
12
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
11
|
+
<script type="module" crossorigin src="/assets/index-is5CvJWH.js"></script>
|
|
12
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CI5wgghI.css">
|
|
13
13
|
</head>
|
|
14
14
|
<body>
|
|
15
15
|
<div id="root"></div>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
CREATE TABLE `minds` (
|
|
2
|
+
`name` text PRIMARY KEY NOT NULL,
|
|
3
|
+
`port` integer NOT NULL,
|
|
4
|
+
`parent` text REFERENCES `minds`(`name`) ON DELETE CASCADE,
|
|
5
|
+
`dir` text,
|
|
6
|
+
`branch` text,
|
|
7
|
+
`stage` text,
|
|
8
|
+
`template` text,
|
|
9
|
+
`template_hash` text,
|
|
10
|
+
`running` integer NOT NULL DEFAULT 0,
|
|
11
|
+
`created_at` text NOT NULL DEFAULT (datetime('now'))
|
|
12
|
+
);
|
|
13
|
+
--> statement-breakpoint
|
|
14
|
+
CREATE UNIQUE INDEX `idx_minds_port` ON `minds` (`port`);
|
|
15
|
+
--> statement-breakpoint
|
|
16
|
+
CREATE INDEX `idx_minds_parent` ON `minds` (`parent`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compaction_warning": "Context is getting long — compaction is about to summarize this conversation. Before that happens, save anything important to files (MEMORY.md, memory/journal/${date}.md, etc.) since those survive compaction. Focus on: decisions made, open tasks, and anything you'd need to pick up where you left off.",
|
|
3
3
|
"compaction_instructions": "Preserve your sense of who you are, what matters to you, what happened in this conversation, and the threads of thought and connection you'd want to return to.",
|
|
4
|
-
"reply_instructions": "To reply to this message, use: volute send ${channel} \"your message\"",
|
|
5
|
-
"channel_invite": "[Channel Invite]\n${headers}\n\n[${sender} — ${time}]\n${preview}\n\nFurther messages will be saved to ${filePath}\n\nTo accept, add to .config/routes.json:\n Rule: { \"channel\": \"${channel}\", \"session\": \"${suggestedSession}\" }\n${batchRecommendation}To respond, use: volute send ${channel} \"your message\"\nTo reject, delete ${filePath}"
|
|
4
|
+
"reply_instructions": "To reply to this message, use: volute chat send ${channel} \"your message\"",
|
|
5
|
+
"channel_invite": "[Channel Invite]\n${headers}\n\n[${sender} — ${time}]\n${preview}\n\nFurther messages will be saved to ${filePath}\n\nTo accept, add to .config/routes.json:\n Rule: { \"channel\": \"${channel}\", \"session\": \"${suggestedSession}\" }\n${batchRecommendation}To respond, use: volute chat send ${channel} \"your message\"\nTo reject, delete ${filePath}"
|
|
6
6
|
}
|
|
@@ -4,17 +4,17 @@ You are a volute mind — a persistent being that communicates across multiple c
|
|
|
4
4
|
|
|
5
5
|
## How to Communicate
|
|
6
6
|
|
|
7
|
-
**Your text output stays in your session — it is not sent to anyone.** To send a message, you must use the `volute send` command:
|
|
7
|
+
**Your text output stays in your session — it is not sent to anyone.** To send a message, you must use the `volute chat send` command:
|
|
8
8
|
|
|
9
9
|
```sh
|
|
10
|
-
volute send @other-mind "hello" # DM another user
|
|
11
|
-
volute send discord:server/channel "hello" # send to a channel
|
|
12
|
-
volute send animal-chat "hello" # send to a volute channel
|
|
10
|
+
volute chat send @other-mind "hello" # DM another user
|
|
11
|
+
volute chat send discord:server/channel "hello" # send to a channel
|
|
12
|
+
volute chat send animal-chat "hello" # send to a volute channel
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
This applies to everything: replying to messages, talking to other minds, and reaching out on your own initiative. Piping from stdin avoids shell escaping issues:
|
|
16
16
|
```sh
|
|
17
|
-
echo "message with 'quotes' and $special chars" | volute send @other-mind
|
|
17
|
+
echo "message with 'quotes' and $special chars" | volute chat send @other-mind
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
## Channels
|
|
@@ -128,7 +128,7 @@ const DEFAULT_PROMPTS: MindPrompts = {
|
|
|
128
128
|
"Context is getting long — compaction is about to summarize this conversation. Before that happens, save anything important to files (MEMORY.md, memory/journal/${date}.md, etc.) since those survive compaction. Focus on: decisions made, open tasks, and anything you'd need to pick up where you left off.",
|
|
129
129
|
compaction_instructions:
|
|
130
130
|
"Preserve your sense of who you are, what matters to you, what happened in this conversation, and the threads of thought and connection you'd want to return to.",
|
|
131
|
-
reply_instructions: 'To reply to this message, use: volute send ${channel} "your message"',
|
|
131
|
+
reply_instructions: 'To reply to this message, use: volute chat send ${channel} "your message"',
|
|
132
132
|
channel_invite: `[Channel Invite]
|
|
133
133
|
\${headers}
|
|
134
134
|
|
|
@@ -139,7 +139,7 @@ Further messages will be saved to \${filePath}
|
|
|
139
139
|
|
|
140
140
|
To accept, add to .config/routes.json:
|
|
141
141
|
Rule: { "channel": "\${channel}", "session": "\${suggestedSession}" }
|
|
142
|
-
\${batchRecommendation}To respond, use: volute send \${channel} "your message"
|
|
142
|
+
\${batchRecommendation}To respond, use: volute chat send \${channel} "your message"
|
|
143
143
|
To reject, delete \${filePath}`,
|
|
144
144
|
};
|
|
145
145
|
|
package/dist/channel-ZVZV42UD.js
DELETED
|
@@ -1,260 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
daemonFetch
|
|
4
|
-
} from "./chunk-WGOGUMPO.js";
|
|
5
|
-
import {
|
|
6
|
-
getClient,
|
|
7
|
-
urlOf
|
|
8
|
-
} from "./chunk-4RQBJWQX.js";
|
|
9
|
-
import {
|
|
10
|
-
resolveMindName
|
|
11
|
-
} from "./chunk-NAOW2CLO.js";
|
|
12
|
-
import {
|
|
13
|
-
parseArgs
|
|
14
|
-
} from "./chunk-D424ZQGI.js";
|
|
15
|
-
import "./chunk-B2CPS4QU.js";
|
|
16
|
-
import "./chunk-K3NQKI34.js";
|
|
17
|
-
|
|
18
|
-
// src/commands/channel.ts
|
|
19
|
-
async function run(args) {
|
|
20
|
-
const subcommand = args[0];
|
|
21
|
-
switch (subcommand) {
|
|
22
|
-
case "read":
|
|
23
|
-
await readChannel(args.slice(1));
|
|
24
|
-
break;
|
|
25
|
-
case "list":
|
|
26
|
-
await listChannels(args.slice(1));
|
|
27
|
-
break;
|
|
28
|
-
case "users":
|
|
29
|
-
await listUsers(args.slice(1));
|
|
30
|
-
break;
|
|
31
|
-
case "create":
|
|
32
|
-
await createChannel(args.slice(1));
|
|
33
|
-
break;
|
|
34
|
-
case "typing":
|
|
35
|
-
await typingChannel(args.slice(1));
|
|
36
|
-
break;
|
|
37
|
-
case "invite":
|
|
38
|
-
await inviteChannel(args.slice(1));
|
|
39
|
-
break;
|
|
40
|
-
case "pending":
|
|
41
|
-
await pendingChannel(args.slice(1));
|
|
42
|
-
break;
|
|
43
|
-
case "--help":
|
|
44
|
-
case "-h":
|
|
45
|
-
case void 0:
|
|
46
|
-
printUsage();
|
|
47
|
-
break;
|
|
48
|
-
default:
|
|
49
|
-
printUsage();
|
|
50
|
-
process.exit(1);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
function printUsage() {
|
|
54
|
-
console.log(`Usage:
|
|
55
|
-
volute channel read <channel-uri> [--limit N] [--mind <name>]
|
|
56
|
-
volute channel list [<platform>] [--mind <name>]
|
|
57
|
-
volute channel users <platform> [--mind <name>]
|
|
58
|
-
volute channel create <platform> --participants user1,user2 [--name "..."] [--mind <name>]
|
|
59
|
-
volute channel typing <channel-uri> [--mind <name>]
|
|
60
|
-
volute channel invite <channel-name> <username>
|
|
61
|
-
volute channel pending [--mind <name>]`);
|
|
62
|
-
}
|
|
63
|
-
async function readChannel(args) {
|
|
64
|
-
const { positional, flags } = parseArgs(args, {
|
|
65
|
-
mind: { type: "string" },
|
|
66
|
-
limit: { type: "number" }
|
|
67
|
-
});
|
|
68
|
-
const uri = positional[0];
|
|
69
|
-
if (!uri) {
|
|
70
|
-
console.error("Usage: volute channel read <channel-uri> [--limit N] [--mind <name>]");
|
|
71
|
-
process.exit(1);
|
|
72
|
-
}
|
|
73
|
-
const mindName = resolveMindName(flags);
|
|
74
|
-
const { platform } = parseUri(uri);
|
|
75
|
-
const limit = flags.limit ?? 20;
|
|
76
|
-
const client = getClient();
|
|
77
|
-
const url = client.api.minds[":name"].channels.read.$url({ param: { name: mindName } });
|
|
78
|
-
url.searchParams.set("platform", platform);
|
|
79
|
-
url.searchParams.set("uri", uri);
|
|
80
|
-
url.searchParams.set("limit", String(limit));
|
|
81
|
-
const res = await daemonFetch(urlOf(url));
|
|
82
|
-
if (!res.ok) {
|
|
83
|
-
const body = await res.json().catch(() => ({}));
|
|
84
|
-
console.error(body.error ?? `Server responded with ${res.status}`);
|
|
85
|
-
process.exit(1);
|
|
86
|
-
}
|
|
87
|
-
const output = await res.text();
|
|
88
|
-
console.log(output);
|
|
89
|
-
}
|
|
90
|
-
async function listChannels(args) {
|
|
91
|
-
const { positional, flags } = parseArgs(args, {
|
|
92
|
-
mind: { type: "string" }
|
|
93
|
-
});
|
|
94
|
-
const platform = positional[0];
|
|
95
|
-
const mindName = resolveMindName(flags);
|
|
96
|
-
const client = getClient();
|
|
97
|
-
const url = client.api.minds[":name"].channels.list.$url({ param: { name: mindName } });
|
|
98
|
-
if (platform) url.searchParams.set("platform", platform);
|
|
99
|
-
const res = await daemonFetch(urlOf(url));
|
|
100
|
-
if (!res.ok) {
|
|
101
|
-
const body = await res.json().catch(() => ({}));
|
|
102
|
-
console.error(body.error ?? `Server responded with ${res.status}`);
|
|
103
|
-
process.exit(1);
|
|
104
|
-
}
|
|
105
|
-
const results = await res.json();
|
|
106
|
-
for (const [p, convs] of Object.entries(results)) {
|
|
107
|
-
for (const conv of convs) {
|
|
108
|
-
if (conv.error) {
|
|
109
|
-
console.error(`${p}: ${conv.error}`);
|
|
110
|
-
continue;
|
|
111
|
-
}
|
|
112
|
-
const parts = [conv.id.padEnd(24), conv.name.padEnd(28), conv.type];
|
|
113
|
-
if (conv.participantCount != null) {
|
|
114
|
-
parts.push(String(conv.participantCount));
|
|
115
|
-
}
|
|
116
|
-
console.log(parts.join(" "));
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
async function listUsers(args) {
|
|
121
|
-
const { positional, flags } = parseArgs(args, {
|
|
122
|
-
mind: { type: "string" }
|
|
123
|
-
});
|
|
124
|
-
const platform = positional[0];
|
|
125
|
-
if (!platform) {
|
|
126
|
-
console.error("Usage: volute channel users <platform> [--mind <name>]");
|
|
127
|
-
process.exit(1);
|
|
128
|
-
}
|
|
129
|
-
const mindName = resolveMindName(flags);
|
|
130
|
-
const client = getClient();
|
|
131
|
-
const url = client.api.minds[":name"].channels.users.$url({ param: { name: mindName } });
|
|
132
|
-
url.searchParams.set("platform", platform);
|
|
133
|
-
const res = await daemonFetch(urlOf(url));
|
|
134
|
-
if (!res.ok) {
|
|
135
|
-
const body = await res.json().catch(() => ({}));
|
|
136
|
-
console.error(body.error ?? `Server responded with ${res.status}`);
|
|
137
|
-
process.exit(1);
|
|
138
|
-
}
|
|
139
|
-
const users = await res.json();
|
|
140
|
-
for (const user of users) {
|
|
141
|
-
console.log(`${user.username.padEnd(20)} ${user.id.padEnd(20)} ${user.type ?? ""}`);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
async function createChannel(args) {
|
|
145
|
-
const { positional, flags } = parseArgs(args, {
|
|
146
|
-
mind: { type: "string" },
|
|
147
|
-
participants: { type: "string" },
|
|
148
|
-
name: { type: "string" }
|
|
149
|
-
});
|
|
150
|
-
const platform = positional[0];
|
|
151
|
-
if (!platform || !flags.participants) {
|
|
152
|
-
console.error(
|
|
153
|
-
'Usage: volute channel create <platform> --participants user1,user2 [--name "..."] [--mind <name>]'
|
|
154
|
-
);
|
|
155
|
-
process.exit(1);
|
|
156
|
-
}
|
|
157
|
-
const mindName = resolveMindName(flags);
|
|
158
|
-
const participants = flags.participants.split(",").map((s) => s.trim());
|
|
159
|
-
const client = getClient();
|
|
160
|
-
const res = await daemonFetch(
|
|
161
|
-
urlOf(client.api.minds[":name"].channels.create.$url({ param: { name: mindName } })),
|
|
162
|
-
{
|
|
163
|
-
method: "POST",
|
|
164
|
-
headers: { "Content-Type": "application/json" },
|
|
165
|
-
body: JSON.stringify({ platform, participants, name: flags.name })
|
|
166
|
-
}
|
|
167
|
-
);
|
|
168
|
-
if (!res.ok) {
|
|
169
|
-
const body = await res.json().catch(() => ({}));
|
|
170
|
-
console.error(body.error ?? `Server responded with ${res.status}`);
|
|
171
|
-
process.exit(1);
|
|
172
|
-
}
|
|
173
|
-
const data = await res.json();
|
|
174
|
-
console.log(data.slug);
|
|
175
|
-
}
|
|
176
|
-
async function typingChannel(args) {
|
|
177
|
-
const { positional, flags } = parseArgs(args, {
|
|
178
|
-
mind: { type: "string" }
|
|
179
|
-
});
|
|
180
|
-
const uri = positional[0];
|
|
181
|
-
if (!uri) {
|
|
182
|
-
console.error("Usage: volute channel typing <channel-uri> [--mind <name>]");
|
|
183
|
-
process.exit(1);
|
|
184
|
-
}
|
|
185
|
-
const mindName = resolveMindName(flags);
|
|
186
|
-
try {
|
|
187
|
-
const client = getClient();
|
|
188
|
-
const url = client.api.minds[":name"].typing.$url({ param: { name: mindName } });
|
|
189
|
-
url.searchParams.set("channel", uri);
|
|
190
|
-
const res = await daemonFetch(urlOf(url));
|
|
191
|
-
if (!res.ok) {
|
|
192
|
-
const body = await res.json().catch(() => ({}));
|
|
193
|
-
console.error(body.error ?? `Server responded with ${res.status}`);
|
|
194
|
-
process.exit(1);
|
|
195
|
-
}
|
|
196
|
-
const data = await res.json();
|
|
197
|
-
if (data.typing.length > 0) {
|
|
198
|
-
console.log(data.typing.join(", "));
|
|
199
|
-
}
|
|
200
|
-
} catch (err) {
|
|
201
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
202
|
-
process.exit(1);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
async function inviteChannel(args) {
|
|
206
|
-
const { positional } = parseArgs(args, {});
|
|
207
|
-
const channelName = positional[0];
|
|
208
|
-
const username = positional[1];
|
|
209
|
-
if (!channelName || !username) {
|
|
210
|
-
console.error("Usage: volute channel invite <channel-name> <username>");
|
|
211
|
-
process.exit(1);
|
|
212
|
-
}
|
|
213
|
-
const res = await daemonFetch(`/api/volute/channels/${encodeURIComponent(channelName)}/invite`, {
|
|
214
|
-
method: "POST",
|
|
215
|
-
headers: { "Content-Type": "application/json" },
|
|
216
|
-
body: JSON.stringify({ username })
|
|
217
|
-
});
|
|
218
|
-
if (!res.ok) {
|
|
219
|
-
const body = await res.json().catch(() => ({}));
|
|
220
|
-
console.error(body.error ?? `Server responded with ${res.status}`);
|
|
221
|
-
process.exit(1);
|
|
222
|
-
}
|
|
223
|
-
console.log(`Invited ${username} to #${channelName}`);
|
|
224
|
-
}
|
|
225
|
-
async function pendingChannel(args) {
|
|
226
|
-
const { flags } = parseArgs(args, {
|
|
227
|
-
mind: { type: "string" }
|
|
228
|
-
});
|
|
229
|
-
const mindName = resolveMindName(flags);
|
|
230
|
-
const res = await daemonFetch(`/api/minds/${encodeURIComponent(mindName)}/delivery/pending`);
|
|
231
|
-
if (!res.ok) {
|
|
232
|
-
const body = await res.json().catch(() => ({}));
|
|
233
|
-
console.error(body.error ?? `Server responded with ${res.status}`);
|
|
234
|
-
process.exit(1);
|
|
235
|
-
}
|
|
236
|
-
const pending = await res.json();
|
|
237
|
-
if (pending.length === 0) {
|
|
238
|
-
console.log("No pending messages.");
|
|
239
|
-
return;
|
|
240
|
-
}
|
|
241
|
-
for (const entry of pending) {
|
|
242
|
-
console.log(
|
|
243
|
-
`${(entry.channel ?? "unknown").padEnd(30)} ${String(entry.count).padEnd(6)} ${entry.sender ?? "unknown"}`
|
|
244
|
-
);
|
|
245
|
-
console.log(` First seen: ${entry.firstSeen}`);
|
|
246
|
-
console.log(` Preview: ${entry.preview}`);
|
|
247
|
-
console.log();
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
function parseUri(uri) {
|
|
251
|
-
const colonIdx = uri.indexOf(":");
|
|
252
|
-
if (colonIdx === -1) {
|
|
253
|
-
console.error(`Invalid channel URI: ${uri} (expected format: platform:id)`);
|
|
254
|
-
process.exit(1);
|
|
255
|
-
}
|
|
256
|
-
return { platform: uri.slice(0, colonIdx), channelId: uri.slice(colonIdx + 1) };
|
|
257
|
-
}
|
|
258
|
-
export {
|
|
259
|
-
run
|
|
260
|
-
};
|
package/dist/chunk-B2CPS4QU.js
DELETED
|
@@ -1,283 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// src/lib/registry.ts
|
|
4
|
-
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync, writeFileSync as writeFileSync2 } from "fs";
|
|
5
|
-
import { homedir } from "os";
|
|
6
|
-
import { dirname, resolve as resolve2 } from "path";
|
|
7
|
-
import { fileURLToPath } from "url";
|
|
8
|
-
|
|
9
|
-
// src/lib/variants.ts
|
|
10
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
11
|
-
import { resolve } from "path";
|
|
12
|
-
function variantsPath() {
|
|
13
|
-
return resolve(voluteHome(), "variants.json");
|
|
14
|
-
}
|
|
15
|
-
function readAllVariants() {
|
|
16
|
-
const path = variantsPath();
|
|
17
|
-
if (!existsSync(path)) return {};
|
|
18
|
-
try {
|
|
19
|
-
return JSON.parse(readFileSync(path, "utf-8"));
|
|
20
|
-
} catch {
|
|
21
|
-
return {};
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
function writeAllVariants(all) {
|
|
25
|
-
mkdirSync(voluteHome(), { recursive: true });
|
|
26
|
-
writeFileSync(variantsPath(), `${JSON.stringify(all, null, 2)}
|
|
27
|
-
`);
|
|
28
|
-
}
|
|
29
|
-
function readVariants(mindName) {
|
|
30
|
-
return readAllVariants()[mindName] ?? [];
|
|
31
|
-
}
|
|
32
|
-
function writeVariants(mindName, variants) {
|
|
33
|
-
const all = readAllVariants();
|
|
34
|
-
if (variants.length === 0) {
|
|
35
|
-
delete all[mindName];
|
|
36
|
-
} else {
|
|
37
|
-
all[mindName] = variants;
|
|
38
|
-
}
|
|
39
|
-
writeAllVariants(all);
|
|
40
|
-
}
|
|
41
|
-
function addVariant(mindName, variant) {
|
|
42
|
-
const variants = readVariants(mindName);
|
|
43
|
-
const filtered = variants.filter((v) => v.name !== variant.name);
|
|
44
|
-
filtered.push(variant);
|
|
45
|
-
writeVariants(mindName, filtered);
|
|
46
|
-
}
|
|
47
|
-
function removeVariant(mindName, name) {
|
|
48
|
-
const variants = readVariants(mindName);
|
|
49
|
-
writeVariants(
|
|
50
|
-
mindName,
|
|
51
|
-
variants.filter((v) => v.name !== name)
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
function findVariant(mindName, name) {
|
|
55
|
-
return readVariants(mindName).find((v) => v.name === name);
|
|
56
|
-
}
|
|
57
|
-
function setVariantRunning(mindName, variantName, running) {
|
|
58
|
-
const all = readAllVariants();
|
|
59
|
-
const variants = all[mindName] ?? [];
|
|
60
|
-
const variant = variants.find((v) => v.name === variantName);
|
|
61
|
-
if (variant) {
|
|
62
|
-
variant.running = running;
|
|
63
|
-
all[mindName] = variants;
|
|
64
|
-
writeAllVariants(all);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
function getAllRunningVariants() {
|
|
68
|
-
const all = readAllVariants();
|
|
69
|
-
const result = [];
|
|
70
|
-
for (const [mindName, variants] of Object.entries(all)) {
|
|
71
|
-
for (const variant of variants) {
|
|
72
|
-
if (variant.running) {
|
|
73
|
-
result.push({ mindName, variant });
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
return result;
|
|
78
|
-
}
|
|
79
|
-
function removeAllVariants(mindName) {
|
|
80
|
-
const all = readAllVariants();
|
|
81
|
-
delete all[mindName];
|
|
82
|
-
writeAllVariants(all);
|
|
83
|
-
}
|
|
84
|
-
async function checkHealth(port) {
|
|
85
|
-
try {
|
|
86
|
-
const res = await fetch(`http://127.0.0.1:${port}/health`, {
|
|
87
|
-
signal: AbortSignal.timeout(2e3)
|
|
88
|
-
});
|
|
89
|
-
if (!res.ok) return { ok: false };
|
|
90
|
-
const data = await res.json();
|
|
91
|
-
return { ok: true, name: data.name };
|
|
92
|
-
} catch {
|
|
93
|
-
return { ok: false };
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
var SAFE_BRANCH_RE = /^[a-zA-Z0-9._\-/]+$/;
|
|
97
|
-
function validateBranchName(branch) {
|
|
98
|
-
if (!SAFE_BRANCH_RE.test(branch)) {
|
|
99
|
-
return `Invalid branch name: ${branch}. Only alphanumeric, '.', '_', '-', '/' allowed.`;
|
|
100
|
-
}
|
|
101
|
-
if (branch.includes("..")) {
|
|
102
|
-
return `Invalid branch name: ${branch}. '..' not allowed.`;
|
|
103
|
-
}
|
|
104
|
-
return null;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// src/lib/registry.ts
|
|
108
|
-
var registryCache = null;
|
|
109
|
-
function initRegistryCache() {
|
|
110
|
-
registryCache = readRegistryFromDisk();
|
|
111
|
-
}
|
|
112
|
-
function getRegistryCache() {
|
|
113
|
-
return registryCache;
|
|
114
|
-
}
|
|
115
|
-
function readRegistryFromDisk() {
|
|
116
|
-
const registryPath = resolve2(voluteHome(), "minds.json");
|
|
117
|
-
if (!existsSync2(registryPath)) return [];
|
|
118
|
-
try {
|
|
119
|
-
const entries = JSON.parse(readFileSync2(registryPath, "utf-8"));
|
|
120
|
-
return entries.map((e) => ({
|
|
121
|
-
...e,
|
|
122
|
-
running: e.running ?? false,
|
|
123
|
-
stage: e.stage ?? "sprouted"
|
|
124
|
-
}));
|
|
125
|
-
} catch {
|
|
126
|
-
return [];
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
function voluteHome() {
|
|
130
|
-
if (process.env.VOLUTE_HOME) return process.env.VOLUTE_HOME;
|
|
131
|
-
const dir = dirname(fileURLToPath(import.meta.url));
|
|
132
|
-
if (dir.endsWith("/src/lib")) {
|
|
133
|
-
throw new Error(
|
|
134
|
-
'VOLUTE_HOME must be set when running from source. For tests, run via "npm test" or add "--import ./test/setup.ts".'
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
return resolve2(homedir(), ".volute");
|
|
138
|
-
}
|
|
139
|
-
function ensureVoluteHome() {
|
|
140
|
-
const mindsBase = process.env.VOLUTE_MINDS_DIR ?? resolve2(voluteHome(), "minds");
|
|
141
|
-
mkdirSync2(mindsBase, { recursive: true });
|
|
142
|
-
}
|
|
143
|
-
function readRegistry() {
|
|
144
|
-
if (registryCache) return registryCache;
|
|
145
|
-
return readRegistryFromDisk();
|
|
146
|
-
}
|
|
147
|
-
function writeRegistry(entries) {
|
|
148
|
-
if (registryCache) registryCache = entries;
|
|
149
|
-
ensureVoluteHome();
|
|
150
|
-
const registryPath = resolve2(voluteHome(), "minds.json");
|
|
151
|
-
const tmpPath = `${registryPath}.tmp`;
|
|
152
|
-
writeFileSync2(tmpPath, `${JSON.stringify(entries, null, 2)}
|
|
153
|
-
`);
|
|
154
|
-
renameSync(tmpPath, registryPath);
|
|
155
|
-
}
|
|
156
|
-
var MIND_NAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
|
|
157
|
-
var MIND_NAME_MAX = 64;
|
|
158
|
-
function validateMindName(name) {
|
|
159
|
-
if (!name) return "Mind name is required";
|
|
160
|
-
if (name.length > MIND_NAME_MAX) return `Mind name must be at most ${MIND_NAME_MAX} characters`;
|
|
161
|
-
if (!MIND_NAME_RE.test(name)) {
|
|
162
|
-
return "Mind name must start with alphanumeric and contain only alphanumeric, dots, dashes, or underscores";
|
|
163
|
-
}
|
|
164
|
-
return null;
|
|
165
|
-
}
|
|
166
|
-
function addMind(name, port, stage, template) {
|
|
167
|
-
const err = validateMindName(name);
|
|
168
|
-
if (err) throw new Error(err);
|
|
169
|
-
const entries = readRegistry();
|
|
170
|
-
const filtered = entries.filter((e) => e.name !== name);
|
|
171
|
-
filtered.push({ name, port, created: (/* @__PURE__ */ new Date()).toISOString(), running: false, stage, template });
|
|
172
|
-
writeRegistry(filtered);
|
|
173
|
-
}
|
|
174
|
-
function removeMind(name) {
|
|
175
|
-
const entries = readRegistry();
|
|
176
|
-
writeRegistry(entries.filter((e) => e.name !== name));
|
|
177
|
-
}
|
|
178
|
-
function setMindRunning(name, running) {
|
|
179
|
-
const entries = readRegistry();
|
|
180
|
-
const entry = entries.find((e) => e.name === name);
|
|
181
|
-
if (entry) {
|
|
182
|
-
entry.running = running;
|
|
183
|
-
writeRegistry(entries);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
function setMindStage(name, stage) {
|
|
187
|
-
const entries = readRegistry();
|
|
188
|
-
const entry = entries.find((e) => e.name === name);
|
|
189
|
-
if (entry) {
|
|
190
|
-
entry.stage = stage;
|
|
191
|
-
writeRegistry(entries);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
function setMindTemplateHash(name, hash) {
|
|
195
|
-
const entries = readRegistry();
|
|
196
|
-
const entry = entries.find((e) => e.name === name);
|
|
197
|
-
if (entry) {
|
|
198
|
-
entry.templateHash = hash;
|
|
199
|
-
writeRegistry(entries);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
function findMind(name) {
|
|
203
|
-
return readRegistry().find((e) => e.name === name);
|
|
204
|
-
}
|
|
205
|
-
function mindDir(name) {
|
|
206
|
-
if (process.env.VOLUTE_MINDS_DIR) {
|
|
207
|
-
return resolve2(process.env.VOLUTE_MINDS_DIR, name);
|
|
208
|
-
}
|
|
209
|
-
return resolve2(voluteHome(), "minds", name);
|
|
210
|
-
}
|
|
211
|
-
function stateDir(name) {
|
|
212
|
-
return resolve2(voluteHome(), "state", name);
|
|
213
|
-
}
|
|
214
|
-
function nextPort() {
|
|
215
|
-
const entries = readRegistry();
|
|
216
|
-
const usedPorts = new Set(entries.map((e) => e.port));
|
|
217
|
-
for (const entry of entries) {
|
|
218
|
-
for (const v of readVariants(entry.name)) {
|
|
219
|
-
if (v.port) usedPorts.add(v.port);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
const basePort = parseInt(process.env.VOLUTE_BASE_PORT || "4100", 10);
|
|
223
|
-
let port = basePort;
|
|
224
|
-
while (usedPorts.has(port)) port++;
|
|
225
|
-
if (port > 65535) throw new Error("No available ports \u2014 all ports 4100-65535 are allocated");
|
|
226
|
-
return port;
|
|
227
|
-
}
|
|
228
|
-
function daemonLoopback() {
|
|
229
|
-
const host = process.env.VOLUTE_DAEMON_HOSTNAME || "127.0.0.1";
|
|
230
|
-
if (host === "0.0.0.0") return "127.0.0.1";
|
|
231
|
-
if (host === "::") return "[::1]";
|
|
232
|
-
return host;
|
|
233
|
-
}
|
|
234
|
-
function resolveMind(name) {
|
|
235
|
-
const [baseName, variantName] = name.split("@", 2);
|
|
236
|
-
const entry = findMind(baseName);
|
|
237
|
-
if (!entry) {
|
|
238
|
-
throw new Error(`Unknown mind: ${baseName}`);
|
|
239
|
-
}
|
|
240
|
-
const dir = mindDir(baseName);
|
|
241
|
-
if (!existsSync2(dir)) {
|
|
242
|
-
throw new Error(`Mind directory missing: ${dir}`);
|
|
243
|
-
}
|
|
244
|
-
if (variantName) {
|
|
245
|
-
const variant = findVariant(baseName, variantName);
|
|
246
|
-
if (!variant) {
|
|
247
|
-
throw new Error(`Unknown variant: ${variantName} (mind: ${baseName})`);
|
|
248
|
-
}
|
|
249
|
-
return { entry: { ...entry, port: variant.port }, dir: variant.path };
|
|
250
|
-
}
|
|
251
|
-
return { entry, dir };
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
export {
|
|
255
|
-
readVariants,
|
|
256
|
-
writeVariants,
|
|
257
|
-
addVariant,
|
|
258
|
-
removeVariant,
|
|
259
|
-
findVariant,
|
|
260
|
-
setVariantRunning,
|
|
261
|
-
getAllRunningVariants,
|
|
262
|
-
removeAllVariants,
|
|
263
|
-
checkHealth,
|
|
264
|
-
validateBranchName,
|
|
265
|
-
initRegistryCache,
|
|
266
|
-
getRegistryCache,
|
|
267
|
-
voluteHome,
|
|
268
|
-
ensureVoluteHome,
|
|
269
|
-
readRegistry,
|
|
270
|
-
writeRegistry,
|
|
271
|
-
validateMindName,
|
|
272
|
-
addMind,
|
|
273
|
-
removeMind,
|
|
274
|
-
setMindRunning,
|
|
275
|
-
setMindStage,
|
|
276
|
-
setMindTemplateHash,
|
|
277
|
-
findMind,
|
|
278
|
-
mindDir,
|
|
279
|
-
stateDir,
|
|
280
|
-
nextPort,
|
|
281
|
-
daemonLoopback,
|
|
282
|
-
resolveMind
|
|
283
|
-
};
|
package/dist/chunk-SIAG3QMM.js
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// src/lib/volute-config.ts
|
|
4
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
5
|
-
import { dirname, resolve } from "path";
|
|
6
|
-
function readJson(path) {
|
|
7
|
-
if (!existsSync(path)) return null;
|
|
8
|
-
try {
|
|
9
|
-
return JSON.parse(readFileSync(path, "utf-8"));
|
|
10
|
-
} catch (err) {
|
|
11
|
-
console.error(`[volute-config] failed to parse ${path}: ${err}`);
|
|
12
|
-
return null;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
function readVoluteConfig(mindDir) {
|
|
16
|
-
const path = resolve(mindDir, "home/.config/volute.json");
|
|
17
|
-
const config = readJson(path);
|
|
18
|
-
if (!config) return null;
|
|
19
|
-
const legacy = config;
|
|
20
|
-
if (!config.profile && ("displayName" in config || "description" in config || "avatar" in config)) {
|
|
21
|
-
config.profile = {
|
|
22
|
-
displayName: legacy.displayName,
|
|
23
|
-
description: legacy.description,
|
|
24
|
-
avatar: legacy.avatar
|
|
25
|
-
};
|
|
26
|
-
delete legacy.displayName;
|
|
27
|
-
delete legacy.description;
|
|
28
|
-
delete legacy.avatar;
|
|
29
|
-
}
|
|
30
|
-
return config;
|
|
31
|
-
}
|
|
32
|
-
function writeVoluteConfig(mindDir, config) {
|
|
33
|
-
const path = resolve(mindDir, "home/.config/volute.json");
|
|
34
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
35
|
-
writeFileSync(path, `${JSON.stringify(config, null, 2)}
|
|
36
|
-
`);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export {
|
|
40
|
-
readVoluteConfig,
|
|
41
|
-
writeVoluteConfig
|
|
42
|
-
};
|