@fyresmith/hive-server 2.2.0 → 2.3.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 CHANGED
@@ -127,8 +127,11 @@ Diagnostics:
127
127
  ```bash
128
128
  hive doctor
129
129
  hive status
130
+ hive update
130
131
  ```
131
132
 
133
+ `hive update` installs the latest npm release for the current package and then restarts the Hive OS service and cloudflared service when they are installed.
134
+
132
135
  ## Migration Notes
133
136
 
134
137
  On first `hive setup`, if legacy `server/.env` exists and no `~/.hive/config.json` exists, setup will offer to import legacy env values.
package/cli/main.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import { existsSync } from 'fs';
2
- import { access } from 'fs/promises';
2
+ import { access, readFile } from 'fs/promises';
3
3
  import { constants as fsConstants } from 'fs';
4
+ import { homedir } from 'os';
5
+ import { join } from 'path';
4
6
  import process from 'process';
5
7
  import { Command, CommanderError } from 'commander';
6
8
  import prompts from 'prompts';
@@ -26,11 +28,12 @@ import {
26
28
  writeEnvFile,
27
29
  } from './env-file.js';
28
30
  import { isPortAvailable, pathExists, validateDomain } from './checks.js';
29
- import { run } from './exec.js';
31
+ import { run, runInherit } from './exec.js';
30
32
  import {
31
33
  cloudflaredServiceStatus,
32
34
  installCloudflaredService,
33
35
  runTunnelForeground,
36
+ restartCloudflaredServiceIfInstalled,
34
37
  setupTunnel,
35
38
  tunnelStatus,
36
39
  getCloudflaredPath,
@@ -85,6 +88,54 @@ function resolveServiceConfig(config) {
85
88
  };
86
89
  }
87
90
 
91
+ async function loadPackageMeta() {
92
+ const raw = await readFile(new URL('../package.json', import.meta.url), 'utf-8');
93
+ const parsed = JSON.parse(raw);
94
+ const name = String(parsed?.name ?? '').trim();
95
+ const version = String(parsed?.version ?? '').trim() || 'unknown';
96
+ if (!name) {
97
+ throw new CliError('Could not resolve package name from package.json', EXIT.FAIL);
98
+ }
99
+ return { name, version };
100
+ }
101
+
102
+ function isHiveServiceInstalled({ servicePlatform, serviceName }) {
103
+ if (servicePlatform === 'launchd') {
104
+ return existsSync(join(homedir(), 'Library', 'LaunchAgents', `${serviceName}.plist`));
105
+ }
106
+ return existsSync(`/etc/systemd/system/${serviceName}.service`);
107
+ }
108
+
109
+ async function runUpdateFlow(options = {}) {
110
+ section('Hive Update');
111
+
112
+ const { config } = await resolveContext({});
113
+ const pkg = await loadPackageMeta();
114
+ const packageName = requiredOrFallback(options.package, pkg.name);
115
+ const hiveService = resolveServiceConfig(config);
116
+
117
+ info(`Current CLI version: ${pkg.version}`);
118
+ info(`Updating ${packageName} from npm (latest)`);
119
+ await runInherit('npm', ['install', '-g', `${packageName}@latest`]);
120
+ success(`Installed latest ${packageName}`);
121
+
122
+ if (isHiveServiceInstalled(hiveService)) {
123
+ info(`Restarting Hive service: ${hiveService.serviceName}`);
124
+ await restartHiveService(hiveService);
125
+ success('Hive service restarted');
126
+ } else {
127
+ info(`Hive service not installed: ${hiveService.serviceName}`);
128
+ }
129
+
130
+ info('Restarting cloudflared service if installed');
131
+ const tunnelRestart = await restartCloudflaredServiceIfInstalled();
132
+ if (tunnelRestart.installed) {
133
+ success('cloudflared service restarted');
134
+ } else {
135
+ info('cloudflared service not installed');
136
+ }
137
+ }
138
+
88
139
  async function loadValidatedEnv(envFile, { requireFile = true } = {}) {
89
140
  if (requireFile && !existsSync(envFile)) {
90
141
  throw new CliError(`Env file not found: ${envFile}. Run: hive env init`, EXIT.FAIL);
@@ -645,6 +696,12 @@ function registerRootCommands(program) {
645
696
  .option('--yes', 'non-interactive mode', false)
646
697
  .action(runSetupWizard);
647
698
 
699
+ program
700
+ .command('update')
701
+ .description('Update Hive from npm and restart installed services')
702
+ .option('--package <name>', 'npm package override')
703
+ .action(runUpdateFlow);
704
+
648
705
  program
649
706
  .command('doctor')
650
707
  .description('Run prerequisite and configuration checks')
package/cli/tunnel.js CHANGED
@@ -160,6 +160,44 @@ export async function installCloudflaredService() {
160
160
  await runInherit('sudo', ['cloudflared', 'service', 'install']);
161
161
  }
162
162
 
163
+ function isMissingCloudflaredService(output) {
164
+ const text = String(output ?? '').toLowerCase();
165
+ return (
166
+ text.includes('could not find service')
167
+ || text.includes('service does not exist')
168
+ || text.includes('unit cloudflared.service not found')
169
+ || text.includes('could not be found')
170
+ || text.includes('not loaded')
171
+ || text.includes('no such file or directory')
172
+ );
173
+ }
174
+
175
+ export async function restartCloudflaredServiceIfInstalled() {
176
+ const platform = detectPlatform();
177
+
178
+ try {
179
+ if (platform === 'darwin') {
180
+ await runInherit('sudo', ['launchctl', 'kickstart', '-k', 'system/com.cloudflare.cloudflared']);
181
+ } else {
182
+ await runInherit('sudo', ['systemctl', 'restart', 'cloudflared']);
183
+ }
184
+ return { installed: true, restarted: true };
185
+ } catch (err) {
186
+ const output = [
187
+ err?.stdout,
188
+ err?.stderr,
189
+ err?.shortMessage,
190
+ err?.message,
191
+ ]
192
+ .filter(Boolean)
193
+ .join('\n');
194
+ if (isMissingCloudflaredService(output)) {
195
+ return { installed: false, restarted: false };
196
+ }
197
+ throw err;
198
+ }
199
+ }
200
+
163
201
  export async function cloudflaredServiceStatus() {
164
202
  const platform = detectPlatform();
165
203
  if (platform === 'darwin') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fyresmith/hive-server",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "type": "module",
5
5
  "description": "Collaborative Obsidian vault server",
6
6
  "main": "index.js",
@@ -31,6 +31,7 @@
31
31
  "test": "npm run verify"
32
32
  },
33
33
  "dependencies": {
34
+ "@fyresmith/hive-server": "^2.2.0",
34
35
  "chalk": "^5.6.2",
35
36
  "chokidar": "^3.6.0",
36
37
  "commander": "^13.1.0",