sleev 0.0.13 → 0.1.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.
Files changed (133) hide show
  1. package/README.md +9 -8
  2. package/bin/sleev.js +84 -0
  3. package/package.json +36 -55
  4. package/postinstall.mjs +66 -0
  5. package/dist/auth/browser.d.ts +0 -12
  6. package/dist/auth/browser.js +0 -89
  7. package/dist/auth/browser.js.map +0 -1
  8. package/dist/auth/firebase-tokens.d.ts +0 -39
  9. package/dist/auth/firebase-tokens.js +0 -173
  10. package/dist/auth/firebase-tokens.js.map +0 -1
  11. package/dist/auth/firebase.d.ts +0 -17
  12. package/dist/auth/firebase.js +0 -55
  13. package/dist/auth/firebase.js.map +0 -1
  14. package/dist/auth/login.d.ts +0 -22
  15. package/dist/auth/login.js +0 -58
  16. package/dist/auth/login.js.map +0 -1
  17. package/dist/auth/loopback.d.ts +0 -19
  18. package/dist/auth/loopback.js +0 -204
  19. package/dist/auth/loopback.js.map +0 -1
  20. package/dist/auth/schema.d.ts +0 -26
  21. package/dist/auth/schema.js +0 -88
  22. package/dist/auth/schema.js.map +0 -1
  23. package/dist/auth/token.d.ts +0 -19
  24. package/dist/auth/token.js +0 -53
  25. package/dist/auth/token.js.map +0 -1
  26. package/dist/auth/verify.d.ts +0 -6
  27. package/dist/auth/verify.js +0 -22
  28. package/dist/auth/verify.js.map +0 -1
  29. package/dist/cli.d.ts +0 -8
  30. package/dist/cli.js +0 -255
  31. package/dist/cli.js.map +0 -1
  32. package/dist/config/paths.d.ts +0 -12
  33. package/dist/config/paths.js +0 -34
  34. package/dist/config/paths.js.map +0 -1
  35. package/dist/config/store.d.ts +0 -63
  36. package/dist/config/store.js +0 -270
  37. package/dist/config/store.js.map +0 -1
  38. package/dist/control/local-installations.d.ts +0 -13
  39. package/dist/control/local-installations.js +0 -37
  40. package/dist/control/local-installations.js.map +0 -1
  41. package/dist/daemon/config.d.ts +0 -10
  42. package/dist/daemon/config.js +0 -52
  43. package/dist/daemon/config.js.map +0 -1
  44. package/dist/daemon/controller.d.ts +0 -29
  45. package/dist/daemon/controller.js +0 -34
  46. package/dist/daemon/controller.js.map +0 -1
  47. package/dist/daemon/health.d.ts +0 -8
  48. package/dist/daemon/health.js +0 -47
  49. package/dist/daemon/health.js.map +0 -1
  50. package/dist/daemon/start.d.ts +0 -5
  51. package/dist/daemon/start.js +0 -24
  52. package/dist/daemon/start.js.map +0 -1
  53. package/dist/daemon/systemd.d.ts +0 -35
  54. package/dist/daemon/systemd.js +0 -181
  55. package/dist/daemon/systemd.js.map +0 -1
  56. package/dist/gateway/acquire.d.ts +0 -14
  57. package/dist/gateway/acquire.js +0 -112
  58. package/dist/gateway/acquire.js.map +0 -1
  59. package/dist/gateway/manifest.d.ts +0 -26
  60. package/dist/gateway/manifest.js +0 -98
  61. package/dist/gateway/manifest.js.map +0 -1
  62. package/dist/gateway/platform.d.ts +0 -5
  63. package/dist/gateway/platform.js +0 -27
  64. package/dist/gateway/platform.js.map +0 -1
  65. package/dist/harness/claude.d.ts +0 -8
  66. package/dist/harness/claude.js +0 -151
  67. package/dist/harness/claude.js.map +0 -1
  68. package/dist/harness/codex.d.ts +0 -10
  69. package/dist/harness/codex.js +0 -213
  70. package/dist/harness/codex.js.map +0 -1
  71. package/dist/harness/index.d.ts +0 -13
  72. package/dist/harness/index.js +0 -63
  73. package/dist/harness/index.js.map +0 -1
  74. package/dist/harness/opencode-config.d.ts +0 -8
  75. package/dist/harness/opencode-config.js +0 -301
  76. package/dist/harness/opencode-config.js.map +0 -1
  77. package/dist/harness/opencode.d.ts +0 -14
  78. package/dist/harness/opencode.js +0 -50
  79. package/dist/harness/opencode.js.map +0 -1
  80. package/dist/harness/providers.d.ts +0 -8
  81. package/dist/harness/providers.js +0 -30
  82. package/dist/harness/providers.js.map +0 -1
  83. package/dist/harness/types.d.ts +0 -14
  84. package/dist/harness/types.js +0 -2
  85. package/dist/harness/types.js.map +0 -1
  86. package/dist/product.d.ts +0 -12
  87. package/dist/product.js +0 -70
  88. package/dist/product.js.map +0 -1
  89. package/dist/tui/clipboard.d.ts +0 -1
  90. package/dist/tui/clipboard.js +0 -48
  91. package/dist/tui/clipboard.js.map +0 -1
  92. package/dist/tui/openTuiApp.d.ts +0 -1
  93. package/dist/tui/openTuiApp.js +0 -1436
  94. package/dist/tui/openTuiApp.js.map +0 -1
  95. package/dist/tui/pages/flow-screen.d.ts +0 -50
  96. package/dist/tui/pages/flow-screen.js +0 -435
  97. package/dist/tui/pages/flow-screen.js.map +0 -1
  98. package/dist/tui/pages/footer.d.ts +0 -7
  99. package/dist/tui/pages/footer.js +0 -14
  100. package/dist/tui/pages/footer.js.map +0 -1
  101. package/dist/tui/pages/home-screen.d.ts +0 -19
  102. package/dist/tui/pages/home-screen.js +0 -89
  103. package/dist/tui/pages/home-screen.js.map +0 -1
  104. package/dist/tui/pages/index.d.ts +0 -17
  105. package/dist/tui/pages/index.js +0 -11
  106. package/dist/tui/pages/index.js.map +0 -1
  107. package/dist/tui/pages/local-setup-screen.d.ts +0 -23
  108. package/dist/tui/pages/local-setup-screen.js +0 -134
  109. package/dist/tui/pages/local-setup-screen.js.map +0 -1
  110. package/dist/tui/pages/location-card.d.ts +0 -19
  111. package/dist/tui/pages/location-card.js +0 -116
  112. package/dist/tui/pages/location-card.js.map +0 -1
  113. package/dist/tui/pages/menu-screen.d.ts +0 -24
  114. package/dist/tui/pages/menu-screen.js +0 -117
  115. package/dist/tui/pages/menu-screen.js.map +0 -1
  116. package/dist/tui/pages/session-grid.d.ts +0 -23
  117. package/dist/tui/pages/session-grid.js +0 -552
  118. package/dist/tui/pages/session-grid.js.map +0 -1
  119. package/dist/tui/pages/setup-screen.d.ts +0 -15
  120. package/dist/tui/pages/setup-screen.js +0 -80
  121. package/dist/tui/pages/setup-screen.js.map +0 -1
  122. package/dist/tui/pages/terminal-log.d.ts +0 -14
  123. package/dist/tui/pages/terminal-log.js +0 -104
  124. package/dist/tui/pages/terminal-log.js.map +0 -1
  125. package/dist/tui/pages/welcome-screen.d.ts +0 -16
  126. package/dist/tui/pages/welcome-screen.js +0 -80
  127. package/dist/tui/pages/welcome-screen.js.map +0 -1
  128. package/dist/tui/saved-tokens.d.ts +0 -10
  129. package/dist/tui/saved-tokens.js +0 -104
  130. package/dist/tui/saved-tokens.js.map +0 -1
  131. package/dist/tui/theme.d.ts +0 -25
  132. package/dist/tui/theme.js +0 -27
  133. package/dist/tui/theme.js.map +0 -1
@@ -1,13 +0,0 @@
1
- import { FirebaseTokenManager } from "../auth/firebase-tokens.js";
2
- export type CreatedLocalInstallation = {
3
- installationId: string;
4
- accountId: string;
5
- credential: string;
6
- controlUrl: string;
7
- };
8
- export type CreateLocalInstallationOptions = {
9
- label?: string | null;
10
- manager?: FirebaseTokenManager;
11
- fetchImpl?: typeof fetch;
12
- };
13
- export declare function createLocalInstallation(options?: CreateLocalInstallationOptions): Promise<CreatedLocalInstallation>;
@@ -1,37 +0,0 @@
1
- // Creates a local installation in control after Firebase sign-in (when the
2
- // user selects local mode) and returns the credential the installed-local
3
- // daemon will use for usage uploads.
4
- import { FirebaseTokenManager } from "../auth/firebase-tokens.js";
5
- import { apiOrigin } from "../product.js";
6
- export async function createLocalInstallation(options = {}) {
7
- const manager = options.manager ?? new FirebaseTokenManager();
8
- const fetchImpl = options.fetchImpl ?? fetch;
9
- const controlUrl = apiOrigin();
10
- const idToken = await manager.getIdToken();
11
- const response = await fetchImpl(`${controlUrl}/api/local/installations`, {
12
- method: "POST",
13
- headers: {
14
- authorization: `Bearer ${idToken}`,
15
- "content-type": "application/json",
16
- },
17
- body: JSON.stringify({ label: options.label ?? null }),
18
- });
19
- if (!response.ok) {
20
- throw new Error(`Control rejected local installation creation (HTTP ${response.status}).`);
21
- }
22
- const body = (await response.json().catch(() => null));
23
- if (!body ||
24
- typeof body.installation_id !== "string" ||
25
- typeof body.account_id !== "string" ||
26
- typeof body.credential !== "string" ||
27
- !body.credential.startsWith("slv_inst_")) {
28
- throw new Error("Control returned an unexpected local installation body.");
29
- }
30
- return {
31
- installationId: body.installation_id,
32
- accountId: body.account_id,
33
- credential: body.credential,
34
- controlUrl,
35
- };
36
- }
37
- //# sourceMappingURL=local-installations.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"local-installations.js","sourceRoot":"","sources":["../../src/control/local-installations.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,0EAA0E;AAC1E,qCAAqC;AACrC,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAe1C,MAAM,CAAC,KAAK,UAAU,uBAAuB,CACzC,UAA0C,EAAE;IAE5C,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,IAAI,oBAAoB,EAAE,CAAC;IAC9D,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,KAAK,CAAC;IAC7C,MAAM,UAAU,GAAG,SAAS,EAAE,CAAC;IAC/B,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,EAAE,CAAC;IAE3C,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,GAAG,UAAU,0BAA0B,EAAE;QACtE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACL,aAAa,EAAE,UAAU,OAAO,EAAE;YAClC,cAAc,EAAE,kBAAkB;SACrC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI,EAAE,CAAC;KACzD,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CACX,sDAAsD,QAAQ,CAAC,MAAM,IAAI,CAC5E,CAAC;IACN,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAI7C,CAAC;IAET,IACI,CAAC,IAAI;QACL,OAAO,IAAI,CAAC,eAAe,KAAK,QAAQ;QACxC,OAAO,IAAI,CAAC,UAAU,KAAK,QAAQ;QACnC,OAAO,IAAI,CAAC,UAAU,KAAK,QAAQ;QACnC,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,WAAW,CAAC,EAC1C,CAAC;QACC,MAAM,IAAI,KAAK,CACX,yDAAyD,CAC5D,CAAC;IACN,CAAC;IAED,OAAO;QACH,cAAc,EAAE,IAAI,CAAC,eAAe;QACpC,SAAS,EAAE,IAAI,CAAC,UAAU;QAC1B,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,UAAU;KACb,CAAC;AACN,CAAC"}
@@ -1,10 +0,0 @@
1
- import type { SleevedConfig } from "../config/store.js";
2
- export type GatewayConfig = {
3
- account_id: string;
4
- installation_id: string;
5
- credential: string;
6
- control_url: string;
7
- };
8
- export declare function buildGatewayConfig(config: SleevedConfig): GatewayConfig | null;
9
- export declare function writeGatewayConfig(config: SleevedConfig): Promise<string>;
10
- export declare function clearGatewayConfig(): Promise<void>;
@@ -1,52 +0,0 @@
1
- // Bridges the credential sleeved persists into the JSON file the gateway
2
- // daemon reads at startup (its InstalledLocalConfig). The two sides use
3
- // different shapes: sleeved stores camelCase `localInstallation` plus the
4
- // account id under `auth`, while the daemon expects a flat snake_case
5
- // document. This module is the producer; the gateway is the consumer.
6
- import fs from "node:fs/promises";
7
- import path from "node:path";
8
- import { resolveSleevedPaths } from "../config/paths.js";
9
- // Returns the daemon config, or null when sleeved is not in a state that can
10
- // configure the daemon (signed out, or local installation not provisioned).
11
- export function buildGatewayConfig(config) {
12
- const accountId = config.auth.accountId;
13
- const local = config.localInstallation;
14
- if (!config.auth.signedIn || !accountId || !local) {
15
- return null;
16
- }
17
- return {
18
- account_id: accountId,
19
- installation_id: local.installationId,
20
- credential: local.credential,
21
- control_url: local.controlUrl,
22
- };
23
- }
24
- // Writes the daemon config file (0600) and returns its path. Throws if the
25
- // config is not materializable — callers should only start the daemon when
26
- // buildGatewayConfig would succeed.
27
- export async function writeGatewayConfig(config) {
28
- const gateway = buildGatewayConfig(config);
29
- if (gateway === null) {
30
- throw new Error("Cannot write gateway config: not signed in or local installation missing.");
31
- }
32
- const paths = resolveSleevedPaths();
33
- await fs.mkdir(path.dirname(paths.gatewayConfigFile), { recursive: true });
34
- await fs.writeFile(paths.gatewayConfigFile, `${JSON.stringify(gateway, null, 2)}\n`, { mode: 0o600 });
35
- return paths.gatewayConfigFile;
36
- }
37
- // Removes the daemon config file (used on sign-out / teardown). Absent file
38
- // is not an error.
39
- export async function clearGatewayConfig() {
40
- const paths = resolveSleevedPaths();
41
- try {
42
- await fs.unlink(paths.gatewayConfigFile);
43
- }
44
- catch (error) {
45
- if (!(error instanceof Error &&
46
- "code" in error &&
47
- error.code === "ENOENT")) {
48
- throw error;
49
- }
50
- }
51
- }
52
- //# sourceMappingURL=config.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/daemon/config.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,wEAAwE;AACxE,0EAA0E;AAC1E,sEAAsE;AACtE,sEAAsE;AACtE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAWzD,6EAA6E;AAC7E,4EAA4E;AAC5E,MAAM,UAAU,kBAAkB,CAC9B,MAAqB;IAErB,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC;IACxC,MAAM,KAAK,GAAG,MAAM,CAAC,iBAAiB,CAAC;IACvC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,SAAS,IAAI,CAAC,KAAK,EAAE,CAAC;QAChD,OAAO,IAAI,CAAC;IAChB,CAAC;IACD,OAAO;QACH,UAAU,EAAE,SAAS;QACrB,eAAe,EAAE,KAAK,CAAC,cAAc;QACrC,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,WAAW,EAAE,KAAK,CAAC,UAAU;KAChC,CAAC;AACN,CAAC;AAED,2EAA2E;AAC3E,2EAA2E;AAC3E,oCAAoC;AACpC,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACpC,MAAqB;IAErB,MAAM,OAAO,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;IAC3C,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CACX,2EAA2E,CAC9E,CAAC;IACN,CAAC;IACD,MAAM,KAAK,GAAG,mBAAmB,EAAE,CAAC;IACpC,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,iBAAiB,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3E,MAAM,EAAE,CAAC,SAAS,CACd,KAAK,CAAC,iBAAiB,EACvB,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EACvC,EAAE,IAAI,EAAE,KAAK,EAAE,CAClB,CAAC;IACF,OAAO,KAAK,CAAC,iBAAiB,CAAC;AACnC,CAAC;AAED,4EAA4E;AAC5E,mBAAmB;AACnB,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACpC,MAAM,KAAK,GAAG,mBAAmB,EAAE,CAAC;IACpC,IAAI,CAAC;QACD,MAAM,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;IAC7C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,IACI,CAAC,CACG,KAAK,YAAY,KAAK;YACtB,MAAM,IAAI,KAAK;YACd,KAA+B,CAAC,IAAI,KAAK,QAAQ,CACrD,EACH,CAAC;YACC,MAAM,KAAK,CAAC;QAChB,CAAC;IACL,CAAC;AACL,CAAC"}
@@ -1,29 +0,0 @@
1
- import type { SleevedConfig } from "../config/store.js";
2
- export type DaemonBackend = "systemd";
3
- export type DaemonStatus = {
4
- backend: DaemonBackend;
5
- running: boolean;
6
- healthy: boolean;
7
- configured: boolean | null;
8
- version: string | null;
9
- pid: number | null;
10
- address: string;
11
- detail: string | null;
12
- };
13
- export interface DaemonController {
14
- readonly backend: DaemonBackend;
15
- start(): Promise<DaemonStatus>;
16
- stop(): Promise<DaemonStatus>;
17
- restart(): Promise<DaemonStatus>;
18
- status(): Promise<DaemonStatus>;
19
- teardown(): Promise<void>;
20
- }
21
- export declare class UnsupportedDaemonEnvironmentError extends Error {
22
- constructor(message: string);
23
- }
24
- export type ControllerDeps = {
25
- platform?: NodeJS.Platform;
26
- env?: NodeJS.ProcessEnv;
27
- systemctlAvailable?: boolean;
28
- };
29
- export declare function createController(config: SleevedConfig, deps?: ControllerDeps): Promise<DaemonController>;
@@ -1,34 +0,0 @@
1
- // One interface over the daemon's lifecycle. The only compliant backend
2
- // today is systemd --user (Linux): it survives reboot and is supervised
3
- // without the CLI running. Environments that can't meet that contract fail
4
- // loudly rather than degrading to a non-persistent process — a daemon that
5
- // silently dies on reboot is worse than a clear error. macOS (launchd) and
6
- // Windows (service) are future backends behind this same seam.
7
- import os from "node:os";
8
- // Thrown when the host can't run a compliant (reboot-persistent,
9
- // supervised) daemon. Callers surface this; they must not fall back to an
10
- // unsupervised process.
11
- export class UnsupportedDaemonEnvironmentError extends Error {
12
- constructor(message) {
13
- super(message);
14
- this.name = "UnsupportedDaemonEnvironmentError";
15
- }
16
- }
17
- export async function createController(config, deps = {}) {
18
- const platform = deps.platform ?? os.platform();
19
- const env = deps.env ?? process.env;
20
- if (platform !== "linux") {
21
- throw new UnsupportedDaemonEnvironmentError(`The Sleeve gateway daemon is only supported on Linux ` +
22
- `(systemd --user) right now; detected ${platform}. ` +
23
- `macOS and Windows support is planned.`);
24
- }
25
- const { SystemdUserController, systemctlAvailable } = await import("./systemd.js");
26
- const available = deps.systemctlAvailable ?? (await systemctlAvailable());
27
- if (!available) {
28
- throw new UnsupportedDaemonEnvironmentError("The Sleeve gateway daemon requires a systemd --user session " +
29
- "(systemctl --user unavailable). A persistent, supervised " +
30
- "daemon cannot be guaranteed without it.");
31
- }
32
- return new SystemdUserController(config, env);
33
- }
34
- //# sourceMappingURL=controller.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"controller.js","sourceRoot":"","sources":["../../src/daemon/controller.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,wEAAwE;AACxE,2EAA2E;AAC3E,2EAA2E;AAC3E,2EAA2E;AAC3E,+DAA+D;AAC/D,OAAO,EAAE,MAAM,SAAS,CAAC;AAoCzB,iEAAiE;AACjE,0EAA0E;AAC1E,wBAAwB;AACxB,MAAM,OAAO,iCAAkC,SAAQ,KAAK;IACxD,YAAY,OAAe;QACvB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,mCAAmC,CAAC;IACpD,CAAC;CACJ;AASD,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAClC,MAAqB,EACrB,OAAuB,EAAE;IAEzB,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC,QAAQ,EAAE,CAAC;IAChD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;IAEpC,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QACvB,MAAM,IAAI,iCAAiC,CACvC,uDAAuD;YACnD,wCAAwC,QAAQ,IAAI;YACpD,uCAAuC,CAC9C,CAAC;IACN,CAAC;IAED,MAAM,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,GAC/C,MAAM,MAAM,CAAC,cAAc,CAAC,CAAC;IACjC,MAAM,SAAS,GAAG,IAAI,CAAC,kBAAkB,IAAI,CAAC,MAAM,kBAAkB,EAAE,CAAC,CAAC;IAC1E,IAAI,CAAC,SAAS,EAAE,CAAC;QACb,MAAM,IAAI,iCAAiC,CACvC,8DAA8D;YAC1D,2DAA2D;YAC3D,yCAAyC,CAChD,CAAC;IACN,CAAC;IACD,OAAO,IAAI,qBAAqB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAClD,CAAC"}
@@ -1,8 +0,0 @@
1
- export type DaemonHealth = {
2
- ok: boolean;
3
- mode: string;
4
- configured: boolean | null;
5
- version: string | null;
6
- };
7
- export declare function probeHealth(host: string, port: number, timeoutMs?: number, fetchImpl?: typeof fetch): Promise<DaemonHealth | null>;
8
- export declare function isProcessAlive(pid: number): boolean;
@@ -1,47 +0,0 @@
1
- // GETs /__sleeve_local/health. Returns null on any failure (down,
2
- // unreachable, timeout, non-200, garbage) — callers treat null as
3
- // "not running / not ours".
4
- export async function probeHealth(host, port, timeoutMs = 1500, fetchImpl = fetch) {
5
- const controller = new AbortController();
6
- const timer = setTimeout(() => controller.abort(), timeoutMs);
7
- try {
8
- const response = await fetchImpl(`http://${host}:${port}/__sleeve_local/health`, { signal: controller.signal });
9
- if (!response.ok)
10
- return null;
11
- const body = (await response.json().catch(() => null));
12
- if (!body || body.ok !== true)
13
- return null;
14
- return {
15
- ok: true,
16
- mode: typeof body.mode === "string" ? body.mode : "",
17
- configured: typeof body.configured === "boolean" ? body.configured : null,
18
- version: typeof body.version === "string" ? body.version : null,
19
- };
20
- }
21
- catch {
22
- return null;
23
- }
24
- finally {
25
- clearTimeout(timer);
26
- }
27
- }
28
- // True if a process with this pid exists and is signalable by us.
29
- // `kill(pid, 0)` sends no signal; it only performs the existence/permission
30
- // check. EPERM means it exists but is owned by someone else (still alive).
31
- export function isProcessAlive(pid) {
32
- if (!Number.isInteger(pid) || pid <= 0)
33
- return false;
34
- try {
35
- process.kill(pid, 0);
36
- return true;
37
- }
38
- catch (error) {
39
- if (error instanceof Error &&
40
- "code" in error &&
41
- error.code === "EPERM") {
42
- return true;
43
- }
44
- return false;
45
- }
46
- }
47
- //# sourceMappingURL=health.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/daemon/health.ts"],"names":[],"mappings":"AAUA,kEAAkE;AAClE,kEAAkE;AAClE,4BAA4B;AAC5B,MAAM,CAAC,KAAK,UAAU,WAAW,CAC7B,IAAY,EACZ,IAAY,EACZ,SAAS,GAAG,IAAI,EAChB,YAA0B,KAAK;IAE/B,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,SAAS,CAAC,CAAC;IAC9D,IAAI,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,SAAS,CAC5B,UAAU,IAAI,IAAI,IAAI,wBAAwB,EAC9C,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAChC,CAAC;QACF,IAAI,CAAC,QAAQ,CAAC,EAAE;YAAE,OAAO,IAAI,CAAC;QAC9B,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAK7C,CAAC;QACT,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,EAAE,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QAC3C,OAAO;YACH,EAAE,EAAE,IAAI;YACR,IAAI,EAAE,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE;YACpD,UAAU,EACN,OAAO,IAAI,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI;YACjE,OAAO,EAAE,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;SAClE,CAAC;IACN,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,IAAI,CAAC;IAChB,CAAC;YAAS,CAAC;QACP,YAAY,CAAC,KAAK,CAAC,CAAC;IACxB,CAAC;AACL,CAAC;AAED,kEAAkE;AAClE,4EAA4E;AAC5E,2EAA2E;AAC3E,MAAM,UAAU,cAAc,CAAC,GAAW;IACtC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACrD,IAAI,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IAChB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,IACI,KAAK,YAAY,KAAK;YACtB,MAAM,IAAI,KAAK;YACd,KAA+B,CAAC,IAAI,KAAK,OAAO,EACnD,CAAC;YACC,OAAO,IAAI,CAAC;QAChB,CAAC;QACD,OAAO,KAAK,CAAC;IACjB,CAAC;AACL,CAAC"}
@@ -1,5 +0,0 @@
1
- import type { SleevedConfig } from "../config/store.js";
2
- export declare class GatewayBinaryNotConfiguredError extends Error {
3
- constructor();
4
- }
5
- export declare function resolveGatewayBinary(config: SleevedConfig, env?: NodeJS.ProcessEnv): string;
@@ -1,24 +0,0 @@
1
- // Raised when no gateway executable is configured. Binary acquisition will
2
- // set SLEEVE_GATEWAY_BIN to the installed daemon; until then a developer
3
- // sets it by hand. The CLI deliberately never runs the gateway from a
4
- // source checkout.
5
- export class GatewayBinaryNotConfiguredError extends Error {
6
- constructor() {
7
- super("No gateway binary available. Run `sleeved gateway install` " +
8
- "(or set SLEEVE_GATEWAY_BIN to an explicit binary for dev).");
9
- this.name = "GatewayBinaryNotConfiguredError";
10
- }
11
- }
12
- // Resolves which gateway executable to launch: an explicit
13
- // SLEEVE_GATEWAY_BIN (dev override) wins, else the acquired binary recorded
14
- // in config. Throws if neither is available.
15
- export function resolveGatewayBinary(config, env = process.env) {
16
- const override = env.SLEEVE_GATEWAY_BIN?.trim();
17
- if (override)
18
- return override;
19
- const installed = config.gateway?.binPath;
20
- if (installed)
21
- return installed;
22
- throw new GatewayBinaryNotConfiguredError();
23
- }
24
- //# sourceMappingURL=start.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"start.js","sourceRoot":"","sources":["../../src/daemon/start.ts"],"names":[],"mappings":"AAIA,2EAA2E;AAC3E,yEAAyE;AACzE,sEAAsE;AACtE,mBAAmB;AACnB,MAAM,OAAO,+BAAgC,SAAQ,KAAK;IACtD;QACI,KAAK,CACD,6DAA6D;YACzD,4DAA4D,CACnE,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,iCAAiC,CAAC;IAClD,CAAC;CACJ;AAED,2DAA2D;AAC3D,4EAA4E;AAC5E,6CAA6C;AAC7C,MAAM,UAAU,oBAAoB,CAChC,MAAqB,EACrB,MAAyB,OAAO,CAAC,GAAG;IAEpC,MAAM,QAAQ,GAAG,GAAG,CAAC,kBAAkB,EAAE,IAAI,EAAE,CAAC;IAChD,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1C,IAAI,SAAS;QAAE,OAAO,SAAS,CAAC;IAChC,MAAM,IAAI,+BAA+B,EAAE,CAAC;AAChD,CAAC"}
@@ -1,35 +0,0 @@
1
- import type { SleevedConfig } from "../config/store.js";
2
- import type { DaemonController, DaemonStatus } from "./controller.js";
3
- export type CommandResult = {
4
- code: number;
5
- stdout: string;
6
- stderr: string;
7
- };
8
- export type CommandRunner = (cmd: string, args: string[]) => Promise<CommandResult>;
9
- export declare function systemctlAvailable(run?: CommandRunner): Promise<boolean>;
10
- export declare function renderUnit(binPath: string, configPath: string, host: string, port: number): string;
11
- export type SystemdDeps = {
12
- run?: CommandRunner;
13
- fetchImpl?: typeof fetch;
14
- sleep?: (ms: number) => Promise<void>;
15
- readinessTimeoutMs?: number;
16
- };
17
- export declare class SystemdUserController implements DaemonController {
18
- readonly backend: "systemd";
19
- private readonly config;
20
- private readonly env;
21
- private readonly run;
22
- private readonly fetchImpl;
23
- private readonly sleep;
24
- private readonly readinessTimeoutMs;
25
- constructor(config: SleevedConfig, env?: NodeJS.ProcessEnv, deps?: SystemdDeps);
26
- private get address();
27
- private sc;
28
- private install;
29
- private waitHealthy;
30
- start(): Promise<DaemonStatus>;
31
- stop(): Promise<DaemonStatus>;
32
- restart(): Promise<DaemonStatus>;
33
- status(): Promise<DaemonStatus>;
34
- teardown(): Promise<void>;
35
- }
@@ -1,181 +0,0 @@
1
- // systemd --user backend (Linux): the daemon runs as a user service that
2
- // starts on login and (with linger) on boot, supervised without sleeved.
3
- // This is the product path on Linux.
4
- import { spawn } from "node:child_process";
5
- import fs from "node:fs/promises";
6
- import os from "node:os";
7
- import path from "node:path";
8
- import { writeGatewayConfig } from "./config.js";
9
- import { probeHealth } from "./health.js";
10
- import { resolveGatewayBinary } from "./start.js";
11
- const UNIT_NAME = "sleeve-gateway.service";
12
- const defaultRun = (cmd, args) => new Promise((resolve) => {
13
- const child = spawn(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
14
- let stdout = "";
15
- let stderr = "";
16
- child.stdout?.on("data", (d) => (stdout += d.toString()));
17
- child.stderr?.on("data", (d) => (stderr += d.toString()));
18
- child.once("error", () => resolve({ code: 127, stdout, stderr: "spawn failed" }));
19
- child.once("exit", (code) => resolve({ code: code ?? 1, stdout, stderr }));
20
- });
21
- export async function systemctlAvailable(run = defaultRun) {
22
- if (os.platform() !== "linux")
23
- return false;
24
- const { code } = await run("systemctl", ["--user", "--version"]);
25
- return code === 0;
26
- }
27
- function userUnitPath(env) {
28
- const base = env.XDG_CONFIG_HOME?.trim() || path.join(os.homedir(), ".config");
29
- return path.join(base, "systemd", "user", UNIT_NAME);
30
- }
31
- // Pure: the unit file content. Paths are quoted so spaces survive systemd's
32
- // ExecStart tokenisation.
33
- export function renderUnit(binPath, configPath, host, port) {
34
- const exec = [
35
- q(binPath),
36
- "--local-installation",
37
- "--config",
38
- q(configPath),
39
- "--host",
40
- host,
41
- "--port",
42
- String(port),
43
- ].join(" ");
44
- return [
45
- "[Unit]",
46
- "Description=Sleeve local gateway",
47
- "After=network-online.target",
48
- "Wants=network-online.target",
49
- "",
50
- "[Service]",
51
- "Type=simple",
52
- `ExecStart=${exec}`,
53
- "Restart=on-failure",
54
- "RestartSec=2",
55
- "",
56
- "[Install]",
57
- "WantedBy=default.target",
58
- "",
59
- ].join("\n");
60
- }
61
- function q(value) {
62
- return `"${value.replace(/"/g, '\\"')}"`;
63
- }
64
- const defaultSleep = (ms) => new Promise((r) => setTimeout(r, ms));
65
- export class SystemdUserController {
66
- backend = "systemd";
67
- config;
68
- env;
69
- run;
70
- fetchImpl;
71
- sleep;
72
- readinessTimeoutMs;
73
- constructor(config, env = process.env, deps = {}) {
74
- this.config = config;
75
- this.env = env;
76
- this.run = deps.run ?? defaultRun;
77
- this.fetchImpl = deps.fetchImpl ?? fetch;
78
- this.sleep = deps.sleep ?? defaultSleep;
79
- this.readinessTimeoutMs = deps.readinessTimeoutMs ?? 10_000;
80
- }
81
- get address() {
82
- return `http://${this.config.proxy.host}:${this.config.proxy.port}`;
83
- }
84
- async sc(args) {
85
- return this.run("systemctl", ["--user", ...args]);
86
- }
87
- // Writes the unit + the gateway config, then reloads systemd. The unit
88
- // is regenerated every time so a changed binary path/port is picked up.
89
- async install() {
90
- const binPath = resolveGatewayBinary(this.config, this.env);
91
- const configPath = await writeGatewayConfig(this.config);
92
- const unitPath = userUnitPath(this.env);
93
- await fs.mkdir(path.dirname(unitPath), { recursive: true });
94
- await fs.writeFile(unitPath, renderUnit(binPath, configPath, this.config.proxy.host, this.config.proxy.port), { mode: 0o644 });
95
- await this.sc(["daemon-reload"]);
96
- }
97
- async waitHealthy() {
98
- const deadline = Date.now() + this.readinessTimeoutMs;
99
- while (Date.now() < deadline) {
100
- await this.sleep(250);
101
- const health = await probeHealth(this.config.proxy.host, this.config.proxy.port, 1000, this.fetchImpl);
102
- if (health)
103
- return;
104
- }
105
- const { stdout } = await this.sc(["is-active", UNIT_NAME]).catch(() => ({ stdout: "", code: 1, stderr: "" }));
106
- throw new Error(`Gateway service did not become healthy within ` +
107
- `${this.readinessTimeoutMs}ms (unit ${stdout.trim() || "inactive"}).`);
108
- }
109
- async start() {
110
- const existing = await this.status();
111
- if (existing.healthy)
112
- return existing;
113
- await this.install();
114
- // enable --now: start immediately and on every login. Linger lets
115
- // it also start at boot before any login; best-effort because it
116
- // can require elevated privileges.
117
- const enabled = await this.sc(["enable", "--now", UNIT_NAME]);
118
- if (enabled.code !== 0) {
119
- throw new Error(`systemctl --user enable --now failed: ${enabled.stderr.trim()}`);
120
- }
121
- await this.run("loginctl", [
122
- "enable-linger",
123
- os.userInfo().username,
124
- ]).catch(() => undefined);
125
- await this.waitHealthy();
126
- return this.status();
127
- }
128
- async stop() {
129
- await this.sc(["stop", UNIT_NAME]);
130
- return this.status();
131
- }
132
- async restart() {
133
- // Reinstall first so a changed config/binary/port is applied.
134
- await this.install();
135
- const r = await this.sc(["restart", UNIT_NAME]);
136
- if (r.code !== 0) {
137
- throw new Error(`systemctl --user restart failed: ${r.stderr.trim()}`);
138
- }
139
- await this.waitHealthy();
140
- return this.status();
141
- }
142
- async status() {
143
- const active = await this.sc(["is-active", UNIT_NAME]);
144
- const running = active.stdout.trim() === "active";
145
- const health = await probeHealth(this.config.proxy.host, this.config.proxy.port, 1500, this.fetchImpl);
146
- let pid = null;
147
- const show = await this.sc([
148
- "show",
149
- "-p",
150
- "MainPID",
151
- "--value",
152
- UNIT_NAME,
153
- ]);
154
- const parsed = Number.parseInt(show.stdout.trim(), 10);
155
- if (Number.isInteger(parsed) && parsed > 0)
156
- pid = parsed;
157
- return {
158
- backend: this.backend,
159
- running: running || health !== null,
160
- healthy: health !== null,
161
- configured: health?.configured ?? null,
162
- version: health?.version ?? null,
163
- pid,
164
- address: this.address,
165
- detail: health !== null
166
- ? null
167
- : `unit ${active.stdout.trim() || "inactive"}`,
168
- };
169
- }
170
- async teardown() {
171
- await this.sc(["disable", "--now", UNIT_NAME]);
172
- try {
173
- await fs.unlink(userUnitPath(this.env));
174
- }
175
- catch {
176
- /* already gone */
177
- }
178
- await this.sc(["daemon-reload"]);
179
- }
180
- }
181
- //# sourceMappingURL=systemd.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"systemd.js","sourceRoot":"","sources":["../../src/daemon/systemd.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,yEAAyE;AACzE,qCAAqC;AACrC,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAElD,MAAM,SAAS,GAAG,wBAAwB,CAAC;AAQ3C,MAAM,UAAU,GAAkB,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAC5C,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;IACpB,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;IACtE,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;IAClE,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;IAClE,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CACrB,OAAO,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC,CACzD,CAAC;IACF,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CACxB,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAC/C,CAAC;AACN,CAAC,CAAC,CAAC;AAEP,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACpC,MAAqB,UAAU;IAE/B,IAAI,EAAE,CAAC,QAAQ,EAAE,KAAK,OAAO;QAAE,OAAO,KAAK,CAAC;IAC5C,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,GAAG,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC;IACjE,OAAO,IAAI,KAAK,CAAC,CAAC;AACtB,CAAC;AAED,SAAS,YAAY,CAAC,GAAsB;IACxC,MAAM,IAAI,GACN,GAAG,CAAC,eAAe,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;IACtE,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;AACzD,CAAC;AAED,4EAA4E;AAC5E,0BAA0B;AAC1B,MAAM,UAAU,UAAU,CACtB,OAAe,EACf,UAAkB,EAClB,IAAY,EACZ,IAAY;IAEZ,MAAM,IAAI,GAAG;QACT,CAAC,CAAC,OAAO,CAAC;QACV,sBAAsB;QACtB,UAAU;QACV,CAAC,CAAC,UAAU,CAAC;QACb,QAAQ;QACR,IAAI;QACJ,QAAQ;QACR,MAAM,CAAC,IAAI,CAAC;KACf,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACZ,OAAO;QACH,QAAQ;QACR,kCAAkC;QAClC,6BAA6B;QAC7B,6BAA6B;QAC7B,EAAE;QACF,WAAW;QACX,aAAa;QACb,aAAa,IAAI,EAAE;QACnB,oBAAoB;QACpB,cAAc;QACd,EAAE;QACF,WAAW;QACX,yBAAyB;QACzB,EAAE;KACL,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACjB,CAAC;AAED,SAAS,CAAC,CAAC,KAAa;IACpB,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC;AAC7C,CAAC;AASD,MAAM,YAAY,GAAG,CAAC,EAAU,EAAiB,EAAE,CAC/C,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;AAE1C,MAAM,OAAO,qBAAqB;IACrB,OAAO,GAAG,SAAkB,CAAC;IACrB,MAAM,CAAgB;IACtB,GAAG,CAAoB;IACvB,GAAG,CAAgB;IACnB,SAAS,CAAe;IACxB,KAAK,CAAgC;IACrC,kBAAkB,CAAS;IAE5C,YACI,MAAqB,EACrB,MAAyB,OAAO,CAAC,GAAG,EACpC,OAAoB,EAAE;QAEtB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,UAAU,CAAC;QAClC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC;QACzC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,YAAY,CAAC;QACxC,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC,kBAAkB,IAAI,MAAM,CAAC;IAChE,CAAC;IAED,IAAY,OAAO;QACf,OAAO,UAAU,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;IACxE,CAAC;IAEO,KAAK,CAAC,EAAE,CAAC,IAAc;QAC3B,OAAO,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;IACtD,CAAC;IAED,uEAAuE;IACvE,wEAAwE;IAChE,KAAK,CAAC,OAAO;QACjB,MAAM,OAAO,GAAG,oBAAoB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;QAC5D,MAAM,UAAU,GAAG,MAAM,kBAAkB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACzD,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACxC,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5D,MAAM,EAAE,CAAC,SAAS,CACd,QAAQ,EACR,UAAU,CACN,OAAO,EACP,UAAU,EACV,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EACtB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CACzB,EACD,EAAE,IAAI,EAAE,KAAK,EAAE,CAClB,CAAC;QACF,MAAM,IAAI,CAAC,EAAE,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC;IACrC,CAAC;IAEO,KAAK,CAAC,WAAW;QACrB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,kBAAkB,CAAC;QACtD,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;YAC3B,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YACtB,MAAM,MAAM,GAAG,MAAM,WAAW,CAC5B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EACtB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EACtB,IAAI,EACJ,IAAI,CAAC,SAAS,CACjB,CAAC;YACF,IAAI,MAAM;gBAAE,OAAO;QACvB,CAAC;QACD,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC,CAAC,KAAK,CAC5D,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAC9C,CAAC;QACF,MAAM,IAAI,KAAK,CACX,gDAAgD;YAC5C,GAAG,IAAI,CAAC,kBAAkB,YAAY,MAAM,CAAC,IAAI,EAAE,IAAI,UAAU,IAAI,CAC5E,CAAC;IACN,CAAC;IAED,KAAK,CAAC,KAAK;QACP,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;QACrC,IAAI,QAAQ,CAAC,OAAO;YAAE,OAAO,QAAQ,CAAC;QACtC,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QACrB,kEAAkE;QAClE,iEAAiE;QACjE,mCAAmC;QACnC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC;QAC9D,IAAI,OAAO,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CACX,yCAAyC,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CACnE,CAAC;QACN,CAAC;QACD,MAAM,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE;YACvB,eAAe;YACf,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ;SACzB,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC1B,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;QACzB,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,IAAI;QACN,MAAM,IAAI,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;QACnC,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,OAAO;QACT,8DAA8D;QAC9D,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QACrB,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC;QAChD,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CACX,oCAAoC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CACxD,CAAC;QACN,CAAC;QACD,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;QACzB,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,MAAM;QACR,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC,CAAC;QACvD,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,QAAQ,CAAC;QAClD,MAAM,MAAM,GAAG,MAAM,WAAW,CAC5B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EACtB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EACtB,IAAI,EACJ,IAAI,CAAC,SAAS,CACjB,CAAC;QACF,IAAI,GAAG,GAAkB,IAAI,CAAC;QAC9B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC;YACvB,MAAM;YACN,IAAI;YACJ,SAAS;YACT,SAAS;YACT,SAAS;SACZ,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QACvD,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC;YAAE,GAAG,GAAG,MAAM,CAAC;QAEzD,OAAO;YACH,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,OAAO,EAAE,OAAO,IAAI,MAAM,KAAK,IAAI;YACnC,OAAO,EAAE,MAAM,KAAK,IAAI;YACxB,UAAU,EAAE,MAAM,EAAE,UAAU,IAAI,IAAI;YACtC,OAAO,EAAE,MAAM,EAAE,OAAO,IAAI,IAAI;YAChC,GAAG;YACH,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,MAAM,EACF,MAAM,KAAK,IAAI;gBACX,CAAC,CAAC,IAAI;gBACN,CAAC,CAAC,QAAQ,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,UAAU,EAAE;SACzD,CAAC;IACN,CAAC;IAED,KAAK,CAAC,QAAQ;QACV,MAAM,IAAI,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC;QAC/C,IAAI,CAAC;YACD,MAAM,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAC5C,CAAC;QAAC,MAAM,CAAC;YACL,kBAAkB;QACtB,CAAC;QACD,MAAM,IAAI,CAAC,EAAE,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC;IACrC,CAAC;CACJ"}
@@ -1,14 +0,0 @@
1
- import { type GatewayPlatform } from "./platform.js";
2
- export type InstalledGateway = {
3
- binPath: string;
4
- version: string;
5
- platform: GatewayPlatform;
6
- };
7
- export type AcquireDeps = {
8
- fetchImpl?: typeof fetch;
9
- extract?: (archive: string, destDir: string, kind: "tar.gz" | "zip") => Promise<void>;
10
- currentVersion?: string | null;
11
- platform?: GatewayPlatform;
12
- env?: NodeJS.ProcessEnv;
13
- };
14
- export declare function acquireGateway(deps?: AcquireDeps): Promise<InstalledGateway>;
@@ -1,112 +0,0 @@
1
- // Downloads, verifies, and installs the packaged gateway binary the channel
2
- // manifest points at. The CLI never builds from source; this is how the
3
- // `SLEEVE_GATEWAY_BIN`-shaped seam gets populated for real users.
4
- import { createHash } from "node:crypto";
5
- import { spawn } from "node:child_process";
6
- import fs from "node:fs/promises";
7
- import os from "node:os";
8
- import path from "node:path";
9
- import { resolveSleevedPaths } from "../config/paths.js";
10
- import { archivedBinaryCandidates, archiveExtension, currentPlatformKey, installedBinaryName, } from "./platform.js";
11
- import { resolveLatestRelease, } from "./manifest.js";
12
- function gatewayInstallDir(version) {
13
- return path.join(resolveSleevedPaths().dataDir, "gateway", version);
14
- }
15
- async function pathExists(p) {
16
- try {
17
- await fs.access(p);
18
- return true;
19
- }
20
- catch {
21
- return false;
22
- }
23
- }
24
- async function defaultExtract(archive, destDir, _kind) {
25
- // `tar` extracts both gzip tarballs and (via libarchive/bsdtar, present
26
- // on macOS and Windows 10+) zips. Linux ships GNU tar but only ever
27
- // receives .tar.gz, so this is safe across the three built targets.
28
- //
29
- // Defense-in-depth: the archive is integrity-pinned upstream, but it is
30
- // still attacker-influenced if the manifest is compromised. GNU tar (and
31
- // bsdtar) already strip a leading `/` and refuse `..` members by default,
32
- // so extraction cannot escape destDir without an explicit -P/--absolute-
33
- // names; we never pass one. (An earlier `--no-absolute-filenames` flag
34
- // was not a real GNU tar option — tar rejected it with exit 64 before
35
- // extracting anything.)
36
- await new Promise((resolve, reject) => {
37
- const child = spawn("tar", ["-xf", archive, "-C", destDir], {
38
- stdio: "ignore",
39
- });
40
- child.once("error", reject);
41
- child.once("exit", (code) => code === 0
42
- ? resolve()
43
- : reject(new Error(`tar exited with code ${code ?? "null"}`)));
44
- });
45
- }
46
- function sha256(buffer) {
47
- return createHash("sha256").update(buffer).digest("hex");
48
- }
49
- // Resolves latest, and unless it's already installed, downloads + verifies +
50
- // installs it. Returns the path the daemon should be launched from.
51
- export async function acquireGateway(deps = {}) {
52
- const fetchImpl = deps.fetchImpl ?? fetch;
53
- const extract = deps.extract ?? defaultExtract;
54
- const platform = deps.platform ?? currentPlatformKey();
55
- const release = await resolveLatestRelease({
56
- fetchImpl,
57
- platform,
58
- env: deps.env,
59
- });
60
- const binPath = path.join(gatewayInstallDir(release.version), installedBinaryName(platform));
61
- // Skip when the resolved version is already installed and present.
62
- if (deps.currentVersion === release.version &&
63
- (await pathExists(binPath))) {
64
- return { binPath, version: release.version, platform };
65
- }
66
- await installArtifact(release.artifact, {
67
- fetchImpl,
68
- extract,
69
- archiveKind: archiveExtension(platform),
70
- archivedNames: archivedBinaryCandidates(platform),
71
- binPath,
72
- });
73
- return { binPath, version: release.version, platform };
74
- }
75
- async function installArtifact(artifact, opts) {
76
- const response = await opts.fetchImpl(artifact.url);
77
- if (!response.ok) {
78
- throw new Error(`Failed to download gateway binary (${artifact.url}): HTTP ${response.status}.`);
79
- }
80
- const buffer = Buffer.from(await response.arrayBuffer());
81
- if (buffer.byteLength !== artifact.size) {
82
- throw new Error(`Downloaded gateway archive size ${buffer.byteLength} != expected ${artifact.size}.`);
83
- }
84
- const digest = sha256(buffer);
85
- if (digest !== artifact.sha256) {
86
- throw new Error(`Downloaded gateway archive sha256 mismatch (got ${digest}).`);
87
- }
88
- const work = await fs.mkdtemp(path.join(os.tmpdir(), "sleeve-gw-"));
89
- try {
90
- const archivePath = path.join(work, `archive.${opts.archiveKind === "zip" ? "zip" : "tar.gz"}`);
91
- await fs.writeFile(archivePath, buffer);
92
- await opts.extract(archivePath, work, opts.archiveKind);
93
- let extracted = null;
94
- for (const name of opts.archivedNames) {
95
- const candidate = path.join(work, name);
96
- if (await pathExists(candidate)) {
97
- extracted = candidate;
98
- break;
99
- }
100
- }
101
- if (extracted === null) {
102
- throw new Error(`Gateway archive did not contain any of: ${opts.archivedNames.join(", ")}.`);
103
- }
104
- await fs.mkdir(path.dirname(opts.binPath), { recursive: true });
105
- await fs.copyFile(extracted, opts.binPath);
106
- await fs.chmod(opts.binPath, 0o755);
107
- }
108
- finally {
109
- await fs.rm(work, { recursive: true, force: true });
110
- }
111
- }
112
- //# sourceMappingURL=acquire.js.map