instar 1.2.34 → 1.2.35

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.
@@ -0,0 +1,51 @@
1
+ /**
2
+ * CloudflareNamedProvider — Tier-1 persistent Cloudflare named tunnel.
3
+ *
4
+ * Extracted from the original `TunnelManager.startNamedTunnel()` and
5
+ * `startConfigFileTunnel()` so the manager can drive multiple providers
6
+ * through `TunnelProvider`. The named path supports two auth modes,
7
+ * preserved verbatim from the original behavior:
8
+ *
9
+ * - Token auth: `cloudflared` via `Tunnel.withToken(token)`. The URL
10
+ * is the configured hostname (passed via `hostname`); cloudflared
11
+ * surfaces 'connected' rather than 'url'.
12
+ * - Config-file auth: spawn `cloudflared tunnel --config <file> run`
13
+ * directly. URL is the configured hostname; we watch stderr for
14
+ * "Registered tunnel connection" / "Connection registered" to know
15
+ * the tunnel is up.
16
+ *
17
+ * `isAvailable()` returns false when neither token NOR configFile is
18
+ * configured, so the manager skips this provider on quick-only installs.
19
+ *
20
+ * Like the quick provider, this module owns ONLY spawn + URL emission
21
+ * + teardown. Retry, reconnect, fallback, and notification are owned by
22
+ * the manager per the spec's single-owner mandate.
23
+ */
24
+ import type { TunnelProvider, TunnelProviderHandle, ProviderName, ProviderTier } from './TunnelProvider.js';
25
+ export interface CloudflareNamedProviderOptions {
26
+ /** Cloudflare tunnel token (token-auth mode). */
27
+ token?: string;
28
+ /** Config file path (config-file-auth mode). */
29
+ configFile?: string;
30
+ /** Public hostname for the tunnel (e.g. echo.dawn-tunnel.dev). */
31
+ hostname?: string;
32
+ /** Start timeout in milliseconds (default: 30_000 for token; 15_000 for config-file). */
33
+ startTimeoutMs?: number;
34
+ }
35
+ export declare class CloudflareNamedProvider implements TunnelProvider {
36
+ readonly name: ProviderName;
37
+ readonly tier: ProviderTier;
38
+ private readonly token?;
39
+ private readonly configFile?;
40
+ private readonly hostname?;
41
+ private readonly startTimeoutMs?;
42
+ constructor(opts: CloudflareNamedProviderOptions);
43
+ isAvailable(): Promise<boolean>;
44
+ start(localPort: number): Promise<TunnelProviderHandle>;
45
+ private startWithToken;
46
+ private startConfigFile;
47
+ private stopHandle;
48
+ private stopChild;
49
+ private killWithEscalation;
50
+ }
51
+ //# sourceMappingURL=CloudflareNamedProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CloudflareNamedProvider.d.ts","sourceRoot":"","sources":["../../src/tunnel/CloudflareNamedProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAKH,OAAO,KAAK,EACV,cAAc,EACd,oBAAoB,EACpB,YAAY,EACZ,YAAY,EACb,MAAM,qBAAqB,CAAC;AAE7B,MAAM,WAAW,8BAA8B;IAC7C,iDAAiD;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gDAAgD;IAChD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yFAAyF;IACzF,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,qBAAa,uBAAwB,YAAW,cAAc;IAC5D,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAsB;IACjD,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAK;IAEhC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAS;gBAE7B,IAAI,EAAE,8BAA8B;IAO1C,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAMrC,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC;YAczC,cAAc;IAuE5B,OAAO,CAAC,eAAe;YAuET,UAAU;YAQV,SAAS;YAOT,kBAAkB;CAwBjC"}
@@ -0,0 +1,258 @@
1
+ /**
2
+ * CloudflareNamedProvider — Tier-1 persistent Cloudflare named tunnel.
3
+ *
4
+ * Extracted from the original `TunnelManager.startNamedTunnel()` and
5
+ * `startConfigFileTunnel()` so the manager can drive multiple providers
6
+ * through `TunnelProvider`. The named path supports two auth modes,
7
+ * preserved verbatim from the original behavior:
8
+ *
9
+ * - Token auth: `cloudflared` via `Tunnel.withToken(token)`. The URL
10
+ * is the configured hostname (passed via `hostname`); cloudflared
11
+ * surfaces 'connected' rather than 'url'.
12
+ * - Config-file auth: spawn `cloudflared tunnel --config <file> run`
13
+ * directly. URL is the configured hostname; we watch stderr for
14
+ * "Registered tunnel connection" / "Connection registered" to know
15
+ * the tunnel is up.
16
+ *
17
+ * `isAvailable()` returns false when neither token NOR configFile is
18
+ * configured, so the manager skips this provider on quick-only installs.
19
+ *
20
+ * Like the quick provider, this module owns ONLY spawn + URL emission
21
+ * + teardown. Retry, reconnect, fallback, and notification are owned by
22
+ * the manager per the spec's single-owner mandate.
23
+ */
24
+ import fs from 'node:fs';
25
+ import { spawn } from 'node:child_process';
26
+ import { bin, install, Tunnel } from 'cloudflared';
27
+ export class CloudflareNamedProvider {
28
+ name = 'cloudflare-named';
29
+ tier = 1;
30
+ token;
31
+ configFile;
32
+ hostname;
33
+ startTimeoutMs;
34
+ constructor(opts) {
35
+ this.token = opts.token;
36
+ this.configFile = opts.configFile;
37
+ this.hostname = opts.hostname;
38
+ this.startTimeoutMs = opts.startTimeoutMs;
39
+ }
40
+ async isAvailable() {
41
+ if (!this.token && !this.configFile)
42
+ return false;
43
+ if (this.configFile && !fs.existsSync(this.configFile))
44
+ return false;
45
+ return typeof bin === 'string' && bin.length > 0;
46
+ }
47
+ start(localPort) {
48
+ // Named tunnel doesn't actually use localPort — the hostname is
49
+ // pre-configured in Cloudflare to map to the agent's local port.
50
+ // Accepting the parameter keeps the TunnelProvider signature uniform.
51
+ void localPort;
52
+ if (this.configFile) {
53
+ return this.startConfigFile();
54
+ }
55
+ if (this.token) {
56
+ return this.startWithToken();
57
+ }
58
+ return Promise.reject(new Error('binary-missing: named tunnel requires either a token or a configFile'));
59
+ }
60
+ async startWithToken() {
61
+ if (!fs.existsSync(bin)) {
62
+ try {
63
+ await install(bin);
64
+ }
65
+ catch (err) {
66
+ throw new Error(`binary-missing: cloudflared install failed: ${err.message}`);
67
+ }
68
+ }
69
+ return new Promise((resolve, reject) => {
70
+ let tunnel;
71
+ try {
72
+ tunnel = Tunnel.withToken(this.token);
73
+ }
74
+ catch (err) {
75
+ reject(new Error(`process-exit: failed to spawn cloudflared (token): ${err.message}`));
76
+ return;
77
+ }
78
+ const hostname = this.hostname;
79
+ const timeoutMs = this.startTimeoutMs ?? 30_000;
80
+ let resolved = false;
81
+ const timeout = setTimeout(() => {
82
+ if (resolved)
83
+ return;
84
+ resolved = true;
85
+ try {
86
+ tunnel.stop();
87
+ }
88
+ catch { /* may already be dead */ }
89
+ reject(new Error('timeout: cloudflared named tunnel did not connect within the start budget'));
90
+ }, timeoutMs);
91
+ const finalize = (url) => {
92
+ resolved = true;
93
+ clearTimeout(timeout);
94
+ resolve({
95
+ url,
96
+ stop: () => this.stopHandle(tunnel),
97
+ });
98
+ };
99
+ tunnel.once('url', (url) => {
100
+ if (resolved)
101
+ return;
102
+ finalize(url);
103
+ });
104
+ tunnel.once('connected', () => {
105
+ // For token-auth named tunnels, cloudflared often surfaces
106
+ // 'connected' before/instead of 'url'; the public URL is the
107
+ // pre-configured hostname.
108
+ if (resolved)
109
+ return;
110
+ if (hostname) {
111
+ finalize(`https://${hostname}`);
112
+ }
113
+ });
114
+ tunnel.on('error', (err) => {
115
+ if (resolved)
116
+ return;
117
+ resolved = true;
118
+ clearTimeout(timeout);
119
+ const msg = (err?.message ?? '').toLowerCase();
120
+ if (msg.includes('429') || msg.includes('1015') || msg.includes('rate limit')) {
121
+ reject(new Error(`rate-limited: cloudflared named tunnel rate-limited: ${err.message}`));
122
+ return;
123
+ }
124
+ reject(err);
125
+ });
126
+ tunnel.on('exit', (code) => {
127
+ if (resolved)
128
+ return;
129
+ resolved = true;
130
+ clearTimeout(timeout);
131
+ reject(new Error(`process-exit code ${code}: named tunnel exited before URL emit`));
132
+ });
133
+ });
134
+ }
135
+ startConfigFile() {
136
+ const configFile = this.configFile;
137
+ const hostname = this.hostname;
138
+ const timeoutMs = this.startTimeoutMs ?? 15_000;
139
+ return new Promise((resolve, reject) => {
140
+ if (!fs.existsSync(configFile)) {
141
+ reject(new Error(`binary-missing: tunnel config file not found: ${configFile}`));
142
+ return;
143
+ }
144
+ if (!fs.existsSync(bin)) {
145
+ // Spawn will fail; surface as binary-missing for consistency.
146
+ reject(new Error(`binary-missing: cloudflared binary not installed: ${bin}`));
147
+ return;
148
+ }
149
+ const child = spawn(bin, ['tunnel', '--config', configFile, 'run'], {
150
+ stdio: ['ignore', 'pipe', 'pipe'],
151
+ });
152
+ let resolved = false;
153
+ let stderrBuffer = '';
154
+ const timeout = setTimeout(() => {
155
+ if (resolved)
156
+ return;
157
+ resolved = true;
158
+ // Config-file tunnels sometimes never log a 'connection registered'
159
+ // line but still come up. If hostname is configured, accept it as
160
+ // the URL — same behavior as the legacy implementation.
161
+ if (hostname) {
162
+ resolve({
163
+ url: `https://${hostname}`,
164
+ stop: () => this.stopChild(child),
165
+ });
166
+ }
167
+ else {
168
+ try {
169
+ child.kill('SIGTERM');
170
+ }
171
+ catch { /* already dead */ }
172
+ reject(new Error('timeout: named config-file tunnel timed out and no hostname configured'));
173
+ }
174
+ }, timeoutMs);
175
+ child.stderr.on('data', (data) => {
176
+ const line = data.toString();
177
+ stderrBuffer = (stderrBuffer + line).slice(-500);
178
+ if (resolved)
179
+ return;
180
+ if (line.includes('Registered tunnel connection') || line.includes('Connection registered')) {
181
+ if (hostname) {
182
+ resolved = true;
183
+ clearTimeout(timeout);
184
+ resolve({
185
+ url: `https://${hostname}`,
186
+ stop: () => this.stopChild(child),
187
+ });
188
+ }
189
+ }
190
+ });
191
+ child.on('error', (err) => {
192
+ if (resolved)
193
+ return;
194
+ resolved = true;
195
+ clearTimeout(timeout);
196
+ reject(new Error(`process-exit: failed to start config-file tunnel: ${err.message}`));
197
+ });
198
+ child.on('exit', (code) => {
199
+ if (resolved)
200
+ return;
201
+ resolved = true;
202
+ clearTimeout(timeout);
203
+ reject(new Error(`process-exit code ${code}: config-file tunnel exited before connection: ${stderrBuffer}`));
204
+ });
205
+ });
206
+ }
207
+ async stopHandle(tunnel) {
208
+ const proc = tunnel.process;
209
+ const pid = proc?.pid;
210
+ try {
211
+ tunnel.stop();
212
+ }
213
+ catch { /* already dead */ }
214
+ if (!pid)
215
+ return;
216
+ await this.killWithEscalation(pid);
217
+ }
218
+ async stopChild(child) {
219
+ const pid = child.pid;
220
+ try {
221
+ child.kill('SIGTERM');
222
+ }
223
+ catch { /* already dead */ }
224
+ if (!pid)
225
+ return;
226
+ await this.killWithEscalation(pid);
227
+ }
228
+ async killWithEscalation(pid, timeoutMs = 5_000) {
229
+ const exited = await new Promise((resolve) => {
230
+ const timer = setTimeout(() => resolve(false), timeoutMs);
231
+ try {
232
+ process.kill(pid, 0);
233
+ }
234
+ catch {
235
+ clearTimeout(timer);
236
+ resolve(true);
237
+ return;
238
+ }
239
+ const poll = setInterval(() => {
240
+ try {
241
+ process.kill(pid, 0);
242
+ }
243
+ catch {
244
+ clearInterval(poll);
245
+ clearTimeout(timer);
246
+ resolve(true);
247
+ }
248
+ }, 200);
249
+ });
250
+ if (!exited) {
251
+ try {
252
+ process.kill(pid, 'SIGKILL');
253
+ }
254
+ catch { /* already dead */ }
255
+ }
256
+ }
257
+ }
258
+ //# sourceMappingURL=CloudflareNamedProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CloudflareNamedProvider.js","sourceRoot":"","sources":["../../src/tunnel/CloudflareNamedProvider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * CloudflareQuickProvider — Tier-1 zero-config Cloudflare quick tunnel.
3
+ *
4
+ * Extracted from the original `TunnelManager.startQuickTunnel()` so the
5
+ * manager can drive multiple providers through the common
6
+ * `TunnelProvider` interface. Behavior preserved verbatim where it
7
+ * doesn't conflict with the spec's single-owner mandate:
8
+ *
9
+ * - Owns: spawning `cloudflared` with --config-isolation; resolving
10
+ * when the quick-tunnel URL emits; tearing down on stop().
11
+ * - Does NOT own: retry/backoff, reconnect-on-disconnect, failure
12
+ * notification, episode/consent state. Those are the manager's
13
+ * responsibility now (per
14
+ * specs/dev-infrastructure/tunnel-failure-resilience.md Part 1's
15
+ * "single-owner mandate" that retires the old in-provider reconnect
16
+ * loop).
17
+ *
18
+ * Failure-mode classification is surfaced via the rejection Error's
19
+ * `message`; the manager parses to `ProviderFailureReason`. This module
20
+ * uses fixed substrings the manager recognizes:
21
+ * - "rate-limited" (Cloudflare 429 / 1015 detected from cloudflared stderr)
22
+ * - "binary-missing" (the bundled cloudflared binary path doesn't exist)
23
+ * - "timeout" (URL didn't emit within the start budget)
24
+ * - "process-exit code N" (child exited before URL emit)
25
+ */
26
+ import type { TunnelProvider, TunnelProviderHandle, ProviderName, ProviderTier } from './TunnelProvider.js';
27
+ export interface CloudflareQuickProviderOptions {
28
+ /** Local port to expose. */
29
+ port: number;
30
+ /** State directory for the quick-tunnel isolation config file. */
31
+ stateDir: string;
32
+ /** Start timeout in milliseconds (default: 30_000). */
33
+ startTimeoutMs?: number;
34
+ }
35
+ export declare class CloudflareQuickProvider implements TunnelProvider {
36
+ readonly name: ProviderName;
37
+ readonly tier: ProviderTier;
38
+ private readonly port;
39
+ private readonly stateDir;
40
+ private readonly startTimeoutMs;
41
+ constructor(opts: CloudflareQuickProviderOptions);
42
+ isAvailable(): Promise<boolean>;
43
+ start(localPort: number): Promise<TunnelProviderHandle>;
44
+ private startInner;
45
+ private classifyError;
46
+ private stopHandle;
47
+ }
48
+ //# sourceMappingURL=CloudflareQuickProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CloudflareQuickProvider.d.ts","sourceRoot":"","sources":["../../src/tunnel/CloudflareQuickProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAKH,OAAO,KAAK,EACV,cAAc,EACd,oBAAoB,EACpB,YAAY,EACZ,YAAY,EACb,MAAM,qBAAqB,CAAC;AAE7B,MAAM,WAAW,8BAA8B;IAC7C,4BAA4B;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,kEAAkE;IAClE,QAAQ,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,qBAAa,uBAAwB,YAAW,cAAc;IAC5D,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAsB;IACjD,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAK;IAEhC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;gBAE5B,IAAI,EAAE,8BAA8B;IAM1C,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAQrC,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC;YASzC,UAAU;IA6FxB,OAAO,CAAC,aAAa;YAiBP,UAAU;CAmCzB"}
@@ -0,0 +1,201 @@
1
+ /**
2
+ * CloudflareQuickProvider — Tier-1 zero-config Cloudflare quick tunnel.
3
+ *
4
+ * Extracted from the original `TunnelManager.startQuickTunnel()` so the
5
+ * manager can drive multiple providers through the common
6
+ * `TunnelProvider` interface. Behavior preserved verbatim where it
7
+ * doesn't conflict with the spec's single-owner mandate:
8
+ *
9
+ * - Owns: spawning `cloudflared` with --config-isolation; resolving
10
+ * when the quick-tunnel URL emits; tearing down on stop().
11
+ * - Does NOT own: retry/backoff, reconnect-on-disconnect, failure
12
+ * notification, episode/consent state. Those are the manager's
13
+ * responsibility now (per
14
+ * specs/dev-infrastructure/tunnel-failure-resilience.md Part 1's
15
+ * "single-owner mandate" that retires the old in-provider reconnect
16
+ * loop).
17
+ *
18
+ * Failure-mode classification is surfaced via the rejection Error's
19
+ * `message`; the manager parses to `ProviderFailureReason`. This module
20
+ * uses fixed substrings the manager recognizes:
21
+ * - "rate-limited" (Cloudflare 429 / 1015 detected from cloudflared stderr)
22
+ * - "binary-missing" (the bundled cloudflared binary path doesn't exist)
23
+ * - "timeout" (URL didn't emit within the start budget)
24
+ * - "process-exit code N" (child exited before URL emit)
25
+ */
26
+ import fs from 'node:fs';
27
+ import path from 'node:path';
28
+ import { bin, install, Tunnel } from 'cloudflared';
29
+ export class CloudflareQuickProvider {
30
+ name = 'cloudflare-quick';
31
+ tier = 1;
32
+ port;
33
+ stateDir;
34
+ startTimeoutMs;
35
+ constructor(opts) {
36
+ this.port = opts.port;
37
+ this.stateDir = opts.stateDir;
38
+ this.startTimeoutMs = opts.startTimeoutMs ?? 30_000;
39
+ }
40
+ async isAvailable() {
41
+ // The cloudflared npm package installs the binary on-demand via
42
+ // install(bin). availability == we can install or already have it.
43
+ // We don't actually do the install here; we just confirm the path
44
+ // is resolvable. Install happens during start().
45
+ return typeof bin === 'string' && bin.length > 0;
46
+ }
47
+ start(localPort) {
48
+ // localPort overrides constructor-time port for flexibility, but we
49
+ // default to the constructor value.
50
+ const port = localPort || this.port;
51
+ return new Promise((resolve, reject) => {
52
+ void this.startInner(port, resolve, reject);
53
+ });
54
+ }
55
+ async startInner(port, resolve, reject) {
56
+ try {
57
+ if (!fs.existsSync(bin)) {
58
+ try {
59
+ await install(bin);
60
+ }
61
+ catch (installErr) {
62
+ reject(new Error(`binary-missing: cloudflared install failed: ${installErr.message}`));
63
+ return;
64
+ }
65
+ }
66
+ }
67
+ catch {
68
+ reject(new Error('binary-missing: cloudflared binary path unresolvable'));
69
+ return;
70
+ }
71
+ const localUrl = `http://127.0.0.1:${port}`;
72
+ let tunnel;
73
+ try {
74
+ // Empty config to prevent cloudflared from reading
75
+ // ~/.cloudflared/config.yml, whose named-tunnel ingress rules
76
+ // would override the quick-tunnel's --url proxy. Preserved from
77
+ // the original implementation.
78
+ const emptyConfig = path.join(this.stateDir, 'cloudflared-quick.yml');
79
+ const dir = path.dirname(emptyConfig);
80
+ if (!fs.existsSync(dir)) {
81
+ fs.mkdirSync(dir, { recursive: true });
82
+ }
83
+ fs.writeFileSync(emptyConfig, '# Quick tunnel — no ingress rules\n');
84
+ tunnel = Tunnel.quick(localUrl, { '--config': emptyConfig });
85
+ }
86
+ catch (err) {
87
+ reject(new Error(`process-exit: failed to spawn cloudflared: ${err.message}`));
88
+ return;
89
+ }
90
+ let resolved = false;
91
+ let urlEmitted = null;
92
+ let stderrTail = '';
93
+ const timeout = setTimeout(() => {
94
+ if (!resolved) {
95
+ resolved = true;
96
+ try {
97
+ tunnel.stop();
98
+ }
99
+ catch { /* may already be dead */ }
100
+ reject(new Error('timeout: cloudflared quick tunnel did not emit a URL within the start budget'));
101
+ }
102
+ }, this.startTimeoutMs);
103
+ tunnel.once('url', (url) => {
104
+ if (resolved)
105
+ return;
106
+ resolved = true;
107
+ clearTimeout(timeout);
108
+ urlEmitted = url;
109
+ resolve({
110
+ url,
111
+ stop: () => this.stopHandle(tunnel),
112
+ });
113
+ });
114
+ tunnel.on('error', (err) => {
115
+ // Capture stderr-ish context. The cloudflared npm wrapper emits
116
+ // 'error' for child exit failures; we use the message as is.
117
+ stderrTail = (stderrTail + ' ' + (err?.message ?? '')).slice(-500);
118
+ if (!resolved) {
119
+ resolved = true;
120
+ clearTimeout(timeout);
121
+ reject(this.classifyError(err?.message ?? '', stderrTail));
122
+ }
123
+ // After resolution, we do NOT auto-reconnect. The manager owns
124
+ // disconnect handling per the single-owner mandate.
125
+ });
126
+ tunnel.on('exit', (code) => {
127
+ if (!resolved) {
128
+ resolved = true;
129
+ clearTimeout(timeout);
130
+ const msg = code === 0
131
+ ? 'process-exit: cloudflared exited cleanly before URL emission'
132
+ : `process-exit code ${code}: ${stderrTail || 'no stderr captured'}`;
133
+ reject(this.classifyError(msg, stderrTail));
134
+ }
135
+ // Post-resolution exits are surfaced to the manager via the
136
+ // handle's lifecycle. (The handle itself doesn't reconnect.)
137
+ });
138
+ // urlEmitted is referenced only inside the closure above; this
139
+ // suppresses an unused-variable lint while documenting intent.
140
+ void urlEmitted;
141
+ }
142
+ classifyError(msg, stderr) {
143
+ const haystack = `${msg} ${stderr}`.toLowerCase();
144
+ // Cloudflare's quick-tunnel rate-limit surfaces as 429 / error 1015.
145
+ // Both substrings appear in cloudflared's stderr on rate-limit.
146
+ if (haystack.includes('429') || haystack.includes('1015') || haystack.includes('rate limit') || haystack.includes('too many requests')) {
147
+ return new Error(`rate-limited: cloudflared quick-tunnel rate-limited (${msg})`);
148
+ }
149
+ if (haystack.includes('enoent') || haystack.includes('not found') || haystack.includes('binary-missing')) {
150
+ return new Error(`binary-missing: ${msg}`);
151
+ }
152
+ if (haystack.includes('dns') || haystack.includes('eai_again') || haystack.includes('econnrefused') || haystack.includes('network')) {
153
+ return new Error(`network: ${msg}`);
154
+ }
155
+ // Preserve as-is so the manager's classifier sees the original prefix.
156
+ return new Error(msg);
157
+ }
158
+ async stopHandle(tunnel) {
159
+ // Force-stop with PID escalation, mirroring TunnelManager.forceStop's
160
+ // SIGINT → SIGKILL pattern. This is security-load-bearing per spec
161
+ // Part 5 (relay teardown must guarantee the child process dies).
162
+ const proc = tunnel.process;
163
+ const pid = proc?.pid;
164
+ try {
165
+ tunnel.stop();
166
+ }
167
+ catch { /* may already be dead */ }
168
+ if (!pid)
169
+ return;
170
+ // Poll for exit; escalate to SIGKILL after 5s.
171
+ const timeoutMs = 5_000;
172
+ const exited = await new Promise((resolve) => {
173
+ const timer = setTimeout(() => resolve(false), timeoutMs);
174
+ try {
175
+ process.kill(pid, 0);
176
+ }
177
+ catch {
178
+ clearTimeout(timer);
179
+ resolve(true);
180
+ return;
181
+ }
182
+ const poll = setInterval(() => {
183
+ try {
184
+ process.kill(pid, 0);
185
+ }
186
+ catch {
187
+ clearInterval(poll);
188
+ clearTimeout(timer);
189
+ resolve(true);
190
+ }
191
+ }, 200);
192
+ });
193
+ if (!exited) {
194
+ try {
195
+ process.kill(pid, 'SIGKILL');
196
+ }
197
+ catch { /* already dead */ }
198
+ }
199
+ }
200
+ }
201
+ //# sourceMappingURL=CloudflareQuickProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CloudflareQuickProvider.js","sourceRoot":"","sources":["../../src/tunnel/CloudflareQuickProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAiBnD,MAAM,OAAO,uBAAuB;IACzB,IAAI,GAAiB,kBAAkB,CAAC;IACxC,IAAI,GAAiB,CAAC,CAAC;IAEf,IAAI,CAAS;IACb,QAAQ,CAAS;IACjB,cAAc,CAAS;IAExC,YAAY,IAAoC;QAC9C,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACtB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC9B,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,cAAc,IAAI,MAAM,CAAC;IACtD,CAAC;IAED,KAAK,CAAC,WAAW;QACf,gEAAgE;QAChE,mEAAmE;QACnE,kEAAkE;QAClE,iDAAiD;QACjD,OAAO,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC;IACnD,CAAC;IAED,KAAK,CAAC,SAAiB;QACrB,oEAAoE;QACpE,oCAAoC;QACpC,MAAM,IAAI,GAAG,SAAS,IAAI,IAAI,CAAC,IAAI,CAAC;QACpC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,KAAK,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;QAC9C,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,UAAU,CACtB,IAAY,EACZ,OAA0C,EAC1C,MAA4B;QAE5B,IAAI,CAAC;YACH,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACxB,IAAI,CAAC;oBACH,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC;gBACrB,CAAC;gBAAC,OAAO,UAAU,EAAE,CAAC;oBACpB,MAAM,CAAC,IAAI,KAAK,CAAC,+CAAgD,UAAoB,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;oBAClG,OAAO;gBACT,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,CAAC,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC,CAAC;YAC1E,OAAO;QACT,CAAC;QAED,MAAM,QAAQ,GAAG,oBAAoB,IAAI,EAAE,CAAC;QAC5C,IAAI,MAAc,CAAC;QAEnB,IAAI,CAAC;YACH,mDAAmD;YACnD,8DAA8D;YAC9D,gEAAgE;YAChE,+BAA+B;YAC/B,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,uBAAuB,CAAC,CAAC;YACtE,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;YACtC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACxB,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACzC,CAAC;YACD,EAAE,CAAC,aAAa,CAAC,WAAW,EAAE,qCAAqC,CAAC,CAAC;YACrE,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC,CAAC;QAC/D,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,IAAI,KAAK,CAAC,8CAA+C,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YAC1F,OAAO;QACT,CAAC;QAED,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,IAAI,UAAU,GAAkB,IAAI,CAAC;QACrC,IAAI,UAAU,GAAG,EAAE,CAAC;QAEpB,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;YAC9B,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,QAAQ,GAAG,IAAI,CAAC;gBAChB,IAAI,CAAC;oBAAC,MAAM,CAAC,IAAI,EAAE,CAAC;gBAAC,CAAC;gBAAC,MAAM,CAAC,CAAC,yBAAyB,CAAC,CAAC;gBAC1D,MAAM,CAAC,IAAI,KAAK,CAAC,8EAA8E,CAAC,CAAC,CAAC;YACpG,CAAC;QACH,CAAC,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;QAExB,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,GAAW,EAAE,EAAE;YACjC,IAAI,QAAQ;gBAAE,OAAO;YACrB,QAAQ,GAAG,IAAI,CAAC;YAChB,YAAY,CAAC,OAAO,CAAC,CAAC;YACtB,UAAU,GAAG,GAAG,CAAC;YACjB,OAAO,CAAC;gBACN,GAAG;gBACH,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;aACpC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAU,EAAE,EAAE;YAChC,gEAAgE;YAChE,6DAA6D;YAC7D,UAAU,GAAG,CAAC,UAAU,GAAG,GAAG,GAAG,CAAC,GAAG,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC;YACnE,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,QAAQ,GAAG,IAAI,CAAC;gBAChB,YAAY,CAAC,OAAO,CAAC,CAAC;gBACtB,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,OAAO,IAAI,EAAE,EAAE,UAAU,CAAC,CAAC,CAAC;YAC7D,CAAC;YACD,+DAA+D;YAC/D,oDAAoD;QACtD,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAmB,EAAE,EAAE;YACxC,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,QAAQ,GAAG,IAAI,CAAC;gBAChB,YAAY,CAAC,OAAO,CAAC,CAAC;gBACtB,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC;oBACpB,CAAC,CAAC,8DAA8D;oBAChE,CAAC,CAAC,qBAAqB,IAAI,KAAK,UAAU,IAAI,oBAAoB,EAAE,CAAC;gBACvE,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC;YAC9C,CAAC;YACD,4DAA4D;YAC5D,6DAA6D;QAC/D,CAAC,CAAC,CAAC;QAEH,+DAA+D;QAC/D,+DAA+D;QAC/D,KAAK,UAAU,CAAC;IAClB,CAAC;IAEO,aAAa,CAAC,GAAW,EAAE,MAAc;QAC/C,MAAM,QAAQ,GAAG,GAAG,GAAG,IAAI,MAAM,EAAE,CAAC,WAAW,EAAE,CAAC;QAClD,qEAAqE;QACrE,gEAAgE;QAChE,IAAI,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC;YACvI,OAAO,IAAI,KAAK,CAAC,wDAAwD,GAAG,GAAG,CAAC,CAAC;QACnF,CAAC;QACD,IAAI,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC;YACzG,OAAO,IAAI,KAAK,CAAC,mBAAmB,GAAG,EAAE,CAAC,CAAC;QAC7C,CAAC;QACD,IAAI,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YACpI,OAAO,IAAI,KAAK,CAAC,YAAY,GAAG,EAAE,CAAC,CAAC;QACtC,CAAC;QACD,uEAAuE;QACvE,OAAO,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC;IAEO,KAAK,CAAC,UAAU,CAAC,MAAc;QACrC,sEAAsE;QACtE,mEAAmE;QACnE,iEAAiE;QACjE,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC;QAC5B,MAAM,GAAG,GAAG,IAAI,EAAE,GAAG,CAAC;QACtB,IAAI,CAAC;YAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,yBAAyB,CAAC,CAAC;QAC1D,IAAI,CAAC,GAAG;YAAE,OAAO;QAEjB,+CAA+C;QAC/C,MAAM,SAAS,GAAG,KAAK,CAAC;QACxB,MAAM,MAAM,GAAG,MAAM,IAAI,OAAO,CAAU,CAAC,OAAO,EAAE,EAAE;YACpD,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC,CAAC;YAC1D,IAAI,CAAC;gBACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;YACvB,CAAC;YAAC,MAAM,CAAC;gBACP,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,OAAO,CAAC,IAAI,CAAC,CAAC;gBACd,OAAO;YACT,CAAC;YACD,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,EAAE;gBAC5B,IAAI,CAAC;oBACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;gBACvB,CAAC;gBAAC,MAAM,CAAC;oBACP,aAAa,CAAC,IAAI,CAAC,CAAC;oBACpB,YAAY,CAAC,KAAK,CAAC,CAAC;oBACpB,OAAO,CAAC,IAAI,CAAC,CAAC;gBAChB,CAAC;YACH,CAAC,EAAE,GAAG,CAAC,CAAC;QACV,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,IAAI,CAAC;gBAAC,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,kBAAkB,CAAC,CAAC;QACpE,CAAC;IACH,CAAC;CACF"}