@songsid/agend 2.1.6-beta.1 → 2.1.6-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/apply-job.d.ts +114 -0
- package/dist/apply-job.js +214 -0
- package/dist/apply-job.js.map +1 -0
- package/dist/backend/credential-profile.d.ts +114 -0
- package/dist/backend/credential-profile.js +242 -0
- package/dist/backend/credential-profile.js.map +1 -0
- package/dist/backend/kiro-auth-store.d.ts +25 -0
- package/dist/backend/kiro-auth-store.js +61 -0
- package/dist/backend/kiro-auth-store.js.map +1 -0
- package/dist/backend/kiro.d.ts +8 -0
- package/dist/backend/kiro.js +31 -2
- package/dist/backend/kiro.js.map +1 -1
- package/dist/cli.js +113 -0
- package/dist/cli.js.map +1 -1
- package/dist/config-validator.js +21 -0
- package/dist/config-validator.js.map +1 -1
- package/dist/daemon.d.ts +10 -0
- package/dist/daemon.js +27 -1
- package/dist/daemon.js.map +1 -1
- package/dist/fleet-level-config.d.ts +42 -0
- package/dist/fleet-level-config.js +78 -0
- package/dist/fleet-level-config.js.map +1 -0
- package/dist/fleet-lock.d.ts +21 -0
- package/dist/fleet-lock.js +42 -8
- package/dist/fleet-lock.js.map +1 -1
- package/dist/fleet-manager.d.ts +122 -1
- package/dist/fleet-manager.js +537 -83
- package/dist/fleet-manager.js.map +1 -1
- package/dist/general-knowledge/skills/credential-profiles/SKILL.md +121 -0
- package/dist/instance-config-impact.d.ts +55 -0
- package/dist/instance-config-impact.js +152 -0
- package/dist/instance-config-impact.js.map +1 -0
- package/dist/locale.js +2 -0
- package/dist/locale.js.map +1 -1
- package/dist/outbound-handlers.d.ts +5 -1
- package/dist/outbound-handlers.js +179 -1
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +6 -5
- package/dist/outbound-schemas.js +1 -0
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/provider-probe.d.ts +45 -0
- package/dist/provider-probe.js +111 -0
- package/dist/provider-probe.js.map +1 -0
- package/dist/quickstart-api.d.ts +161 -0
- package/dist/quickstart-api.js +340 -0
- package/dist/quickstart-api.js.map +1 -0
- package/dist/quickstart.js +23 -50
- package/dist/quickstart.js.map +1 -1
- package/dist/self-restart-limit.d.ts +21 -0
- package/dist/self-restart-limit.js +129 -0
- package/dist/self-restart-limit.js.map +1 -0
- package/dist/settings-api.d.ts +15 -0
- package/dist/settings-api.js +126 -1
- package/dist/settings-api.js.map +1 -1
- package/dist/setup-auth.d.ts +113 -0
- package/dist/setup-auth.js +181 -0
- package/dist/setup-auth.js.map +1 -0
- package/dist/setup-form.d.ts +18 -0
- package/dist/setup-form.js +315 -0
- package/dist/setup-form.js.map +1 -0
- package/dist/setup-host.d.ts +162 -0
- package/dist/setup-host.js +496 -0
- package/dist/setup-host.js.map +1 -0
- package/dist/setup-marker.d.ts +8 -0
- package/dist/setup-marker.js +39 -0
- package/dist/setup-marker.js.map +1 -0
- package/dist/setup-tunnel-consent.d.ts +53 -0
- package/dist/setup-tunnel-consent.js +82 -0
- package/dist/setup-tunnel-consent.js.map +1 -0
- package/dist/tips.js +1 -1
- package/dist/tips.js.map +1 -1
- package/dist/transcript-sources.d.ts +12 -1
- package/dist/transcript-sources.js +19 -3
- package/dist/transcript-sources.js.map +1 -1
- package/dist/tunnel/cloudflared.d.ts +71 -0
- package/dist/tunnel/cloudflared.js +447 -0
- package/dist/tunnel/cloudflared.js.map +1 -0
- package/dist/tunnel/lease.d.ts +75 -0
- package/dist/tunnel/lease.js +209 -0
- package/dist/tunnel/lease.js.map +1 -0
- package/dist/tunnel/manager.d.ts +56 -0
- package/dist/tunnel/manager.js +167 -0
- package/dist/tunnel/manager.js.map +1 -0
- package/dist/tunnel/types.d.ts +119 -0
- package/dist/tunnel/types.js +33 -0
- package/dist/tunnel/types.js.map +1 -0
- package/dist/ui/dashboard.html +4 -3
- package/dist/ui/settings.html +640 -153
- package/dist/usage/providers.d.ts +16 -13
- package/dist/usage/providers.js +79 -53
- package/dist/usage/providers.js.map +1 -1
- package/dist/web-api.js +6 -4
- package/dist/web-api.js.map +1 -1
- package/dist/web-auth.d.ts +75 -0
- package/dist/web-auth.js +208 -2
- package/dist/web-auth.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloudflare Quick Tunnel as a transport, and only a transport.
|
|
3
|
+
*
|
|
4
|
+
* The URL this produces is not a credential and nothing here authorizes
|
|
5
|
+
* anything. What it must get right is narrower and harder: never run a shell,
|
|
6
|
+
* never hand the child an environment it does not need, never believe a word of
|
|
7
|
+
* its output that has not been through the validator, and never report a death
|
|
8
|
+
* it has not proven.
|
|
9
|
+
*
|
|
10
|
+
* Verified flag shape against cloudflared's documented Quick Tunnel invocation;
|
|
11
|
+
* `--config /dev/null` is load-bearing, because Cloudflare's own docs say a
|
|
12
|
+
* `~/.cloudflared/config.yaml` can stop quick tunnels working, and we cannot
|
|
13
|
+
* assume the operator's home is empty.
|
|
14
|
+
*/
|
|
15
|
+
import { spawn } from "node:child_process";
|
|
16
|
+
import { accessSync, constants as fsConstants } from "node:fs";
|
|
17
|
+
import { delimiter, isAbsolute, join } from "node:path";
|
|
18
|
+
import { probeProcess } from "../web-terminal.js";
|
|
19
|
+
import { TunnelStartError, TUNNEL_STARTUP_DEADLINE_MS, TUNNEL_STOP_GRACE_MS, } from "./types.js";
|
|
20
|
+
/** A Quick Tunnel hostname is one label under trycloudflare.com. Nothing else. */
|
|
21
|
+
const QUICK_TUNNEL_HOST = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.trycloudflare\.com$/;
|
|
22
|
+
/**
|
|
23
|
+
* Everything the child is allowed to inherit.
|
|
24
|
+
*
|
|
25
|
+
* It would otherwise get the whole environment of the shell that ran
|
|
26
|
+
* `agend setup`, which on a developer machine routinely holds cloud
|
|
27
|
+
* credentials. A tunnel needs to reach the internet; it does not need
|
|
28
|
+
* AWS_SECRET_ACCESS_KEY to do it.
|
|
29
|
+
*/
|
|
30
|
+
const ENV_ALLOW_LIST = [
|
|
31
|
+
"HTTP_PROXY", "HTTPS_PROXY", "NO_PROXY",
|
|
32
|
+
"http_proxy", "https_proxy", "no_proxy",
|
|
33
|
+
"SSL_CERT_FILE", "SSL_CERT_DIR",
|
|
34
|
+
];
|
|
35
|
+
export function minimalChildEnv(env = process.env) {
|
|
36
|
+
const out = {};
|
|
37
|
+
for (const key of ENV_ALLOW_LIST) {
|
|
38
|
+
const value = env[key];
|
|
39
|
+
if (typeof value === "string" && value !== "")
|
|
40
|
+
out[key] = value;
|
|
41
|
+
}
|
|
42
|
+
return out;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The one URL shape this provider will accept.
|
|
46
|
+
*
|
|
47
|
+
* Strict on purpose, and against the whole token rather than a match inside it:
|
|
48
|
+
* `https://evil.example/?next=https://x.trycloudflare.com/` contains a perfectly
|
|
49
|
+
* good quick-tunnel URL and must still be refused, because what the child
|
|
50
|
+
* actually printed was a link to evil.example.
|
|
51
|
+
*/
|
|
52
|
+
export function validateQuickTunnelUrl(candidate) {
|
|
53
|
+
if (candidate.length > 200)
|
|
54
|
+
return null;
|
|
55
|
+
// Control characters never appear in a URL; their presence means the token
|
|
56
|
+
// came out of an escape sequence or a mangled line, not a URL.
|
|
57
|
+
if (/[\u0000-\u001f\u007f]/.test(candidate))
|
|
58
|
+
return null;
|
|
59
|
+
let url;
|
|
60
|
+
try {
|
|
61
|
+
url = new URL(candidate);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
if (url.protocol !== "https:")
|
|
67
|
+
return null;
|
|
68
|
+
if (url.username || url.password)
|
|
69
|
+
return null;
|
|
70
|
+
if (url.port)
|
|
71
|
+
return null;
|
|
72
|
+
if (url.search || url.hash)
|
|
73
|
+
return null;
|
|
74
|
+
if (url.pathname !== "/")
|
|
75
|
+
return null;
|
|
76
|
+
if (!QUICK_TUNNEL_HOST.test(url.hostname))
|
|
77
|
+
return null;
|
|
78
|
+
return `https://${url.hostname}`;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Pull candidate URLs out of a chunk of child output.
|
|
82
|
+
*
|
|
83
|
+
* Whatever a token is, it has to be the *complete* thing the child printed —
|
|
84
|
+
* validating a fragment is how `https://evil.example/?u=https://x.trycloudflare.com/`
|
|
85
|
+
* becomes acceptable. So the two output shapes are handled as what they are
|
|
86
|
+
* rather than smashed through one splitter:
|
|
87
|
+
*
|
|
88
|
+
* - A JSON log line is parsed as JSON, and each string value is a whole token.
|
|
89
|
+
* Splitting it on quotes and commas instead would pull a hostname out of a
|
|
90
|
+
* query string and call it the tunnel.
|
|
91
|
+
* - Plain text is split on whitespace and `|` only, because that is what
|
|
92
|
+
* cloudflared draws its box with and neither can occur inside a URL. Quotes
|
|
93
|
+
* and commas are deliberately NOT separators: they can appear in a URL, so
|
|
94
|
+
* splitting on them shortens a hostile token into an acceptable one.
|
|
95
|
+
*/
|
|
96
|
+
export function extractTunnelUrl(text) {
|
|
97
|
+
// Strip ANSI first so colouring around the URL does not become part of it.
|
|
98
|
+
const plain = text.replace(/\u001b\[[0-9;?]*[ -/]*[@-~]/g, "");
|
|
99
|
+
for (const line of plain.split(/\r?\n/)) {
|
|
100
|
+
const found = jsonLineUrl(line) ?? plainLineUrl(line);
|
|
101
|
+
if (found)
|
|
102
|
+
return found;
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
function plainLineUrl(line) {
|
|
107
|
+
for (const token of line.split(/[\s|]+/)) {
|
|
108
|
+
const valid = validateQuickTunnelUrl(token);
|
|
109
|
+
if (valid)
|
|
110
|
+
return valid;
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
/** Depth-bounded: a log line is not a place to walk an arbitrary object graph. */
|
|
115
|
+
function jsonLineUrl(line) {
|
|
116
|
+
const trimmed = line.trim();
|
|
117
|
+
if (!trimmed.startsWith("{"))
|
|
118
|
+
return null;
|
|
119
|
+
let parsed;
|
|
120
|
+
try {
|
|
121
|
+
parsed = JSON.parse(trimmed);
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
const walk = (value, depth) => {
|
|
127
|
+
if (depth > 4)
|
|
128
|
+
return null;
|
|
129
|
+
if (typeof value === "string")
|
|
130
|
+
return validateQuickTunnelUrl(value);
|
|
131
|
+
if (Array.isArray(value)) {
|
|
132
|
+
for (const item of value) {
|
|
133
|
+
const hit = walk(item, depth + 1);
|
|
134
|
+
if (hit)
|
|
135
|
+
return hit;
|
|
136
|
+
}
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
if (value && typeof value === "object") {
|
|
140
|
+
for (const item of Object.values(value)) {
|
|
141
|
+
const hit = walk(item, depth + 1);
|
|
142
|
+
if (hit)
|
|
143
|
+
return hit;
|
|
144
|
+
}
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
return null;
|
|
148
|
+
};
|
|
149
|
+
return walk(parsed, 0);
|
|
150
|
+
}
|
|
151
|
+
/** Bounded: a child that never prints a URL must not grow our memory. */
|
|
152
|
+
const MAX_BUFFERED_OUTPUT = 64 * 1024;
|
|
153
|
+
function resolveBinary(name, env) {
|
|
154
|
+
if (name.includes("/"))
|
|
155
|
+
return isAbsolute(name) ? name : null;
|
|
156
|
+
for (const dir of (env.PATH ?? "").split(delimiter)) {
|
|
157
|
+
if (!dir)
|
|
158
|
+
continue;
|
|
159
|
+
const candidate = join(dir, name);
|
|
160
|
+
try {
|
|
161
|
+
accessSync(candidate, fsConstants.X_OK);
|
|
162
|
+
return candidate;
|
|
163
|
+
}
|
|
164
|
+
catch { /* keep looking */ }
|
|
165
|
+
}
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
export class CloudflaredProvider {
|
|
169
|
+
opts;
|
|
170
|
+
name = "cloudflared";
|
|
171
|
+
constructor(opts = {}) {
|
|
172
|
+
this.opts = opts;
|
|
173
|
+
}
|
|
174
|
+
get env() { return this.opts.env ?? process.env; }
|
|
175
|
+
get binaryName() { return this.opts.binaryName ?? "cloudflared"; }
|
|
176
|
+
async preflight(signal) {
|
|
177
|
+
if (signal.aborted)
|
|
178
|
+
return { ok: false, errorKind: "cancelled", detail: "cancelled before preflight" };
|
|
179
|
+
const path = resolveBinary(this.binaryName, this.env);
|
|
180
|
+
if (!path) {
|
|
181
|
+
return {
|
|
182
|
+
ok: false,
|
|
183
|
+
errorKind: "binary-missing",
|
|
184
|
+
detail: `${this.binaryName} is not on PATH. Install it from Cloudflare's documentation; AgEnD never downloads it for you.`,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
try {
|
|
188
|
+
accessSync(path, fsConstants.X_OK);
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
return { ok: false, errorKind: "binary-not-executable", detail: `${path} is not executable` };
|
|
192
|
+
}
|
|
193
|
+
return { ok: true, binaryPath: path };
|
|
194
|
+
}
|
|
195
|
+
async start(ctx) {
|
|
196
|
+
const now = this.opts.now ?? Date.now;
|
|
197
|
+
const deadlineMs = this.opts.deadlineMs ?? TUNNEL_STARTUP_DEADLINE_MS;
|
|
198
|
+
// One budget for spawn, URL and readiness together. Per-step timeouts are
|
|
199
|
+
// how a startup that "never times out" happens: each step resets the clock.
|
|
200
|
+
const deadline = Math.min(now() + deadlineMs, ctx.expiresAt);
|
|
201
|
+
if (ctx.origin.hostname !== "127.0.0.1" || ctx.origin.protocol !== "http:") {
|
|
202
|
+
throw new TunnelStartError("bad-url", `refusing to expose ${ctx.origin.origin}: only http://127.0.0.1:<port> may be tunnelled`);
|
|
203
|
+
}
|
|
204
|
+
const pre = await this.preflight(ctx.signal);
|
|
205
|
+
if (!pre.ok)
|
|
206
|
+
throw new TunnelStartError(pre.errorKind, pre.detail);
|
|
207
|
+
const spawnProcess = this.opts.spawnProcess ?? spawn;
|
|
208
|
+
let child;
|
|
209
|
+
try {
|
|
210
|
+
child = spawnProcess(pre.binaryPath, [
|
|
211
|
+
"tunnel",
|
|
212
|
+
"--no-autoupdate",
|
|
213
|
+
"--config", "/dev/null",
|
|
214
|
+
"--url", ctx.origin.origin,
|
|
215
|
+
], {
|
|
216
|
+
// No shell, ever: the arguments are fixed and one of them is an origin.
|
|
217
|
+
shell: false,
|
|
218
|
+
env: minimalChildEnv(this.env),
|
|
219
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
catch (err) {
|
|
223
|
+
throw new TunnelStartError("spawn-failed", `could not start ${this.binaryName}: ${err.message}`);
|
|
224
|
+
}
|
|
225
|
+
const state = new CloudflaredHandle(child, this.opts);
|
|
226
|
+
try {
|
|
227
|
+
const base = await state.awaitUrl(deadline, ctx.signal, now);
|
|
228
|
+
const pageUrl = `${base}${ctx.pagePath}`;
|
|
229
|
+
// After the validator, before the probe: cloudflared passes the public
|
|
230
|
+
// Host through to the origin, so the origin has to be expecting it or it
|
|
231
|
+
// will refuse our own readiness check.
|
|
232
|
+
ctx.onCandidateHost?.(new URL(base).host);
|
|
233
|
+
await this.probeReady(pageUrl, ctx, deadline, now);
|
|
234
|
+
state.publish(base, pageUrl);
|
|
235
|
+
return state;
|
|
236
|
+
}
|
|
237
|
+
catch (err) {
|
|
238
|
+
// A child exists. Its death has to be proven before this failure can be
|
|
239
|
+
// reported as a clean one, or a fallback would run beside a live tunnel.
|
|
240
|
+
const stopped = await state.stop("startup failed");
|
|
241
|
+
if (!stopped.confirmed) {
|
|
242
|
+
throw new TunnelStartError(err instanceof TunnelStartError ? err.errorKind : "spawn-failed", `${err.message}; and the tunnel process could not be confirmed stopped`, { pid: stopped.pid, identity: stopped.identity });
|
|
243
|
+
}
|
|
244
|
+
throw err;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
async probeReady(pageUrl, ctx, deadline, now) {
|
|
248
|
+
const fetchPage = this.opts.fetchPage ?? defaultFetchPage;
|
|
249
|
+
let lastDetail = "no attempt completed";
|
|
250
|
+
while (now() < deadline) {
|
|
251
|
+
if (ctx.signal.aborted)
|
|
252
|
+
throw new TunnelStartError("cancelled", "cancelled during readiness probe");
|
|
253
|
+
const attempt = new AbortController();
|
|
254
|
+
const timer = setTimeout(() => attempt.abort(), Math.max(1, Math.min(5_000, deadline - now())));
|
|
255
|
+
try {
|
|
256
|
+
const res = await fetchPage(pageUrl, attempt.signal);
|
|
257
|
+
if (res.status === 200 && res.contentType.startsWith("text/html") && res.body.includes(ctx.readinessMarker)) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
lastDetail = `status ${res.status}, content-type ${res.contentType || "(none)"}`;
|
|
261
|
+
}
|
|
262
|
+
catch (err) {
|
|
263
|
+
lastDetail = err.message;
|
|
264
|
+
}
|
|
265
|
+
finally {
|
|
266
|
+
clearTimeout(timer);
|
|
267
|
+
}
|
|
268
|
+
// The edge takes a moment to learn the hostname; a failure here is
|
|
269
|
+
// expected early and only fatal at the deadline.
|
|
270
|
+
await new Promise(r => setTimeout(r, 500));
|
|
271
|
+
}
|
|
272
|
+
throw new TunnelStartError("readiness-failed", `the public URL never served this page (${lastDetail})`);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
async function defaultFetchPage(url, signal) {
|
|
276
|
+
// `manual`: a redirect is not this page, and following one would let the edge
|
|
277
|
+
// decide what we call ready.
|
|
278
|
+
const res = await fetch(url, { signal, redirect: "manual", cache: "no-store" });
|
|
279
|
+
return {
|
|
280
|
+
status: res.status,
|
|
281
|
+
contentType: res.headers.get("content-type") ?? "",
|
|
282
|
+
body: res.status === 200 ? await res.text() : "",
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
class CloudflaredHandle {
|
|
286
|
+
child;
|
|
287
|
+
opts;
|
|
288
|
+
provider = "cloudflared";
|
|
289
|
+
visibility = "public";
|
|
290
|
+
baseUrl = "";
|
|
291
|
+
pageUrl = "";
|
|
292
|
+
pid;
|
|
293
|
+
identity;
|
|
294
|
+
buffered = "";
|
|
295
|
+
urlResolved = null;
|
|
296
|
+
foundBase = null;
|
|
297
|
+
exit = null;
|
|
298
|
+
/**
|
|
299
|
+
* A spawn that failed before a process existed.
|
|
300
|
+
*
|
|
301
|
+
* Node reports this as `error` and never as `exit`, and leaves `pid`
|
|
302
|
+
* undefined. Without handling it the start waits out its whole deadline and
|
|
303
|
+
* then cannot prove a death — for a process that was never created, which
|
|
304
|
+
* would hold the lease and block every later tunnel over nothing.
|
|
305
|
+
*/
|
|
306
|
+
spawnError = null;
|
|
307
|
+
exitListeners = new Set();
|
|
308
|
+
published = false;
|
|
309
|
+
stopping = null;
|
|
310
|
+
constructor(child, opts) {
|
|
311
|
+
this.child = child;
|
|
312
|
+
this.opts = opts;
|
|
313
|
+
this.pid = child.pid ?? null;
|
|
314
|
+
// Recorded now, not at stop time: the fingerprint of the process we
|
|
315
|
+
// actually started is the only thing that makes a later kill safe.
|
|
316
|
+
const probeFn = opts.probe ?? probeProcess;
|
|
317
|
+
const probe = (this.pid !== null ? probeFn(this.pid) : { kind: "unknown" });
|
|
318
|
+
this.identity = probe.kind === "identified" ? probe.identity : null;
|
|
319
|
+
const absorb = (chunk) => {
|
|
320
|
+
if (this.foundBase)
|
|
321
|
+
return;
|
|
322
|
+
this.buffered = (this.buffered + String(chunk)).slice(-MAX_BUFFERED_OUTPUT);
|
|
323
|
+
const base = extractTunnelUrl(this.buffered);
|
|
324
|
+
if (!base)
|
|
325
|
+
return;
|
|
326
|
+
this.foundBase = base;
|
|
327
|
+
this.urlResolved?.(base);
|
|
328
|
+
};
|
|
329
|
+
child.stdout?.on("data", absorb);
|
|
330
|
+
child.stderr?.on("data", absorb);
|
|
331
|
+
child.on("exit", (code, signal) => {
|
|
332
|
+
this.exit = { code, signal };
|
|
333
|
+
if (this.published)
|
|
334
|
+
for (const listener of this.exitListeners)
|
|
335
|
+
listener(this.exit);
|
|
336
|
+
});
|
|
337
|
+
child.on("error", err => {
|
|
338
|
+
this.spawnError = err;
|
|
339
|
+
if (this.pid === null)
|
|
340
|
+
this.spawnFailed?.(this.spawnError);
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
spawnFailed = null;
|
|
344
|
+
/** True when we can prove no process was ever created. */
|
|
345
|
+
get neverStarted() {
|
|
346
|
+
return this.pid === null && this.spawnError !== null;
|
|
347
|
+
}
|
|
348
|
+
publish(base, pageUrl) {
|
|
349
|
+
this.baseUrl = base;
|
|
350
|
+
this.pageUrl = pageUrl;
|
|
351
|
+
this.published = true;
|
|
352
|
+
}
|
|
353
|
+
async awaitUrl(deadline, signal, now) {
|
|
354
|
+
if (this.foundBase)
|
|
355
|
+
return this.foundBase;
|
|
356
|
+
if (this.neverStarted) {
|
|
357
|
+
throw new TunnelStartError("spawn-failed", `could not start cloudflared: ${this.spawnError.message}`);
|
|
358
|
+
}
|
|
359
|
+
return new Promise((resolve, reject) => {
|
|
360
|
+
const settle = (fn) => { cleanup(); fn(); };
|
|
361
|
+
const timer = setTimeout(() => settle(() => reject(new TunnelStartError("timeout", "cloudflared did not print a usable tunnel URL in time"))), Math.max(1, deadline - now()));
|
|
362
|
+
const onAbort = () => settle(() => reject(new TunnelStartError("cancelled", "cancelled while waiting for the tunnel URL")));
|
|
363
|
+
const onExit = () => settle(() => reject(new TunnelStartError("no-url", "cloudflared exited before printing a tunnel URL")));
|
|
364
|
+
// A spawn that never produced a process must not wait out the deadline:
|
|
365
|
+
// there is nothing to wait for, and the delay is what turns a missing
|
|
366
|
+
// binary into a blocked lease.
|
|
367
|
+
const onSpawnFail = (err) => settle(() => reject(new TunnelStartError("spawn-failed", `could not start cloudflared: ${err.message}`)));
|
|
368
|
+
const cleanup = () => {
|
|
369
|
+
clearTimeout(timer);
|
|
370
|
+
signal.removeEventListener("abort", onAbort);
|
|
371
|
+
this.child.off("exit", onExit);
|
|
372
|
+
this.urlResolved = null;
|
|
373
|
+
this.spawnFailed = null;
|
|
374
|
+
};
|
|
375
|
+
this.urlResolved = base => settle(() => resolve(base));
|
|
376
|
+
this.spawnFailed = onSpawnFail;
|
|
377
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
378
|
+
this.child.on("exit", onExit);
|
|
379
|
+
if (this.exit)
|
|
380
|
+
onExit();
|
|
381
|
+
else if (this.neverStarted)
|
|
382
|
+
onSpawnFail(this.spawnError);
|
|
383
|
+
else if (signal.aborted)
|
|
384
|
+
onAbort();
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
onUnexpectedExit(listener) {
|
|
388
|
+
this.exitListeners.add(listener);
|
|
389
|
+
return () => { this.exitListeners.delete(listener); };
|
|
390
|
+
}
|
|
391
|
+
/** Single-flight: a second caller joins the first rather than re-signalling. */
|
|
392
|
+
stop(reason) {
|
|
393
|
+
this.stopping ??= this.doStop(reason);
|
|
394
|
+
return this.stopping;
|
|
395
|
+
}
|
|
396
|
+
async doStop(reason) {
|
|
397
|
+
void reason;
|
|
398
|
+
this.exitListeners.clear();
|
|
399
|
+
if (this.exit)
|
|
400
|
+
return { confirmed: true };
|
|
401
|
+
// Proven, not assumed: Node reported the spawn itself failed and never
|
|
402
|
+
// handed us a pid, so there is no process. Reporting this as unconfirmed
|
|
403
|
+
// would hold the lease against a child that does not exist.
|
|
404
|
+
if (this.neverStarted)
|
|
405
|
+
return { confirmed: true };
|
|
406
|
+
if (this.pid === null) {
|
|
407
|
+
return { confirmed: false, reason: "the process was never given a pid", pid: null, identity: null };
|
|
408
|
+
}
|
|
409
|
+
const probe = this.opts.probe ?? probeProcess;
|
|
410
|
+
const graceMs = this.opts.graceMs ?? TUNNEL_STOP_GRACE_MS;
|
|
411
|
+
for (const signal of ["SIGTERM", "SIGKILL"]) {
|
|
412
|
+
try {
|
|
413
|
+
this.child.kill(signal);
|
|
414
|
+
}
|
|
415
|
+
catch { /* may have exited between checks */ }
|
|
416
|
+
const exited = await this.waitForExit(graceMs);
|
|
417
|
+
if (exited)
|
|
418
|
+
return { confirmed: true };
|
|
419
|
+
const after = probe(this.pid);
|
|
420
|
+
if (after.kind === "gone")
|
|
421
|
+
return { confirmed: true };
|
|
422
|
+
// The pid belongs to something else now, so ours is gone — and we must
|
|
423
|
+
// not send it the next signal.
|
|
424
|
+
if (after.kind === "identified" && this.identity && after.identity !== this.identity) {
|
|
425
|
+
return { confirmed: true };
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
return {
|
|
429
|
+
confirmed: false,
|
|
430
|
+
// Never "stopped": a kill() that did not throw proves nothing, and this
|
|
431
|
+
// sentence is what stops a caller writing "closed safely" in a log.
|
|
432
|
+
reason: "the process did not exit after SIGTERM and SIGKILL, and is still running under its original identity",
|
|
433
|
+
pid: this.pid,
|
|
434
|
+
identity: this.identity,
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
waitForExit(ms) {
|
|
438
|
+
if (this.exit)
|
|
439
|
+
return Promise.resolve(true);
|
|
440
|
+
return new Promise(resolve => {
|
|
441
|
+
const timer = setTimeout(() => { this.child.off("exit", onExit); resolve(false); }, ms);
|
|
442
|
+
const onExit = () => { clearTimeout(timer); resolve(true); };
|
|
443
|
+
this.child.once("exit", onExit);
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
//# sourceMappingURL=cloudflared.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudflared.js","sourceRoot":"","sources":["../../src/tunnel/cloudflared.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { probeProcess } from "../web-terminal.js";
|
|
2
|
+
export interface TunnelLease {
|
|
3
|
+
readonly sid: string;
|
|
4
|
+
readonly provider: string;
|
|
5
|
+
readonly originPort: number;
|
|
6
|
+
/** Null only in the instant between reserving the lease and the spawn returning. */
|
|
7
|
+
readonly providerPid: number | null;
|
|
8
|
+
/** `probeProcess` identity at spawn. Null on platforms that cannot produce one. */
|
|
9
|
+
readonly strongIdentity: string | null;
|
|
10
|
+
readonly expiresAt: number;
|
|
11
|
+
/** The process that owns this lease; a live owner means it is not ours to reap. */
|
|
12
|
+
readonly ownerPid: number;
|
|
13
|
+
/**
|
|
14
|
+
* The owner's own fingerprint at the time it wrote the lease.
|
|
15
|
+
*
|
|
16
|
+
* Without it, "the owner pid is alive" is a guess: after a crash the number
|
|
17
|
+
* can belong to something entirely unrelated, and the lease would then be
|
|
18
|
+
* held forever by a process that has never heard of it. Ticket 5 had the same
|
|
19
|
+
* trap in `fleet.lock`.
|
|
20
|
+
*/
|
|
21
|
+
readonly ownerIdentity: string | null;
|
|
22
|
+
}
|
|
23
|
+
export declare function leasePath(dataDir: string): string;
|
|
24
|
+
/**
|
|
25
|
+
* Atomic, owner-only, and on disk before the call returns.
|
|
26
|
+
*
|
|
27
|
+
* The fsync is not ceremony: the lease has to survive the crash that makes it
|
|
28
|
+
* matter, and a rename that is only in page cache does not.
|
|
29
|
+
*/
|
|
30
|
+
export declare function writeLease(dataDir: string, lease: TunnelLease): void;
|
|
31
|
+
export declare function readLease(dataDir: string): TunnelLease | null;
|
|
32
|
+
export declare function clearLease(dataDir: string): void;
|
|
33
|
+
export type ReapOutcome =
|
|
34
|
+
/** No lease; a new managed tunnel may start. */
|
|
35
|
+
{
|
|
36
|
+
readonly kind: "clear";
|
|
37
|
+
}
|
|
38
|
+
/** A live owner holds it. Not ours to reap, and not ours to replace. */
|
|
39
|
+
| {
|
|
40
|
+
readonly kind: "held";
|
|
41
|
+
readonly ownerPid: number;
|
|
42
|
+
}
|
|
43
|
+
/** The process was already gone, or was killed and proven gone. Lease removed. */
|
|
44
|
+
| {
|
|
45
|
+
readonly kind: "reaped";
|
|
46
|
+
readonly how: "already-gone" | "pid-reused" | "killed";
|
|
47
|
+
readonly pid: number | null;
|
|
48
|
+
}
|
|
49
|
+
/** Could not prove death. Lease kept, and it keeps blocking. */
|
|
50
|
+
| {
|
|
51
|
+
readonly kind: "manual";
|
|
52
|
+
readonly reason: string;
|
|
53
|
+
readonly pid: number | null;
|
|
54
|
+
readonly leasePath: string;
|
|
55
|
+
};
|
|
56
|
+
export interface ReapOptions {
|
|
57
|
+
now?: () => number;
|
|
58
|
+
/** Injected so a test never signals a real process. */
|
|
59
|
+
probe?: typeof probeProcess;
|
|
60
|
+
kill?: (pid: number, signal: NodeJS.Signals) => void;
|
|
61
|
+
/** Injected so the grace periods do not make tests slow. */
|
|
62
|
+
wait?: (ms: number) => Promise<void>;
|
|
63
|
+
graceMs?: number;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Resolve whatever the last run left behind, before anything new is started.
|
|
67
|
+
*
|
|
68
|
+
* Runs from any process — the fleet at startup and the pre-fleet setup host
|
|
69
|
+
* both call it, which is why it takes a directory rather than a manager.
|
|
70
|
+
*/
|
|
71
|
+
export declare function reapStaleTunnel(dataDir: string, opts?: ReapOptions): Promise<ReapOutcome>;
|
|
72
|
+
/** What to tell a human when a lease could not be resolved. Never "closed safely". */
|
|
73
|
+
export declare function manualCleanupMessage(outcome: Extract<ReapOutcome, {
|
|
74
|
+
kind: "manual";
|
|
75
|
+
}>): string;
|