@serfab/cadre-host 0.7.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/README.md +101 -0
- package/dist/auth/duration.d.ts +1 -0
- package/dist/auth/duration.js +32 -0
- package/dist/auth/duration.js.map +1 -0
- package/dist/auth/index.d.ts +9 -0
- package/dist/auth/index.js +8 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/trust-circle-store.d.ts +26 -0
- package/dist/auth/trust-circle-store.js +129 -0
- package/dist/auth/trust-circle-store.js.map +1 -0
- package/dist/auth/trust-circle.d.ts +91 -0
- package/dist/auth/trust-circle.js +230 -0
- package/dist/auth/trust-circle.js.map +1 -0
- package/dist/auth/types.d.ts +81 -0
- package/dist/auth/types.js +24 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/bin/host.d.ts +13 -0
- package/dist/bin/host.js +826 -0
- package/dist/bin/host.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/installer/browser.d.ts +14 -0
- package/dist/installer/browser.js +35 -0
- package/dist/installer/browser.js.map +1 -0
- package/dist/installer/config.d.ts +43 -0
- package/dist/installer/config.js +113 -0
- package/dist/installer/config.js.map +1 -0
- package/dist/installer/identity.d.ts +23 -0
- package/dist/installer/identity.js +36 -0
- package/dist/installer/identity.js.map +1 -0
- package/dist/installer/index.d.ts +83 -0
- package/dist/installer/index.js +204 -0
- package/dist/installer/index.js.map +1 -0
- package/dist/installer/paths.d.ts +33 -0
- package/dist/installer/paths.js +84 -0
- package/dist/installer/paths.js.map +1 -0
- package/dist/installer/platform.d.ts +9 -0
- package/dist/installer/platform.js +21 -0
- package/dist/installer/platform.js.map +1 -0
- package/dist/installer/service-host/index.d.ts +11 -0
- package/dist/installer/service-host/index.js +16 -0
- package/dist/installer/service-host/index.js.map +1 -0
- package/dist/installer/service-host/launchd.d.ts +16 -0
- package/dist/installer/service-host/launchd.js +79 -0
- package/dist/installer/service-host/launchd.js.map +1 -0
- package/dist/installer/service-host/nssm.d.ts +20 -0
- package/dist/installer/service-host/nssm.js +88 -0
- package/dist/installer/service-host/nssm.js.map +1 -0
- package/dist/installer/service-host/systemd.d.ts +16 -0
- package/dist/installer/service-host/systemd.js +78 -0
- package/dist/installer/service-host/systemd.js.map +1 -0
- package/dist/installer/service-host/template.d.ts +6 -0
- package/dist/installer/service-host/template.js +20 -0
- package/dist/installer/service-host/template.js.map +1 -0
- package/dist/installer/service-host/types.d.ts +40 -0
- package/dist/installer/service-host/types.js +6 -0
- package/dist/installer/service-host/types.js.map +1 -0
- package/dist/installer/wizard.d.ts +31 -0
- package/dist/installer/wizard.js +67 -0
- package/dist/installer/wizard.js.map +1 -0
- package/dist/nat/address-resolver.d.ts +22 -0
- package/dist/nat/address-resolver.js +31 -0
- package/dist/nat/address-resolver.js.map +1 -0
- package/dist/nat/ddns/duckdns.d.ts +12 -0
- package/dist/nat/ddns/duckdns.js +57 -0
- package/dist/nat/ddns/duckdns.js.map +1 -0
- package/dist/nat/ddns/index.d.ts +30 -0
- package/dist/nat/ddns/index.js +21 -0
- package/dist/nat/ddns/index.js.map +1 -0
- package/dist/nat/ddns/updater.d.ts +68 -0
- package/dist/nat/ddns/updater.js +194 -0
- package/dist/nat/ddns/updater.js.map +1 -0
- package/dist/nat/external-ip.d.ts +62 -0
- package/dist/nat/external-ip.js +114 -0
- package/dist/nat/external-ip.js.map +1 -0
- package/dist/nat/index.d.ts +22 -0
- package/dist/nat/index.js +16 -0
- package/dist/nat/index.js.map +1 -0
- package/dist/nat/nat-service.d.ts +101 -0
- package/dist/nat/nat-service.js +284 -0
- package/dist/nat/nat-service.js.map +1 -0
- package/dist/nat/nat-store.d.ts +19 -0
- package/dist/nat/nat-store.js +136 -0
- package/dist/nat/nat-store.js.map +1 -0
- package/dist/nat/port-mapper.d.ts +98 -0
- package/dist/nat/port-mapper.js +226 -0
- package/dist/nat/port-mapper.js.map +1 -0
- package/dist/nat/reachability.d.ts +19 -0
- package/dist/nat/reachability.js +29 -0
- package/dist/nat/reachability.js.map +1 -0
- package/dist/nat/secrets/file-store.d.ts +23 -0
- package/dist/nat/secrets/file-store.js +102 -0
- package/dist/nat/secrets/file-store.js.map +1 -0
- package/dist/nat/secrets/index.d.ts +36 -0
- package/dist/nat/secrets/index.js +56 -0
- package/dist/nat/secrets/index.js.map +1 -0
- package/dist/nat/secrets/keytar-store.d.ts +18 -0
- package/dist/nat/secrets/keytar-store.js +52 -0
- package/dist/nat/secrets/keytar-store.js.map +1 -0
- package/dist/nat/types.d.ts +120 -0
- package/dist/nat/types.js +24 -0
- package/dist/nat/types.js.map +1 -0
- package/dist/orchestrator/host-process-orchestrator.d.ts +87 -0
- package/dist/orchestrator/host-process-orchestrator.js +557 -0
- package/dist/orchestrator/host-process-orchestrator.js.map +1 -0
- package/dist/orchestrator/index.d.ts +3 -0
- package/dist/orchestrator/index.js +2 -0
- package/dist/orchestrator/index.js.map +1 -0
- package/dist/orchestrator/log-rotator.d.ts +25 -0
- package/dist/orchestrator/log-rotator.js +56 -0
- package/dist/orchestrator/log-rotator.js.map +1 -0
- package/dist/orchestrator/pid-liveness.d.ts +11 -0
- package/dist/orchestrator/pid-liveness.js +26 -0
- package/dist/orchestrator/pid-liveness.js.map +1 -0
- package/dist/orchestrator/port-allocator.d.ts +16 -0
- package/dist/orchestrator/port-allocator.js +38 -0
- package/dist/orchestrator/port-allocator.js.map +1 -0
- package/dist/orchestrator/state-store.d.ts +32 -0
- package/dist/orchestrator/state-store.js +44 -0
- package/dist/orchestrator/state-store.js.map +1 -0
- package/dist/orchestrator/types.d.ts +85 -0
- package/dist/orchestrator/types.js +31 -0
- package/dist/orchestrator/types.js.map +1 -0
- package/dist/server/error-handler.d.ts +26 -0
- package/dist/server/error-handler.js +95 -0
- package/dist/server/error-handler.js.map +1 -0
- package/dist/server/events/bus.d.ts +17 -0
- package/dist/server/events/bus.js +36 -0
- package/dist/server/events/bus.js.map +1 -0
- package/dist/server/events/sse-route.d.ts +22 -0
- package/dist/server/events/sse-route.js +65 -0
- package/dist/server/events/sse-route.js.map +1 -0
- package/dist/server/events/types.d.ts +25 -0
- package/dist/server/events/types.js +8 -0
- package/dist/server/events/types.js.map +1 -0
- package/dist/server/index.d.ts +67 -0
- package/dist/server/index.js +149 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/origin-guard.d.ts +24 -0
- package/dist/server/origin-guard.js +72 -0
- package/dist/server/origin-guard.js.map +1 -0
- package/dist/server/routes/nat.d.ts +16 -0
- package/dist/server/routes/nat.js +42 -0
- package/dist/server/routes/nat.js.map +1 -0
- package/dist/server/routes/nodes.d.ts +23 -0
- package/dist/server/routes/nodes.js +128 -0
- package/dist/server/routes/nodes.js.map +1 -0
- package/dist/server/routes/settings.d.ts +26 -0
- package/dist/server/routes/settings.js +97 -0
- package/dist/server/routes/settings.js.map +1 -0
- package/dist/server/routes/status.d.ts +42 -0
- package/dist/server/routes/status.js +76 -0
- package/dist/server/routes/status.js.map +1 -0
- package/dist/server/routes/trust-circle.d.ts +17 -0
- package/dist/server/routes/trust-circle.js +35 -0
- package/dist/server/routes/trust-circle.js.map +1 -0
- package/dist/server/routes/update.d.ts +16 -0
- package/dist/server/routes/update.js +29 -0
- package/dist/server/routes/update.js.map +1 -0
- package/dist/server/server.d.ts +40 -0
- package/dist/server/server.js +90 -0
- package/dist/server/server.js.map +1 -0
- package/dist/server/settings-store.d.ts +24 -0
- package/dist/server/settings-store.js +27 -0
- package/dist/server/settings-store.js.map +1 -0
- package/dist/server/static.d.ts +25 -0
- package/dist/server/static.js +177 -0
- package/dist/server/static.js.map +1 -0
- package/dist/ui/assets/index-BwU5mJGF.css +1 -0
- package/dist/ui/assets/index-C9uChk2P.js +14 -0
- package/dist/ui/assets/index-C9uChk2P.js.map +1 -0
- package/dist/ui/index.html +14 -0
- package/dist/update/apply.d.ts +37 -0
- package/dist/update/apply.js +102 -0
- package/dist/update/apply.js.map +1 -0
- package/dist/update/index.d.ts +101 -0
- package/dist/update/index.js +228 -0
- package/dist/update/index.js.map +1 -0
- package/dist/update/manifest.d.ts +48 -0
- package/dist/update/manifest.js +152 -0
- package/dist/update/manifest.js.map +1 -0
- package/dist/update/release-key.d.ts +24 -0
- package/dist/update/release-key.js +52 -0
- package/dist/update/release-key.js.map +1 -0
- package/dist/update/store.d.ts +22 -0
- package/dist/update/store.js +81 -0
- package/dist/update/store.js.map +1 -0
- package/dist/update/types.d.ts +90 -0
- package/dist/update/types.js +22 -0
- package/dist/update/types.js.map +1 -0
- package/dist/update/version.d.ts +18 -0
- package/dist/update/version.js +76 -0
- package/dist/update/version.js.map +1 -0
- package/package.json +80 -0
- package/service/README.md +86 -0
- package/service/cadre-host.service.tmpl +16 -0
- package/service/com.serfab.cadre-host.plist.tmpl +35 -0
- package/service/install-service.ps1 +98 -0
- package/service/uninstall-service.ps1 +19 -0
- package/src/__tests__/cli-invite.smoke.test.ts +187 -0
- package/src/__tests__/cli-nat.smoke.test.ts +207 -0
- package/src/__tests__/cli.smoke.test.ts +41 -0
- package/src/__tests__/orchestrator.test.ts +570 -0
- package/src/auth/__tests__/duration.test.ts +28 -0
- package/src/auth/__tests__/trust-circle-integration.test.ts +128 -0
- package/src/auth/__tests__/trust-circle-store.test.ts +115 -0
- package/src/auth/__tests__/trust-circle.test.ts +334 -0
- package/src/auth/duration.ts +33 -0
- package/src/auth/index.ts +19 -0
- package/src/auth/trust-circle-store.ts +155 -0
- package/src/auth/trust-circle.ts +304 -0
- package/src/auth/types.ts +96 -0
- package/src/bin/host.ts +905 -0
- package/src/index.ts +167 -0
- package/src/installer/__tests__/config.test.ts +86 -0
- package/src/installer/__tests__/identity.test.ts +34 -0
- package/src/installer/__tests__/installer.smoke.test.ts +124 -0
- package/src/installer/__tests__/paths.test.ts +47 -0
- package/src/installer/__tests__/service-host.test.ts +62 -0
- package/src/installer/__tests__/wizard.test.ts +60 -0
- package/src/installer/browser.ts +42 -0
- package/src/installer/config.ts +153 -0
- package/src/installer/identity.ts +49 -0
- package/src/installer/index.ts +303 -0
- package/src/installer/paths.ts +109 -0
- package/src/installer/platform.ts +27 -0
- package/src/installer/service-host/index.ts +21 -0
- package/src/installer/service-host/launchd.ts +90 -0
- package/src/installer/service-host/nssm.ts +100 -0
- package/src/installer/service-host/systemd.ts +88 -0
- package/src/installer/service-host/template.ts +21 -0
- package/src/installer/service-host/types.ts +48 -0
- package/src/installer/wizard.ts +92 -0
- package/src/nat/__tests__/address-resolver.test.ts +82 -0
- package/src/nat/__tests__/ddns-duckdns.test.ts +83 -0
- package/src/nat/__tests__/ddns-updater.test.ts +253 -0
- package/src/nat/__tests__/external-ip.test.ts +124 -0
- package/src/nat/__tests__/nat-service.test.ts +305 -0
- package/src/nat/__tests__/nat-store.test.ts +93 -0
- package/src/nat/__tests__/port-mapper.test.ts +191 -0
- package/src/nat/__tests__/reachability.test.ts +24 -0
- package/src/nat/__tests__/secrets.test.ts +156 -0
- package/src/nat/address-resolver.ts +51 -0
- package/src/nat/ddns/duckdns.ts +76 -0
- package/src/nat/ddns/index.ts +55 -0
- package/src/nat/ddns/updater.ts +243 -0
- package/src/nat/external-ip.ts +154 -0
- package/src/nat/index.ts +71 -0
- package/src/nat/nat-service.ts +375 -0
- package/src/nat/nat-store.ts +154 -0
- package/src/nat/port-mapper.ts +329 -0
- package/src/nat/reachability.ts +34 -0
- package/src/nat/secrets/file-store.ts +130 -0
- package/src/nat/secrets/index.ts +82 -0
- package/src/nat/secrets/keytar-store.ts +67 -0
- package/src/nat/types.ts +157 -0
- package/src/orchestrator/host-process-orchestrator.ts +626 -0
- package/src/orchestrator/index.ts +3 -0
- package/src/orchestrator/log-rotator.ts +53 -0
- package/src/orchestrator/pid-liveness.ts +24 -0
- package/src/orchestrator/port-allocator.ts +43 -0
- package/src/orchestrator/state-store.ts +72 -0
- package/src/orchestrator/types.ts +95 -0
- package/src/server/__tests__/bus.test.ts +38 -0
- package/src/server/__tests__/error-handler.test.ts +96 -0
- package/src/server/__tests__/nodes-route.test.ts +179 -0
- package/src/server/__tests__/origin-guard.test.ts +72 -0
- package/src/server/__tests__/publishers.test.ts +216 -0
- package/src/server/__tests__/server.smoke.test.ts +178 -0
- package/src/server/__tests__/settings-route.test.ts +177 -0
- package/src/server/__tests__/sse-route.test.ts +119 -0
- package/src/server/__tests__/status-route.test.ts +113 -0
- package/src/server/error-handler.ts +108 -0
- package/src/server/events/bus.ts +42 -0
- package/src/server/events/sse-route.ts +81 -0
- package/src/server/events/types.ts +22 -0
- package/src/server/index.ts +209 -0
- package/src/server/origin-guard.ts +84 -0
- package/src/server/routes/nat.ts +63 -0
- package/src/server/routes/nodes.ts +147 -0
- package/src/server/routes/settings.ts +115 -0
- package/src/server/routes/status.ts +108 -0
- package/src/server/routes/trust-circle.ts +49 -0
- package/src/server/routes/update.ts +43 -0
- package/src/server/server.ts +122 -0
- package/src/server/settings-store.ts +37 -0
- package/src/server/static.ts +191 -0
- package/src/update/__tests__/apply.test.ts +101 -0
- package/src/update/__tests__/manifest.test.ts +93 -0
- package/src/update/__tests__/store.test.ts +81 -0
- package/src/update/__tests__/update-service.integration.test.ts +221 -0
- package/src/update/__tests__/version.test.ts +31 -0
- package/src/update/apply.ts +148 -0
- package/src/update/index.ts +303 -0
- package/src/update/manifest.ts +172 -0
- package/src/update/release-key.ts +55 -0
- package/src/update/store.ts +101 -0
- package/src/update/types.ts +109 -0
- package/src/update/version.ts +80 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public installer surface for cadre-host.
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates the install / uninstall flow described in
|
|
5
|
+
* `tickets/implement/6.4.1-cadre-host-installer.md`.
|
|
6
|
+
*
|
|
7
|
+
* The class is purposefully thin — heavy lifting lives in the focused
|
|
8
|
+
* subsystem modules (wizard.ts, identity.ts, config.ts,
|
|
9
|
+
* service-host/{systemd,launchd,nssm}.ts, browser.ts).
|
|
10
|
+
*/
|
|
11
|
+
import { existsSync, mkdirSync, readFileSync, rmSync } from 'node:fs';
|
|
12
|
+
import { randomBytes } from 'node:crypto';
|
|
13
|
+
import debug from 'debug';
|
|
14
|
+
import { NatStore } from '../nat/index.js';
|
|
15
|
+
import { openBrowser } from './browser.js';
|
|
16
|
+
import { writeHostConfig } from './config.js';
|
|
17
|
+
import { generateIdentity } from './identity.js';
|
|
18
|
+
import { configPath, defaultHostJs, defaultServiceDir, identityPath, logsDir, } from './paths.js';
|
|
19
|
+
import { detectPlatform, } from './platform.js';
|
|
20
|
+
import { createServiceHost } from './service-host/index.js';
|
|
21
|
+
import { defaultsForPlatform, runWizard, runWizardWith, DEFAULT_UI_PORT, DEFAULT_LIBP2P_PORT, } from './wizard.js';
|
|
22
|
+
const log = debug('cadre:host:installer');
|
|
23
|
+
export class Installer {
|
|
24
|
+
constructor(opts = {}) {
|
|
25
|
+
this.platform = opts.platform ?? detectPlatform();
|
|
26
|
+
this.installerVersion = opts.installerVersion ?? readPackageVersion();
|
|
27
|
+
}
|
|
28
|
+
async install(opts) {
|
|
29
|
+
if (opts.system) {
|
|
30
|
+
throw new Error('cadre-host install --system is not yet supported. Run without --system ' +
|
|
31
|
+
'for a per-user install; see packages/cadre-host/service/README.md.');
|
|
32
|
+
}
|
|
33
|
+
// 1. Resolve answers — wizard for interactive, defaults+overrides otherwise.
|
|
34
|
+
const answers = await this.resolveAnswers(opts);
|
|
35
|
+
// 2. Make sure the data dir exists.
|
|
36
|
+
mkdirSync(answers.dataDir, { recursive: true });
|
|
37
|
+
mkdirSync(logsDir(answers.dataDir), { recursive: true });
|
|
38
|
+
// 3. Identity + config. Don't overwrite an existing key — re-running
|
|
39
|
+
// install on a populated data dir must preserve the host's peer ID.
|
|
40
|
+
const idPath = identityPath(answers.dataDir);
|
|
41
|
+
if (existsSync(idPath)) {
|
|
42
|
+
log('identity already exists at %s — leaving it', idPath);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
await generateIdentity(idPath);
|
|
46
|
+
}
|
|
47
|
+
const cfg = {
|
|
48
|
+
version: 2,
|
|
49
|
+
installId: randomInstallId(),
|
|
50
|
+
uiPort: answers.uiPort,
|
|
51
|
+
libp2pPort: answers.libp2pPort,
|
|
52
|
+
dataDir: answers.dataDir,
|
|
53
|
+
identityPath: idPath,
|
|
54
|
+
upnpEnabled: answers.upnpEnabled,
|
|
55
|
+
installedAt: new Date().toISOString(),
|
|
56
|
+
installerVersion: this.installerVersion,
|
|
57
|
+
updates: { autoApply: false },
|
|
58
|
+
};
|
|
59
|
+
const cfgPath = configPath(answers.dataDir);
|
|
60
|
+
writeHostConfig(cfgPath, cfg);
|
|
61
|
+
// 4. Seed nat.json with the chosen libp2p port + UPnP flag so the
|
|
62
|
+
// NatService picks them up on first start.
|
|
63
|
+
seedNatSettings(answers.dataDir, answers.libp2pPort, answers.upnpEnabled);
|
|
64
|
+
// 5. Service-host registration.
|
|
65
|
+
const serviceHost = opts.serviceHost ?? createServiceHost(this.platform);
|
|
66
|
+
const ctx = {
|
|
67
|
+
nodePath: opts.nodePath ?? process.execPath,
|
|
68
|
+
hostJs: defaultHostJs(),
|
|
69
|
+
dataDir: answers.dataDir,
|
|
70
|
+
serviceDir: defaultServiceDir(),
|
|
71
|
+
};
|
|
72
|
+
await serviceHost.install(ctx);
|
|
73
|
+
// 6. Browser open (best-effort).
|
|
74
|
+
const uiUrl = `http://127.0.0.1:${answers.uiPort}/`;
|
|
75
|
+
const shouldOpenBrowser = opts.openBrowser !== false && !opts.nonInteractive && process.stdout.isTTY;
|
|
76
|
+
if (shouldOpenBrowser) {
|
|
77
|
+
openBrowser(uiUrl);
|
|
78
|
+
}
|
|
79
|
+
// 7. Enrollment invite (best-effort — the listener may not be up yet).
|
|
80
|
+
let invite;
|
|
81
|
+
if (!opts.noInvite && !opts.nonInteractive) {
|
|
82
|
+
invite = await fetchEnrollmentInvite(answers.uiPort).catch((err) => {
|
|
83
|
+
log('enrollment-invite fetch failed: %s', err.message);
|
|
84
|
+
return undefined;
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
dataDir: answers.dataDir,
|
|
89
|
+
uiUrl,
|
|
90
|
+
serviceName: serviceHost.name,
|
|
91
|
+
configPath: cfgPath,
|
|
92
|
+
...(invite ? { enrollmentInvite: invite } : {}),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
async uninstall(opts) {
|
|
96
|
+
const dataDir = opts.dataDir ?? defaultsForPlatform(this.platform).dataDir;
|
|
97
|
+
const serviceHost = opts.serviceHost ?? createServiceHost(this.platform);
|
|
98
|
+
const ctx = {
|
|
99
|
+
nodePath: process.execPath,
|
|
100
|
+
hostJs: defaultHostJs(),
|
|
101
|
+
dataDir,
|
|
102
|
+
serviceDir: defaultServiceDir(),
|
|
103
|
+
};
|
|
104
|
+
await serviceHost.uninstall(ctx);
|
|
105
|
+
if (opts.removeData) {
|
|
106
|
+
if (existsSync(dataDir)) {
|
|
107
|
+
rmSync(dataDir, { recursive: true, force: true });
|
|
108
|
+
log('removed data dir %s', dataDir);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
async status(opts = {}) {
|
|
113
|
+
const serviceHost = opts.serviceHost ?? createServiceHost(this.platform);
|
|
114
|
+
const ctx = {
|
|
115
|
+
nodePath: process.execPath,
|
|
116
|
+
hostJs: defaultHostJs(),
|
|
117
|
+
dataDir: defaultsForPlatform(this.platform).dataDir,
|
|
118
|
+
serviceDir: defaultServiceDir(),
|
|
119
|
+
};
|
|
120
|
+
return await serviceHost.status(ctx);
|
|
121
|
+
}
|
|
122
|
+
/** Exposed for tests / unattended CI. Resolves the wizard answers without running prompts. */
|
|
123
|
+
resolveAnswersFromOptions(opts) {
|
|
124
|
+
const defaults = defaultsForPlatform(this.platform);
|
|
125
|
+
return {
|
|
126
|
+
dataDir: opts.dataDir ?? defaults.dataDir,
|
|
127
|
+
uiPort: opts.uiPort ?? defaults.uiPort,
|
|
128
|
+
libp2pPort: opts.libp2pPort ?? defaults.libp2pPort,
|
|
129
|
+
upnpEnabled: opts.noUpnp ? false : defaults.upnpEnabled,
|
|
130
|
+
configureDdns: false,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
async resolveAnswers(opts) {
|
|
134
|
+
if (opts.nonInteractive) {
|
|
135
|
+
return this.resolveAnswersFromOptions(opts);
|
|
136
|
+
}
|
|
137
|
+
const defaults = defaultsForPlatform(this.platform);
|
|
138
|
+
const answers = await runWizard(defaults);
|
|
139
|
+
// CLI flags still take precedence over wizard input — useful when the
|
|
140
|
+
// user passes `--ui-port 9000` but accepts defaults at the prompts.
|
|
141
|
+
return {
|
|
142
|
+
dataDir: opts.dataDir ?? answers.dataDir,
|
|
143
|
+
uiPort: opts.uiPort ?? answers.uiPort,
|
|
144
|
+
libp2pPort: opts.libp2pPort ?? answers.libp2pPort,
|
|
145
|
+
upnpEnabled: opts.noUpnp ? false : answers.upnpEnabled,
|
|
146
|
+
configureDdns: answers.configureDdns,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
export { runWizardWith, defaultsForPlatform, DEFAULT_UI_PORT, DEFAULT_LIBP2P_PORT };
|
|
151
|
+
function seedNatSettings(dataDir, libp2pPort, upnpEnabled) {
|
|
152
|
+
const store = new NatStore(dataDir);
|
|
153
|
+
// `update()` merges + persists.
|
|
154
|
+
store.update({
|
|
155
|
+
externalPort: libp2pPort,
|
|
156
|
+
internalPort: libp2pPort,
|
|
157
|
+
upnpEnabled,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
function randomInstallId() {
|
|
161
|
+
return randomBytes(16).toString('hex');
|
|
162
|
+
}
|
|
163
|
+
function readPackageVersion() {
|
|
164
|
+
// Best-effort: read the package.json next to dist/ at runtime.
|
|
165
|
+
try {
|
|
166
|
+
const here = new URL('../../package.json', import.meta.url);
|
|
167
|
+
const raw = readFileSync(here, 'utf8');
|
|
168
|
+
const parsed = JSON.parse(raw);
|
|
169
|
+
return parsed.version ?? '0.0.0-unknown';
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
return '0.0.0-unknown';
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
async function fetchEnrollmentInvite(uiPort) {
|
|
176
|
+
const url = `http://127.0.0.1:${uiPort}/auth/invites`;
|
|
177
|
+
// Node 18+'s fetch has no default timeout. Bound it so a slow / hung
|
|
178
|
+
// local-UI listener can't wedge `cadre-host install` indefinitely.
|
|
179
|
+
const controller = new AbortController();
|
|
180
|
+
const timer = setTimeout(() => controller.abort(), 5000);
|
|
181
|
+
try {
|
|
182
|
+
const response = await fetch(url, {
|
|
183
|
+
method: 'POST',
|
|
184
|
+
headers: { 'content-type': 'application/json' },
|
|
185
|
+
body: JSON.stringify({ label: 'Install — first device', ttlMs: 24 * 60 * 60 * 1000 }),
|
|
186
|
+
signal: controller.signal,
|
|
187
|
+
});
|
|
188
|
+
if (!response.ok)
|
|
189
|
+
return undefined;
|
|
190
|
+
const body = await response.json();
|
|
191
|
+
if (!body.encodedInvite)
|
|
192
|
+
return undefined;
|
|
193
|
+
return {
|
|
194
|
+
encodedInvite: body.encodedInvite,
|
|
195
|
+
...(body.expiresAt ? { expiresAt: body.expiresAt } : {}),
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
finally {
|
|
199
|
+
clearTimeout(timer);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// Exposed for tests that drive the wizard with a custom prompt fn.
|
|
203
|
+
export { runWizard };
|
|
204
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/installer/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-platform default paths for the installer.
|
|
3
|
+
*
|
|
4
|
+
* Linux: XDG_DATA_HOME / ~/.local/share/cadre-host
|
|
5
|
+
* macOS: ~/Library/Application Support/CadreHost
|
|
6
|
+
* Windows: %LocalAppData%\CadreHost
|
|
7
|
+
*
|
|
8
|
+
* The package root (where the installed `dist/bin/host.js` and `service/`
|
|
9
|
+
* directory live) is resolved at runtime relative to this module. The
|
|
10
|
+
* installer needs both: the data dir is per-user state, while the package
|
|
11
|
+
* root is what the service-host unit's `ExecStart` line points at.
|
|
12
|
+
*/
|
|
13
|
+
import type { SupportedPlatform } from './platform.js';
|
|
14
|
+
export interface InstallerEnv {
|
|
15
|
+
HOME?: string;
|
|
16
|
+
XDG_DATA_HOME?: string;
|
|
17
|
+
XDG_CONFIG_HOME?: string;
|
|
18
|
+
LocalAppData?: string;
|
|
19
|
+
}
|
|
20
|
+
/** Default data dir for the given platform. Honors XDG_DATA_HOME / LocalAppData. */
|
|
21
|
+
export declare function defaultDataDir(platform: SupportedPlatform, env?: InstallerEnv, home?: string): string;
|
|
22
|
+
/** Path on disk for the installed cadre-host CLI entrypoint. */
|
|
23
|
+
export declare function defaultHostJs(): string;
|
|
24
|
+
/** Directory containing service templates (cadre-host.service.tmpl etc.). */
|
|
25
|
+
export declare function defaultServiceDir(): string;
|
|
26
|
+
/** Where the wizard writes the JSON config. */
|
|
27
|
+
export declare function configPath(dataDir: string): string;
|
|
28
|
+
/** Where the wizard writes the Ed25519 identity. */
|
|
29
|
+
export declare function identityPath(dataDir: string): string;
|
|
30
|
+
/** Logs directory (used by launchd / NSSM unit templates). */
|
|
31
|
+
export declare function logsDir(dataDir: string): string;
|
|
32
|
+
/** Per-platform install dir for the rendered service unit file. */
|
|
33
|
+
export declare function serviceInstallPath(platform: SupportedPlatform, env?: InstallerEnv, home?: string): string;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-platform default paths for the installer.
|
|
3
|
+
*
|
|
4
|
+
* Linux: XDG_DATA_HOME / ~/.local/share/cadre-host
|
|
5
|
+
* macOS: ~/Library/Application Support/CadreHost
|
|
6
|
+
* Windows: %LocalAppData%\CadreHost
|
|
7
|
+
*
|
|
8
|
+
* The package root (where the installed `dist/bin/host.js` and `service/`
|
|
9
|
+
* directory live) is resolved at runtime relative to this module. The
|
|
10
|
+
* installer needs both: the data dir is per-user state, while the package
|
|
11
|
+
* root is what the service-host unit's `ExecStart` line points at.
|
|
12
|
+
*/
|
|
13
|
+
import { homedir } from 'node:os';
|
|
14
|
+
import { posix, win32, join, resolve, dirname } from 'node:path';
|
|
15
|
+
import { fileURLToPath } from 'node:url';
|
|
16
|
+
/** Use the path joiner the *target* platform would use, not the runtime's. */
|
|
17
|
+
function joinFor(platform, ...parts) {
|
|
18
|
+
return platform === 'win32' ? win32.join(...parts) : posix.join(...parts);
|
|
19
|
+
}
|
|
20
|
+
/** Default data dir for the given platform. Honors XDG_DATA_HOME / LocalAppData. */
|
|
21
|
+
export function defaultDataDir(platform, env = process.env, home = homedir()) {
|
|
22
|
+
switch (platform) {
|
|
23
|
+
case 'linux': {
|
|
24
|
+
const base = env.XDG_DATA_HOME?.trim() || joinFor(platform, env.HOME?.trim() || home, '.local', 'share');
|
|
25
|
+
return joinFor(platform, base, 'cadre-host');
|
|
26
|
+
}
|
|
27
|
+
case 'darwin': {
|
|
28
|
+
return joinFor(platform, env.HOME?.trim() || home, 'Library', 'Application Support', 'CadreHost');
|
|
29
|
+
}
|
|
30
|
+
case 'win32': {
|
|
31
|
+
// %LocalAppData% is the canonical per-user, non-roaming app data dir.
|
|
32
|
+
const base = env.LocalAppData?.trim();
|
|
33
|
+
if (!base) {
|
|
34
|
+
// Fallback: derive from HOME (works under cygwin/MSYS test envs too).
|
|
35
|
+
return joinFor(platform, home, 'AppData', 'Local', 'CadreHost');
|
|
36
|
+
}
|
|
37
|
+
return joinFor(platform, base, 'CadreHost');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/** Path on disk for the installed cadre-host CLI entrypoint. */
|
|
42
|
+
export function defaultHostJs() {
|
|
43
|
+
// dist/installer/paths.js -> ../../dist/bin/host.js
|
|
44
|
+
// src/installer/paths.ts -> ../bin/host.ts (only used by tests; build emits dist)
|
|
45
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
46
|
+
return resolve(here, '..', 'bin', 'host.js');
|
|
47
|
+
}
|
|
48
|
+
/** Directory containing service templates (cadre-host.service.tmpl etc.). */
|
|
49
|
+
export function defaultServiceDir() {
|
|
50
|
+
// dist/installer/paths.js -> ../../service/
|
|
51
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
52
|
+
return resolve(here, '..', '..', 'service');
|
|
53
|
+
}
|
|
54
|
+
/** Where the wizard writes the JSON config. */
|
|
55
|
+
export function configPath(dataDir) {
|
|
56
|
+
return join(dataDir, 'host.config.json');
|
|
57
|
+
}
|
|
58
|
+
/** Where the wizard writes the Ed25519 identity. */
|
|
59
|
+
export function identityPath(dataDir) {
|
|
60
|
+
return join(dataDir, 'identity.key');
|
|
61
|
+
}
|
|
62
|
+
/** Logs directory (used by launchd / NSSM unit templates). */
|
|
63
|
+
export function logsDir(dataDir) {
|
|
64
|
+
return join(dataDir, 'logs');
|
|
65
|
+
}
|
|
66
|
+
/** Per-platform install dir for the rendered service unit file. */
|
|
67
|
+
export function serviceInstallPath(platform, env = process.env, home = homedir()) {
|
|
68
|
+
const hd = env.HOME?.trim() || home;
|
|
69
|
+
switch (platform) {
|
|
70
|
+
case 'linux': {
|
|
71
|
+
const base = env.XDG_CONFIG_HOME?.trim() || joinFor(platform, hd, '.config');
|
|
72
|
+
return joinFor(platform, base, 'systemd', 'user', 'cadre-host.service');
|
|
73
|
+
}
|
|
74
|
+
case 'darwin': {
|
|
75
|
+
return joinFor(platform, hd, 'Library', 'LaunchAgents', 'com.serfab.cadre-host.plist');
|
|
76
|
+
}
|
|
77
|
+
case 'win32': {
|
|
78
|
+
// NSSM stores service config in the registry; this path is unused
|
|
79
|
+
// on Windows but returned for symmetry / logging.
|
|
80
|
+
return 'HKLM\\SYSTEM\\CurrentControlSet\\Services\\CadreHost';
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/installer/paths.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Platform detection helpers for the installer.
|
|
3
|
+
*
|
|
4
|
+
* Kept narrow on purpose — `process.platform` everywhere else in the
|
|
5
|
+
* installer is funneled through here so tests can stub a single point.
|
|
6
|
+
*/
|
|
7
|
+
export type SupportedPlatform = 'linux' | 'darwin' | 'win32';
|
|
8
|
+
export declare function detectPlatform(p?: NodeJS.Platform): SupportedPlatform;
|
|
9
|
+
export declare function isPosix(p: SupportedPlatform): boolean;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Platform detection helpers for the installer.
|
|
3
|
+
*
|
|
4
|
+
* Kept narrow on purpose — `process.platform` everywhere else in the
|
|
5
|
+
* installer is funneled through here so tests can stub a single point.
|
|
6
|
+
*/
|
|
7
|
+
const SUPPORTED = new Set([
|
|
8
|
+
'linux',
|
|
9
|
+
'darwin',
|
|
10
|
+
'win32',
|
|
11
|
+
]);
|
|
12
|
+
export function detectPlatform(p = process.platform) {
|
|
13
|
+
if (!SUPPORTED.has(p)) {
|
|
14
|
+
throw new Error(`Unsupported platform: ${p}. cadre-host install supports linux, darwin, win32.`);
|
|
15
|
+
}
|
|
16
|
+
return p;
|
|
17
|
+
}
|
|
18
|
+
export function isPosix(p) {
|
|
19
|
+
return p === 'linux' || p === 'darwin';
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=platform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform.js","sourceRoot":"","sources":["../../src/installer/platform.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,MAAM,SAAS,GAAiC,IAAI,GAAG,CAAkB;IACvE,OAAO;IACP,QAAQ;IACR,OAAO;CACR,CAAC,CAAC;AAEH,MAAM,UAAU,cAAc,CAAC,IAAqB,OAAO,CAAC,QAAQ;IAClE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CACb,yBAAyB,CAAC,qDAAqD,CAChF,CAAC;IACJ,CAAC;IACD,OAAO,CAAsB,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,CAAoB;IAC1C,OAAO,CAAC,KAAK,OAAO,IAAI,CAAC,KAAK,QAAQ,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pick the right service-host impl for the current platform.
|
|
3
|
+
*/
|
|
4
|
+
import { type SupportedPlatform } from '../platform.js';
|
|
5
|
+
import { SystemdServiceHost } from './systemd.js';
|
|
6
|
+
import { LaunchdServiceHost } from './launchd.js';
|
|
7
|
+
import { NssmServiceHost } from './nssm.js';
|
|
8
|
+
import type { ServiceHost } from './types.js';
|
|
9
|
+
export type { ServiceHost, ServiceHostContext, ServiceHostStatus } from './types.js';
|
|
10
|
+
export declare function createServiceHost(platform?: SupportedPlatform): ServiceHost;
|
|
11
|
+
export { SystemdServiceHost, LaunchdServiceHost, NssmServiceHost };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pick the right service-host impl for the current platform.
|
|
3
|
+
*/
|
|
4
|
+
import { detectPlatform } from '../platform.js';
|
|
5
|
+
import { SystemdServiceHost } from './systemd.js';
|
|
6
|
+
import { LaunchdServiceHost } from './launchd.js';
|
|
7
|
+
import { NssmServiceHost } from './nssm.js';
|
|
8
|
+
export function createServiceHost(platform = detectPlatform()) {
|
|
9
|
+
switch (platform) {
|
|
10
|
+
case 'linux': return new SystemdServiceHost();
|
|
11
|
+
case 'darwin': return new LaunchdServiceHost();
|
|
12
|
+
case 'win32': return new NssmServiceHost();
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export { SystemdServiceHost, LaunchdServiceHost, NssmServiceHost };
|
|
16
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/installer/service-host/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,cAAc,EAA0B,MAAM,gBAAgB,CAAC;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAK5C,MAAM,UAAU,iBAAiB,CAAC,WAA8B,cAAc,EAAE;IAC9E,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,OAAO,CAAC,CAAE,OAAO,IAAI,kBAAkB,EAAE,CAAC;QAC/C,KAAK,QAAQ,CAAC,CAAC,OAAO,IAAI,kBAAkB,EAAE,CAAC;QAC/C,KAAK,OAAO,CAAC,CAAE,OAAO,IAAI,eAAe,EAAE,CAAC;IAC9C,CAAC;AACH,CAAC;AAED,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,eAAe,EAAE,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* launchd LaunchAgent registration.
|
|
3
|
+
*
|
|
4
|
+
* Renders the .plist into `~/Library/LaunchAgents/com.serfab.cadre-host.plist`,
|
|
5
|
+
* `launchctl bootstrap`s it into the gui session, then `kickstart -k` to
|
|
6
|
+
* start (or restart) it. Uninstall uses `bootout` (the modern unload).
|
|
7
|
+
*/
|
|
8
|
+
import type { ServiceHost, ServiceHostContext, ServiceHostStatus } from './types.js';
|
|
9
|
+
export declare class LaunchdServiceHost implements ServiceHost {
|
|
10
|
+
readonly name = "cadre-host";
|
|
11
|
+
renderUnit(ctx: ServiceHostContext): string;
|
|
12
|
+
install(ctx: ServiceHostContext): Promise<void>;
|
|
13
|
+
uninstall(ctx: ServiceHostContext): Promise<void>;
|
|
14
|
+
restart(ctx: ServiceHostContext): Promise<void>;
|
|
15
|
+
status(ctx: ServiceHostContext): Promise<ServiceHostStatus>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* launchd LaunchAgent registration.
|
|
3
|
+
*
|
|
4
|
+
* Renders the .plist into `~/Library/LaunchAgents/com.serfab.cadre-host.plist`,
|
|
5
|
+
* `launchctl bootstrap`s it into the gui session, then `kickstart -k` to
|
|
6
|
+
* start (or restart) it. Uninstall uses `bootout` (the modern unload).
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
9
|
+
import { dirname } from 'node:path';
|
|
10
|
+
import { execFileSync, spawnSync } from 'node:child_process';
|
|
11
|
+
import debug from 'debug';
|
|
12
|
+
import { renderTemplate } from './template.js';
|
|
13
|
+
import { logsDir, serviceInstallPath } from '../paths.js';
|
|
14
|
+
const log = debug('cadre:host:installer:launchd');
|
|
15
|
+
const SERVICE_NAME = 'cadre-host';
|
|
16
|
+
const LABEL = 'com.serfab.cadre-host';
|
|
17
|
+
export class LaunchdServiceHost {
|
|
18
|
+
constructor() {
|
|
19
|
+
this.name = SERVICE_NAME;
|
|
20
|
+
}
|
|
21
|
+
renderUnit(ctx) {
|
|
22
|
+
const tmpl = readFileSync(`${ctx.serviceDir}/com.serfab.cadre-host.plist.tmpl`, 'utf8');
|
|
23
|
+
return renderTemplate(tmpl, {
|
|
24
|
+
NODE_PATH: ctx.nodePath,
|
|
25
|
+
HOST_JS: ctx.hostJs,
|
|
26
|
+
DATA_DIR: ctx.dataDir,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
async install(ctx) {
|
|
30
|
+
const unitPath = serviceInstallPath('darwin');
|
|
31
|
+
const unit = this.renderUnit(ctx);
|
|
32
|
+
mkdirSync(logsDir(ctx.dataDir), { recursive: true });
|
|
33
|
+
mkdirSync(dirname(unitPath), { recursive: true });
|
|
34
|
+
writeFileSync(unitPath, unit, { mode: 0o644 });
|
|
35
|
+
log('wrote %s', unitPath);
|
|
36
|
+
const target = guiTarget();
|
|
37
|
+
// bootout first to make install idempotent. May fail if it wasn't
|
|
38
|
+
// loaded — that's fine.
|
|
39
|
+
spawnSync('launchctl', ['bootout', target, unitPath], { stdio: 'ignore' });
|
|
40
|
+
run('launchctl', ['bootstrap', target, unitPath]);
|
|
41
|
+
run('launchctl', ['kickstart', '-k', `${target}/${LABEL}`]);
|
|
42
|
+
}
|
|
43
|
+
async uninstall(ctx) {
|
|
44
|
+
const unitPath = serviceInstallPath('darwin');
|
|
45
|
+
if (existsSync(unitPath)) {
|
|
46
|
+
const target = guiTarget();
|
|
47
|
+
spawnSync('launchctl', ['bootout', target, unitPath], { stdio: 'inherit' });
|
|
48
|
+
rmSync(unitPath, { force: true });
|
|
49
|
+
log('removed %s', unitPath);
|
|
50
|
+
}
|
|
51
|
+
void ctx;
|
|
52
|
+
}
|
|
53
|
+
async restart(ctx) {
|
|
54
|
+
void ctx;
|
|
55
|
+
const target = guiTarget();
|
|
56
|
+
run('launchctl', ['kickstart', '-k', `${target}/${LABEL}`]);
|
|
57
|
+
}
|
|
58
|
+
async status(ctx) {
|
|
59
|
+
void ctx;
|
|
60
|
+
const unitPath = serviceInstallPath('darwin');
|
|
61
|
+
const installed = existsSync(unitPath);
|
|
62
|
+
const target = guiTarget();
|
|
63
|
+
const result = spawnSync('launchctl', ['print', `${target}/${LABEL}`], { encoding: 'utf8' });
|
|
64
|
+
const running = result.status === 0 && /state\s*=\s*running/i.test(result.stdout);
|
|
65
|
+
return { installed, running };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function guiTarget() {
|
|
69
|
+
return `gui/${process.getuid?.() ?? ''}`;
|
|
70
|
+
}
|
|
71
|
+
function run(cmd, args) {
|
|
72
|
+
try {
|
|
73
|
+
execFileSync(cmd, args, { stdio: 'inherit' });
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
throw new Error(`${cmd} ${args.join(' ')} failed: ${err.message}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=launchd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"launchd.js","sourceRoot":"","sources":["../../../src/installer/service-host/launchd.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NSSM-backed Windows service.
|
|
3
|
+
*
|
|
4
|
+
* Delegates to the bundled PowerShell scripts (`install-service.ps1` /
|
|
5
|
+
* `uninstall-service.ps1`) which encapsulate the NSSM CLI dance and the
|
|
6
|
+
* stdout/stderr log redirection.
|
|
7
|
+
*
|
|
8
|
+
* Bundling NSSM itself with the npm package is out of scope for v1 — the
|
|
9
|
+
* standalone-binary distribution will ship it (see backlog).
|
|
10
|
+
*/
|
|
11
|
+
import type { ServiceHost, ServiceHostContext, ServiceHostStatus } from './types.js';
|
|
12
|
+
export declare class NssmServiceHost implements ServiceHost {
|
|
13
|
+
readonly name = "CadreHost";
|
|
14
|
+
/** No template to render — NSSM stores its config in the registry. */
|
|
15
|
+
renderUnit(ctx: ServiceHostContext): string | null;
|
|
16
|
+
install(ctx: ServiceHostContext): Promise<void>;
|
|
17
|
+
uninstall(ctx: ServiceHostContext): Promise<void>;
|
|
18
|
+
restart(ctx: ServiceHostContext): Promise<void>;
|
|
19
|
+
status(ctx: ServiceHostContext): Promise<ServiceHostStatus>;
|
|
20
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NSSM-backed Windows service.
|
|
3
|
+
*
|
|
4
|
+
* Delegates to the bundled PowerShell scripts (`install-service.ps1` /
|
|
5
|
+
* `uninstall-service.ps1`) which encapsulate the NSSM CLI dance and the
|
|
6
|
+
* stdout/stderr log redirection.
|
|
7
|
+
*
|
|
8
|
+
* Bundling NSSM itself with the npm package is out of scope for v1 — the
|
|
9
|
+
* standalone-binary distribution will ship it (see backlog).
|
|
10
|
+
*/
|
|
11
|
+
import { spawnSync } from 'node:child_process';
|
|
12
|
+
import { existsSync } from 'node:fs';
|
|
13
|
+
import { join } from 'node:path';
|
|
14
|
+
import debug from 'debug';
|
|
15
|
+
const log = debug('cadre:host:installer:nssm');
|
|
16
|
+
const SERVICE_NAME = 'CadreHost';
|
|
17
|
+
export class NssmServiceHost {
|
|
18
|
+
constructor() {
|
|
19
|
+
this.name = SERVICE_NAME;
|
|
20
|
+
}
|
|
21
|
+
/** No template to render — NSSM stores its config in the registry. */
|
|
22
|
+
renderUnit(ctx) {
|
|
23
|
+
void ctx;
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
async install(ctx) {
|
|
27
|
+
requireNssmAvailable();
|
|
28
|
+
const script = join(ctx.serviceDir, 'install-service.ps1');
|
|
29
|
+
if (!existsSync(script)) {
|
|
30
|
+
throw new Error(`install-service.ps1 not found at ${script}`);
|
|
31
|
+
}
|
|
32
|
+
runPwsh([
|
|
33
|
+
'-ExecutionPolicy', 'Bypass',
|
|
34
|
+
'-NoProfile',
|
|
35
|
+
'-File', script,
|
|
36
|
+
'-NodePath', ctx.nodePath,
|
|
37
|
+
'-HostJs', ctx.hostJs,
|
|
38
|
+
'-DataDir', ctx.dataDir,
|
|
39
|
+
]);
|
|
40
|
+
// Start the service after install — install-service.ps1 sets
|
|
41
|
+
// SERVICE_AUTO_START but doesn't run it immediately.
|
|
42
|
+
runPwsh(['-ExecutionPolicy', 'Bypass', '-NoProfile', '-Command', `nssm.exe start ${SERVICE_NAME}`]);
|
|
43
|
+
}
|
|
44
|
+
async uninstall(ctx) {
|
|
45
|
+
requireNssmAvailable();
|
|
46
|
+
const script = join(ctx.serviceDir, 'uninstall-service.ps1');
|
|
47
|
+
if (!existsSync(script)) {
|
|
48
|
+
// Fall back to a direct nssm remove if the script isn't there.
|
|
49
|
+
runPwsh(['-ExecutionPolicy', 'Bypass', '-NoProfile', '-Command', `nssm.exe stop ${SERVICE_NAME} confirm; nssm.exe remove ${SERVICE_NAME} confirm`]);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
runPwsh(['-ExecutionPolicy', 'Bypass', '-NoProfile', '-File', script]);
|
|
53
|
+
}
|
|
54
|
+
async restart(ctx) {
|
|
55
|
+
void ctx;
|
|
56
|
+
requireNssmAvailable();
|
|
57
|
+
runPwsh(['-ExecutionPolicy', 'Bypass', '-NoProfile', '-Command', `nssm.exe restart ${SERVICE_NAME}`]);
|
|
58
|
+
}
|
|
59
|
+
async status(ctx) {
|
|
60
|
+
void ctx;
|
|
61
|
+
const statusResult = spawnSync('nssm.exe', ['status', SERVICE_NAME], { encoding: 'utf8' });
|
|
62
|
+
if (statusResult.error || statusResult.status !== 0) {
|
|
63
|
+
return { installed: false, running: false };
|
|
64
|
+
}
|
|
65
|
+
const stateLine = statusResult.stdout.trim();
|
|
66
|
+
return { installed: true, running: /SERVICE_RUNNING/i.test(stateLine) };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function requireNssmAvailable() {
|
|
70
|
+
const probe = spawnSync('nssm.exe', ['--version'], { encoding: 'utf8' });
|
|
71
|
+
if (probe.error) {
|
|
72
|
+
throw new Error('nssm.exe not found on PATH. Install NSSM from https://nssm.cc/download ' +
|
|
73
|
+
'and add it to PATH, then re-run `cadre-host install`.');
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function runPwsh(args) {
|
|
77
|
+
// powershell.exe is the in-box Windows PowerShell 5.1 — present on every
|
|
78
|
+
// supported Windows version. pwsh.exe (PowerShell 7) is an optional install.
|
|
79
|
+
const result = spawnSync('powershell.exe', args, { stdio: 'inherit' });
|
|
80
|
+
if (result.error) {
|
|
81
|
+
throw new Error(`powershell.exe failed: ${result.error.message}`);
|
|
82
|
+
}
|
|
83
|
+
if (result.status !== 0) {
|
|
84
|
+
log('powershell.exe exit %s for args %j', result.status, args);
|
|
85
|
+
throw new Error(`powershell.exe exited with status ${result.status}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=nssm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nssm.js","sourceRoot":"","sources":["../../../src/installer/service-host/nssm.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,MAAM,GAAG,GAAG,KAAK,CAAC,2BAA2B,CAAC,CAAC;AAE/C,MAAM,YAAY,GAAG,WAAW,CAAC;AAEjC,MAAM,OAAO,eAAe;IAA5B;QACW,SAAI,GAAG,YAAY,CAAC;IAqD/B,CAAC;IAnDC,sEAAsE;IACtE,UAAU,CAAC,GAAuB;QAChC,KAAK,GAAG,CAAC;QACT,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,GAAuB;QACnC,oBAAoB,EAAE,CAAC;QACvB,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,qBAAqB,CAAC,CAAC;QAC3D,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,oCAAoC,MAAM,EAAE,CAAC,CAAC;QAChE,CAAC;QACD,OAAO,CAAC;YACN,kBAAkB,EAAE,QAAQ;YAC5B,YAAY;YACZ,OAAO,EAAE,MAAM;YACf,WAAW,EAAE,GAAG,CAAC,QAAQ;YACzB,SAAS,EAAE,GAAG,CAAC,MAAM;YACrB,UAAU,EAAE,GAAG,CAAC,OAAO;SACxB,CAAC,CAAC;QACH,6DAA6D;QAC7D,qDAAqD;QACrD,OAAO,CAAC,CAAC,kBAAkB,EAAE,QAAQ,EAAE,YAAY,EAAE,UAAU,EAAE,kBAAkB,YAAY,EAAE,CAAC,CAAC,CAAC;IACtG,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,GAAuB;QACrC,oBAAoB,EAAE,CAAC;QACvB,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,uBAAuB,CAAC,CAAC;QAC7D,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YACxB,+DAA+D;YAC/D,OAAO,CAAC,CAAC,kBAAkB,EAAE,QAAQ,EAAE,YAAY,EAAE,UAAU,EAAE,iBAAiB,YAAY,6BAA6B,YAAY,UAAU,CAAC,CAAC,CAAC;YACpJ,OAAO;QACT,CAAC;QACD,OAAO,CAAC,CAAC,kBAAkB,EAAE,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;IACzE,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,GAAuB;QACnC,KAAK,GAAG,CAAC;QACT,oBAAoB,EAAE,CAAC;QACvB,OAAO,CAAC,CAAC,kBAAkB,EAAE,QAAQ,EAAE,YAAY,EAAE,UAAU,EAAE,oBAAoB,YAAY,EAAE,CAAC,CAAC,CAAC;IACxG,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAuB;QAClC,KAAK,GAAG,CAAC;QACT,MAAM,YAAY,GAAG,SAAS,CAAC,UAAU,EAAE,CAAC,QAAQ,EAAE,YAAY,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;QAC3F,IAAI,YAAY,CAAC,KAAK,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACpD,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QAC9C,CAAC;QACD,MAAM,SAAS,GAAG,YAAY,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QAC7C,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;IAC1E,CAAC;CACF;AAED,SAAS,oBAAoB;IAC3B,MAAM,KAAK,GAAG,SAAS,CAAC,UAAU,EAAE,CAAC,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;IACzE,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CACb,yEAAyE;YACvE,uDAAuD,CAC1D,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,OAAO,CAAC,IAAc;IAC7B,yEAAyE;IACzE,6EAA6E;IAC7E,MAAM,MAAM,GAAG,SAAS,CAAC,gBAAgB,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IACvE,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,0BAA0B,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;IACpE,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,GAAG,CAAC,oCAAoC,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC/D,MAAM,IAAI,KAAK,CAAC,qCAAqC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IACxE,CAAC;AACH,CAAC"}
|