@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.
Files changed (300) hide show
  1. package/README.md +101 -0
  2. package/dist/auth/duration.d.ts +1 -0
  3. package/dist/auth/duration.js +32 -0
  4. package/dist/auth/duration.js.map +1 -0
  5. package/dist/auth/index.d.ts +9 -0
  6. package/dist/auth/index.js +8 -0
  7. package/dist/auth/index.js.map +1 -0
  8. package/dist/auth/trust-circle-store.d.ts +26 -0
  9. package/dist/auth/trust-circle-store.js +129 -0
  10. package/dist/auth/trust-circle-store.js.map +1 -0
  11. package/dist/auth/trust-circle.d.ts +91 -0
  12. package/dist/auth/trust-circle.js +230 -0
  13. package/dist/auth/trust-circle.js.map +1 -0
  14. package/dist/auth/types.d.ts +81 -0
  15. package/dist/auth/types.js +24 -0
  16. package/dist/auth/types.js.map +1 -0
  17. package/dist/bin/host.d.ts +13 -0
  18. package/dist/bin/host.js +826 -0
  19. package/dist/bin/host.js.map +1 -0
  20. package/dist/index.d.ts +32 -0
  21. package/dist/index.js +27 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/installer/browser.d.ts +14 -0
  24. package/dist/installer/browser.js +35 -0
  25. package/dist/installer/browser.js.map +1 -0
  26. package/dist/installer/config.d.ts +43 -0
  27. package/dist/installer/config.js +113 -0
  28. package/dist/installer/config.js.map +1 -0
  29. package/dist/installer/identity.d.ts +23 -0
  30. package/dist/installer/identity.js +36 -0
  31. package/dist/installer/identity.js.map +1 -0
  32. package/dist/installer/index.d.ts +83 -0
  33. package/dist/installer/index.js +204 -0
  34. package/dist/installer/index.js.map +1 -0
  35. package/dist/installer/paths.d.ts +33 -0
  36. package/dist/installer/paths.js +84 -0
  37. package/dist/installer/paths.js.map +1 -0
  38. package/dist/installer/platform.d.ts +9 -0
  39. package/dist/installer/platform.js +21 -0
  40. package/dist/installer/platform.js.map +1 -0
  41. package/dist/installer/service-host/index.d.ts +11 -0
  42. package/dist/installer/service-host/index.js +16 -0
  43. package/dist/installer/service-host/index.js.map +1 -0
  44. package/dist/installer/service-host/launchd.d.ts +16 -0
  45. package/dist/installer/service-host/launchd.js +79 -0
  46. package/dist/installer/service-host/launchd.js.map +1 -0
  47. package/dist/installer/service-host/nssm.d.ts +20 -0
  48. package/dist/installer/service-host/nssm.js +88 -0
  49. package/dist/installer/service-host/nssm.js.map +1 -0
  50. package/dist/installer/service-host/systemd.d.ts +16 -0
  51. package/dist/installer/service-host/systemd.js +78 -0
  52. package/dist/installer/service-host/systemd.js.map +1 -0
  53. package/dist/installer/service-host/template.d.ts +6 -0
  54. package/dist/installer/service-host/template.js +20 -0
  55. package/dist/installer/service-host/template.js.map +1 -0
  56. package/dist/installer/service-host/types.d.ts +40 -0
  57. package/dist/installer/service-host/types.js +6 -0
  58. package/dist/installer/service-host/types.js.map +1 -0
  59. package/dist/installer/wizard.d.ts +31 -0
  60. package/dist/installer/wizard.js +67 -0
  61. package/dist/installer/wizard.js.map +1 -0
  62. package/dist/nat/address-resolver.d.ts +22 -0
  63. package/dist/nat/address-resolver.js +31 -0
  64. package/dist/nat/address-resolver.js.map +1 -0
  65. package/dist/nat/ddns/duckdns.d.ts +12 -0
  66. package/dist/nat/ddns/duckdns.js +57 -0
  67. package/dist/nat/ddns/duckdns.js.map +1 -0
  68. package/dist/nat/ddns/index.d.ts +30 -0
  69. package/dist/nat/ddns/index.js +21 -0
  70. package/dist/nat/ddns/index.js.map +1 -0
  71. package/dist/nat/ddns/updater.d.ts +68 -0
  72. package/dist/nat/ddns/updater.js +194 -0
  73. package/dist/nat/ddns/updater.js.map +1 -0
  74. package/dist/nat/external-ip.d.ts +62 -0
  75. package/dist/nat/external-ip.js +114 -0
  76. package/dist/nat/external-ip.js.map +1 -0
  77. package/dist/nat/index.d.ts +22 -0
  78. package/dist/nat/index.js +16 -0
  79. package/dist/nat/index.js.map +1 -0
  80. package/dist/nat/nat-service.d.ts +101 -0
  81. package/dist/nat/nat-service.js +284 -0
  82. package/dist/nat/nat-service.js.map +1 -0
  83. package/dist/nat/nat-store.d.ts +19 -0
  84. package/dist/nat/nat-store.js +136 -0
  85. package/dist/nat/nat-store.js.map +1 -0
  86. package/dist/nat/port-mapper.d.ts +98 -0
  87. package/dist/nat/port-mapper.js +226 -0
  88. package/dist/nat/port-mapper.js.map +1 -0
  89. package/dist/nat/reachability.d.ts +19 -0
  90. package/dist/nat/reachability.js +29 -0
  91. package/dist/nat/reachability.js.map +1 -0
  92. package/dist/nat/secrets/file-store.d.ts +23 -0
  93. package/dist/nat/secrets/file-store.js +102 -0
  94. package/dist/nat/secrets/file-store.js.map +1 -0
  95. package/dist/nat/secrets/index.d.ts +36 -0
  96. package/dist/nat/secrets/index.js +56 -0
  97. package/dist/nat/secrets/index.js.map +1 -0
  98. package/dist/nat/secrets/keytar-store.d.ts +18 -0
  99. package/dist/nat/secrets/keytar-store.js +52 -0
  100. package/dist/nat/secrets/keytar-store.js.map +1 -0
  101. package/dist/nat/types.d.ts +120 -0
  102. package/dist/nat/types.js +24 -0
  103. package/dist/nat/types.js.map +1 -0
  104. package/dist/orchestrator/host-process-orchestrator.d.ts +87 -0
  105. package/dist/orchestrator/host-process-orchestrator.js +557 -0
  106. package/dist/orchestrator/host-process-orchestrator.js.map +1 -0
  107. package/dist/orchestrator/index.d.ts +3 -0
  108. package/dist/orchestrator/index.js +2 -0
  109. package/dist/orchestrator/index.js.map +1 -0
  110. package/dist/orchestrator/log-rotator.d.ts +25 -0
  111. package/dist/orchestrator/log-rotator.js +56 -0
  112. package/dist/orchestrator/log-rotator.js.map +1 -0
  113. package/dist/orchestrator/pid-liveness.d.ts +11 -0
  114. package/dist/orchestrator/pid-liveness.js +26 -0
  115. package/dist/orchestrator/pid-liveness.js.map +1 -0
  116. package/dist/orchestrator/port-allocator.d.ts +16 -0
  117. package/dist/orchestrator/port-allocator.js +38 -0
  118. package/dist/orchestrator/port-allocator.js.map +1 -0
  119. package/dist/orchestrator/state-store.d.ts +32 -0
  120. package/dist/orchestrator/state-store.js +44 -0
  121. package/dist/orchestrator/state-store.js.map +1 -0
  122. package/dist/orchestrator/types.d.ts +85 -0
  123. package/dist/orchestrator/types.js +31 -0
  124. package/dist/orchestrator/types.js.map +1 -0
  125. package/dist/server/error-handler.d.ts +26 -0
  126. package/dist/server/error-handler.js +95 -0
  127. package/dist/server/error-handler.js.map +1 -0
  128. package/dist/server/events/bus.d.ts +17 -0
  129. package/dist/server/events/bus.js +36 -0
  130. package/dist/server/events/bus.js.map +1 -0
  131. package/dist/server/events/sse-route.d.ts +22 -0
  132. package/dist/server/events/sse-route.js +65 -0
  133. package/dist/server/events/sse-route.js.map +1 -0
  134. package/dist/server/events/types.d.ts +25 -0
  135. package/dist/server/events/types.js +8 -0
  136. package/dist/server/events/types.js.map +1 -0
  137. package/dist/server/index.d.ts +67 -0
  138. package/dist/server/index.js +149 -0
  139. package/dist/server/index.js.map +1 -0
  140. package/dist/server/origin-guard.d.ts +24 -0
  141. package/dist/server/origin-guard.js +72 -0
  142. package/dist/server/origin-guard.js.map +1 -0
  143. package/dist/server/routes/nat.d.ts +16 -0
  144. package/dist/server/routes/nat.js +42 -0
  145. package/dist/server/routes/nat.js.map +1 -0
  146. package/dist/server/routes/nodes.d.ts +23 -0
  147. package/dist/server/routes/nodes.js +128 -0
  148. package/dist/server/routes/nodes.js.map +1 -0
  149. package/dist/server/routes/settings.d.ts +26 -0
  150. package/dist/server/routes/settings.js +97 -0
  151. package/dist/server/routes/settings.js.map +1 -0
  152. package/dist/server/routes/status.d.ts +42 -0
  153. package/dist/server/routes/status.js +76 -0
  154. package/dist/server/routes/status.js.map +1 -0
  155. package/dist/server/routes/trust-circle.d.ts +17 -0
  156. package/dist/server/routes/trust-circle.js +35 -0
  157. package/dist/server/routes/trust-circle.js.map +1 -0
  158. package/dist/server/routes/update.d.ts +16 -0
  159. package/dist/server/routes/update.js +29 -0
  160. package/dist/server/routes/update.js.map +1 -0
  161. package/dist/server/server.d.ts +40 -0
  162. package/dist/server/server.js +90 -0
  163. package/dist/server/server.js.map +1 -0
  164. package/dist/server/settings-store.d.ts +24 -0
  165. package/dist/server/settings-store.js +27 -0
  166. package/dist/server/settings-store.js.map +1 -0
  167. package/dist/server/static.d.ts +25 -0
  168. package/dist/server/static.js +177 -0
  169. package/dist/server/static.js.map +1 -0
  170. package/dist/ui/assets/index-BwU5mJGF.css +1 -0
  171. package/dist/ui/assets/index-C9uChk2P.js +14 -0
  172. package/dist/ui/assets/index-C9uChk2P.js.map +1 -0
  173. package/dist/ui/index.html +14 -0
  174. package/dist/update/apply.d.ts +37 -0
  175. package/dist/update/apply.js +102 -0
  176. package/dist/update/apply.js.map +1 -0
  177. package/dist/update/index.d.ts +101 -0
  178. package/dist/update/index.js +228 -0
  179. package/dist/update/index.js.map +1 -0
  180. package/dist/update/manifest.d.ts +48 -0
  181. package/dist/update/manifest.js +152 -0
  182. package/dist/update/manifest.js.map +1 -0
  183. package/dist/update/release-key.d.ts +24 -0
  184. package/dist/update/release-key.js +52 -0
  185. package/dist/update/release-key.js.map +1 -0
  186. package/dist/update/store.d.ts +22 -0
  187. package/dist/update/store.js +81 -0
  188. package/dist/update/store.js.map +1 -0
  189. package/dist/update/types.d.ts +90 -0
  190. package/dist/update/types.js +22 -0
  191. package/dist/update/types.js.map +1 -0
  192. package/dist/update/version.d.ts +18 -0
  193. package/dist/update/version.js +76 -0
  194. package/dist/update/version.js.map +1 -0
  195. package/package.json +80 -0
  196. package/service/README.md +86 -0
  197. package/service/cadre-host.service.tmpl +16 -0
  198. package/service/com.serfab.cadre-host.plist.tmpl +35 -0
  199. package/service/install-service.ps1 +98 -0
  200. package/service/uninstall-service.ps1 +19 -0
  201. package/src/__tests__/cli-invite.smoke.test.ts +187 -0
  202. package/src/__tests__/cli-nat.smoke.test.ts +207 -0
  203. package/src/__tests__/cli.smoke.test.ts +41 -0
  204. package/src/__tests__/orchestrator.test.ts +570 -0
  205. package/src/auth/__tests__/duration.test.ts +28 -0
  206. package/src/auth/__tests__/trust-circle-integration.test.ts +128 -0
  207. package/src/auth/__tests__/trust-circle-store.test.ts +115 -0
  208. package/src/auth/__tests__/trust-circle.test.ts +334 -0
  209. package/src/auth/duration.ts +33 -0
  210. package/src/auth/index.ts +19 -0
  211. package/src/auth/trust-circle-store.ts +155 -0
  212. package/src/auth/trust-circle.ts +304 -0
  213. package/src/auth/types.ts +96 -0
  214. package/src/bin/host.ts +905 -0
  215. package/src/index.ts +167 -0
  216. package/src/installer/__tests__/config.test.ts +86 -0
  217. package/src/installer/__tests__/identity.test.ts +34 -0
  218. package/src/installer/__tests__/installer.smoke.test.ts +124 -0
  219. package/src/installer/__tests__/paths.test.ts +47 -0
  220. package/src/installer/__tests__/service-host.test.ts +62 -0
  221. package/src/installer/__tests__/wizard.test.ts +60 -0
  222. package/src/installer/browser.ts +42 -0
  223. package/src/installer/config.ts +153 -0
  224. package/src/installer/identity.ts +49 -0
  225. package/src/installer/index.ts +303 -0
  226. package/src/installer/paths.ts +109 -0
  227. package/src/installer/platform.ts +27 -0
  228. package/src/installer/service-host/index.ts +21 -0
  229. package/src/installer/service-host/launchd.ts +90 -0
  230. package/src/installer/service-host/nssm.ts +100 -0
  231. package/src/installer/service-host/systemd.ts +88 -0
  232. package/src/installer/service-host/template.ts +21 -0
  233. package/src/installer/service-host/types.ts +48 -0
  234. package/src/installer/wizard.ts +92 -0
  235. package/src/nat/__tests__/address-resolver.test.ts +82 -0
  236. package/src/nat/__tests__/ddns-duckdns.test.ts +83 -0
  237. package/src/nat/__tests__/ddns-updater.test.ts +253 -0
  238. package/src/nat/__tests__/external-ip.test.ts +124 -0
  239. package/src/nat/__tests__/nat-service.test.ts +305 -0
  240. package/src/nat/__tests__/nat-store.test.ts +93 -0
  241. package/src/nat/__tests__/port-mapper.test.ts +191 -0
  242. package/src/nat/__tests__/reachability.test.ts +24 -0
  243. package/src/nat/__tests__/secrets.test.ts +156 -0
  244. package/src/nat/address-resolver.ts +51 -0
  245. package/src/nat/ddns/duckdns.ts +76 -0
  246. package/src/nat/ddns/index.ts +55 -0
  247. package/src/nat/ddns/updater.ts +243 -0
  248. package/src/nat/external-ip.ts +154 -0
  249. package/src/nat/index.ts +71 -0
  250. package/src/nat/nat-service.ts +375 -0
  251. package/src/nat/nat-store.ts +154 -0
  252. package/src/nat/port-mapper.ts +329 -0
  253. package/src/nat/reachability.ts +34 -0
  254. package/src/nat/secrets/file-store.ts +130 -0
  255. package/src/nat/secrets/index.ts +82 -0
  256. package/src/nat/secrets/keytar-store.ts +67 -0
  257. package/src/nat/types.ts +157 -0
  258. package/src/orchestrator/host-process-orchestrator.ts +626 -0
  259. package/src/orchestrator/index.ts +3 -0
  260. package/src/orchestrator/log-rotator.ts +53 -0
  261. package/src/orchestrator/pid-liveness.ts +24 -0
  262. package/src/orchestrator/port-allocator.ts +43 -0
  263. package/src/orchestrator/state-store.ts +72 -0
  264. package/src/orchestrator/types.ts +95 -0
  265. package/src/server/__tests__/bus.test.ts +38 -0
  266. package/src/server/__tests__/error-handler.test.ts +96 -0
  267. package/src/server/__tests__/nodes-route.test.ts +179 -0
  268. package/src/server/__tests__/origin-guard.test.ts +72 -0
  269. package/src/server/__tests__/publishers.test.ts +216 -0
  270. package/src/server/__tests__/server.smoke.test.ts +178 -0
  271. package/src/server/__tests__/settings-route.test.ts +177 -0
  272. package/src/server/__tests__/sse-route.test.ts +119 -0
  273. package/src/server/__tests__/status-route.test.ts +113 -0
  274. package/src/server/error-handler.ts +108 -0
  275. package/src/server/events/bus.ts +42 -0
  276. package/src/server/events/sse-route.ts +81 -0
  277. package/src/server/events/types.ts +22 -0
  278. package/src/server/index.ts +209 -0
  279. package/src/server/origin-guard.ts +84 -0
  280. package/src/server/routes/nat.ts +63 -0
  281. package/src/server/routes/nodes.ts +147 -0
  282. package/src/server/routes/settings.ts +115 -0
  283. package/src/server/routes/status.ts +108 -0
  284. package/src/server/routes/trust-circle.ts +49 -0
  285. package/src/server/routes/update.ts +43 -0
  286. package/src/server/server.ts +122 -0
  287. package/src/server/settings-store.ts +37 -0
  288. package/src/server/static.ts +191 -0
  289. package/src/update/__tests__/apply.test.ts +101 -0
  290. package/src/update/__tests__/manifest.test.ts +93 -0
  291. package/src/update/__tests__/store.test.ts +81 -0
  292. package/src/update/__tests__/update-service.integration.test.ts +221 -0
  293. package/src/update/__tests__/version.test.ts +31 -0
  294. package/src/update/apply.ts +148 -0
  295. package/src/update/index.ts +303 -0
  296. package/src/update/manifest.ts +172 -0
  297. package/src/update/release-key.ts +55 -0
  298. package/src/update/store.ts +101 -0
  299. package/src/update/types.ts +109 -0
  300. package/src/update/version.ts +80 -0
@@ -0,0 +1,14 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <meta name="color-scheme" content="light dark" />
7
+ <title>cadre-host</title>
8
+ <script type="module" crossorigin src="./assets/index-C9uChk2P.js"></script>
9
+ <link rel="stylesheet" crossorigin href="./assets/index-BwU5mJGF.css">
10
+ </head>
11
+ <body>
12
+ <div id="app"></div>
13
+ </body>
14
+ </html>
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Apply an update.
3
+ *
4
+ * Apply == reinstall the package globally via `npm install -g <pkg>@<ver>`.
5
+ * Rollback == reinstall the previously-recorded version. Both are dispatched
6
+ * through an injectable `NpmExecutor` so tests don't shell out.
7
+ *
8
+ * Service-host restart is best-effort: the binary swap succeeded even if
9
+ * `systemctl/launchctl/nssm restart` returned non-zero, so the state stays
10
+ * successful and the caller surfaces a warning. (User can restart by hand.)
11
+ */
12
+ import { type UpdateApplyResult } from './types.js';
13
+ export interface NpmResult {
14
+ exitCode: number;
15
+ stdout: string;
16
+ stderr: string;
17
+ }
18
+ /** Pluggable npm runner — replaced in tests. */
19
+ export type NpmExecutor = (args: string[]) => Promise<NpmResult>;
20
+ /** Optional service-host restart hook. Returns undefined on success, an error message on failure. */
21
+ export type ServiceRestarter = () => Promise<string | undefined>;
22
+ export interface ApplyOptions {
23
+ npmPackage: string;
24
+ fromVersion: string;
25
+ toVersion: string;
26
+ npmExecutor?: NpmExecutor;
27
+ restart?: ServiceRestarter;
28
+ timeoutMs?: number;
29
+ }
30
+ /**
31
+ * Drive the install + restart. Throws `UpdateErrorException('apply_failed')`
32
+ * if the install itself fails (after attempting rollback). On install
33
+ * success but restart failure, returns a result with the restart error set.
34
+ */
35
+ export declare function applyUpdate(opts: ApplyOptions): Promise<UpdateApplyResult>;
36
+ /** Default executor: spawn `npm` with the given args. Adds a hard timeout. */
37
+ export declare function defaultNpmExecutor(timeoutMs: number): NpmExecutor;
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Apply an update.
3
+ *
4
+ * Apply == reinstall the package globally via `npm install -g <pkg>@<ver>`.
5
+ * Rollback == reinstall the previously-recorded version. Both are dispatched
6
+ * through an injectable `NpmExecutor` so tests don't shell out.
7
+ *
8
+ * Service-host restart is best-effort: the binary swap succeeded even if
9
+ * `systemctl/launchctl/nssm restart` returned non-zero, so the state stays
10
+ * successful and the caller surfaces a warning. (User can restart by hand.)
11
+ */
12
+ import { spawn } from 'node:child_process';
13
+ import debug from 'debug';
14
+ import { UpdateErrorException, } from './types.js';
15
+ const log = debug('cadre:host:update-apply');
16
+ const DEFAULT_NPM_TIMEOUT_MS = 5 * 60 * 1000;
17
+ /**
18
+ * Drive the install + restart. Throws `UpdateErrorException('apply_failed')`
19
+ * if the install itself fails (after attempting rollback). On install
20
+ * success but restart failure, returns a result with the restart error set.
21
+ */
22
+ export async function applyUpdate(opts) {
23
+ const executor = opts.npmExecutor ?? defaultNpmExecutor(opts.timeoutMs ?? DEFAULT_NPM_TIMEOUT_MS);
24
+ const installArgs = ['install', '-g', `${opts.npmPackage}@${opts.toVersion}`];
25
+ let installResult;
26
+ try {
27
+ installResult = await executor(installArgs);
28
+ }
29
+ catch (err) {
30
+ await tryRollback(opts.npmPackage, opts.fromVersion, executor);
31
+ throw new UpdateErrorException('apply_failed', `npm install threw: ${err.message}`);
32
+ }
33
+ if (installResult.exitCode !== 0) {
34
+ const stderrTail = tail(installResult.stderr || installResult.stdout, 400);
35
+ const rollbackErr = await tryRollback(opts.npmPackage, opts.fromVersion, executor);
36
+ if (rollbackErr) {
37
+ throw new UpdateErrorException('rollback_failed', `npm install failed (exit ${installResult.exitCode}); rollback also failed: ${rollbackErr}. Original error: ${stderrTail}`);
38
+ }
39
+ throw new UpdateErrorException('apply_failed', `npm install failed (exit ${installResult.exitCode}): ${stderrTail}`);
40
+ }
41
+ log('npm install -g %s@%s succeeded', opts.npmPackage, opts.toVersion);
42
+ let restartError;
43
+ if (opts.restart) {
44
+ try {
45
+ restartError = await opts.restart();
46
+ }
47
+ catch (err) {
48
+ restartError = err.message;
49
+ }
50
+ if (restartError) {
51
+ log('service restart failed: %s', restartError);
52
+ }
53
+ }
54
+ return {
55
+ fromVersion: opts.fromVersion,
56
+ toVersion: opts.toVersion,
57
+ restarted: opts.restart !== undefined && restartError === undefined,
58
+ ...(restartError ? { restartError } : {}),
59
+ };
60
+ }
61
+ /** Best-effort rollback. Returns an error string if the rollback itself failed. */
62
+ async function tryRollback(pkg, fromVersion, executor) {
63
+ log('rolling back to %s@%s', pkg, fromVersion);
64
+ try {
65
+ const rollback = await executor(['install', '-g', `${pkg}@${fromVersion}`]);
66
+ if (rollback.exitCode !== 0) {
67
+ return `npm install exit ${rollback.exitCode}: ${tail(rollback.stderr || rollback.stdout, 400)}`;
68
+ }
69
+ return undefined;
70
+ }
71
+ catch (err) {
72
+ return `npm install threw: ${err.message}`;
73
+ }
74
+ }
75
+ /** Default executor: spawn `npm` with the given args. Adds a hard timeout. */
76
+ export function defaultNpmExecutor(timeoutMs) {
77
+ return (args) => new Promise((resolveExec) => {
78
+ // Windows resolves `npm` via `npm.cmd`; using shell:true is the
79
+ // simplest cross-platform answer that still works with PATH lookup.
80
+ const child = spawn('npm', args, { shell: process.platform === 'win32', windowsHide: true });
81
+ let stdout = '';
82
+ let stderr = '';
83
+ child.stdout?.on('data', (chunk) => { stdout += String(chunk); });
84
+ child.stderr?.on('data', (chunk) => { stderr += String(chunk); });
85
+ const timer = setTimeout(() => {
86
+ stderr += `\n[cadre-host] npm install timed out after ${timeoutMs}ms, killing`;
87
+ child.kill('SIGTERM');
88
+ }, timeoutMs);
89
+ child.on('error', (err) => {
90
+ clearTimeout(timer);
91
+ resolveExec({ exitCode: 127, stdout, stderr: stderr + `\nspawn failed: ${err.message}` });
92
+ });
93
+ child.on('close', (code) => {
94
+ clearTimeout(timer);
95
+ resolveExec({ exitCode: code ?? -1, stdout, stderr });
96
+ });
97
+ });
98
+ }
99
+ function tail(s, n) {
100
+ return s.length <= n ? s : '…' + s.slice(-n);
101
+ }
102
+ //# sourceMappingURL=apply.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply.js","sourceRoot":"","sources":["../../src/update/apply.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * UpdateService — the composition root for the cadre-host update flow.
3
+ *
4
+ * Background check cadence: once at construction time (`check()` called
5
+ * explicitly by `cadre-host start` after the management API binds) and a
6
+ * 24-hour interval timer thereafter. Failures are silent — UI consumers see
7
+ * `lastChecked` only when a fetch succeeds.
8
+ *
9
+ * Apply flow: re-fetch + re-verify the manifest (never trust the cached
10
+ * `available`), record `applyInProgress`, run `npm install -g`, restart the
11
+ * service via the optional `ServiceRestarter`, and roll back on failure.
12
+ */
13
+ import { type NpmExecutor, type ServiceRestarter } from './apply.js';
14
+ import { type UpdateApplyResult, type UpdateSettings, type UpdateState } from './types.js';
15
+ export interface UpdateServiceOptions {
16
+ /** cadre-host data directory; UpdateState file lives here. */
17
+ dataDir: string;
18
+ /** Currently-running package version (from package.json). */
19
+ currentVersion: string;
20
+ /** Manifest URL. Defaults: env CADRE_HOST_UPDATE_MANIFEST_URL → DEFAULT_MANIFEST_URL. */
21
+ manifestUrl?: string;
22
+ /** Fetcher (test injection). */
23
+ fetcher?: typeof fetch;
24
+ /** npm runner (test injection). */
25
+ npmExecutor?: NpmExecutor;
26
+ /** Service-host restart hook (test injection / wiring from cadre-host start). */
27
+ restart?: ServiceRestarter;
28
+ /** Initial settings (autoApply etc) — typically sourced from host.config.json. */
29
+ settings?: UpdateSettings;
30
+ /** Periodic-check interval; defaults to 24h. */
31
+ checkIntervalMs?: number;
32
+ /** npm install timeout; defaults to 5m. */
33
+ npmTimeoutMs?: number;
34
+ }
35
+ /** Handlers for the management-API listener. */
36
+ export interface UpdateHandlers {
37
+ getState(): Promise<UpdateState>;
38
+ postApply(): Promise<UpdateApplyResult>;
39
+ getSettings(): Promise<UpdateSettings>;
40
+ putSettings(body: {
41
+ autoApply?: boolean;
42
+ manifestUrl?: string;
43
+ }): Promise<UpdateSettings>;
44
+ }
45
+ export declare class UpdateService {
46
+ private readonly store;
47
+ private readonly currentVersion;
48
+ private readonly fetcher;
49
+ private readonly npmExecutor;
50
+ private readonly restart;
51
+ private readonly checkIntervalMs;
52
+ private readonly npmTimeoutMs;
53
+ private manifestUrl;
54
+ private settings;
55
+ private timer;
56
+ private applying;
57
+ /** Persisted settings changes are bubbled out via this callback (wired in cadre-host start). */
58
+ private onSettingsChange?;
59
+ constructor(opts: UpdateServiceOptions);
60
+ /** Read the persisted state for UI consumption. */
61
+ getState(): Promise<UpdateState>;
62
+ /** Current settings (in-memory). */
63
+ getSettings(): UpdateSettings;
64
+ /**
65
+ * Update settings (autoApply / manifestUrl). The optional `onSettingsChange`
66
+ * hook is invoked so the caller can persist to host.config.json.
67
+ */
68
+ putSettings(patch: {
69
+ autoApply?: boolean;
70
+ manifestUrl?: string;
71
+ }): Promise<UpdateSettings>;
72
+ /** Register the persistence hook called after every settings mutation. */
73
+ onSettingsChanged(handler: (next: UpdateSettings) => void | Promise<void>): void;
74
+ /**
75
+ * Background manifest check. Never throws — fetch/parse failures log at
76
+ * debug; signature failures are recorded as `lastError` so the UI can warn.
77
+ */
78
+ check(): Promise<void>;
79
+ /**
80
+ * Trigger an apply. Re-verifies the manifest, runs npm install -g, attempts
81
+ * a service restart, and rolls back on failure. Throws UpdateErrorException
82
+ * on any hard failure; success returns the new state.
83
+ */
84
+ apply(): Promise<UpdateApplyResult>;
85
+ /** Start the daily-check timer. `start()` ensures we don't double-arm. */
86
+ start(): void;
87
+ /** Stop the daily-check timer. */
88
+ stop(): void;
89
+ }
90
+ /**
91
+ * Typed handler facade — mirrors `createTrustCircleHandlers` /
92
+ * `createNatHandlers`. The local-UI ticket consumes these.
93
+ */
94
+ export declare function createUpdateHandlers(service: UpdateService): UpdateHandlers;
95
+ export type { UpdateState, UpdateApplyResult, UpdateSettings, UpdateManifest } from './types.js';
96
+ export { UpdateErrorException, type UpdateErrorCode } from './types.js';
97
+ export { compareVersions, parseVersion } from './version.js';
98
+ export { fetchManifest, verifyManifest, canonicalJson, signManifestForTesting } from './manifest.js';
99
+ export { UpdateStateStore } from './store.js';
100
+ export { applyUpdate, defaultNpmExecutor, type NpmExecutor, type ServiceRestarter } from './apply.js';
101
+ export { getReleasePublicKey, getReleasePublicKeyBase64, ed25519FromRaw } from './release-key.js';
@@ -0,0 +1,228 @@
1
+ /**
2
+ * UpdateService — the composition root for the cadre-host update flow.
3
+ *
4
+ * Background check cadence: once at construction time (`check()` called
5
+ * explicitly by `cadre-host start` after the management API binds) and a
6
+ * 24-hour interval timer thereafter. Failures are silent — UI consumers see
7
+ * `lastChecked` only when a fetch succeeds.
8
+ *
9
+ * Apply flow: re-fetch + re-verify the manifest (never trust the cached
10
+ * `available`), record `applyInProgress`, run `npm install -g`, restart the
11
+ * service via the optional `ServiceRestarter`, and roll back on failure.
12
+ */
13
+ import debug from 'debug';
14
+ import { applyUpdate, defaultNpmExecutor, } from './apply.js';
15
+ import { DEFAULT_MANIFEST_URL, fetchManifest, } from './manifest.js';
16
+ import { UpdateStateStore } from './store.js';
17
+ import { UpdateErrorException, } from './types.js';
18
+ import { compareVersions } from './version.js';
19
+ const log = debug('cadre:host:update-service');
20
+ const DEFAULT_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
21
+ const DEFAULT_NPM_TIMEOUT_MS = 5 * 60 * 1000;
22
+ export class UpdateService {
23
+ constructor(opts) {
24
+ this.timer = null;
25
+ this.applying = false;
26
+ this.store = new UpdateStateStore(opts.dataDir);
27
+ this.currentVersion = opts.currentVersion;
28
+ this.fetcher = opts.fetcher ?? fetch;
29
+ this.npmTimeoutMs = opts.npmTimeoutMs ?? DEFAULT_NPM_TIMEOUT_MS;
30
+ this.npmExecutor = opts.npmExecutor ?? defaultNpmExecutor(this.npmTimeoutMs);
31
+ this.restart = opts.restart;
32
+ this.checkIntervalMs = opts.checkIntervalMs ?? DEFAULT_CHECK_INTERVAL_MS;
33
+ this.settings = opts.settings ?? { autoApply: false };
34
+ this.manifestUrl = resolveManifestUrl(opts.manifestUrl, this.settings.manifestUrl);
35
+ }
36
+ /** Read the persisted state for UI consumption. */
37
+ async getState() {
38
+ return this.store.load();
39
+ }
40
+ /** Current settings (in-memory). */
41
+ getSettings() {
42
+ return { ...this.settings };
43
+ }
44
+ /**
45
+ * Update settings (autoApply / manifestUrl). The optional `onSettingsChange`
46
+ * hook is invoked so the caller can persist to host.config.json.
47
+ */
48
+ async putSettings(patch) {
49
+ if (patch.autoApply !== undefined)
50
+ this.settings.autoApply = patch.autoApply;
51
+ if (patch.manifestUrl !== undefined)
52
+ this.settings.manifestUrl = patch.manifestUrl;
53
+ this.manifestUrl = resolveManifestUrl(undefined, this.settings.manifestUrl);
54
+ if (this.onSettingsChange)
55
+ await this.onSettingsChange({ ...this.settings });
56
+ return { ...this.settings };
57
+ }
58
+ /** Register the persistence hook called after every settings mutation. */
59
+ onSettingsChanged(handler) {
60
+ this.onSettingsChange = handler;
61
+ }
62
+ /**
63
+ * Background manifest check. Never throws — fetch/parse failures log at
64
+ * debug; signature failures are recorded as `lastError` so the UI can warn.
65
+ */
66
+ async check() {
67
+ let manifest;
68
+ try {
69
+ manifest = await fetchManifest({
70
+ url: this.manifestUrl,
71
+ fetcher: this.fetcher,
72
+ });
73
+ }
74
+ catch (err) {
75
+ if (err instanceof UpdateErrorException && err.code === 'signature_invalid') {
76
+ log('manifest signature invalid: %s', err.message);
77
+ this.store.update({
78
+ lastError: { code: err.code, message: err.message, at: new Date().toISOString() },
79
+ });
80
+ return;
81
+ }
82
+ // Network / parse / endpoint-down: silent.
83
+ log('manifest fetch failed: %s', err.message);
84
+ return;
85
+ }
86
+ const now = new Date().toISOString();
87
+ const compared = compareVersions(this.currentVersion, manifest.version);
88
+ if (compared >= 0) {
89
+ // Already up-to-date — clear any stale `available` record.
90
+ this.store.update({
91
+ lastChecked: now,
92
+ available: undefined,
93
+ lastError: undefined,
94
+ });
95
+ log('check ok: current %s ≥ %s', this.currentVersion, manifest.version);
96
+ return;
97
+ }
98
+ this.store.update({
99
+ lastChecked: now,
100
+ available: {
101
+ version: manifest.version,
102
+ publishedAt: manifest.publishedAt,
103
+ ...(manifest.releaseNotesUrl ? { releaseNotesUrl: manifest.releaseNotesUrl } : {}),
104
+ },
105
+ lastError: undefined,
106
+ });
107
+ log('update available: %s → %s', this.currentVersion, manifest.version);
108
+ if (this.settings.autoApply) {
109
+ try {
110
+ await this.apply();
111
+ }
112
+ catch (err) {
113
+ // apply() already recorded the error; we just don't want to crash the timer.
114
+ log('auto-apply failed: %s', err.message);
115
+ }
116
+ }
117
+ }
118
+ /**
119
+ * Trigger an apply. Re-verifies the manifest, runs npm install -g, attempts
120
+ * a service restart, and rolls back on failure. Throws UpdateErrorException
121
+ * on any hard failure; success returns the new state.
122
+ */
123
+ async apply() {
124
+ if (this.applying) {
125
+ throw new UpdateErrorException('apply_in_progress', 'another apply is already running');
126
+ }
127
+ const state = this.store.load();
128
+ if (state.applyInProgress) {
129
+ throw new UpdateErrorException('apply_in_progress', 'state already records an in-flight apply');
130
+ }
131
+ this.applying = true;
132
+ try {
133
+ const manifest = await fetchManifest({
134
+ url: this.manifestUrl,
135
+ fetcher: this.fetcher,
136
+ });
137
+ if (compareVersions(this.currentVersion, manifest.version) >= 0) {
138
+ throw new UpdateErrorException('no_update_available', `current ${this.currentVersion} is already ≥ latest ${manifest.version}`);
139
+ }
140
+ if (manifest.minPreviousVersion && compareVersions(this.currentVersion, manifest.minPreviousVersion) < 0) {
141
+ throw new UpdateErrorException('min_previous_version', `current ${this.currentVersion} is below minPreviousVersion ${manifest.minPreviousVersion}; install an intermediate release first`);
142
+ }
143
+ const startedAt = new Date().toISOString();
144
+ this.store.update({
145
+ applyInProgress: {
146
+ fromVersion: this.currentVersion,
147
+ toVersion: manifest.version,
148
+ startedAt,
149
+ },
150
+ });
151
+ try {
152
+ const result = await applyUpdate({
153
+ npmPackage: manifest.channels.npm.package,
154
+ fromVersion: this.currentVersion,
155
+ toVersion: manifest.version,
156
+ npmExecutor: this.npmExecutor,
157
+ ...(this.restart ? { restart: this.restart } : {}),
158
+ timeoutMs: this.npmTimeoutMs,
159
+ });
160
+ // Success: clear progress + any prior error.
161
+ this.store.update({
162
+ applyInProgress: undefined,
163
+ lastError: undefined,
164
+ });
165
+ return result;
166
+ }
167
+ catch (err) {
168
+ const code = err instanceof UpdateErrorException ? err.code : 'apply_failed';
169
+ const message = err.message;
170
+ this.store.update({
171
+ applyInProgress: undefined,
172
+ lastError: { code, message, at: new Date().toISOString() },
173
+ });
174
+ throw err instanceof UpdateErrorException ? err : new UpdateErrorException('apply_failed', message);
175
+ }
176
+ }
177
+ finally {
178
+ this.applying = false;
179
+ }
180
+ }
181
+ /** Start the daily-check timer. `start()` ensures we don't double-arm. */
182
+ start() {
183
+ if (this.timer)
184
+ return;
185
+ this.timer = setInterval(() => {
186
+ void this.check().catch((err) => log('scheduled check threw (should not happen): %s', err.message));
187
+ }, this.checkIntervalMs);
188
+ // Don't keep the event loop alive just for this timer.
189
+ if (typeof this.timer === 'object' && this.timer && 'unref' in this.timer) {
190
+ this.timer.unref?.();
191
+ }
192
+ }
193
+ /** Stop the daily-check timer. */
194
+ stop() {
195
+ if (this.timer) {
196
+ clearInterval(this.timer);
197
+ this.timer = null;
198
+ }
199
+ }
200
+ }
201
+ /**
202
+ * Typed handler facade — mirrors `createTrustCircleHandlers` /
203
+ * `createNatHandlers`. The local-UI ticket consumes these.
204
+ */
205
+ export function createUpdateHandlers(service) {
206
+ return {
207
+ async getState() { return service.getState(); },
208
+ async postApply() { return await service.apply(); },
209
+ async getSettings() { return service.getSettings(); },
210
+ async putSettings(body) { return await service.putSettings(body); },
211
+ };
212
+ }
213
+ function resolveManifestUrl(explicit, fromSettings) {
214
+ // Env var wins — operators routinely point this at staging endpoints, and
215
+ // both the README and host.ts documentation promise that behavior.
216
+ // Then explicit constructor arg → persisted settings → compiled default.
217
+ const envUrl = process.env.CADRE_HOST_UPDATE_MANIFEST_URL?.trim();
218
+ if (envUrl)
219
+ return envUrl;
220
+ return explicit ?? fromSettings ?? DEFAULT_MANIFEST_URL;
221
+ }
222
+ export { UpdateErrorException } from './types.js';
223
+ export { compareVersions, parseVersion } from './version.js';
224
+ export { fetchManifest, verifyManifest, canonicalJson, signManifestForTesting } from './manifest.js';
225
+ export { UpdateStateStore } from './store.js';
226
+ export { applyUpdate, defaultNpmExecutor } from './apply.js';
227
+ export { getReleasePublicKey, getReleasePublicKeyBase64, ed25519FromRaw } from './release-key.js';
228
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/update/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Fetch + verify the signed update manifest.
3
+ *
4
+ * The manifest is a JSON envelope `{ manifest, sig }` where `sig` is a
5
+ * detached Ed25519 signature of `canonicalJson(manifest)`. cadre-host only
6
+ * trusts manifests whose signature matches the embedded release public key.
7
+ *
8
+ * Canonical serialization is a tiny local impl — depending on
9
+ * `json-canonicalize` (or similar) for a single call site is overkill, and
10
+ * the shape is fixed (`UpdateManifest`).
11
+ */
12
+ import { type KeyObject } from 'node:crypto';
13
+ import { ed25519FromRaw } from './release-key.js';
14
+ import { type SignedManifest, type UpdateManifest } from './types.js';
15
+ /** Default release-manifest URL. Overridable via env var on the caller. */
16
+ export declare const DEFAULT_MANIFEST_URL = "https://releases.serfab.io/cadre-host/latest.json";
17
+ export interface FetchManifestOptions {
18
+ url: string;
19
+ fetcher?: typeof fetch;
20
+ /** Total fetch timeout (connect + read) in ms; default 10000. */
21
+ readTimeoutMs?: number;
22
+ }
23
+ /**
24
+ * Fetch the manifest endpoint and validate the signature against the embedded
25
+ * release public key (or `CADRE_HOST_UPDATE_DEV_KEY` if set).
26
+ *
27
+ * Throws `UpdateErrorException` on any failure (bad JSON, bad signature,
28
+ * etc.). Network errors are *thrown* — the caller decides whether to log
29
+ * them at debug (the cadence path) or surface them (the apply path).
30
+ */
31
+ export declare function fetchManifest(opts: FetchManifestOptions): Promise<UpdateManifest>;
32
+ /**
33
+ * Verify a parsed signed-manifest body. Pure (no network), so unit tests
34
+ * exercise this directly.
35
+ */
36
+ export declare function verifyManifest(body: unknown, env?: NodeJS.ProcessEnv): UpdateManifest;
37
+ /** Exposed for test signers. */
38
+ export declare function signManifestForTesting(manifest: UpdateManifest, privateKey: KeyObject): SignedManifest;
39
+ /** Used by `apply.ts` to detect which key the signer matches. */
40
+ export declare function publicKeyMatchesEmbedded(rawBase64: string, env?: NodeJS.ProcessEnv): boolean;
41
+ /** Wrapper for `getReleasePublicKey`, re-exported for parity with `ed25519FromRaw`. */
42
+ export { ed25519FromRaw };
43
+ /**
44
+ * Canonical JSON: recursively sort object keys, no whitespace, no trailing
45
+ * newline. Matches a typical RFC 8785 subset for the fixed shape we sign.
46
+ * Arrays preserve order.
47
+ */
48
+ export declare function canonicalJson(value: unknown): string;