@fyresmith/hive-server 4.0.0 → 5.0.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 +20 -17
- package/assets/plugin/hive/main.js +16384 -0
- package/assets/plugin/hive/manifest.json +9 -0
- package/assets/plugin/hive/styles.css +1040 -0
- package/assets/template-vault/.obsidian/app.json +1 -0
- package/assets/template-vault/.obsidian/appearance.json +1 -0
- package/assets/template-vault/.obsidian/core-plugins.json +33 -0
- package/assets/template-vault/.obsidian/graph.json +22 -0
- package/assets/template-vault/.obsidian/workspace.json +206 -0
- package/assets/template-vault/Welcome.md +5 -0
- package/cli/commands/env.js +4 -4
- package/cli/commands/managed.js +22 -17
- package/cli/commands/root.js +48 -2
- package/cli/commands/tunnel.js +1 -16
- package/cli/constants.js +1 -13
- package/cli/core/context.js +3 -22
- package/cli/env-file.js +15 -33
- package/cli/flows/doctor.js +1 -6
- package/cli/flows/setup.js +106 -33
- package/cli/tunnel.js +1 -4
- package/index.js +92 -39
- package/lib/accountState.js +189 -0
- package/lib/authTokens.js +75 -0
- package/lib/bundleBuilder.js +169 -0
- package/lib/dashboardAuth.js +80 -0
- package/lib/managedState.js +262 -55
- package/lib/setupOrchestrator.js +76 -0
- package/lib/yjsServer.js +12 -11
- package/package.json +3 -2
- package/routes/auth.js +403 -78
- package/routes/dashboard.js +590 -0
- package/routes/managed.js +0 -163
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"file-explorer": true,
|
|
3
|
+
"global-search": true,
|
|
4
|
+
"switcher": true,
|
|
5
|
+
"graph": true,
|
|
6
|
+
"backlink": true,
|
|
7
|
+
"canvas": true,
|
|
8
|
+
"outgoing-link": true,
|
|
9
|
+
"tag-pane": true,
|
|
10
|
+
"footnotes": false,
|
|
11
|
+
"properties": true,
|
|
12
|
+
"page-preview": true,
|
|
13
|
+
"daily-notes": true,
|
|
14
|
+
"templates": true,
|
|
15
|
+
"note-composer": true,
|
|
16
|
+
"command-palette": true,
|
|
17
|
+
"slash-command": false,
|
|
18
|
+
"editor-status": true,
|
|
19
|
+
"bookmarks": true,
|
|
20
|
+
"markdown-importer": false,
|
|
21
|
+
"zk-prefixer": false,
|
|
22
|
+
"random-note": false,
|
|
23
|
+
"outline": true,
|
|
24
|
+
"word-count": true,
|
|
25
|
+
"slides": false,
|
|
26
|
+
"audio-recorder": false,
|
|
27
|
+
"workspaces": false,
|
|
28
|
+
"file-recovery": true,
|
|
29
|
+
"publish": false,
|
|
30
|
+
"sync": true,
|
|
31
|
+
"bases": true,
|
|
32
|
+
"webviewer": false
|
|
33
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"collapse-filter": true,
|
|
3
|
+
"search": "",
|
|
4
|
+
"showTags": false,
|
|
5
|
+
"showAttachments": false,
|
|
6
|
+
"hideUnresolved": false,
|
|
7
|
+
"showOrphans": true,
|
|
8
|
+
"collapse-color-groups": true,
|
|
9
|
+
"colorGroups": [],
|
|
10
|
+
"collapse-display": true,
|
|
11
|
+
"showArrow": false,
|
|
12
|
+
"textFadeMultiplier": 0,
|
|
13
|
+
"nodeSizeMultiplier": 1,
|
|
14
|
+
"lineSizeMultiplier": 1,
|
|
15
|
+
"collapse-forces": true,
|
|
16
|
+
"centerStrength": 0.518713248970312,
|
|
17
|
+
"repelStrength": 10,
|
|
18
|
+
"linkStrength": 1,
|
|
19
|
+
"linkDistance": 250,
|
|
20
|
+
"scale": 1,
|
|
21
|
+
"close": true
|
|
22
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
{
|
|
2
|
+
"main": {
|
|
3
|
+
"id": "7b9f3c22c91d897c",
|
|
4
|
+
"type": "split",
|
|
5
|
+
"children": [
|
|
6
|
+
{
|
|
7
|
+
"id": "eea5e6133fb6313d",
|
|
8
|
+
"type": "tabs",
|
|
9
|
+
"children": [
|
|
10
|
+
{
|
|
11
|
+
"id": "20957127bee176ea",
|
|
12
|
+
"type": "leaf",
|
|
13
|
+
"state": {
|
|
14
|
+
"type": "markdown",
|
|
15
|
+
"state": {
|
|
16
|
+
"file": "Welcome.md",
|
|
17
|
+
"mode": "source",
|
|
18
|
+
"source": false
|
|
19
|
+
},
|
|
20
|
+
"icon": "lucide-file",
|
|
21
|
+
"title": "Welcome"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"id": "3606ffb170dd4603",
|
|
28
|
+
"type": "tabs",
|
|
29
|
+
"children": [
|
|
30
|
+
{
|
|
31
|
+
"id": "457fdb78bef02573",
|
|
32
|
+
"type": "leaf",
|
|
33
|
+
"state": {
|
|
34
|
+
"type": "graph",
|
|
35
|
+
"state": {},
|
|
36
|
+
"icon": "lucide-git-fork",
|
|
37
|
+
"title": "Graph view"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
"direction": "vertical"
|
|
44
|
+
},
|
|
45
|
+
"left": {
|
|
46
|
+
"id": "c93fdc6c295009fd",
|
|
47
|
+
"type": "split",
|
|
48
|
+
"children": [
|
|
49
|
+
{
|
|
50
|
+
"id": "0b2bc1cb1a6c5fdb",
|
|
51
|
+
"type": "tabs",
|
|
52
|
+
"children": [
|
|
53
|
+
{
|
|
54
|
+
"id": "332749683c18294c",
|
|
55
|
+
"type": "leaf",
|
|
56
|
+
"state": {
|
|
57
|
+
"type": "file-explorer",
|
|
58
|
+
"state": {
|
|
59
|
+
"sortOrder": "alphabetical",
|
|
60
|
+
"autoReveal": false
|
|
61
|
+
},
|
|
62
|
+
"icon": "lucide-folder-closed",
|
|
63
|
+
"title": "Files"
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"id": "2824dbf53a5c4597",
|
|
68
|
+
"type": "leaf",
|
|
69
|
+
"state": {
|
|
70
|
+
"type": "search",
|
|
71
|
+
"state": {
|
|
72
|
+
"query": "",
|
|
73
|
+
"matchingCase": false,
|
|
74
|
+
"explainSearch": false,
|
|
75
|
+
"collapseAll": false,
|
|
76
|
+
"extraContext": false,
|
|
77
|
+
"sortOrder": "alphabetical"
|
|
78
|
+
},
|
|
79
|
+
"icon": "lucide-search",
|
|
80
|
+
"title": "Search"
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"id": "9f275c920d60b878",
|
|
85
|
+
"type": "leaf",
|
|
86
|
+
"state": {
|
|
87
|
+
"type": "bookmarks",
|
|
88
|
+
"state": {},
|
|
89
|
+
"icon": "lucide-bookmark",
|
|
90
|
+
"title": "Bookmarks"
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
]
|
|
94
|
+
}
|
|
95
|
+
],
|
|
96
|
+
"direction": "horizontal",
|
|
97
|
+
"width": 300
|
|
98
|
+
},
|
|
99
|
+
"right": {
|
|
100
|
+
"id": "720628f43492de54",
|
|
101
|
+
"type": "split",
|
|
102
|
+
"children": [
|
|
103
|
+
{
|
|
104
|
+
"id": "e2e6ee489b84f739",
|
|
105
|
+
"type": "tabs",
|
|
106
|
+
"children": [
|
|
107
|
+
{
|
|
108
|
+
"id": "97a8f2230c937318",
|
|
109
|
+
"type": "leaf",
|
|
110
|
+
"state": {
|
|
111
|
+
"type": "backlink",
|
|
112
|
+
"state": {
|
|
113
|
+
"file": "Welcome.md",
|
|
114
|
+
"collapseAll": false,
|
|
115
|
+
"extraContext": false,
|
|
116
|
+
"sortOrder": "alphabetical",
|
|
117
|
+
"showSearch": false,
|
|
118
|
+
"searchQuery": "",
|
|
119
|
+
"backlinkCollapsed": false,
|
|
120
|
+
"unlinkedCollapsed": true
|
|
121
|
+
},
|
|
122
|
+
"icon": "links-coming-in",
|
|
123
|
+
"title": "Backlinks for Welcome"
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"id": "2a344f218762d53a",
|
|
128
|
+
"type": "leaf",
|
|
129
|
+
"state": {
|
|
130
|
+
"type": "outgoing-link",
|
|
131
|
+
"state": {
|
|
132
|
+
"file": "Welcome.md",
|
|
133
|
+
"linksCollapsed": false,
|
|
134
|
+
"unlinkedCollapsed": true
|
|
135
|
+
},
|
|
136
|
+
"icon": "links-going-out",
|
|
137
|
+
"title": "Outgoing links from Welcome"
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"id": "072e764fb5cc77fa",
|
|
142
|
+
"type": "leaf",
|
|
143
|
+
"state": {
|
|
144
|
+
"type": "tag",
|
|
145
|
+
"state": {
|
|
146
|
+
"sortOrder": "frequency",
|
|
147
|
+
"useHierarchy": true,
|
|
148
|
+
"showSearch": false,
|
|
149
|
+
"searchQuery": ""
|
|
150
|
+
},
|
|
151
|
+
"icon": "lucide-tags",
|
|
152
|
+
"title": "Tags"
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"id": "f304846ef7927a68",
|
|
157
|
+
"type": "leaf",
|
|
158
|
+
"state": {
|
|
159
|
+
"type": "all-properties",
|
|
160
|
+
"state": {
|
|
161
|
+
"sortOrder": "frequency",
|
|
162
|
+
"showSearch": false,
|
|
163
|
+
"searchQuery": ""
|
|
164
|
+
},
|
|
165
|
+
"icon": "lucide-archive",
|
|
166
|
+
"title": "All properties"
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
"id": "de791fb3b1b7afdb",
|
|
171
|
+
"type": "leaf",
|
|
172
|
+
"state": {
|
|
173
|
+
"type": "outline",
|
|
174
|
+
"state": {
|
|
175
|
+
"file": "Welcome.md",
|
|
176
|
+
"followCursor": false,
|
|
177
|
+
"showSearch": false,
|
|
178
|
+
"searchQuery": ""
|
|
179
|
+
},
|
|
180
|
+
"icon": "lucide-list",
|
|
181
|
+
"title": "Outline of Welcome"
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
]
|
|
185
|
+
}
|
|
186
|
+
],
|
|
187
|
+
"direction": "horizontal",
|
|
188
|
+
"width": 300,
|
|
189
|
+
"collapsed": true
|
|
190
|
+
},
|
|
191
|
+
"left-ribbon": {
|
|
192
|
+
"hiddenItems": {
|
|
193
|
+
"switcher:Open quick switcher": false,
|
|
194
|
+
"graph:Open graph view": false,
|
|
195
|
+
"canvas:Create new canvas": false,
|
|
196
|
+
"daily-notes:Open today's daily note": false,
|
|
197
|
+
"templates:Insert template": false,
|
|
198
|
+
"command-palette:Open command palette": false,
|
|
199
|
+
"bases:Create new base": false
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
"active": "20957127bee176ea",
|
|
203
|
+
"lastOpenFiles": [
|
|
204
|
+
"Welcome.md"
|
|
205
|
+
]
|
|
206
|
+
}
|
package/cli/commands/env.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { section, success, fail } from '../output.js';
|
|
2
2
|
import { EXIT } from '../constants.js';
|
|
3
3
|
import { CliError } from '../errors.js';
|
|
4
|
-
import {
|
|
4
|
+
import { loadEnvFile, normalizeEnv, promptForEnv, redactEnv, validateEnvValues } from '../env-file.js';
|
|
5
5
|
import { updateHiveConfig } from '../config.js';
|
|
6
6
|
import { assertEnvFileExists, loadValidatedEnv, resolveContext } from '../core/context.js';
|
|
7
7
|
|
|
@@ -18,13 +18,13 @@ export function registerEnvCommands(program) {
|
|
|
18
18
|
const { config, envFile } = await resolveContext(options);
|
|
19
19
|
const existing = await loadEnvFile(envFile);
|
|
20
20
|
const values = await promptForEnv({ envFile, existing, yes: options.yes });
|
|
21
|
-
const issues = validateEnvValues(values);
|
|
21
|
+
const issues = validateEnvValues(values, { requireVaultPath: false });
|
|
22
22
|
if (issues.length > 0) {
|
|
23
23
|
for (const issue of issues) fail(issue);
|
|
24
24
|
throw new CliError('Env file has validation issues', EXIT.FAIL);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
const domain =
|
|
27
|
+
const domain = config.domain;
|
|
28
28
|
await updateHiveConfig({ envFile, domain });
|
|
29
29
|
success(`Env file ready at ${envFile}`);
|
|
30
30
|
});
|
|
@@ -41,7 +41,7 @@ export function registerEnvCommands(program) {
|
|
|
41
41
|
|
|
42
42
|
const existing = await loadEnvFile(envFile);
|
|
43
43
|
const values = await promptForEnv({ envFile, existing, yes: options.yes });
|
|
44
|
-
const issues = validateEnvValues(values);
|
|
44
|
+
const issues = validateEnvValues(values, { requireVaultPath: false });
|
|
45
45
|
if (issues.length > 0) {
|
|
46
46
|
for (const issue of issues) fail(issue);
|
|
47
47
|
throw new CliError('Env file has validation issues', EXIT.FAIL);
|
package/cli/commands/managed.js
CHANGED
|
@@ -19,14 +19,14 @@ async function resolveManagedInputs(options) {
|
|
|
19
19
|
}
|
|
20
20
|
return {
|
|
21
21
|
vaultPath: env.VAULT_PATH,
|
|
22
|
-
|
|
22
|
+
hiveServerUrl: env.HIVE_SERVER_URL || '',
|
|
23
23
|
envFile,
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
function assertInitialized(state) {
|
|
28
28
|
if (!state) {
|
|
29
|
-
throw new CliError('Managed vault is not initialized. Run
|
|
29
|
+
throw new CliError('Managed vault is not initialized. Run hive setup or dashboard setup first.', EXIT.FAIL);
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
@@ -38,21 +38,19 @@ export function registerManagedCommands(program) {
|
|
|
38
38
|
.description('Show managed vault status')
|
|
39
39
|
.option('--env-file <path>', 'env file path')
|
|
40
40
|
.action(async (options) => {
|
|
41
|
-
const { vaultPath,
|
|
41
|
+
const { vaultPath, envFile } = await resolveManagedInputs(options);
|
|
42
42
|
const state = await loadManagedState(vaultPath);
|
|
43
43
|
section('Managed Status');
|
|
44
44
|
console.log(`Env: ${envFile}`);
|
|
45
45
|
if (!state) {
|
|
46
46
|
console.log('Initialized: no');
|
|
47
|
-
console.log(`Configured owner: ${ownerDiscordId}`);
|
|
48
47
|
return;
|
|
49
48
|
}
|
|
50
|
-
const status = describeManagedStatus(state,
|
|
49
|
+
const status = describeManagedStatus(state, state.ownerId);
|
|
51
50
|
console.log('Initialized: yes');
|
|
51
|
+
console.log(`Vault Name: ${state.vaultName ?? '(not set)'}`);
|
|
52
52
|
console.log(`Vault ID: ${status.vaultId}`);
|
|
53
|
-
console.log(`Owner: ${state.
|
|
54
|
-
console.log(`Configured owner: ${ownerDiscordId}`);
|
|
55
|
-
console.log(`Owner matches env: ${state.ownerDiscordId === ownerDiscordId ? 'yes' : 'no'}`);
|
|
53
|
+
console.log(`Owner: ${state.ownerId}`);
|
|
56
54
|
console.log(`Members: ${status.memberCount}`);
|
|
57
55
|
console.log(`Invites: ${Object.keys(state.invites ?? {}).length}`);
|
|
58
56
|
});
|
|
@@ -64,13 +62,20 @@ export function registerManagedCommands(program) {
|
|
|
64
62
|
.description('Create a single-use invite code')
|
|
65
63
|
.option('--env-file <path>', 'env file path')
|
|
66
64
|
.action(async (options) => {
|
|
67
|
-
const { vaultPath,
|
|
65
|
+
const { vaultPath, hiveServerUrl } = await resolveManagedInputs(options);
|
|
66
|
+
const state = await loadManagedState(vaultPath);
|
|
67
|
+
if (!state) {
|
|
68
|
+
throw new CliError('Managed vault is not initialized. Run hive setup or dashboard setup.', EXIT.FAIL);
|
|
69
|
+
}
|
|
68
70
|
const created = await createInvite({
|
|
69
71
|
vaultPath,
|
|
70
|
-
|
|
71
|
-
createdBy: ownerDiscordId,
|
|
72
|
+
createdBy: state.ownerId,
|
|
72
73
|
});
|
|
73
74
|
success(`Invite created: ${created.code}`);
|
|
75
|
+
if (hiveServerUrl) {
|
|
76
|
+
console.log(`Claim URL: ${hiveServerUrl}/auth/claim?code=${created.code}`);
|
|
77
|
+
console.log('Next: recipient opens claim URL, signs in, then downloads the Hive vault shell.');
|
|
78
|
+
}
|
|
74
79
|
});
|
|
75
80
|
|
|
76
81
|
invite
|
|
@@ -122,21 +127,21 @@ export function registerManagedCommands(program) {
|
|
|
122
127
|
return;
|
|
123
128
|
}
|
|
124
129
|
for (const row of members) {
|
|
125
|
-
const ownerMark = row.id === state.
|
|
130
|
+
const ownerMark = row.id === state.ownerId ? ' (owner)' : '';
|
|
126
131
|
console.log(`${row.id}${ownerMark} @${row.username} added ${row.addedAt}`);
|
|
127
132
|
}
|
|
128
133
|
});
|
|
129
134
|
|
|
130
135
|
member
|
|
131
|
-
.command('remove <
|
|
136
|
+
.command('remove <userId>')
|
|
132
137
|
.description('Remove a paired member')
|
|
133
138
|
.option('--env-file <path>', 'env file path')
|
|
134
|
-
.action(async (
|
|
139
|
+
.action(async (userId, options) => {
|
|
135
140
|
const { vaultPath } = await resolveManagedInputs(options);
|
|
136
|
-
const result = await removeMember({ vaultPath,
|
|
141
|
+
const result = await removeMember({ vaultPath, userId });
|
|
137
142
|
if (!result.removed) {
|
|
138
|
-
throw new CliError(`Member not found: ${
|
|
143
|
+
throw new CliError(`Member not found: ${userId}`, EXIT.FAIL);
|
|
139
144
|
}
|
|
140
|
-
success(`Removed member: ${
|
|
145
|
+
success(`Removed member: ${userId}`);
|
|
141
146
|
});
|
|
142
147
|
}
|
package/cli/commands/root.js
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import { existsSync } from 'fs';
|
|
2
|
+
import { platform } from 'os';
|
|
2
3
|
import { HIVE_CONFIG_FILE, EXIT } from '../constants.js';
|
|
3
4
|
import { CliError } from '../errors.js';
|
|
4
5
|
import { loadHiveConfig } from '../config.js';
|
|
5
|
-
import { section, info } from '../output.js';
|
|
6
|
+
import { section, info, success } from '../output.js';
|
|
6
7
|
import { cloudflaredServiceStatus } from '../tunnel.js';
|
|
7
8
|
import { getHiveServiceStatus } from '../service.js';
|
|
8
|
-
import { resolveContext, resolveServiceConfig } from '../core/context.js';
|
|
9
|
+
import { resolveContext, resolveServiceConfig, loadValidatedEnv } from '../core/context.js';
|
|
10
|
+
import { loadEnvFile, normalizeEnv, writeEnvFile } from '../env-file.js';
|
|
9
11
|
import { runDoctorChecks } from '../flows/doctor.js';
|
|
10
12
|
import { runSetupWizard } from '../flows/setup.js';
|
|
11
13
|
import { runDownFlow, runLogsFlow, runUpFlow, runUpdateFlow } from '../flows/system.js';
|
|
12
14
|
import { startHiveServer } from '../../index.js';
|
|
15
|
+
import { run } from '../exec.js';
|
|
16
|
+
import { randomBytes } from 'crypto';
|
|
13
17
|
|
|
14
18
|
export function registerRootCommands(program) {
|
|
15
19
|
program
|
|
@@ -69,6 +73,48 @@ export function registerRootCommands(program) {
|
|
|
69
73
|
info(`Hive server started using env: ${envFile}`);
|
|
70
74
|
});
|
|
71
75
|
|
|
76
|
+
program
|
|
77
|
+
.command('dashboard')
|
|
78
|
+
.description('Start/open the owner dashboard')
|
|
79
|
+
.option('--env-file <path>', 'env file path')
|
|
80
|
+
.action(async (options) => {
|
|
81
|
+
const { envFile } = await resolveContext(options);
|
|
82
|
+
const { env } = await loadValidatedEnv(envFile, { requireFile: false, requireVaultPath: false });
|
|
83
|
+
|
|
84
|
+
const port = String(env.PORT || '3000').trim();
|
|
85
|
+
const serverUrl = String(env.HIVE_SERVER_URL || '').trim() || `http://localhost:${port}`;
|
|
86
|
+
const dashboardUrl = `${serverUrl}/dashboard`;
|
|
87
|
+
const localUrl = `http://127.0.0.1:${port}`;
|
|
88
|
+
const useLocalRuntime = !String(env.HIVE_SERVER_URL || '').trim();
|
|
89
|
+
|
|
90
|
+
if (useLocalRuntime) {
|
|
91
|
+
if (!String(env.JWT_SECRET || '').trim()) {
|
|
92
|
+
const existing = await loadEnvFile(envFile);
|
|
93
|
+
const next = normalizeEnv(existing);
|
|
94
|
+
next.JWT_SECRET = randomBytes(32).toString('hex');
|
|
95
|
+
await writeEnvFile(envFile, next);
|
|
96
|
+
env.JWT_SECRET = next.JWT_SECRET;
|
|
97
|
+
info(`Generated JWT_SECRET in ${envFile}`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const health = await fetch(`${localUrl}/health`).then((res) => res.ok).catch(() => false);
|
|
101
|
+
if (!health) {
|
|
102
|
+
await startHiveServer({ envFile, quiet: true, allowSetupMode: true });
|
|
103
|
+
info(`Hive server started using env: ${envFile}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
console.log(`Dashboard: ${dashboardUrl}`);
|
|
108
|
+
|
|
109
|
+
const opener = platform() === 'win32' ? 'explorer' : platform() === 'darwin' ? 'open' : 'xdg-open';
|
|
110
|
+
try {
|
|
111
|
+
await run(opener, [dashboardUrl]);
|
|
112
|
+
success('Opened dashboard in browser');
|
|
113
|
+
} catch {
|
|
114
|
+
info('Could not open browser automatically. Visit the URL above.');
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
|
|
72
118
|
program
|
|
73
119
|
.command('status')
|
|
74
120
|
.description('Quick status summary (service + tunnel + doctor-lite)')
|
package/cli/commands/tunnel.js
CHANGED
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
EXIT,
|
|
7
7
|
} from '../constants.js';
|
|
8
8
|
import { CliError } from '../errors.js';
|
|
9
|
-
import { inferDomainFromRedirect } from '../env-file.js';
|
|
10
9
|
import { loadHiveConfig, updateHiveConfig } from '../config.js';
|
|
11
10
|
import { validateDomain } from '../checks.js';
|
|
12
11
|
import { run } from '../exec.js';
|
|
@@ -23,7 +22,6 @@ import {
|
|
|
23
22
|
parseInteger,
|
|
24
23
|
requiredOrFallback,
|
|
25
24
|
resolveContext,
|
|
26
|
-
setRedirectUriForDomain,
|
|
27
25
|
} from '../core/context.js';
|
|
28
26
|
|
|
29
27
|
export function registerTunnelCommands(program) {
|
|
@@ -47,10 +45,7 @@ export function registerTunnelCommands(program) {
|
|
|
47
45
|
throw new CliError('Fix env file first (hive env check)', EXIT.FAIL);
|
|
48
46
|
}
|
|
49
47
|
|
|
50
|
-
const domain = requiredOrFallback(
|
|
51
|
-
options.domain,
|
|
52
|
-
inferDomainFromRedirect(env.DISCORD_REDIRECT_URI) || config.domain
|
|
53
|
-
);
|
|
48
|
+
const domain = requiredOrFallback(options.domain, config.domain);
|
|
54
49
|
if (!validateDomain(domain)) {
|
|
55
50
|
throw new CliError(`Invalid domain: ${domain}`);
|
|
56
51
|
}
|
|
@@ -72,13 +67,6 @@ export function registerTunnelCommands(program) {
|
|
|
72
67
|
installService: Boolean(options.installService),
|
|
73
68
|
});
|
|
74
69
|
|
|
75
|
-
const nextEnv = await setRedirectUriForDomain({
|
|
76
|
-
envFile,
|
|
77
|
-
env,
|
|
78
|
-
domain,
|
|
79
|
-
yes: Boolean(options.yes),
|
|
80
|
-
});
|
|
81
|
-
|
|
82
70
|
await updateHiveConfig({
|
|
83
71
|
envFile,
|
|
84
72
|
domain,
|
|
@@ -88,9 +76,6 @@ export function registerTunnelCommands(program) {
|
|
|
88
76
|
cloudflaredConfigFile,
|
|
89
77
|
});
|
|
90
78
|
|
|
91
|
-
if (nextEnv.DISCORD_REDIRECT_URI !== env.DISCORD_REDIRECT_URI) {
|
|
92
|
-
success('Redirect URI synced for tunnel domain');
|
|
93
|
-
}
|
|
94
79
|
success('Tunnel setup complete');
|
|
95
80
|
});
|
|
96
81
|
|
package/cli/constants.js
CHANGED
|
@@ -1,34 +1,22 @@
|
|
|
1
1
|
import { homedir } from 'os';
|
|
2
|
-
import {
|
|
3
|
-
import { fileURLToPath } from 'url';
|
|
2
|
+
import { join } from 'path';
|
|
4
3
|
|
|
5
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
6
|
-
|
|
7
|
-
export const SERVER_ROOT = join(__dirname, '..');
|
|
8
4
|
export const HIVE_HOME = join(homedir(), '.hive');
|
|
9
5
|
export const HIVE_CONFIG_FILE = join(HIVE_HOME, 'config.json');
|
|
10
6
|
export const DEFAULT_ENV_FILE = join(HIVE_HOME, 'server', '.env');
|
|
11
|
-
export const LEGACY_ENV_FILE = join(SERVER_ROOT, '.env');
|
|
12
7
|
export const DEFAULT_DOMAIN = 'collab.example.com';
|
|
13
8
|
export const DEFAULT_TUNNEL_NAME = 'hive';
|
|
14
9
|
export const DEFAULT_CLOUDFLARED_CONFIG = join(homedir(), '.cloudflared', 'config.yml');
|
|
15
10
|
export const DEFAULT_CLOUDFLARED_CERT = join(homedir(), '.cloudflared', 'cert.pem');
|
|
16
11
|
|
|
17
12
|
export const REQUIRED_ENV_KEYS = [
|
|
18
|
-
'DISCORD_CLIENT_ID',
|
|
19
|
-
'DISCORD_CLIENT_SECRET',
|
|
20
|
-
'DISCORD_REDIRECT_URI',
|
|
21
|
-
'DISCORD_GUILD_ID',
|
|
22
|
-
'OWNER_DISCORD_ID',
|
|
23
13
|
'JWT_SECRET',
|
|
24
14
|
'VAULT_PATH',
|
|
25
15
|
'PORT',
|
|
26
|
-
'YJS_PORT',
|
|
27
16
|
];
|
|
28
17
|
|
|
29
18
|
export const DEFAULT_ENV_VALUES = {
|
|
30
19
|
PORT: '3000',
|
|
31
|
-
YJS_PORT: '3001',
|
|
32
20
|
};
|
|
33
21
|
|
|
34
22
|
export const DEFAULT_CONFIG = {
|
package/cli/core/context.js
CHANGED
|
@@ -6,8 +6,7 @@ import prompts from 'prompts';
|
|
|
6
6
|
import { DEFAULT_ENV_FILE, EXIT } from '../constants.js';
|
|
7
7
|
import { CliError } from '../errors.js';
|
|
8
8
|
import { loadHiveConfig } from '../config.js';
|
|
9
|
-
import { loadEnvFile, normalizeEnv, validateEnvValues
|
|
10
|
-
import { success } from '../output.js';
|
|
9
|
+
import { loadEnvFile, normalizeEnv, validateEnvValues } from '../env-file.js';
|
|
11
10
|
import { getServiceDefaults } from '../service.js';
|
|
12
11
|
|
|
13
12
|
export function parseInteger(value, key) {
|
|
@@ -80,31 +79,13 @@ export function assertEnvFileExists(envFile) {
|
|
|
80
79
|
}
|
|
81
80
|
}
|
|
82
81
|
|
|
83
|
-
export async function loadValidatedEnv(envFile, { requireFile = true } = {}) {
|
|
82
|
+
export async function loadValidatedEnv(envFile, { requireFile = true, requireVaultPath = true } = {}) {
|
|
84
83
|
if (requireFile) {
|
|
85
84
|
assertEnvFileExists(envFile);
|
|
86
85
|
}
|
|
87
86
|
|
|
88
87
|
const raw = await loadEnvFile(envFile);
|
|
89
88
|
const env = normalizeEnv(raw);
|
|
90
|
-
const issues = validateEnvValues(env);
|
|
89
|
+
const issues = validateEnvValues(env, { requireVaultPath });
|
|
91
90
|
return { env, issues };
|
|
92
91
|
}
|
|
93
|
-
|
|
94
|
-
export async function setRedirectUriForDomain({ envFile, env, domain, yes = false }) {
|
|
95
|
-
const expected = `https://${domain}/auth/callback`;
|
|
96
|
-
if (env.DISCORD_REDIRECT_URI === expected) return env;
|
|
97
|
-
|
|
98
|
-
const shouldUpdate = await promptConfirm(
|
|
99
|
-
`Set DISCORD_REDIRECT_URI to ${expected}?`,
|
|
100
|
-
yes,
|
|
101
|
-
true
|
|
102
|
-
);
|
|
103
|
-
|
|
104
|
-
if (!shouldUpdate) return env;
|
|
105
|
-
|
|
106
|
-
const next = { ...env, DISCORD_REDIRECT_URI: expected };
|
|
107
|
-
await writeEnvFile(envFile, next);
|
|
108
|
-
success(`Updated DISCORD_REDIRECT_URI -> ${expected}`);
|
|
109
|
-
return next;
|
|
110
|
-
}
|