@fyresmith/hive-server 2.3.1 → 2.4.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 +54 -89
- package/cli/commands/env.js +80 -0
- package/cli/commands/root.js +91 -0
- package/cli/commands/service.js +112 -0
- package/cli/commands/tunnel.js +165 -0
- package/cli/core/app.js +57 -0
- package/cli/core/context.js +110 -0
- package/cli/flows/doctor.js +101 -0
- package/cli/flows/setup.js +142 -0
- package/cli/flows/system.js +170 -0
- package/cli/main.js +5 -926
- package/cli/tunnel.js +17 -1
- package/lib/adapterRegistry.js +152 -0
- package/lib/collabProtocol.js +25 -0
- package/lib/collabStore.js +448 -0
- package/lib/discordWebhook.js +81 -0
- package/lib/mentionUtils.js +13 -0
- package/lib/socketHandler.js +891 -38
- package/lib/vaultManager.js +220 -4
- package/lib/yjsServer.js +6 -1
- package/package.json +3 -3
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
function parseWebhookMap(raw) {
|
|
2
|
+
if (!raw) return [];
|
|
3
|
+
try {
|
|
4
|
+
const parsed = JSON.parse(raw);
|
|
5
|
+
if (!parsed || typeof parsed !== 'object') return [];
|
|
6
|
+
return Object.entries(parsed)
|
|
7
|
+
.filter(([prefix, url]) => typeof prefix === 'string' && typeof url === 'string')
|
|
8
|
+
.map(([prefix, url]) => ({
|
|
9
|
+
prefix: prefix.replace(/\\/g, '/').replace(/^\//, ''),
|
|
10
|
+
url,
|
|
11
|
+
}))
|
|
12
|
+
.sort((a, b) => b.prefix.length - a.prefix.length);
|
|
13
|
+
} catch {
|
|
14
|
+
return [];
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const mappedWebhooks = parseWebhookMap(process.env.HIVE_DISCORD_WEBHOOKS_JSON);
|
|
19
|
+
|
|
20
|
+
function resolveWebhook(filePath) {
|
|
21
|
+
const normalized = typeof filePath === 'string' ? filePath.replace(/\\/g, '/') : '';
|
|
22
|
+
|
|
23
|
+
for (const entry of mappedWebhooks) {
|
|
24
|
+
if (!entry.prefix) continue;
|
|
25
|
+
if (normalized === entry.prefix || normalized.startsWith(`${entry.prefix}/`)) {
|
|
26
|
+
return entry.url;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const fallback = process.env.HIVE_DISCORD_WEBHOOK_URL;
|
|
31
|
+
return typeof fallback === 'string' && fallback.length > 0 ? fallback : null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function buildMessage(payload) {
|
|
35
|
+
const kind = payload?.kind === 'task' ? 'Task' : 'Mention';
|
|
36
|
+
const actor = payload?.actor?.username ? `@${payload.actor.username}` : 'Someone';
|
|
37
|
+
const filePath = payload?.filePath ?? 'unknown file';
|
|
38
|
+
const threadId = payload?.threadId ?? 'n/a';
|
|
39
|
+
const target = payload?.targetUser?.username ? `@${payload.targetUser.username}` : 'a collaborator';
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
username: 'Hive',
|
|
43
|
+
content: `**${kind}** for ${target} in \`${filePath}\` by ${actor}`,
|
|
44
|
+
embeds: [
|
|
45
|
+
{
|
|
46
|
+
title: `${kind} Notification`,
|
|
47
|
+
fields: [
|
|
48
|
+
{ name: 'File', value: `\`${filePath}\``, inline: false },
|
|
49
|
+
{ name: 'Thread', value: threadId, inline: true },
|
|
50
|
+
{ name: 'Recipient', value: target, inline: true },
|
|
51
|
+
],
|
|
52
|
+
description: typeof payload?.body === 'string' ? payload.body.slice(0, 500) : '',
|
|
53
|
+
timestamp: new Date(payload?.ts ?? Date.now()).toISOString(),
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export async function sendDiscordWebhook(payload) {
|
|
60
|
+
const webhook = resolveWebhook(payload?.filePath);
|
|
61
|
+
if (!webhook) return false;
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
const res = await fetch(webhook, {
|
|
65
|
+
method: 'POST',
|
|
66
|
+
headers: { 'content-type': 'application/json' },
|
|
67
|
+
body: JSON.stringify(buildMessage(payload)),
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
if (!res.ok) {
|
|
71
|
+
const body = await res.text();
|
|
72
|
+
console.warn(`[notify] Discord webhook failed (${res.status}): ${body}`);
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return true;
|
|
77
|
+
} catch (err) {
|
|
78
|
+
console.warn('[notify] Discord webhook error:', err?.message ?? err);
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const MENTION_RE = /(^|\s)@([a-zA-Z0-9](?:[a-zA-Z0-9_.-]{0,62}[a-zA-Z0-9])?)(?=$|\s|[.,!?;:])/g;
|
|
2
|
+
|
|
3
|
+
export function parseMentions(text) {
|
|
4
|
+
if (typeof text !== 'string' || text.length === 0) return [];
|
|
5
|
+
|
|
6
|
+
const mentions = new Set();
|
|
7
|
+
let match;
|
|
8
|
+
while ((match = MENTION_RE.exec(text)) !== null) {
|
|
9
|
+
mentions.add(match[2].toLowerCase());
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return [...mentions];
|
|
13
|
+
}
|