sleev 0.0.11 → 0.0.13

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 (144) hide show
  1. package/README.md +19 -39
  2. package/dist/auth/browser.d.ts +12 -0
  3. package/dist/auth/browser.js +33 -24
  4. package/dist/auth/browser.js.map +1 -0
  5. package/dist/auth/firebase-tokens.d.ts +39 -0
  6. package/dist/auth/firebase-tokens.js +173 -0
  7. package/dist/auth/firebase-tokens.js.map +1 -0
  8. package/dist/auth/firebase.d.ts +17 -0
  9. package/dist/auth/firebase.js +55 -0
  10. package/dist/auth/firebase.js.map +1 -0
  11. package/dist/auth/login.d.ts +22 -0
  12. package/dist/auth/login.js +31 -28
  13. package/dist/auth/login.js.map +1 -0
  14. package/dist/auth/loopback.d.ts +19 -0
  15. package/dist/auth/loopback.js +114 -44
  16. package/dist/auth/loopback.js.map +1 -0
  17. package/dist/auth/schema.d.ts +26 -0
  18. package/dist/auth/schema.js +59 -17
  19. package/dist/auth/schema.js.map +1 -0
  20. package/dist/auth/token.d.ts +19 -0
  21. package/dist/auth/token.js +53 -0
  22. package/dist/auth/token.js.map +1 -0
  23. package/dist/auth/verify.d.ts +6 -0
  24. package/dist/auth/verify.js +22 -0
  25. package/dist/auth/verify.js.map +1 -0
  26. package/dist/cli.d.ts +8 -0
  27. package/dist/cli.js +245 -59
  28. package/dist/cli.js.map +1 -0
  29. package/dist/config/paths.d.ts +12 -0
  30. package/dist/config/paths.js +34 -0
  31. package/dist/config/paths.js.map +1 -0
  32. package/dist/config/store.d.ts +63 -0
  33. package/dist/config/store.js +270 -0
  34. package/dist/config/store.js.map +1 -0
  35. package/dist/control/local-installations.d.ts +13 -0
  36. package/dist/control/local-installations.js +37 -0
  37. package/dist/control/local-installations.js.map +1 -0
  38. package/dist/daemon/config.d.ts +10 -0
  39. package/dist/daemon/config.js +52 -0
  40. package/dist/daemon/config.js.map +1 -0
  41. package/dist/daemon/controller.d.ts +29 -0
  42. package/dist/daemon/controller.js +34 -0
  43. package/dist/daemon/controller.js.map +1 -0
  44. package/dist/daemon/health.d.ts +8 -0
  45. package/dist/daemon/health.js +47 -0
  46. package/dist/daemon/health.js.map +1 -0
  47. package/dist/daemon/start.d.ts +5 -0
  48. package/dist/daemon/start.js +24 -0
  49. package/dist/daemon/start.js.map +1 -0
  50. package/dist/daemon/systemd.d.ts +35 -0
  51. package/dist/daemon/systemd.js +181 -0
  52. package/dist/daemon/systemd.js.map +1 -0
  53. package/dist/gateway/acquire.d.ts +14 -0
  54. package/dist/gateway/acquire.js +112 -0
  55. package/dist/gateway/acquire.js.map +1 -0
  56. package/dist/gateway/manifest.d.ts +26 -0
  57. package/dist/gateway/manifest.js +98 -0
  58. package/dist/gateway/manifest.js.map +1 -0
  59. package/dist/gateway/platform.d.ts +5 -0
  60. package/dist/gateway/platform.js +27 -0
  61. package/dist/gateway/platform.js.map +1 -0
  62. package/dist/harness/claude.d.ts +8 -0
  63. package/dist/{apps → harness}/claude.js +28 -28
  64. package/dist/harness/claude.js.map +1 -0
  65. package/dist/harness/codex.d.ts +10 -0
  66. package/dist/harness/codex.js +213 -0
  67. package/dist/harness/codex.js.map +1 -0
  68. package/dist/harness/index.d.ts +13 -0
  69. package/dist/harness/index.js +63 -0
  70. package/dist/harness/index.js.map +1 -0
  71. package/dist/harness/opencode-config.d.ts +8 -0
  72. package/dist/{apps → harness}/opencode-config.js +116 -83
  73. package/dist/harness/opencode-config.js.map +1 -0
  74. package/dist/harness/opencode.d.ts +14 -0
  75. package/dist/harness/opencode.js +50 -0
  76. package/dist/harness/opencode.js.map +1 -0
  77. package/dist/harness/providers.d.ts +8 -0
  78. package/dist/harness/providers.js +30 -0
  79. package/dist/harness/providers.js.map +1 -0
  80. package/dist/harness/types.d.ts +14 -0
  81. package/dist/harness/types.js +2 -0
  82. package/dist/harness/types.js.map +1 -0
  83. package/dist/product.d.ts +12 -0
  84. package/dist/product.js +53 -10
  85. package/dist/product.js.map +1 -0
  86. package/dist/tui/clipboard.d.ts +1 -0
  87. package/dist/tui/clipboard.js +48 -0
  88. package/dist/tui/clipboard.js.map +1 -0
  89. package/dist/tui/openTuiApp.d.ts +1 -0
  90. package/dist/tui/openTuiApp.js +1436 -0
  91. package/dist/tui/openTuiApp.js.map +1 -0
  92. package/dist/tui/pages/flow-screen.d.ts +50 -0
  93. package/dist/tui/pages/flow-screen.js +435 -0
  94. package/dist/tui/pages/flow-screen.js.map +1 -0
  95. package/dist/tui/pages/footer.d.ts +7 -0
  96. package/dist/tui/pages/footer.js +14 -0
  97. package/dist/tui/pages/footer.js.map +1 -0
  98. package/dist/tui/pages/home-screen.d.ts +19 -0
  99. package/dist/tui/pages/home-screen.js +89 -0
  100. package/dist/tui/pages/home-screen.js.map +1 -0
  101. package/dist/tui/pages/index.d.ts +17 -0
  102. package/dist/tui/pages/index.js +11 -0
  103. package/dist/tui/pages/index.js.map +1 -0
  104. package/dist/tui/pages/local-setup-screen.d.ts +23 -0
  105. package/dist/tui/pages/local-setup-screen.js +134 -0
  106. package/dist/tui/pages/local-setup-screen.js.map +1 -0
  107. package/dist/tui/pages/location-card.d.ts +19 -0
  108. package/dist/tui/pages/location-card.js +116 -0
  109. package/dist/tui/pages/location-card.js.map +1 -0
  110. package/dist/tui/pages/menu-screen.d.ts +24 -0
  111. package/dist/tui/pages/menu-screen.js +117 -0
  112. package/dist/tui/pages/menu-screen.js.map +1 -0
  113. package/dist/tui/pages/session-grid.d.ts +23 -0
  114. package/dist/tui/pages/session-grid.js +552 -0
  115. package/dist/tui/pages/session-grid.js.map +1 -0
  116. package/dist/tui/pages/setup-screen.d.ts +15 -0
  117. package/dist/tui/pages/setup-screen.js +80 -0
  118. package/dist/tui/pages/setup-screen.js.map +1 -0
  119. package/dist/tui/pages/terminal-log.d.ts +14 -0
  120. package/dist/tui/pages/terminal-log.js +104 -0
  121. package/dist/tui/pages/terminal-log.js.map +1 -0
  122. package/dist/tui/pages/welcome-screen.d.ts +16 -0
  123. package/dist/tui/pages/welcome-screen.js +80 -0
  124. package/dist/tui/pages/welcome-screen.js.map +1 -0
  125. package/dist/tui/saved-tokens.d.ts +10 -0
  126. package/dist/tui/saved-tokens.js +104 -0
  127. package/dist/tui/saved-tokens.js.map +1 -0
  128. package/dist/tui/theme.d.ts +25 -0
  129. package/dist/tui/theme.js +27 -0
  130. package/dist/tui/theme.js.map +1 -0
  131. package/package.json +55 -38
  132. package/dist/apps/codex.js +0 -184
  133. package/dist/apps/opencode.js +0 -158
  134. package/dist/apps.js +0 -148
  135. package/dist/auth/command.js +0 -109
  136. package/dist/auth/index.js +0 -2
  137. package/dist/auth/validation.js +0 -27
  138. package/dist/credentials/index.js +0 -3
  139. package/dist/credentials/schema.js +0 -63
  140. package/dist/credentials/store.js +0 -52
  141. package/dist/harnesses.js +0 -66
  142. package/dist/index.js +0 -3
  143. package/dist/providers.js +0 -30
  144. package/dist/terminal.js +0 -55
@@ -0,0 +1,270 @@
1
+ // Owns persisted sleeved config shape, normalization, and state transitions.
2
+ import fs from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { resolveSleevedPaths } from "./paths.js";
5
+ export function defaultConfig() {
6
+ return {
7
+ version: 1,
8
+ proxy: {
9
+ host: "127.0.0.1",
10
+ port: 17321,
11
+ },
12
+ auth: {
13
+ signedIn: false,
14
+ },
15
+ location: {
16
+ mode: null,
17
+ },
18
+ localInstallation: null,
19
+ gateway: null,
20
+ tokens: {
21
+ defaultId: null,
22
+ items: [],
23
+ },
24
+ harnesses: {},
25
+ };
26
+ }
27
+ export function signInConfig(config, session) {
28
+ return {
29
+ ...config,
30
+ auth: {
31
+ signedIn: true,
32
+ label: session.label,
33
+ accountId: session.accountId,
34
+ email: session.email,
35
+ createdAt: session.createdAt,
36
+ },
37
+ };
38
+ }
39
+ export function signOutConfig(config) {
40
+ return {
41
+ ...config,
42
+ auth: {
43
+ signedIn: false,
44
+ },
45
+ localInstallation: null,
46
+ tokens: {
47
+ defaultId: null,
48
+ items: [],
49
+ },
50
+ harnesses: {},
51
+ };
52
+ }
53
+ export function setLocalInstallationConfig(config, localInstallation) {
54
+ return {
55
+ ...config,
56
+ localInstallation,
57
+ };
58
+ }
59
+ export function setGatewayConfig(config, gateway) {
60
+ return {
61
+ ...config,
62
+ gateway,
63
+ };
64
+ }
65
+ export function setLocationConfig(config, mode) {
66
+ return {
67
+ ...config,
68
+ location: {
69
+ mode,
70
+ },
71
+ };
72
+ }
73
+ export function setHarnessesConfig(config, slugs) {
74
+ return {
75
+ ...config,
76
+ harnesses: {
77
+ ...config.harnesses,
78
+ ...Object.fromEntries(slugs.map((slug) => [slug, { configured: true }])),
79
+ },
80
+ };
81
+ }
82
+ export function addTokenConfig(config, token) {
83
+ const items = [
84
+ token,
85
+ ...config.tokens.items.filter((item) => item.id !== token.id),
86
+ ];
87
+ return {
88
+ ...config,
89
+ tokens: {
90
+ defaultId: token.id,
91
+ items,
92
+ },
93
+ };
94
+ }
95
+ export function setDefaultTokenConfig(config, id) {
96
+ const defaultId = config.tokens.items.some((item) => item.id === id)
97
+ ? id
98
+ : config.tokens.defaultId;
99
+ return {
100
+ ...config,
101
+ tokens: {
102
+ ...config.tokens,
103
+ defaultId,
104
+ },
105
+ };
106
+ }
107
+ export async function readConfig() {
108
+ const paths = resolveSleevedPaths();
109
+ try {
110
+ const raw = await fs.readFile(paths.configFile, "utf8");
111
+ return normalizeConfig(JSON.parse(raw));
112
+ }
113
+ catch (error) {
114
+ if (isNotFoundError(error)) {
115
+ return defaultConfig();
116
+ }
117
+ throw error;
118
+ }
119
+ }
120
+ export async function writeConfig(config) {
121
+ const paths = resolveSleevedPaths();
122
+ await fs.mkdir(path.dirname(paths.configFile), { recursive: true });
123
+ await fs.writeFile(paths.configFile, `${JSON.stringify(config, null, 2)}\n`, {
124
+ mode: 0o600,
125
+ });
126
+ }
127
+ function normalizeConfig(value) {
128
+ if (!isRecord(value)) {
129
+ return defaultConfig();
130
+ }
131
+ const defaults = defaultConfig();
132
+ const proxy = isRecord(value.proxy) ? value.proxy : {};
133
+ const auth = isRecord(value.auth) ? value.auth : {};
134
+ const location = isRecord(value.location) ? value.location : {};
135
+ const tokens = isRecord(value.tokens) ? value.tokens : {};
136
+ const harnesses = isRecord(value.harnesses) ? value.harnesses : {};
137
+ const accountId = nonEmptyString(auth.accountId);
138
+ const signedIn = auth.signedIn === true && typeof accountId === "string";
139
+ return {
140
+ version: 1,
141
+ proxy: {
142
+ host: typeof proxy.host === "string"
143
+ ? proxy.host
144
+ : defaults.proxy.host,
145
+ port: typeof proxy.port === "number"
146
+ ? proxy.port
147
+ : defaults.proxy.port,
148
+ },
149
+ auth: {
150
+ signedIn,
151
+ label: nullableString(auth.label),
152
+ accountId,
153
+ email: nullableString(auth.email),
154
+ createdAt: typeof auth.createdAt === "string" ? auth.createdAt : undefined,
155
+ },
156
+ location: {
157
+ mode: locationMode(location.mode),
158
+ },
159
+ localInstallation: normalizeLocalInstallation(value.localInstallation),
160
+ gateway: normalizeGatewayInstall(value.gateway),
161
+ tokens: normalizeTokens(tokens),
162
+ harnesses: normalizeHarnesses(harnesses),
163
+ };
164
+ }
165
+ function locationMode(value) {
166
+ if (value === "local" || value === "cloud")
167
+ return value;
168
+ return null;
169
+ }
170
+ function normalizeGatewayInstall(value) {
171
+ if (!isRecord(value))
172
+ return null;
173
+ const binPath = nonEmptyString(value.binPath);
174
+ const version = nonEmptyString(value.version);
175
+ const channel = nonEmptyString(value.channel);
176
+ if (!binPath || !version || !channel)
177
+ return null;
178
+ return { binPath, version, channel };
179
+ }
180
+ function normalizeLocalInstallation(value) {
181
+ if (!isRecord(value))
182
+ return null;
183
+ const installationId = nonEmptyString(value.installationId);
184
+ const credential = nonEmptyString(value.credential);
185
+ const controlUrl = nonEmptyString(value.controlUrl);
186
+ if (!installationId || !credential || !controlUrl)
187
+ return null;
188
+ if (!credential.startsWith("slv_inst_") || !validUrl(controlUrl)) {
189
+ return null;
190
+ }
191
+ return { installationId, credential, controlUrl };
192
+ }
193
+ function normalizeTokens(value) {
194
+ const items = Array.isArray(value.items)
195
+ ? value.items.flatMap(tokenRecord)
196
+ : [];
197
+ const rawDefaultId = typeof value.defaultId === "string" ? value.defaultId : null;
198
+ const defaultId = items.some((item) => item.id === rawDefaultId)
199
+ ? rawDefaultId
200
+ : (items[0]?.id ?? null);
201
+ return { defaultId, items };
202
+ }
203
+ function tokenRecord(value) {
204
+ if (!isRecord(value))
205
+ return [];
206
+ const id = nonEmptyString(value.id);
207
+ const token = nonEmptyString(value.token);
208
+ const proxyUrl = nonEmptyString(value.proxyUrl);
209
+ const createdAt = nonEmptyString(value.createdAt);
210
+ if (!id || !token || !token.startsWith("slv_live_"))
211
+ return [];
212
+ if (!proxyUrl || !validUrl(proxyUrl))
213
+ return [];
214
+ if (!createdAt || Number.isNaN(Date.parse(createdAt)))
215
+ return [];
216
+ return [
217
+ {
218
+ id,
219
+ token,
220
+ label: nullableString(value.label) ?? null,
221
+ proxyUrl,
222
+ createdAt,
223
+ },
224
+ ];
225
+ }
226
+ function normalizeHarnesses(value) {
227
+ const result = {};
228
+ for (const slug of [
229
+ "claude-code",
230
+ "codex",
231
+ "opencode",
232
+ "gemini",
233
+ ]) {
234
+ const entry = value[slug];
235
+ if (isRecord(entry) && typeof entry.configured === "boolean") {
236
+ result[slug] = { configured: entry.configured };
237
+ }
238
+ }
239
+ return result;
240
+ }
241
+ function isRecord(value) {
242
+ return typeof value === "object" && value !== null && !Array.isArray(value);
243
+ }
244
+ function nullableString(value) {
245
+ if (typeof value === "undefined")
246
+ return undefined;
247
+ if (value === null || typeof value === "string")
248
+ return value;
249
+ return undefined;
250
+ }
251
+ function nonEmptyString(value) {
252
+ if (typeof value !== "string")
253
+ return undefined;
254
+ return value ? value : undefined;
255
+ }
256
+ function validUrl(raw) {
257
+ try {
258
+ const url = new URL(raw);
259
+ return url.protocol === "https:" || url.protocol === "http:";
260
+ }
261
+ catch {
262
+ return false;
263
+ }
264
+ }
265
+ function isNotFoundError(error) {
266
+ return (error instanceof Error &&
267
+ "code" in error &&
268
+ error.code === "ENOENT");
269
+ }
270
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/config/store.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AA+DjD,MAAM,UAAU,aAAa;IACzB,OAAO;QACH,OAAO,EAAE,CAAC;QACV,KAAK,EAAE;YACH,IAAI,EAAE,WAAW;YACjB,IAAI,EAAE,KAAK;SACd;QACD,IAAI,EAAE;YACF,QAAQ,EAAE,KAAK;SAClB;QACD,QAAQ,EAAE;YACN,IAAI,EAAE,IAAI;SACb;QACD,iBAAiB,EAAE,IAAI;QACvB,OAAO,EAAE,IAAI;QACb,MAAM,EAAE;YACJ,SAAS,EAAE,IAAI;YACf,KAAK,EAAE,EAAE;SACZ;QACD,SAAS,EAAE,EAAE;KAChB,CAAC;AACN,CAAC;AAED,MAAM,UAAU,YAAY,CACxB,MAAqB,EACrB,OAAoB;IAEpB,OAAO;QACH,GAAG,MAAM;QACT,IAAI,EAAE;YACF,QAAQ,EAAE,IAAI;YACd,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,SAAS,EAAE,OAAO,CAAC,SAAS;SAC/B;KACJ,CAAC;AACN,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,MAAqB;IAC/C,OAAO;QACH,GAAG,MAAM;QACT,IAAI,EAAE;YACF,QAAQ,EAAE,KAAK;SAClB;QACD,iBAAiB,EAAE,IAAI;QACvB,MAAM,EAAE;YACJ,SAAS,EAAE,IAAI;YACf,KAAK,EAAE,EAAE;SACZ;QACD,SAAS,EAAE,EAAE;KAChB,CAAC;AACN,CAAC;AAED,MAAM,UAAU,0BAA0B,CACtC,MAAqB,EACrB,iBAA2C;IAE3C,OAAO;QACH,GAAG,MAAM;QACT,iBAAiB;KACpB,CAAC;AACN,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC5B,MAAqB,EACrB,OAA8B;IAE9B,OAAO;QACH,GAAG,MAAM;QACT,OAAO;KACV,CAAC;AACN,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC7B,MAAqB,EACrB,IAAqB;IAErB,OAAO;QACH,GAAG,MAAM;QACT,QAAQ,EAAE;YACN,IAAI;SACP;KACJ,CAAC;AACN,CAAC;AAED,MAAM,UAAU,kBAAkB,CAC9B,MAAqB,EACrB,KAA6B;IAE7B,OAAO;QACH,GAAG,MAAM;QACT,SAAS,EAAE;YACP,GAAG,MAAM,CAAC,SAAS;YACnB,GAAG,MAAM,CAAC,WAAW,CACjB,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,CACpD;SACJ;KACJ,CAAC;AACN,CAAC;AAED,MAAM,UAAU,cAAc,CAC1B,MAAqB,EACrB,KAAmB;IAEnB,MAAM,KAAK,GAAG;QACV,KAAK;QACL,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,KAAK,CAAC,EAAE,CAAC;KAChE,CAAC;IACF,OAAO;QACH,GAAG,MAAM;QACT,MAAM,EAAE;YACJ,SAAS,EAAE,KAAK,CAAC,EAAE;YACnB,KAAK;SACR;KACJ,CAAC;AACN,CAAC;AAED,MAAM,UAAU,qBAAqB,CACjC,MAAqB,EACrB,EAAU;IAEV,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC;QAChE,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC;IAC9B,OAAO;QACH,GAAG,MAAM;QACT,MAAM,EAAE;YACJ,GAAG,MAAM,CAAC,MAAM;YAChB,SAAS;SACZ;KACJ,CAAC;AACN,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU;IAC5B,MAAM,KAAK,GAAG,mBAAmB,EAAE,CAAC;IAEpC,IAAI,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QACxD,OAAO,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,IAAI,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO,aAAa,EAAE,CAAC;QAC3B,CAAC;QACD,MAAM,KAAK,CAAC;IAChB,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,MAAqB;IACnD,MAAM,KAAK,GAAG,mBAAmB,EAAE,CAAC;IACpC,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACpE,MAAM,EAAE,CAAC,SAAS,CACd,KAAK,CAAC,UAAU,EAChB,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EACtC;QACI,IAAI,EAAE,KAAK;KACd,CACJ,CAAC;AACN,CAAC;AAED,SAAS,eAAe,CAAC,KAAc;IACnC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACnB,OAAO,aAAa,EAAE,CAAC;IAC3B,CAAC;IAED,MAAM,QAAQ,GAAG,aAAa,EAAE,CAAC;IACjC,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;IACvD,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IACpD,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;IAChE,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1D,MAAM,SAAS,GAAG,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;IACnE,MAAM,SAAS,GAAG,cAAc,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACjD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,KAAK,IAAI,IAAI,OAAO,SAAS,KAAK,QAAQ,CAAC;IAEzE,OAAO;QACH,OAAO,EAAE,CAAC;QACV,KAAK,EAAE;YACH,IAAI,EACA,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ;gBAC1B,CAAC,CAAC,KAAK,CAAC,IAAI;gBACZ,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI;YAC7B,IAAI,EACA,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ;gBAC1B,CAAC,CAAC,KAAK,CAAC,IAAI;gBACZ,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI;SAChC;QACD,IAAI,EAAE;YACF,QAAQ;YACR,KAAK,EAAE,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC;YACjC,SAAS;YACT,KAAK,EAAE,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC;YACjC,SAAS,EACL,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;SACtE;QACD,QAAQ,EAAE;YACN,IAAI,EAAE,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC;SACpC;QACD,iBAAiB,EAAE,0BAA0B,CAAC,KAAK,CAAC,iBAAiB,CAAC;QACtE,OAAO,EAAE,uBAAuB,CAAC,KAAK,CAAC,OAAO,CAAC;QAC/C,MAAM,EAAE,eAAe,CAAC,MAAM,CAAC;QAC/B,SAAS,EAAE,kBAAkB,CAAC,SAAS,CAAC;KAC3C,CAAC;AACN,CAAC;AAED,SAAS,YAAY,CAAC,KAAc;IAChC,IAAI,KAAK,KAAK,OAAO,IAAI,KAAK,KAAK,OAAO;QAAE,OAAO,KAAK,CAAC;IACzD,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,SAAS,uBAAuB,CAAC,KAAc;IAC3C,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAClC,MAAM,OAAO,GAAG,cAAc,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAG,cAAc,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAG,cAAc,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC9C,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAClD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AACzC,CAAC;AAED,SAAS,0BAA0B,CAAC,KAAc;IAC9C,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAClC,MAAM,cAAc,GAAG,cAAc,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IAC5D,MAAM,UAAU,GAAG,cAAc,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IACpD,MAAM,UAAU,GAAG,cAAc,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IACpD,IAAI,CAAC,cAAc,IAAI,CAAC,UAAU,IAAI,CAAC,UAAU;QAAE,OAAO,IAAI,CAAC;IAC/D,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/D,OAAO,IAAI,CAAC;IAChB,CAAC;IACD,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC;AACtD,CAAC;AAED,SAAS,eAAe,CACpB,KAA8B;IAE9B,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;QACpC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC;QAClC,CAAC,CAAC,EAAE,CAAC;IACT,MAAM,YAAY,GACd,OAAO,KAAK,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;IACjE,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,YAAY,CAAC;QAC5D,CAAC,CAAC,YAAY;QACd,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,IAAI,CAAC,CAAC;IAE7B,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;AAChC,CAAC;AAED,SAAS,WAAW,CAAC,KAAc;IAC/B,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEhC,MAAM,EAAE,GAAG,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACpC,MAAM,KAAK,GAAG,cAAc,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC1C,MAAM,QAAQ,GAAG,cAAc,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAChD,MAAM,SAAS,GAAG,cAAc,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAElD,IAAI,CAAC,EAAE,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,WAAW,CAAC;QAAE,OAAO,EAAE,CAAC;IAC/D,IAAI,CAAC,QAAQ,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAAE,OAAO,EAAE,CAAC;IAChD,IAAI,CAAC,SAAS,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC;IAEjE,OAAO;QACH;YACI,EAAE;YACF,KAAK;YACL,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,IAAI;YAC1C,QAAQ;YACR,SAAS;SACZ;KACJ,CAAC;AACN,CAAC;AAED,SAAS,kBAAkB,CACvB,KAA8B;IAE9B,MAAM,MAAM,GAA+B,EAAE,CAAC;IAC9C,KAAK,MAAM,IAAI,IAAI;QACf,aAAa;QACb,OAAO;QACP,UAAU;QACV,QAAQ;KACF,EAAE,CAAC;QACT,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;QAC1B,IAAI,QAAQ,CAAC,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;YAC3D,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC;QACpD,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC;AAClB,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC5B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAChF,CAAC;AAED,SAAS,cAAc,CAAC,KAAc;IAClC,IAAI,OAAO,KAAK,KAAK,WAAW;QAAE,OAAO,SAAS,CAAC;IACnD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC9D,OAAO,SAAS,CAAC;AACrB,CAAC;AAED,SAAS,cAAc,CAAC,KAAc;IAClC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAChD,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACrC,CAAC;AAED,SAAS,QAAQ,CAAC,GAAW;IACzB,IAAI,CAAC;QACD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QACzB,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ,IAAI,GAAG,CAAC,QAAQ,KAAK,OAAO,CAAC;IACjE,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,KAAK,CAAC;IACjB,CAAC;AACL,CAAC;AAED,SAAS,eAAe,CAAC,KAAc;IACnC,OAAO,CACH,KAAK,YAAY,KAAK;QACtB,MAAM,IAAI,KAAK;QACd,KAA+B,CAAC,IAAI,KAAK,QAAQ,CACrD,CAAC;AACN,CAAC"}
@@ -0,0 +1,13 @@
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>;
@@ -0,0 +1,37 @@
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
@@ -0,0 +1 @@
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"}
@@ -0,0 +1,10 @@
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>;
@@ -0,0 +1,52 @@
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
@@ -0,0 +1 @@
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"}
@@ -0,0 +1,29 @@
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>;
@@ -0,0 +1,34 @@
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
@@ -0,0 +1 @@
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"}
@@ -0,0 +1,8 @@
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;
@@ -0,0 +1,47 @@
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
@@ -0,0 +1 @@
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"}
@@ -0,0 +1,5 @@
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;
@@ -0,0 +1,24 @@
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
@@ -0,0 +1 @@
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"}
@@ -0,0 +1,35 @@
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
+ }