@theford/runly 0.0.0-stage → 0.20.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 (128) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +157 -2
  3. package/dist/cli/client.d.ts +80 -0
  4. package/dist/cli/client.js +294 -0
  5. package/dist/cli/client.js.map +1 -0
  6. package/dist/cli/daemon-unit.d.ts +58 -0
  7. package/dist/cli/daemon-unit.js +289 -0
  8. package/dist/cli/daemon-unit.js.map +1 -0
  9. package/dist/cli/index.d.ts +2 -0
  10. package/dist/cli/index.js +1482 -0
  11. package/dist/cli/index.js.map +1 -0
  12. package/dist/cli/install.d.ts +9 -0
  13. package/dist/cli/install.js +53 -0
  14. package/dist/cli/install.js.map +1 -0
  15. package/dist/cli/logs.d.ts +43 -0
  16. package/dist/cli/logs.js +232 -0
  17. package/dist/cli/logs.js.map +1 -0
  18. package/dist/cli/tether.d.ts +3 -0
  19. package/dist/cli/tether.js +65 -0
  20. package/dist/cli/tether.js.map +1 -0
  21. package/dist/cli/watchdog.d.ts +1 -0
  22. package/dist/cli/watchdog.js +81 -0
  23. package/dist/cli/watchdog.js.map +1 -0
  24. package/dist/core/builds.d.ts +20 -0
  25. package/dist/core/builds.js +93 -0
  26. package/dist/core/builds.js.map +1 -0
  27. package/dist/core/caller-env.d.ts +17 -0
  28. package/dist/core/caller-env.js +77 -0
  29. package/dist/core/caller-env.js.map +1 -0
  30. package/dist/core/diagnostics.d.ts +68 -0
  31. package/dist/core/diagnostics.js +34 -0
  32. package/dist/core/diagnostics.js.map +1 -0
  33. package/dist/core/env-vars.d.ts +35 -0
  34. package/dist/core/env-vars.js +32 -0
  35. package/dist/core/env-vars.js.map +1 -0
  36. package/dist/core/events.d.ts +10 -0
  37. package/dist/core/events.js +42 -0
  38. package/dist/core/events.js.map +1 -0
  39. package/dist/core/exec.d.ts +22 -0
  40. package/dist/core/exec.js +84 -0
  41. package/dist/core/exec.js.map +1 -0
  42. package/dist/core/journal.d.ts +227 -0
  43. package/dist/core/journal.js +395 -0
  44. package/dist/core/journal.js.map +1 -0
  45. package/dist/core/logs.d.ts +56 -0
  46. package/dist/core/logs.js +259 -0
  47. package/dist/core/logs.js.map +1 -0
  48. package/dist/core/manifest.d.ts +292 -0
  49. package/dist/core/manifest.js +179 -0
  50. package/dist/core/manifest.js.map +1 -0
  51. package/dist/core/namespaces.d.ts +7 -0
  52. package/dist/core/namespaces.js +59 -0
  53. package/dist/core/namespaces.js.map +1 -0
  54. package/dist/core/paths.d.ts +31 -0
  55. package/dist/core/paths.js +65 -0
  56. package/dist/core/paths.js.map +1 -0
  57. package/dist/core/policy.d.ts +94 -0
  58. package/dist/core/policy.js +122 -0
  59. package/dist/core/policy.js.map +1 -0
  60. package/dist/core/ports.d.ts +52 -0
  61. package/dist/core/ports.js +94 -0
  62. package/dist/core/ports.js.map +1 -0
  63. package/dist/core/presets.d.ts +25 -0
  64. package/dist/core/presets.js +86 -0
  65. package/dist/core/presets.js.map +1 -0
  66. package/dist/core/procscan.d.ts +111 -0
  67. package/dist/core/procscan.js +342 -0
  68. package/dist/core/procscan.js.map +1 -0
  69. package/dist/core/retention.d.ts +97 -0
  70. package/dist/core/retention.js +322 -0
  71. package/dist/core/retention.js.map +1 -0
  72. package/dist/core/sleep.d.ts +51 -0
  73. package/dist/core/sleep.js +99 -0
  74. package/dist/core/sleep.js.map +1 -0
  75. package/dist/core/tree-ledger.d.ts +39 -0
  76. package/dist/core/tree-ledger.js +92 -0
  77. package/dist/core/tree-ledger.js.map +1 -0
  78. package/dist/core/types.d.ts +21 -0
  79. package/dist/core/types.js +6 -0
  80. package/dist/core/types.js.map +1 -0
  81. package/dist/core/units.d.ts +20 -0
  82. package/dist/core/units.js +69 -0
  83. package/dist/core/units.js.map +1 -0
  84. package/dist/core/upkeep.d.ts +90 -0
  85. package/dist/core/upkeep.js +176 -0
  86. package/dist/core/upkeep.js.map +1 -0
  87. package/dist/core/util.d.ts +46 -0
  88. package/dist/core/util.js +168 -0
  89. package/dist/core/util.js.map +1 -0
  90. package/dist/core/version.d.ts +39 -0
  91. package/dist/core/version.js +144 -0
  92. package/dist/core/version.js.map +1 -0
  93. package/dist/core/worktree.d.ts +51 -0
  94. package/dist/core/worktree.js +309 -0
  95. package/dist/core/worktree.js.map +1 -0
  96. package/dist/daemon/budget.d.ts +155 -0
  97. package/dist/daemon/budget.js +288 -0
  98. package/dist/daemon/budget.js.map +1 -0
  99. package/dist/daemon/election.d.ts +9 -0
  100. package/dist/daemon/election.js +140 -0
  101. package/dist/daemon/election.js.map +1 -0
  102. package/dist/daemon/engine.d.ts +1703 -0
  103. package/dist/daemon/engine.js +5305 -0
  104. package/dist/daemon/engine.js.map +1 -0
  105. package/dist/daemon/index.d.ts +2 -0
  106. package/dist/daemon/index.js +412 -0
  107. package/dist/daemon/index.js.map +1 -0
  108. package/dist/daemon/proxy.d.ts +108 -0
  109. package/dist/daemon/proxy.js +615 -0
  110. package/dist/daemon/proxy.js.map +1 -0
  111. package/dist/daemon/supervisor.d.ts +101 -0
  112. package/dist/daemon/supervisor.js +493 -0
  113. package/dist/daemon/supervisor.js.map +1 -0
  114. package/dist/drivers/appliances.d.ts +11 -0
  115. package/dist/drivers/appliances.js +174 -0
  116. package/dist/drivers/appliances.js.map +1 -0
  117. package/dist/drivers/datastores.d.ts +144 -0
  118. package/dist/drivers/datastores.js +775 -0
  119. package/dist/drivers/datastores.js.map +1 -0
  120. package/dist/drivers/preview.d.ts +69 -0
  121. package/dist/drivers/preview.js +605 -0
  122. package/dist/drivers/preview.js.map +1 -0
  123. package/dist/drivers/types.d.ts +70 -0
  124. package/dist/drivers/types.js +13 -0
  125. package/dist/drivers/types.js.map +1 -0
  126. package/package.json +63 -4
  127. package/schema/backlot.schema.json +549 -0
  128. package/schema/runly.schema.json +556 -0
@@ -0,0 +1,605 @@
1
+ /**
2
+ * Lease-scoped public preview publishers (decision 0027).
3
+ *
4
+ * Distinct from the substrate driver's `expose()` — that wires internal service
5
+ * URLs for remote substrates. Preview is an explicit, opt-in verb that publishes
6
+ * one service port to the internet for human inspection.
7
+ */
8
+ import { spawn, spawnSync } from 'node:child_process';
9
+ import { appendFileSync, mkdirSync, existsSync, writeFileSync } from 'node:fs';
10
+ import { homedir, userInfo } from 'node:os';
11
+ import { createHash } from 'node:crypto';
12
+ import { join } from 'node:path';
13
+ import { execFileSync } from 'node:child_process';
14
+ import { BrokerError, now } from '../core/util.js';
15
+ import { killGroupVerified } from '../daemon/supervisor.js';
16
+ import { serviceTag, startTime } from '../core/procscan.js';
17
+ import { stateRoot } from '../core/paths.js';
18
+ import { tunnelBlock } from '../core/ports.js';
19
+ const URL_RE = /https:\/\/[^\s]+trycloudflare\.com/;
20
+ /** How long to wait for a quick tunnel to publish its URL before giving up. */
21
+ const startTimeoutMs = () => {
22
+ const raw = Number(process.env.BACKLOT_PREVIEW_START_TIMEOUT_MS);
23
+ return Number.isFinite(raw) && raw > 0 ? raw : 45_000;
24
+ };
25
+ function cloudflaredBin() {
26
+ const override = process.env.BACKLOT_CLOUDFLARED?.trim();
27
+ if (override)
28
+ return override;
29
+ try {
30
+ return execFileSync('which', ['cloudflared'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
31
+ }
32
+ catch {
33
+ return 'cloudflared';
34
+ }
35
+ }
36
+ function cloudflaredAvailable(bin) {
37
+ if (bin.includes('/') || bin.includes('\\'))
38
+ return existsSync(bin);
39
+ try {
40
+ execFileSync('which', [bin], { stdio: ['ignore', 'ignore', 'ignore'] });
41
+ return true;
42
+ }
43
+ catch {
44
+ return false;
45
+ }
46
+ }
47
+ /**
48
+ * Kill a tunnel process this adapter spawned but is about to stop tracking.
49
+ *
50
+ * `spawnedStart` is the identity recorded at spawn, never re-read here: reading
51
+ * it now would hand `killGroupVerified` the start time of whatever holds that
52
+ * pid at this instant, so its pid-reuse guard could never disagree and a
53
+ * recycled pid would take our SIGTERM/SIGKILL. An already-exited child is the
54
+ * very case where the pid is free for reuse, so it returns before signalling.
55
+ *
56
+ * Returns the pid when it could NOT be confirmed dead. That is the one outcome
57
+ * worth saying out loud: no pid was ever returned, so no lease row records it
58
+ * and `pool gc` has nothing to reclaim it by — the caller must put it in the
59
+ * error it is about to throw, or a live public URL leaves no trace at all.
60
+ */
61
+ async function abandon(proc, spawnedStart) {
62
+ const pid = proc.pid;
63
+ if (pid === undefined || proc.exitCode !== null)
64
+ return undefined;
65
+ try {
66
+ return (await killGroupVerified(pid, spawnedStart)) ? undefined : pid;
67
+ }
68
+ catch {
69
+ return pid;
70
+ }
71
+ }
72
+ /** Append the surviving pid to an error's detail, so the operator can find it. */
73
+ function withSurvivor(err, pid) {
74
+ if (pid === undefined || !(err instanceof BrokerError))
75
+ return err;
76
+ const warning = `the preview process (pid ${pid}) could NOT be confirmed dead and was never recorded anywhere —` +
77
+ ` it may still be serving the preview URL; kill it by hand`;
78
+ return new BrokerError(err.klass, err.message, err.source, `${err.logExcerpt ? `${err.logExcerpt}\n` : ''}${warning}`);
79
+ }
80
+ class CloudflareQuickPublisher {
81
+ name = 'cloudflare-quick';
82
+ checkPrerequisite() {
83
+ const bin = cloudflaredBin();
84
+ if (!cloudflaredAvailable(bin)) {
85
+ throw new BrokerError('env-error', `preview requires cloudflared (Cloudflare quick tunnel) — install it and ensure it is on PATH, or set BACKLOT_CLOUDFLARED to its path`, 'preview');
86
+ }
87
+ }
88
+ async start(opts) {
89
+ this.checkPrerequisite();
90
+ const bin = cloudflaredBin();
91
+ mkdirSync(opts.logDir, { recursive: true });
92
+ const logPath = join(opts.logDir, `preview-${opts.service}.log`);
93
+ const tagName = `preview:${opts.service}`;
94
+ const proc = spawn(bin, ['tunnel', '--url', opts.localUrl], {
95
+ env: { ...process.env, ...serviceTag(opts.envId, tagName, stateRoot()) },
96
+ stdio: ['ignore', 'pipe', 'pipe'],
97
+ detached: true,
98
+ });
99
+ const spawnedStart = proc.pid === undefined ? undefined : startTime(proc.pid);
100
+ let buf = '';
101
+ const sink = (d) => {
102
+ const s = d.toString();
103
+ buf = (buf + s).slice(-16_000);
104
+ try {
105
+ appendFileSync(logPath, s);
106
+ }
107
+ catch {
108
+ /* log dir gone */
109
+ }
110
+ };
111
+ proc.stdout?.on('data', sink);
112
+ proc.stderr?.on('data', sink);
113
+ let url;
114
+ try {
115
+ url = await new Promise((resolve, reject) => {
116
+ const deadline = now() + startTimeoutMs();
117
+ const poll = () => {
118
+ const m = URL_RE.exec(buf);
119
+ if (m)
120
+ return resolve(m[0]);
121
+ if (proc.exitCode !== null) {
122
+ return reject(new BrokerError('env-error', `cloudflared exited before publishing a URL (exit ${proc.exitCode})`, 'preview', buf.slice(-2000)));
123
+ }
124
+ if (now() > deadline) {
125
+ return reject(new BrokerError('env-error', 'timed out waiting for cloudflared to publish a preview URL', 'preview', buf.slice(-2000)));
126
+ }
127
+ setTimeout(poll, 100).unref();
128
+ };
129
+ proc.on('error', (err) => reject(new BrokerError('env-error', `failed to start cloudflared: ${err.message}`, 'preview')));
130
+ poll();
131
+ });
132
+ }
133
+ catch (err) {
134
+ // Giving up on the WAIT is not giving up on the PROCESS. A tunnel that
135
+ // was merely slow publishes its URL a second after the timeout, and with
136
+ // no pid returned there is no lease record, no `pool gc` entry and no
137
+ // `preview stop` that could ever name it — a public, unauthenticated URL
138
+ // serving until the host reboots.
139
+ throw withSurvivor(err, await abandon(proc, spawnedStart));
140
+ }
141
+ const pid = proc.pid;
142
+ if (!pid) {
143
+ throw withSurvivor(new BrokerError('env-error', 'cloudflared started without a pid', 'preview'), await abandon(proc, spawnedStart));
144
+ }
145
+ return { url, pid: { pid, startTime: spawnedStart } };
146
+ }
147
+ async stop(rec) {
148
+ return killGroupVerified(rec.pid, rec.startTime);
149
+ }
150
+ }
151
+ /**
152
+ * cloudflared prints this once a connection to the edge stands. A named tunnel
153
+ * has no URL to announce — we already know the hostname — so readiness is the
154
+ * only thing worth waiting for. Waiting for nothing and returning immediately
155
+ * would hand back a URL that 502s for the next few seconds, which reads as a
156
+ * broken service rather than a tunnel still dialling.
157
+ */
158
+ const NAMED_READY_RE = /Registered tunnel connection/;
159
+ /** Where `cloudflared tunnel login` left the certificate that authorises the zone. */
160
+ function originCert() {
161
+ const override = process.env.TUNNEL_ORIGIN_CERT?.trim();
162
+ return override || join(homedir(), '.cloudflared', 'cert.pem');
163
+ }
164
+ /** Every dotted name in a line of cloudflared output, normalised for comparison. */
165
+ function hostnamesIn(text) {
166
+ return (text.match(/[a-z0-9][a-z0-9-]*(?:\.[a-z0-9][a-z0-9-]*)+\.?/gi) ?? []).map((h) => h.toLowerCase().replace(/\.$/, ''));
167
+ }
168
+ /**
169
+ * `route dns` reports success for a hostname that is NOT the one we asked for.
170
+ *
171
+ * An origin certificate authorises exactly one zone. Ask cloudflared to route a
172
+ * name outside it and it does not refuse — it treats the name as a label
173
+ * *inside* the certificate's zone, creates
174
+ * `backoffice-bcd.baustelle.dev.some-other.com`, prints `Added CNAME …` and
175
+ * exits 0. runly then hands back `https://backoffice-bcd.baustelle.dev`: an
176
+ * address that resolves nowhere, reported as a working preview.
177
+ *
178
+ * That happened on the first real setup, and it was caught by someone happening
179
+ * to run `dig` — which is not a control. The output names the record it really
180
+ * made, so compare it and refuse.
181
+ *
182
+ * Silence is not consent: cloudflared has printed several shapes of this line
183
+ * over the years ("Added CNAME x", "x is already configured to route to your
184
+ * tunnel"). If NO hostname can be found in the output at all, this passes rather
185
+ * than fails — refusing on an unrecognised-but-successful message would break
186
+ * publishing for a cloudflared whose wording we have not seen. The failure this
187
+ * guards is the one where a DIFFERENT name is stated plainly.
188
+ */
189
+ function assertRouted(output, wanted) {
190
+ const seen = hostnamesIn(output);
191
+ if (seen.length === 0 || seen.includes(wanted))
192
+ return;
193
+ const swallowed = seen.find((h) => h.startsWith(`${wanted}.`));
194
+ const zone = swallowed?.slice(wanted.length + 1);
195
+ throw new BrokerError('env-error', zone
196
+ ? `cloudflared routed '${swallowed}' instead of '${wanted}' — the origin certificate authorises '${zone}', not the zone you asked for. Move ~/.cloudflared/cert.pem aside and run 'cloudflared tunnel login' for that zone.`
197
+ : `cloudflared reported routing '${seen.join(', ')}' when '${wanted}' was requested`, 'preview', output.slice(-2000));
198
+ }
199
+ /**
200
+ * One DNS label, from something a human wrote in a manifest.
201
+ *
202
+ * A hostname that Cloudflare rejects surfaces three steps later as a DNS error
203
+ * during `route dns`, long after the tunnel exists — so the bad character is
204
+ * caught here, where the name is still just a string.
205
+ */
206
+ function label(raw) {
207
+ const cleaned = raw
208
+ .toLowerCase()
209
+ .replace(/[^a-z0-9-]+/g, '-')
210
+ .replace(/^-+|-+$/g, '');
211
+ if (!cleaned)
212
+ throw new BrokerError('work-error', `'${raw}' has no character a DNS label may contain`, 'preview');
213
+ return cleaned.slice(0, 63).replace(/-+$/g, '');
214
+ }
215
+ /**
216
+ * Run cloudflared and keep BOTH streams.
217
+ *
218
+ * `execFileSync` hands back stdout alone, and cloudflared says the thing that
219
+ * matters — which record it actually created — on **stderr**. A caller that
220
+ * only sees stdout gets an empty string and concludes nothing is wrong, which
221
+ * is precisely the failure `assertRouted` exists to catch. Machine-readable
222
+ * output (`--output json`) stays on stdout, so the two are kept apart rather
223
+ * than concatenated.
224
+ */
225
+ function cf(bin, args, what) {
226
+ const res = spawnSync(bin, args, { encoding: 'utf8' });
227
+ if (res.error) {
228
+ throw new BrokerError('env-error', `${what} failed: ${res.error.message}`, 'preview');
229
+ }
230
+ if (res.status !== 0) {
231
+ throw new BrokerError('env-error', `${what} failed`, 'preview', String(res.stderr ?? '').slice(-2000));
232
+ }
233
+ return { out: String(res.stdout ?? ''), err: String(res.stderr ?? '') };
234
+ }
235
+ /**
236
+ * The tunnel's id, creating it on first use.
237
+ *
238
+ * Reused rather than recreated: the tunnel and its DNS record are what make the
239
+ * hostname stable across leases, which is the whole point of naming one. A
240
+ * tunnel per publish would churn objects in the operator's Cloudflare account
241
+ * every few minutes and leave the reaping of them as a second lifecycle to get
242
+ * right.
243
+ */
244
+ function ensureTunnel(bin, name) {
245
+ const find = () => {
246
+ const raw = cf(bin, ['tunnel', 'list', '--output', 'json'], 'listing Cloudflare tunnels').out;
247
+ const rows = JSON.parse(raw || '[]');
248
+ if (!Array.isArray(rows))
249
+ return undefined;
250
+ const hit = rows.find((r) => r && typeof r === 'object' && r.name === name);
251
+ return hit ? String(hit.id ?? '') || undefined : undefined;
252
+ };
253
+ const existing = find();
254
+ if (existing)
255
+ return existing;
256
+ cf(bin, ['tunnel', 'create', name], `creating the Cloudflare tunnel '${name}'`);
257
+ const created = find();
258
+ if (!created) {
259
+ throw new BrokerError('env-error', `created the Cloudflare tunnel '${name}' but it is not in the tunnel list`, 'preview');
260
+ }
261
+ return created;
262
+ }
263
+ class CloudflareNamedPublisher {
264
+ name = 'cloudflare-named';
265
+ checkPrerequisite() {
266
+ const bin = cloudflaredBin();
267
+ if (!cloudflaredAvailable(bin)) {
268
+ throw new BrokerError('env-error', `preview requires cloudflared — install it and ensure it is on PATH, or set BACKLOT_CLOUDFLARED to its path`, 'preview');
269
+ }
270
+ const cert = originCert();
271
+ if (!existsSync(cert)) {
272
+ throw new BrokerError('env-error', `the '${this.name}' publisher needs a Cloudflare origin certificate at ${cert} — run 'cloudflared tunnel login' once, or point TUNNEL_ORIGIN_CERT at it`, 'preview');
273
+ }
274
+ }
275
+ async start(opts) {
276
+ this.checkPrerequisite();
277
+ const domain = opts.settings?.domain?.trim();
278
+ if (!domain) {
279
+ // work-error, not env-error: the machine is fine, the manifest is short a
280
+ // line. An env-error would send the operator hunting for a broken install.
281
+ throw new BrokerError('work-error', `the '${this.name}' publisher needs 'preview.domain' in the manifest (the zone to publish under, e.g. example.dev)`, 'preview');
282
+ }
283
+ // Unset prefix means the environment id, which is unique by construction —
284
+ // see PreviewSpec.prefix for why the manifest is the wrong place to make a
285
+ // pooled stack's hostnames distinct.
286
+ const prefix = label(opts.settings?.prefix?.trim() || opts.envId);
287
+ const hostname = `${label(`${opts.service}-${prefix}`)}.${domain}`;
288
+ const tunnelName = `backlot-${label(`${prefix}-${opts.service}`)}`;
289
+ const bin = cloudflaredBin();
290
+ const uuid = ensureTunnel(bin, tunnelName);
291
+ // `--overwrite-dns` because the record outlives the lease on purpose: the
292
+ // second publish of the same hostname must land on the same tunnel rather
293
+ // than fail on the record it left behind last time.
294
+ // cloudflared writes this line to stderr, so `cf` must hand both streams
295
+ // back or the check below would see an empty string and wave everything
296
+ // through — the exact failure it exists to catch.
297
+ const routed = cf(bin, ['tunnel', 'route', 'dns', '--overwrite-dns', tunnelName, hostname], `routing ${hostname} to '${tunnelName}'`);
298
+ assertRouted(`${routed.out}\n${routed.err}`, hostname);
299
+ mkdirSync(opts.logDir, { recursive: true });
300
+ const logPath = join(opts.logDir, `preview-${opts.service}.log`);
301
+ const cfgPath = join(opts.logDir, `preview-${opts.service}.tunnel.yml`);
302
+ // A catch-all is mandatory — cloudflared refuses to start without one.
303
+ writeFileSync(cfgPath, [
304
+ `tunnel: ${uuid}`,
305
+ `credentials-file: ${join(homedir(), '.cloudflared', `${uuid}.json`)}`,
306
+ 'ingress:',
307
+ ` - hostname: ${hostname}`,
308
+ ` service: ${opts.localUrl}`,
309
+ ' - service: http_status:404',
310
+ '',
311
+ ].join('\n'));
312
+ const tagName = `preview:${opts.service}`;
313
+ const proc = spawn(bin, ['tunnel', '--config', cfgPath, 'run', tunnelName], {
314
+ env: { ...process.env, ...serviceTag(opts.envId, tagName, stateRoot()) },
315
+ stdio: ['ignore', 'pipe', 'pipe'],
316
+ detached: true,
317
+ });
318
+ const spawnedStart = proc.pid === undefined ? undefined : startTime(proc.pid);
319
+ let buf = '';
320
+ const sink = (d) => {
321
+ const s = d.toString();
322
+ buf = (buf + s).slice(-16_000);
323
+ try {
324
+ appendFileSync(logPath, s);
325
+ }
326
+ catch {
327
+ /* log dir gone */
328
+ }
329
+ };
330
+ proc.stdout?.on('data', sink);
331
+ proc.stderr?.on('data', sink);
332
+ try {
333
+ await new Promise((resolve, reject) => {
334
+ const deadline = now() + startTimeoutMs();
335
+ const poll = () => {
336
+ if (NAMED_READY_RE.test(buf))
337
+ return resolve();
338
+ if (proc.exitCode !== null) {
339
+ return reject(new BrokerError('env-error', `cloudflared exited before the tunnel connected (exit ${proc.exitCode})`, 'preview', buf.slice(-2000)));
340
+ }
341
+ if (now() > deadline) {
342
+ return reject(new BrokerError('env-error', `timed out waiting for the tunnel to '${hostname}' to connect`, 'preview', buf.slice(-2000)));
343
+ }
344
+ setTimeout(poll, 100).unref();
345
+ };
346
+ proc.on('error', (err) => reject(new BrokerError('env-error', `failed to start cloudflared: ${err.message}`, 'preview')));
347
+ poll();
348
+ });
349
+ }
350
+ catch (err) {
351
+ // Same reasoning as the quick publisher: giving up on the WAIT is not
352
+ // giving up on the PROCESS. A tunnel that merely connected slowly serves
353
+ // the hostname a second after the timeout, and with no pid returned there
354
+ // is nothing that could ever name it.
355
+ throw withSurvivor(err, await abandon(proc, spawnedStart));
356
+ }
357
+ const pid = proc.pid;
358
+ if (!pid) {
359
+ throw withSurvivor(new BrokerError('env-error', 'cloudflared started without a pid', 'preview'), await abandon(proc, spawnedStart));
360
+ }
361
+ return { url: `https://${hostname}`, pid: { pid, startTime: spawnedStart } };
362
+ }
363
+ /**
364
+ * Only the process goes. The tunnel and its DNS record stay, and that is the
365
+ * feature: the next publish of this service reaches the same address, which
366
+ * is the reason to name one at all. Until then the hostname answers with
367
+ * Cloudflare's "tunnel not running" page — a visible, correct answer, not a
368
+ * pointer at someone else's service.
369
+ */
370
+ async stop(rec) {
371
+ return killGroupVerified(rec.pid, rec.startTime);
372
+ }
373
+ }
374
+ /**
375
+ * `tailscale serve` prints this once the tailnet listener stands. Foreground
376
+ * serve has no other readiness signal, and the mapping it announces is live the
377
+ * moment the line is out.
378
+ */
379
+ const TS_READY_RE = /Available within your tailnet|Available on the internet/;
380
+ /**
381
+ * Auto-derived tailnet ports land in the TUNNEL block (decision 0033,
382
+ * 32000–32767 by default) unless `preview.https_port` or `--https-port` pins
383
+ * one. Until 0.14 they were 21000–21999, inside what is now the public block.
384
+ */
385
+ function tailscaleBin() {
386
+ const override = process.env.BACKLOT_TAILSCALE?.trim();
387
+ if (override)
388
+ return override;
389
+ try {
390
+ return execFileSync('which', ['tailscale'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
391
+ }
392
+ catch {
393
+ return 'tailscale';
394
+ }
395
+ }
396
+ function ts(bin, args, what) {
397
+ try {
398
+ return execFileSync(bin, args, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 15_000 });
399
+ }
400
+ catch (err) {
401
+ const detail = err && typeof err === 'object' && 'stderr' in err ? String(err.stderr ?? '') : '';
402
+ throw new BrokerError('env-error', `${what} failed`, 'preview', detail.slice(-2000));
403
+ }
404
+ }
405
+ function tsJson(bin, args, what) {
406
+ const raw = ts(bin, args, what);
407
+ try {
408
+ const parsed = JSON.parse(raw || '{}');
409
+ return parsed && typeof parsed === 'object' ? parsed : {};
410
+ }
411
+ catch {
412
+ throw new BrokerError('env-error', `${what} returned something that is not JSON`, 'preview', raw.slice(-2000));
413
+ }
414
+ }
415
+ /**
416
+ * Every HTTPS port this machine already serves on its tailnet name — the
417
+ * persistent (`--bg`) config and every live foreground session alike. A port
418
+ * taken by anyone, runly or a human with `tailscale serve`, is not ours to
419
+ * take over: tailscale would refuse the listener anyway, but only after the
420
+ * process had started, which reads as a broken tool rather than a busy port.
421
+ */
422
+ export function tailnetPortsInUse(serveStatus) {
423
+ const used = new Set();
424
+ const addTcp = (cfg) => {
425
+ const tcp = cfg && typeof cfg === 'object' ? cfg.TCP : undefined;
426
+ if (!tcp || typeof tcp !== 'object')
427
+ return;
428
+ for (const k of Object.keys(tcp)) {
429
+ const n = Number(k);
430
+ if (Number.isInteger(n) && n > 0)
431
+ used.add(n);
432
+ }
433
+ };
434
+ addTcp(serveStatus);
435
+ const fg = serveStatus.Foreground;
436
+ if (fg && typeof fg === 'object')
437
+ for (const session of Object.values(fg))
438
+ addTcp(session);
439
+ return used;
440
+ }
441
+ /**
442
+ * The tailnet port for an environment's service when the manifest pins none.
443
+ *
444
+ * Derived, not allocated: the same environment and service land on the same
445
+ * port every time, so a re-publish after a rebind — or after `preview stop` —
446
+ * hands the reviewer the address they already have, which is what makes a
447
+ * tailnet name worth having. Ports are stable for an environment's lifetime
448
+ * (decision 0004), and so is this. A collision walks forward to the next free
449
+ * port inside the span; a full span is an env-error, not a silent reuse.
450
+ */
451
+ export function deriveTailnetPort(envId, service, inUse) {
452
+ const { lo, hi } = tunnelBlock();
453
+ const span = hi - lo + 1;
454
+ const h = createHash('sha256').update(`${envId}\0${service}`).digest().readUInt32BE(0);
455
+ const start = h % span;
456
+ for (let i = 0; i < span; i++) {
457
+ const port = lo + ((start + i) % span);
458
+ if (!inUse.has(port))
459
+ return port;
460
+ }
461
+ throw new BrokerError('env-error', `every tailnet port in ${lo}-${hi} is already served on this machine — free some ('tailscale serve status') or pin preview.https_port`, 'preview');
462
+ }
463
+ /**
464
+ * Publishes on this machine's tailnet name through `tailscale serve`, in the
465
+ * FOREGROUND (decision 0031).
466
+ *
467
+ * Foreground is the whole design. A foreground serve config is a session of
468
+ * the `tailscale serve` process: tailscaled drops it the moment that process
469
+ * exits, however it exits — SIGTERM and SIGKILL alike (measured on tailscale
470
+ * 1.102). So the mapping lives exactly as long as a supervised, tagged child of
471
+ * the lease, and every reap path 0027 built for a tunnel process reaps the URL
472
+ * with it. `--bg` would write a persistent config that survives the process,
473
+ * the lease and a daemon crash, with nothing in the journal able to name it.
474
+ *
475
+ * It runs as the daemon's user, never through sudo. sudo with `use_pty` puts
476
+ * the command in its own process group, out of reach of the group kill, and
477
+ * `env_reset` strips the runly tags `pool gc` finds orphans by — a SIGKILLed
478
+ * sudo then leaves a live `tailscale serve` that nothing will ever reap. The
479
+ * operator therefore has to be allowed to drive serve: `tailscale set
480
+ * --operator=<user>`, once per machine.
481
+ */
482
+ class TailscalePublisher {
483
+ name = 'tailscale';
484
+ checkPrerequisite() {
485
+ const bin = tailscaleBin();
486
+ if (!cloudflaredAvailable(bin)) {
487
+ throw new BrokerError('env-error', `the '${this.name}' preview publisher requires the tailscale CLI — install it and ensure it is on PATH, or set BACKLOT_TAILSCALE to its path`, 'preview');
488
+ }
489
+ this.self(bin);
490
+ const user = userInfo();
491
+ if (user.uid !== 0) {
492
+ const prefs = tsJson(bin, ['debug', 'prefs'], 'reading tailscale prefs');
493
+ const operator = typeof prefs.OperatorUser === 'string' ? prefs.OperatorUser : '';
494
+ if (operator !== user.username) {
495
+ throw new BrokerError('env-error', `the '${this.name}' publisher runs 'tailscale serve' as '${user.username}', which is not this machine's tailscale operator` +
496
+ `${operator ? ` ('${operator}' is)` : ''} — run 'sudo tailscale set --operator=${user.username}' once. ` +
497
+ `sudo per publish is not an option: it moves serve out of the process group and environment runly reaps by.`, 'preview');
498
+ }
499
+ }
500
+ }
501
+ /** This machine's tailnet name, refusing a tailscaled that could not serve HTTPS on it. */
502
+ self(bin) {
503
+ const status = tsJson(bin, ['status', '--json'], 'reading tailscale status');
504
+ const backend = typeof status.BackendState === 'string' ? status.BackendState : '';
505
+ if (backend !== 'Running') {
506
+ throw new BrokerError('env-error', `tailscale is not connected on this machine (state '${backend || 'unknown'}') — run 'tailscale up'`, 'preview');
507
+ }
508
+ const certDomains = Array.isArray(status.CertDomains) ? status.CertDomains.filter((d) => typeof d === 'string') : [];
509
+ const selfDns = status.Self?.DNSName;
510
+ const dns = typeof selfDns === 'string' ? selfDns.replace(/\.$/, '') : '';
511
+ if (!dns || certDomains.length === 0) {
512
+ throw new BrokerError('env-error', `this tailnet cannot issue HTTPS certificates for '${dns || 'this machine'}' — enable MagicDNS and HTTPS Certificates in the tailscale admin console (DNS page)`, 'preview');
513
+ }
514
+ return certDomains.includes(dns) ? dns : certDomains[0];
515
+ }
516
+ async start(opts) {
517
+ this.checkPrerequisite();
518
+ const bin = tailscaleBin();
519
+ const host = this.self(bin);
520
+ const inUse = tailnetPortsInUse(tsJson(bin, ['serve', 'status', '--json'], 'reading tailscale serve status'));
521
+ const pinned = opts.settings?.https_port;
522
+ if (pinned !== undefined && inUse.has(pinned)) {
523
+ // work-error, not env-error: nothing on this machine is broken — the
524
+ // manifest pinned a port another publication (or a human) already holds,
525
+ // and taking it over would silently repoint someone else's address.
526
+ throw new BrokerError('work-error', `tailnet port ${pinned} (preview.https_port) is already served on this machine — 'tailscale serve status' shows by what; unpin it to get a derived port, or free it`, 'preview');
527
+ }
528
+ const port = pinned ?? deriveTailnetPort(opts.envId, opts.service, inUse);
529
+ const url = `https://${host}${port === 443 ? '' : `:${port}`}`;
530
+ mkdirSync(opts.logDir, { recursive: true });
531
+ const logPath = join(opts.logDir, `preview-${opts.service}.log`);
532
+ const tagName = `preview:${opts.service}`;
533
+ const proc = spawn(bin, ['serve', `--https=${port}`, opts.localUrl], {
534
+ env: { ...process.env, ...serviceTag(opts.envId, tagName, stateRoot()) },
535
+ stdio: ['ignore', 'pipe', 'pipe'],
536
+ detached: true,
537
+ });
538
+ const spawnedStart = proc.pid === undefined ? undefined : startTime(proc.pid);
539
+ let buf = '';
540
+ const sink = (d) => {
541
+ const s = d.toString();
542
+ buf = (buf + s).slice(-16_000);
543
+ try {
544
+ appendFileSync(logPath, s);
545
+ }
546
+ catch {
547
+ /* log dir gone */
548
+ }
549
+ };
550
+ proc.stdout?.on('data', sink);
551
+ proc.stderr?.on('data', sink);
552
+ try {
553
+ await new Promise((resolve, reject) => {
554
+ const deadline = now() + startTimeoutMs();
555
+ const poll = () => {
556
+ if (TS_READY_RE.test(buf))
557
+ return resolve();
558
+ if (proc.exitCode !== null) {
559
+ const denied = /access denied|permission denied/i.test(buf);
560
+ return reject(new BrokerError('env-error', denied
561
+ ? `tailscale refused to change the serve config for '${userInfo().username}' — run 'sudo tailscale set --operator=${userInfo().username}' once`
562
+ : `tailscale serve exited before the tailnet listener stood (exit ${proc.exitCode})`, 'preview', buf.slice(-2000)));
563
+ }
564
+ if (now() > deadline) {
565
+ return reject(new BrokerError('env-error', `timed out waiting for tailscale to serve ${url}`, 'preview', buf.slice(-2000)));
566
+ }
567
+ setTimeout(poll, 100).unref();
568
+ };
569
+ proc.on('error', (err) => reject(new BrokerError('env-error', `failed to start tailscale serve: ${err.message}`, 'preview')));
570
+ poll();
571
+ });
572
+ }
573
+ catch (err) {
574
+ // As for the tunnels: a serve that was merely slow stands a second after
575
+ // the timeout, and with no pid returned nothing could ever name it.
576
+ throw withSurvivor(err, await abandon(proc, spawnedStart));
577
+ }
578
+ const pid = proc.pid;
579
+ if (!pid) {
580
+ throw withSurvivor(new BrokerError('env-error', 'tailscale serve started without a pid', 'preview'), await abandon(proc, spawnedStart));
581
+ }
582
+ // A derived port depends on what else is served right now; a restart
583
+ // must ask for this one, not derive again.
584
+ return { url, pid: { pid, startTime: spawnedStart }, pinned: { https_port: port } };
585
+ }
586
+ /** The foreground session dies with the process, and the tailnet mapping with it. */
587
+ async stop(rec) {
588
+ return killGroupVerified(rec.pid, rec.startTime);
589
+ }
590
+ }
591
+ const publishers = {
592
+ 'cloudflare-quick': new CloudflareQuickPublisher(),
593
+ 'cloudflare-named': new CloudflareNamedPublisher(),
594
+ tailscale: new TailscalePublisher(),
595
+ };
596
+ export function resolvePreviewPublisher(name) {
597
+ const pub = publishers[name];
598
+ if (!pub) {
599
+ throw new BrokerError('work-error', `unknown preview publisher '${name}' (known: ${Object.keys(publishers).join(', ')})`, 'preview');
600
+ }
601
+ return pub;
602
+ }
603
+ /** Default publisher when the manifest and config do not name one. */
604
+ export const DEFAULT_PREVIEW_PUBLISHER = 'cloudflare-quick';
605
+ //# sourceMappingURL=preview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview.js","sourceRoot":"","sources":["../../src/drivers/preview.ts"],"names":[],"mappings":"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"}