@desplega.ai/qa-use 2.14.1 → 2.15.1
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/README.md +23 -0
- package/dist/lib/api/index.d.ts +5 -1
- package/dist/lib/api/index.d.ts.map +1 -1
- package/dist/lib/api/index.js +112 -5
- package/dist/lib/api/index.js.map +1 -1
- package/dist/lib/api/sse.d.ts +22 -2
- package/dist/lib/api/sse.d.ts.map +1 -1
- package/dist/lib/api/sse.js +77 -5
- package/dist/lib/api/sse.js.map +1 -1
- package/dist/lib/env/index.d.ts +13 -0
- package/dist/lib/env/index.d.ts.map +1 -1
- package/dist/lib/env/index.js +35 -0
- package/dist/lib/env/index.js.map +1 -1
- package/dist/lib/env/localhost.d.ts +22 -0
- package/dist/lib/env/localhost.d.ts.map +1 -0
- package/dist/lib/env/localhost.js +49 -0
- package/dist/lib/env/localhost.js.map +1 -0
- package/dist/lib/env/paths.d.ts +27 -0
- package/dist/lib/env/paths.d.ts.map +1 -0
- package/dist/lib/env/paths.js +42 -0
- package/dist/lib/env/paths.js.map +1 -0
- package/dist/lib/env/sessions.d.ts +55 -0
- package/dist/lib/env/sessions.d.ts.map +1 -0
- package/dist/lib/env/sessions.js +128 -0
- package/dist/lib/env/sessions.js.map +1 -0
- package/dist/lib/tunnel/errors.d.ts +61 -0
- package/dist/lib/tunnel/errors.d.ts.map +1 -0
- package/dist/lib/tunnel/errors.js +152 -0
- package/dist/lib/tunnel/errors.js.map +1 -0
- package/dist/lib/tunnel/index.d.ts.map +1 -1
- package/dist/lib/tunnel/index.js +26 -11
- package/dist/lib/tunnel/index.js.map +1 -1
- package/dist/lib/tunnel/registry.d.ts +182 -0
- package/dist/lib/tunnel/registry.d.ts.map +1 -0
- package/dist/lib/tunnel/registry.js +561 -0
- package/dist/lib/tunnel/registry.js.map +1 -0
- package/dist/package.json +1 -1
- package/dist/src/cli/commands/browser/_detached.d.ts +27 -0
- package/dist/src/cli/commands/browser/_detached.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/_detached.js +422 -0
- package/dist/src/cli/commands/browser/_detached.js.map +1 -0
- package/dist/src/cli/commands/browser/close.d.ts +7 -0
- package/dist/src/cli/commands/browser/close.d.ts.map +1 -1
- package/dist/src/cli/commands/browser/close.js +101 -5
- package/dist/src/cli/commands/browser/close.js.map +1 -1
- package/dist/src/cli/commands/browser/create.d.ts +7 -0
- package/dist/src/cli/commands/browser/create.d.ts.map +1 -1
- package/dist/src/cli/commands/browser/create.js +233 -25
- package/dist/src/cli/commands/browser/create.js.map +1 -1
- package/dist/src/cli/commands/browser/index.d.ts.map +1 -1
- package/dist/src/cli/commands/browser/index.js +3 -0
- package/dist/src/cli/commands/browser/index.js.map +1 -1
- package/dist/src/cli/commands/browser/run.d.ts.map +1 -1
- package/dist/src/cli/commands/browser/run.js +13 -6
- package/dist/src/cli/commands/browser/run.js.map +1 -1
- package/dist/src/cli/commands/browser/status.d.ts +4 -0
- package/dist/src/cli/commands/browser/status.d.ts.map +1 -1
- package/dist/src/cli/commands/browser/status.js +85 -3
- package/dist/src/cli/commands/browser/status.js.map +1 -1
- package/dist/src/cli/commands/doctor.d.ts +45 -0
- package/dist/src/cli/commands/doctor.d.ts.map +1 -0
- package/dist/src/cli/commands/doctor.js +267 -0
- package/dist/src/cli/commands/doctor.js.map +1 -0
- package/dist/src/cli/commands/test/run.d.ts.map +1 -1
- package/dist/src/cli/commands/test/run.js +33 -19
- package/dist/src/cli/commands/test/run.js.map +1 -1
- package/dist/src/cli/commands/tunnel/close.d.ts +18 -0
- package/dist/src/cli/commands/tunnel/close.d.ts.map +1 -0
- package/dist/src/cli/commands/tunnel/close.js +154 -0
- package/dist/src/cli/commands/tunnel/close.js.map +1 -0
- package/dist/src/cli/commands/tunnel/index.d.ts +6 -0
- package/dist/src/cli/commands/tunnel/index.d.ts.map +1 -0
- package/dist/src/cli/commands/tunnel/index.js +17 -0
- package/dist/src/cli/commands/tunnel/index.js.map +1 -0
- package/dist/src/cli/commands/tunnel/ls.d.ts +10 -0
- package/dist/src/cli/commands/tunnel/ls.d.ts.map +1 -0
- package/dist/src/cli/commands/tunnel/ls.js +89 -0
- package/dist/src/cli/commands/tunnel/ls.js.map +1 -0
- package/dist/src/cli/commands/tunnel/start.d.ts +15 -0
- package/dist/src/cli/commands/tunnel/start.d.ts.map +1 -0
- package/dist/src/cli/commands/tunnel/start.js +65 -0
- package/dist/src/cli/commands/tunnel/start.js.map +1 -0
- package/dist/src/cli/commands/tunnel/status.d.ts +8 -0
- package/dist/src/cli/commands/tunnel/status.d.ts.map +1 -0
- package/dist/src/cli/commands/tunnel/status.js +58 -0
- package/dist/src/cli/commands/tunnel/status.js.map +1 -0
- package/dist/src/cli/generated/docs-content.d.ts +1 -1
- package/dist/src/cli/generated/docs-content.d.ts.map +1 -1
- package/dist/src/cli/generated/docs-content.js +157 -100
- package/dist/src/cli/generated/docs-content.js.map +1 -1
- package/dist/src/cli/index.js +8 -0
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/cli/lib/browser.d.ts +25 -9
- package/dist/src/cli/lib/browser.d.ts.map +1 -1
- package/dist/src/cli/lib/browser.js +73 -42
- package/dist/src/cli/lib/browser.js.map +1 -1
- package/dist/src/cli/lib/cli-entry.d.ts +40 -0
- package/dist/src/cli/lib/cli-entry.d.ts.map +1 -0
- package/dist/src/cli/lib/cli-entry.js +65 -0
- package/dist/src/cli/lib/cli-entry.js.map +1 -0
- package/dist/src/cli/lib/runner.d.ts +6 -0
- package/dist/src/cli/lib/runner.d.ts.map +1 -1
- package/dist/src/cli/lib/runner.js +2 -2
- package/dist/src/cli/lib/runner.js.map +1 -1
- package/dist/src/cli/lib/startup-sweep.d.ts +45 -0
- package/dist/src/cli/lib/startup-sweep.d.ts.map +1 -0
- package/dist/src/cli/lib/startup-sweep.js +246 -0
- package/dist/src/cli/lib/startup-sweep.js.map +1 -0
- package/dist/src/cli/lib/tunnel-banner.d.ts +33 -0
- package/dist/src/cli/lib/tunnel-banner.d.ts.map +1 -0
- package/dist/src/cli/lib/tunnel-banner.js +55 -0
- package/dist/src/cli/lib/tunnel-banner.js.map +1 -0
- package/dist/src/cli/lib/tunnel-error-hint.d.ts +20 -0
- package/dist/src/cli/lib/tunnel-error-hint.d.ts.map +1 -0
- package/dist/src/cli/lib/tunnel-error-hint.js +48 -0
- package/dist/src/cli/lib/tunnel-error-hint.js.map +1 -0
- package/dist/src/cli/lib/tunnel-option.d.ts +27 -0
- package/dist/src/cli/lib/tunnel-option.d.ts.map +1 -0
- package/dist/src/cli/lib/tunnel-option.js +77 -0
- package/dist/src/cli/lib/tunnel-option.js.map +1 -0
- package/dist/src/cli/lib/tunnel-resolve.d.ts +42 -0
- package/dist/src/cli/lib/tunnel-resolve.d.ts.map +1 -0
- package/dist/src/cli/lib/tunnel-resolve.js +72 -0
- package/dist/src/cli/lib/tunnel-resolve.js.map +1 -0
- package/lib/api/index.ts +136 -6
- package/lib/api/sse.test.ts +530 -0
- package/lib/api/sse.ts +105 -5
- package/lib/env/index.ts +51 -0
- package/lib/env/localhost.test.ts +63 -0
- package/lib/env/localhost.ts +51 -0
- package/lib/env/paths.ts +46 -0
- package/lib/env/sessions.test.ts +109 -0
- package/lib/env/sessions.ts +155 -0
- package/lib/tunnel/errors.test.ts +105 -0
- package/lib/tunnel/errors.ts +169 -0
- package/lib/tunnel/index.ts +26 -11
- package/lib/tunnel/registry.test.ts +420 -0
- package/lib/tunnel/registry.ts +646 -0
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"startup-sweep.js","sourceRoot":"","sources":["../../../../src/cli/lib/startup-sweep.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-tunnel banner helpers (stderr-only, TTY-aware).
|
|
3
|
+
*
|
|
4
|
+
* `printTunnelStartBanner` is the multi-line boxed notice shown when a
|
|
5
|
+
* new tunnel is started. `printTunnelReuseBanner` is the short
|
|
6
|
+
* single-line notice used when an existing tunnel is reused (Phase 3
|
|
7
|
+
* consumer, but lands here so all banner copy lives in one place).
|
|
8
|
+
*
|
|
9
|
+
* Suppression rules — banner prints nothing when ANY of the following
|
|
10
|
+
* hold:
|
|
11
|
+
* - `process.stderr.isTTY` is falsy (piped / redirected stderr)
|
|
12
|
+
* - `process.env.QA_USE_QUIET === '1'`
|
|
13
|
+
* - caller passes `{ quiet: true }`
|
|
14
|
+
*/
|
|
15
|
+
export interface TunnelBannerOptions {
|
|
16
|
+
/** The detected target URL (usually the localhost base URL). */
|
|
17
|
+
target: string;
|
|
18
|
+
/** The public tunnel URL that was just created / reused. */
|
|
19
|
+
publicUrl: string;
|
|
20
|
+
/** Opt-in quiet flag from a `--json`/`--quiet` caller. */
|
|
21
|
+
quiet?: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Multi-line boxed banner printed on a fresh tunnel start.
|
|
25
|
+
*/
|
|
26
|
+
export declare function printTunnelStartBanner(options: TunnelBannerOptions): void;
|
|
27
|
+
/**
|
|
28
|
+
* Single-line notice printed when an existing tunnel is reused.
|
|
29
|
+
* Used by the Phase-3 registry layer; lives here so all banner copy
|
|
30
|
+
* stays co-located.
|
|
31
|
+
*/
|
|
32
|
+
export declare function printTunnelReuseBanner(options: TunnelBannerOptions): void;
|
|
33
|
+
//# sourceMappingURL=tunnel-banner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tunnel-banner.d.ts","sourceRoot":"","sources":["../../../../src/cli/lib/tunnel-banner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,mBAAmB;IAClC,gEAAgE;IAChE,MAAM,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,SAAS,EAAE,MAAM,CAAC;IAClB,0DAA0D;IAC1D,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AASD;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI,CAkBzE;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI,CAIzE"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-tunnel banner helpers (stderr-only, TTY-aware).
|
|
3
|
+
*
|
|
4
|
+
* `printTunnelStartBanner` is the multi-line boxed notice shown when a
|
|
5
|
+
* new tunnel is started. `printTunnelReuseBanner` is the short
|
|
6
|
+
* single-line notice used when an existing tunnel is reused (Phase 3
|
|
7
|
+
* consumer, but lands here so all banner copy lives in one place).
|
|
8
|
+
*
|
|
9
|
+
* Suppression rules — banner prints nothing when ANY of the following
|
|
10
|
+
* hold:
|
|
11
|
+
* - `process.stderr.isTTY` is falsy (piped / redirected stderr)
|
|
12
|
+
* - `process.env.QA_USE_QUIET === '1'`
|
|
13
|
+
* - caller passes `{ quiet: true }`
|
|
14
|
+
*/
|
|
15
|
+
function shouldSuppress(quiet) {
|
|
16
|
+
if (quiet)
|
|
17
|
+
return true;
|
|
18
|
+
if (process.env.QA_USE_QUIET === '1')
|
|
19
|
+
return true;
|
|
20
|
+
if (!process.stderr.isTTY)
|
|
21
|
+
return true;
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Multi-line boxed banner printed on a fresh tunnel start.
|
|
26
|
+
*/
|
|
27
|
+
export function printTunnelStartBanner(options) {
|
|
28
|
+
if (shouldSuppress(options.quiet))
|
|
29
|
+
return;
|
|
30
|
+
const lines = [
|
|
31
|
+
'╭─ Auto-tunnel active ──────────────────────────────',
|
|
32
|
+
`│ Detected localhost target: ${options.target}`,
|
|
33
|
+
'│ Remote backend cannot reach your machine directly,',
|
|
34
|
+
'│ so qa-use is exposing it via:',
|
|
35
|
+
'│',
|
|
36
|
+
`│ ${options.publicUrl}`,
|
|
37
|
+
'│',
|
|
38
|
+
'│ Disable with --no-tunnel (or use a public URL).',
|
|
39
|
+
'╰───────────────────────────────────────────────────',
|
|
40
|
+
];
|
|
41
|
+
for (const line of lines) {
|
|
42
|
+
console.error(line);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Single-line notice printed when an existing tunnel is reused.
|
|
47
|
+
* Used by the Phase-3 registry layer; lives here so all banner copy
|
|
48
|
+
* stays co-located.
|
|
49
|
+
*/
|
|
50
|
+
export function printTunnelReuseBanner(options) {
|
|
51
|
+
if (shouldSuppress(options.quiet))
|
|
52
|
+
return;
|
|
53
|
+
console.error(`↻ Auto-tunnel reuse: ${options.target} → ${options.publicUrl}`);
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=tunnel-banner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tunnel-banner.js","sourceRoot":"","sources":["../../../../src/cli/lib/tunnel-banner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAWH,SAAS,cAAc,CAAC,KAA0B;IAChD,IAAI,KAAK;QAAE,OAAO,IAAI,CAAC;IACvB,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IAClD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACvC,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,sBAAsB,CAAC,OAA4B;IACjE,IAAI,cAAc,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO;IAE1C,MAAM,KAAK,GAAG;QACZ,sDAAsD;QACtD,gCAAgC,OAAO,CAAC,MAAM,EAAE;QAChD,sDAAsD;QACtD,iCAAiC;QACjC,GAAG;QACH,OAAO,OAAO,CAAC,SAAS,EAAE;QAC1B,GAAG;QACH,mDAAmD;QACnD,sDAAsD;KACvD,CAAC;IAEF,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACtB,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CAAC,OAA4B;IACjE,IAAI,cAAc,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO;IAE1C,OAAO,CAAC,KAAK,CAAC,wBAAwB,OAAO,CAAC,MAAM,MAAM,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;AACjF,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Triage-hint formatter for structured tunnel errors.
|
|
3
|
+
*
|
|
4
|
+
* The brainstorm defines the user-facing copy:
|
|
5
|
+
* × Auto-tunnel failed for <target>
|
|
6
|
+
* Attempted: start tunnel via <provider>
|
|
7
|
+
* Likely cause: <subclass-specific>
|
|
8
|
+
* Next steps:
|
|
9
|
+
* • Retry: ...
|
|
10
|
+
* • Skip tunnel: --no-tunnel ...
|
|
11
|
+
* • Check tunnel status: `qa-use tunnel status`
|
|
12
|
+
*
|
|
13
|
+
* Each tunnel-error subclass picks its own "likely cause" line.
|
|
14
|
+
*/
|
|
15
|
+
import { type TunnelError } from '../../../lib/tunnel/errors.js';
|
|
16
|
+
/**
|
|
17
|
+
* Return a multi-line string ready to print to stderr.
|
|
18
|
+
*/
|
|
19
|
+
export declare function formatTunnelFailure(err: TunnelError): string;
|
|
20
|
+
//# sourceMappingURL=tunnel-error-hint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tunnel-error-hint.d.ts","sourceRoot":"","sources":["../../../../src/cli/lib/tunnel-error-hint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAEL,KAAK,WAAW,EAGjB,MAAM,+BAA+B,CAAC;AAevC;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,WAAW,GAAG,MAAM,CAoB5D"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Triage-hint formatter for structured tunnel errors.
|
|
3
|
+
*
|
|
4
|
+
* The brainstorm defines the user-facing copy:
|
|
5
|
+
* × Auto-tunnel failed for <target>
|
|
6
|
+
* Attempted: start tunnel via <provider>
|
|
7
|
+
* Likely cause: <subclass-specific>
|
|
8
|
+
* Next steps:
|
|
9
|
+
* • Retry: ...
|
|
10
|
+
* • Skip tunnel: --no-tunnel ...
|
|
11
|
+
* • Check tunnel status: `qa-use tunnel status`
|
|
12
|
+
*
|
|
13
|
+
* Each tunnel-error subclass picks its own "likely cause" line.
|
|
14
|
+
*/
|
|
15
|
+
import { TunnelAuthError, TunnelNetworkError, TunnelQuotaError, } from '../../../lib/tunnel/errors.js';
|
|
16
|
+
function likelyCause(err) {
|
|
17
|
+
if (err instanceof TunnelNetworkError) {
|
|
18
|
+
return 'network timeout or connectivity issue reaching the tunnel provider';
|
|
19
|
+
}
|
|
20
|
+
if (err instanceof TunnelAuthError) {
|
|
21
|
+
return 'authentication rejected (bad or expired API key)';
|
|
22
|
+
}
|
|
23
|
+
if (err instanceof TunnelQuotaError) {
|
|
24
|
+
return 'tunnel quota / rate-limit exceeded, or subdomain already in use';
|
|
25
|
+
}
|
|
26
|
+
return 'unknown failure from the tunnel provider';
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Return a multi-line string ready to print to stderr.
|
|
30
|
+
*/
|
|
31
|
+
export function formatTunnelFailure(err) {
|
|
32
|
+
const target = err.target ?? '<unknown target>';
|
|
33
|
+
const provider = err.provider ?? 'localtunnel';
|
|
34
|
+
const lines = [
|
|
35
|
+
`× Auto-tunnel failed for ${target}`,
|
|
36
|
+
` Attempted: start tunnel via ${provider}`,
|
|
37
|
+
` Likely cause: ${likelyCause(err)}`,
|
|
38
|
+
];
|
|
39
|
+
if (err.message) {
|
|
40
|
+
lines.push(` Error: ${err.message}`);
|
|
41
|
+
}
|
|
42
|
+
lines.push(' Next steps:');
|
|
43
|
+
lines.push(' • Retry: the failure may be transient');
|
|
44
|
+
lines.push(' • Skip tunnel: rerun with --no-tunnel (only works if the backend can reach your target)');
|
|
45
|
+
lines.push(' • Check tunnel status: `qa-use tunnel status`');
|
|
46
|
+
return lines.join('\n');
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=tunnel-error-hint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tunnel-error-hint.js","sourceRoot":"","sources":["../../../../src/cli/lib/tunnel-error-hint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EACL,eAAe,EAEf,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,+BAA+B,CAAC;AAEvC,SAAS,WAAW,CAAC,GAAgB;IACnC,IAAI,GAAG,YAAY,kBAAkB,EAAE,CAAC;QACtC,OAAO,oEAAoE,CAAC;IAC9E,CAAC;IACD,IAAI,GAAG,YAAY,eAAe,EAAE,CAAC;QACnC,OAAO,kDAAkD,CAAC;IAC5D,CAAC;IACD,IAAI,GAAG,YAAY,gBAAgB,EAAE,CAAC;QACpC,OAAO,iEAAiE,CAAC;IAC3E,CAAC;IACD,OAAO,0CAA0C,CAAC;AACpD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAgB;IAClD,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,kBAAkB,CAAC;IAChD,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,IAAI,aAAa,CAAC;IAE/C,MAAM,KAAK,GAAa;QACtB,4BAA4B,MAAM,EAAE;QACpC,iCAAiC,QAAQ,EAAE;QAC3C,mBAAmB,WAAW,CAAC,GAAG,CAAC,EAAE;KACtC,CAAC;IACF,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;QAChB,KAAK,CAAC,IAAI,CAAC,YAAY,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;IACxC,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAC5B,KAAK,CAAC,IAAI,CAAC,2CAA2C,CAAC,CAAC;IACxD,KAAK,CAAC,IAAI,CACR,6FAA6F,CAC9F,CAAC;IACF,KAAK,CAAC,IAAI,CAAC,mDAAmD,CAAC,CAAC;IAEhE,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tri-state `--tunnel` option helper for Commander commands.
|
|
3
|
+
*
|
|
4
|
+
* Shape:
|
|
5
|
+
* --tunnel [auto|on|off] (default: auto; bare --tunnel = on)
|
|
6
|
+
* --no-tunnel (alias for --tunnel off)
|
|
7
|
+
*
|
|
8
|
+
* Invalid values are rejected at parse time with a clear error.
|
|
9
|
+
*
|
|
10
|
+
* Bare `--tunnel` (no value) is preserved as sugar for `--tunnel on`, which
|
|
11
|
+
* matches the pre-tri-state UX. Commander represents the bare form by passing
|
|
12
|
+
* the literal string `"true"` (or `undefined` depending on argv shape) through
|
|
13
|
+
* to the parser, so we map both to `'on'`.
|
|
14
|
+
*/
|
|
15
|
+
import { type Command } from 'commander';
|
|
16
|
+
export type TunnelMode = 'auto' | 'on' | 'off';
|
|
17
|
+
/**
|
|
18
|
+
* Attach the tri-state `--tunnel` option to a Commander command.
|
|
19
|
+
*
|
|
20
|
+
* The option is stored under the property name `tunnel` on the options
|
|
21
|
+
* object. Callers can read it as a `TunnelMode`.
|
|
22
|
+
*
|
|
23
|
+
* Also registers `--no-tunnel` as an alias that resolves to `'off'`, and
|
|
24
|
+
* accepts bare `--tunnel` (no value) as sugar for `--tunnel on`.
|
|
25
|
+
*/
|
|
26
|
+
export declare function addTunnelOption(command: Command): Command;
|
|
27
|
+
//# sourceMappingURL=tunnel-option.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tunnel-option.d.ts","sourceRoot":"","sources":["../../../../src/cli/lib/tunnel-option.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,KAAK,OAAO,EAAgC,MAAM,WAAW,CAAC;AAEvE,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,IAAI,GAAG,KAAK,CAAC;AAwC/C;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CA4BzD"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tri-state `--tunnel` option helper for Commander commands.
|
|
3
|
+
*
|
|
4
|
+
* Shape:
|
|
5
|
+
* --tunnel [auto|on|off] (default: auto; bare --tunnel = on)
|
|
6
|
+
* --no-tunnel (alias for --tunnel off)
|
|
7
|
+
*
|
|
8
|
+
* Invalid values are rejected at parse time with a clear error.
|
|
9
|
+
*
|
|
10
|
+
* Bare `--tunnel` (no value) is preserved as sugar for `--tunnel on`, which
|
|
11
|
+
* matches the pre-tri-state UX. Commander represents the bare form by passing
|
|
12
|
+
* the literal string `"true"` (or `undefined` depending on argv shape) through
|
|
13
|
+
* to the parser, so we map both to `'on'`.
|
|
14
|
+
*/
|
|
15
|
+
import { InvalidArgumentError, Option } from 'commander';
|
|
16
|
+
const VALID_MODES = ['auto', 'on', 'off'];
|
|
17
|
+
/**
|
|
18
|
+
* Parser for --tunnel [mode]. Accepts 'auto' | 'on' | 'off'.
|
|
19
|
+
* - Bare `--tunnel` (no value) → 'on' (backward-compat sugar).
|
|
20
|
+
* - `--no-tunnel` (boolean `false`) → 'off'.
|
|
21
|
+
* - Invalid strings are rejected.
|
|
22
|
+
*/
|
|
23
|
+
function parseTunnelMode(value) {
|
|
24
|
+
// `--no-tunnel` arrives here as the boolean `false` (Commander's
|
|
25
|
+
// --no-<flag> negation semantics). Map it to 'off'.
|
|
26
|
+
if (value === false) {
|
|
27
|
+
return 'off';
|
|
28
|
+
}
|
|
29
|
+
// Bare `--tunnel` (no value). With the `[mode]` optional-arg form,
|
|
30
|
+
// Commander substitutes the literal string `"true"` as a placeholder
|
|
31
|
+
// when the flag is present without an explicit value. Treat that and
|
|
32
|
+
// `undefined` as the backward-compat sugar → 'on'.
|
|
33
|
+
if (value === undefined || value === true || value === 'true') {
|
|
34
|
+
return 'on';
|
|
35
|
+
}
|
|
36
|
+
if (typeof value !== 'string') {
|
|
37
|
+
throw new InvalidArgumentError(`Invalid --tunnel value. Expected one of: ${VALID_MODES.join(', ')}.`);
|
|
38
|
+
}
|
|
39
|
+
const normalized = value.toLowerCase();
|
|
40
|
+
if (!VALID_MODES.includes(normalized)) {
|
|
41
|
+
throw new InvalidArgumentError(`Invalid --tunnel value: "${value}". Expected one of: ${VALID_MODES.join(', ')}.`);
|
|
42
|
+
}
|
|
43
|
+
return normalized;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Attach the tri-state `--tunnel` option to a Commander command.
|
|
47
|
+
*
|
|
48
|
+
* The option is stored under the property name `tunnel` on the options
|
|
49
|
+
* object. Callers can read it as a `TunnelMode`.
|
|
50
|
+
*
|
|
51
|
+
* Also registers `--no-tunnel` as an alias that resolves to `'off'`, and
|
|
52
|
+
* accepts bare `--tunnel` (no value) as sugar for `--tunnel on`.
|
|
53
|
+
*/
|
|
54
|
+
export function addTunnelOption(command) {
|
|
55
|
+
const option = new Option('--tunnel [mode]', 'Tunnel mode: auto (localhost-only), on (force, default when flag present without value), off (never)')
|
|
56
|
+
.default('auto')
|
|
57
|
+
.argParser(parseTunnelMode);
|
|
58
|
+
command.addOption(option);
|
|
59
|
+
// `--no-tunnel` as an alias for `--tunnel off`. Commander treats this as a
|
|
60
|
+
// separate boolean option that, when present, sets the `tunnel` property to
|
|
61
|
+
// `false`. Normalise that to `'off'` in a preAction hook so downstream
|
|
62
|
+
// consumers only ever see a `TunnelMode` string.
|
|
63
|
+
command.option('--no-tunnel', 'Disable tunnel (alias for --tunnel off)');
|
|
64
|
+
command.hook('preAction', (cmd) => {
|
|
65
|
+
const opts = cmd.opts();
|
|
66
|
+
if (opts.tunnel === false) {
|
|
67
|
+
cmd.setOptionValue('tunnel', 'off');
|
|
68
|
+
}
|
|
69
|
+
else if (opts.tunnel === true || opts.tunnel === 'true') {
|
|
70
|
+
// Bare `--tunnel` with no argParser invocation path (belt-and-braces):
|
|
71
|
+
// some Commander versions skip argParser when the optional arg is absent.
|
|
72
|
+
cmd.setOptionValue('tunnel', 'on');
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
return command;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=tunnel-option.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tunnel-option.js","sourceRoot":"","sources":["../../../../src/cli/lib/tunnel-option.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAgB,oBAAoB,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAIvE,MAAM,WAAW,GAA0B,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,CAAU,CAAC;AAE1E;;;;;GAKG;AACH,SAAS,eAAe,CAAC,KAAc;IACrC,iEAAiE;IACjE,oDAAoD;IACpD,IAAI,KAAK,KAAK,KAAK,EAAE,CAAC;QACpB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,mEAAmE;IACnE,qEAAqE;IACrE,qEAAqE;IACrE,mDAAmD;IACnD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,MAAM,EAAE,CAAC;QAC9D,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,IAAI,oBAAoB,CAC5B,4CAA4C,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CACtE,CAAC;IACJ,CAAC;IAED,MAAM,UAAU,GAAG,KAAK,CAAC,WAAW,EAAgB,CAAC;IACrD,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,oBAAoB,CAC5B,4BAA4B,KAAK,uBAAuB,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAClF,CAAC;IACJ,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAAC,OAAgB;IAC9C,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB,iBAAiB,EACjB,sGAAsG,CACvG;SACE,OAAO,CAAC,MAAoB,CAAC;SAC7B,SAAS,CAAC,eAAe,CAAC,CAAC;IAE9B,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAE1B,2EAA2E;IAC3E,4EAA4E;IAC5E,uEAAuE;IACvE,iDAAiD;IACjD,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,yCAAyC,CAAC,CAAC;IAEzE,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,GAAG,EAAE,EAAE;QAChC,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;QACxB,IAAI,IAAI,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;YAC1B,GAAG,CAAC,cAAc,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QACtC,CAAC;aAAM,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;YAC1D,uEAAuE;YACvE,0EAA0E;YAC1E,GAAG,CAAC,cAAc,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QACrC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the effective tunnel mode from CLI flag + config file, and
|
|
3
|
+
* turn that mode into a concrete on/off decision using the base URL
|
|
4
|
+
* and API URL.
|
|
5
|
+
*
|
|
6
|
+
* Precedence for `resolveTunnelFlag`:
|
|
7
|
+
* 1. CLI flag (if the user explicitly passed --tunnel / --no-tunnel)
|
|
8
|
+
* 2. Config file (`tunnel` key in ~/.qa-use.json)
|
|
9
|
+
* 3. Default: 'auto'
|
|
10
|
+
*
|
|
11
|
+
* `resolveTunnelMode` is the Phase-2 auto-inference: `'auto'` maps to
|
|
12
|
+
* `'on'` iff `isLocalhostUrl(baseUrl) && !isLocalhostUrl(apiUrl)`, else
|
|
13
|
+
* `'off'`. `'on'` and `'off'` are passed through untouched.
|
|
14
|
+
*/
|
|
15
|
+
import type { TunnelMode } from './tunnel-option.js';
|
|
16
|
+
export type { TunnelMode } from './tunnel-option.js';
|
|
17
|
+
/**
|
|
18
|
+
* Resolve the tunnel mode from CLI + config inputs.
|
|
19
|
+
*
|
|
20
|
+
* @param cliFlag - value of the `--tunnel` option after Commander parsing;
|
|
21
|
+
* may be `undefined` when the caller hasn't wired the flag yet, or when
|
|
22
|
+
* the option has no default (but in practice we default to 'auto', so
|
|
23
|
+
* callers will usually pass 'auto' here)
|
|
24
|
+
* @param configFile - value of `~/.qa-use.json`'s `tunnel` key
|
|
25
|
+
*/
|
|
26
|
+
export declare function resolveTunnelFlag(cliFlag: TunnelMode | undefined, configFile: TunnelMode | undefined): TunnelMode;
|
|
27
|
+
/**
|
|
28
|
+
* Resolve the concrete on/off decision for the tunnel.
|
|
29
|
+
*
|
|
30
|
+
* Matrix:
|
|
31
|
+
* - `mode === 'on'` → `'on'` (force tunnel, even in dev mode)
|
|
32
|
+
* - `mode === 'off'` → `'off'` (never tunnel)
|
|
33
|
+
* - `mode === 'auto'` → `'on'` iff base URL is localhost and API URL is
|
|
34
|
+
* *not* localhost; otherwise `'off'`.
|
|
35
|
+
*
|
|
36
|
+
* If either `baseUrl` or `apiUrl` is unknown in `'auto'` mode, we stay
|
|
37
|
+
* conservative and return `'off'`: without a localhost base URL there's
|
|
38
|
+
* nothing worth tunnelling, and without a known API URL we can't tell
|
|
39
|
+
* whether we're in dev mode (where auto-tunnel should be skipped).
|
|
40
|
+
*/
|
|
41
|
+
export declare function resolveTunnelMode(mode: TunnelMode, baseUrl: string | undefined, apiUrl: string | undefined): 'on' | 'off';
|
|
42
|
+
//# sourceMappingURL=tunnel-resolve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tunnel-resolve.d.ts","sourceRoot":"","sources":["../../../../src/cli/lib/tunnel-resolve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,YAAY,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,UAAU,GAAG,SAAS,EAC/B,UAAU,EAAE,UAAU,GAAG,SAAS,GACjC,UAAU,CAgBZ;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,MAAM,EAAE,MAAM,GAAG,SAAS,GACzB,IAAI,GAAG,KAAK,CAWd"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the effective tunnel mode from CLI flag + config file, and
|
|
3
|
+
* turn that mode into a concrete on/off decision using the base URL
|
|
4
|
+
* and API URL.
|
|
5
|
+
*
|
|
6
|
+
* Precedence for `resolveTunnelFlag`:
|
|
7
|
+
* 1. CLI flag (if the user explicitly passed --tunnel / --no-tunnel)
|
|
8
|
+
* 2. Config file (`tunnel` key in ~/.qa-use.json)
|
|
9
|
+
* 3. Default: 'auto'
|
|
10
|
+
*
|
|
11
|
+
* `resolveTunnelMode` is the Phase-2 auto-inference: `'auto'` maps to
|
|
12
|
+
* `'on'` iff `isLocalhostUrl(baseUrl) && !isLocalhostUrl(apiUrl)`, else
|
|
13
|
+
* `'off'`. `'on'` and `'off'` are passed through untouched.
|
|
14
|
+
*/
|
|
15
|
+
import { isLocalhostUrl } from '../../../lib/env/localhost.js';
|
|
16
|
+
/**
|
|
17
|
+
* Resolve the tunnel mode from CLI + config inputs.
|
|
18
|
+
*
|
|
19
|
+
* @param cliFlag - value of the `--tunnel` option after Commander parsing;
|
|
20
|
+
* may be `undefined` when the caller hasn't wired the flag yet, or when
|
|
21
|
+
* the option has no default (but in practice we default to 'auto', so
|
|
22
|
+
* callers will usually pass 'auto' here)
|
|
23
|
+
* @param configFile - value of `~/.qa-use.json`'s `tunnel` key
|
|
24
|
+
*/
|
|
25
|
+
export function resolveTunnelFlag(cliFlag, configFile) {
|
|
26
|
+
// Phase 1: we can't reliably distinguish "user passed --tunnel auto" from
|
|
27
|
+
// "Commander filled in the 'auto' default" without extra bookkeeping on the
|
|
28
|
+
// command. So the contract here is: the caller gives us the post-parse
|
|
29
|
+
// value. If they want config precedence to apply, they should pass
|
|
30
|
+
// `undefined` when the CLI value is the unchanged default. To keep this
|
|
31
|
+
// phase simple and predictable — and aligned with the plan's "CLI > config
|
|
32
|
+
// > default 'auto'" precedence — we treat a CLI-passed 'auto' as
|
|
33
|
+
// "no explicit override" so the config value can take effect.
|
|
34
|
+
if (cliFlag !== undefined && cliFlag !== 'auto') {
|
|
35
|
+
return cliFlag;
|
|
36
|
+
}
|
|
37
|
+
if (configFile !== undefined) {
|
|
38
|
+
return configFile;
|
|
39
|
+
}
|
|
40
|
+
return 'auto';
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Resolve the concrete on/off decision for the tunnel.
|
|
44
|
+
*
|
|
45
|
+
* Matrix:
|
|
46
|
+
* - `mode === 'on'` → `'on'` (force tunnel, even in dev mode)
|
|
47
|
+
* - `mode === 'off'` → `'off'` (never tunnel)
|
|
48
|
+
* - `mode === 'auto'` → `'on'` iff base URL is localhost and API URL is
|
|
49
|
+
* *not* localhost; otherwise `'off'`.
|
|
50
|
+
*
|
|
51
|
+
* If either `baseUrl` or `apiUrl` is unknown in `'auto'` mode, we stay
|
|
52
|
+
* conservative and return `'off'`: without a localhost base URL there's
|
|
53
|
+
* nothing worth tunnelling, and without a known API URL we can't tell
|
|
54
|
+
* whether we're in dev mode (where auto-tunnel should be skipped).
|
|
55
|
+
*/
|
|
56
|
+
export function resolveTunnelMode(mode, baseUrl, apiUrl) {
|
|
57
|
+
if (mode === 'on')
|
|
58
|
+
return 'on';
|
|
59
|
+
if (mode === 'off')
|
|
60
|
+
return 'off';
|
|
61
|
+
// mode === 'auto'
|
|
62
|
+
if (!baseUrl)
|
|
63
|
+
return 'off';
|
|
64
|
+
if (!isLocalhostUrl(baseUrl))
|
|
65
|
+
return 'off';
|
|
66
|
+
// Dev-mode skip: if the API is itself local, the backend can reach
|
|
67
|
+
// localhost directly — no tunnel needed.
|
|
68
|
+
if (apiUrl && isLocalhostUrl(apiUrl))
|
|
69
|
+
return 'off';
|
|
70
|
+
return 'on';
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=tunnel-resolve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tunnel-resolve.js","sourceRoot":"","sources":["../../../../src/cli/lib/tunnel-resolve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAK/D;;;;;;;;GAQG;AACH,MAAM,UAAU,iBAAiB,CAC/B,OAA+B,EAC/B,UAAkC;IAElC,0EAA0E;IAC1E,4EAA4E;IAC5E,uEAAuE;IACvE,mEAAmE;IACnE,wEAAwE;IACxE,2EAA2E;IAC3E,iEAAiE;IACjE,8DAA8D;IAC9D,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QAChD,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAO,UAAU,CAAC;IACpB,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,iBAAiB,CAC/B,IAAgB,EAChB,OAA2B,EAC3B,MAA0B;IAE1B,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC/B,IAAI,IAAI,KAAK,KAAK;QAAE,OAAO,KAAK,CAAC;IAEjC,kBAAkB;IAClB,IAAI,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC;IAC3B,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC;QAAE,OAAO,KAAK,CAAC;IAC3C,mEAAmE;IACnE,yCAAyC;IACzC,IAAI,MAAM,IAAI,cAAc,CAAC,MAAM,CAAC;QAAE,OAAO,KAAK,CAAC;IACnD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
package/lib/api/index.ts
CHANGED
|
@@ -840,9 +840,89 @@ export class ApiClient {
|
|
|
840
840
|
* Run test definitions with SSE streaming progress
|
|
841
841
|
* @param options - Test execution options
|
|
842
842
|
* @param onEvent - Optional callback for SSE events
|
|
843
|
+
* @param runtimeOptions - Optional runtime controls (idle timeout, external signal)
|
|
843
844
|
* @returns Promise resolving to test result
|
|
844
845
|
*/
|
|
845
|
-
async runCliTest(
|
|
846
|
+
async runCliTest(
|
|
847
|
+
options: RunCliTestOptions,
|
|
848
|
+
onEvent?: SSECallback,
|
|
849
|
+
runtimeOptions?: { idleTimeoutSec?: number; signal?: AbortSignal }
|
|
850
|
+
): Promise<RunCliTestResult> {
|
|
851
|
+
// Internal AbortController is wired into both `fetch` and `streamSSE` so
|
|
852
|
+
// we can deterministically tear down the underlying TCP socket on every
|
|
853
|
+
// exit path. Phase 2 actively aborts on terminal SSE events (`complete` /
|
|
854
|
+
// `error`) so we don't wait for the backend to close the stream.
|
|
855
|
+
// Phase 3 widens the public signature to accept a caller signal and an
|
|
856
|
+
// idle-timeout watchdog (`--timeout` end-to-end).
|
|
857
|
+
const controller = new AbortController();
|
|
858
|
+
|
|
859
|
+
// Tracks why the loop terminated so the catch block can distinguish
|
|
860
|
+
// "we aborted on purpose because of `complete`/`error`" (swallow the
|
|
861
|
+
// AbortError, return result) from "real network error or external abort"
|
|
862
|
+
// (re-throw), or from "idle timeout fired" (throw a typed timeout error).
|
|
863
|
+
//
|
|
864
|
+
// The reason is mutated from inside async callbacks (idle watchdog
|
|
865
|
+
// setTimeout, caller signal abort listener) which TS can't see during
|
|
866
|
+
// control-flow analysis. We use a small wrapper object so TS doesn't
|
|
867
|
+
// narrow the type to the literal assigned in the synchronous flow.
|
|
868
|
+
type TerminationReason = 'complete' | 'idle-timeout' | 'external' | null;
|
|
869
|
+
const reasonRef: { current: TerminationReason } = { current: null };
|
|
870
|
+
let result: RunCliTestResult | null = null;
|
|
871
|
+
|
|
872
|
+
const idleTimeoutSec = runtimeOptions?.idleTimeoutSec;
|
|
873
|
+
const idleTimeoutMs =
|
|
874
|
+
idleTimeoutSec && idleTimeoutSec > 0 ? Math.round(idleTimeoutSec * 1000) : 0;
|
|
875
|
+
let idleTimer: ReturnType<typeof setTimeout> | null = null;
|
|
876
|
+
|
|
877
|
+
const clearIdleTimer = () => {
|
|
878
|
+
if (idleTimer !== null) {
|
|
879
|
+
clearTimeout(idleTimer);
|
|
880
|
+
idleTimer = null;
|
|
881
|
+
}
|
|
882
|
+
};
|
|
883
|
+
|
|
884
|
+
const armIdleTimer = () => {
|
|
885
|
+
if (idleTimeoutMs <= 0) return;
|
|
886
|
+
clearIdleTimer();
|
|
887
|
+
idleTimer = setTimeout(() => {
|
|
888
|
+
// Idle watchdog fired — no SSE bytes for `idleTimeoutSec` seconds.
|
|
889
|
+
// Mark the reason BEFORE aborting so the catch block can throw a
|
|
890
|
+
// typed timeout error instead of swallowing the AbortError.
|
|
891
|
+
reasonRef.current = 'idle-timeout';
|
|
892
|
+
controller.abort();
|
|
893
|
+
}, idleTimeoutMs);
|
|
894
|
+
// Don't keep the event loop alive just for the watchdog.
|
|
895
|
+
if (typeof idleTimer === 'object' && idleTimer && 'unref' in idleTimer) {
|
|
896
|
+
(idleTimer as { unref: () => void }).unref();
|
|
897
|
+
}
|
|
898
|
+
};
|
|
899
|
+
|
|
900
|
+
// Wire caller-provided signal: if it aborts, mark `terminationReason` so
|
|
901
|
+
// the catch block re-throws (instead of swallowing) with the abort error.
|
|
902
|
+
const callerSignal = runtimeOptions?.signal;
|
|
903
|
+
let callerAbortListener: (() => void) | null = null;
|
|
904
|
+
if (callerSignal) {
|
|
905
|
+
if (callerSignal.aborted) {
|
|
906
|
+
reasonRef.current = 'external';
|
|
907
|
+
controller.abort();
|
|
908
|
+
} else {
|
|
909
|
+
callerAbortListener = () => {
|
|
910
|
+
if (reasonRef.current === null) {
|
|
911
|
+
reasonRef.current = 'external';
|
|
912
|
+
}
|
|
913
|
+
controller.abort();
|
|
914
|
+
};
|
|
915
|
+
callerSignal.addEventListener('abort', callerAbortListener, { once: true });
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
// Merge caller signal with internal controller.signal so `fetch` and
|
|
920
|
+
// `streamSSE` see whichever fires first. `AbortSignal.any` is Node 20.3+
|
|
921
|
+
// and the project requires Node 20+.
|
|
922
|
+
const mergedSignal: AbortSignal = callerSignal
|
|
923
|
+
? AbortSignal.any([callerSignal, controller.signal])
|
|
924
|
+
: controller.signal;
|
|
925
|
+
|
|
846
926
|
try {
|
|
847
927
|
const response = await fetch(`${this.getApiUrl()}/vibe-qa/cli/run`, {
|
|
848
928
|
method: 'POST',
|
|
@@ -853,6 +933,7 @@ export class ApiClient {
|
|
|
853
933
|
Accept: 'text/event-stream',
|
|
854
934
|
},
|
|
855
935
|
body: JSON.stringify(options),
|
|
936
|
+
signal: mergedSignal,
|
|
856
937
|
});
|
|
857
938
|
|
|
858
939
|
if (!response.ok) {
|
|
@@ -863,28 +944,77 @@ export class ApiClient {
|
|
|
863
944
|
throw new Error(formatApiError(errorData, `HTTP ${response.status}: Failed to run test`));
|
|
864
945
|
}
|
|
865
946
|
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
//
|
|
869
|
-
|
|
947
|
+
// Arm the idle watchdog AFTER headers arrive — first event may take
|
|
948
|
+
// a while on a cold backend / large test. We don't punish slow first
|
|
949
|
+
// bytes; we punish silence between bytes.
|
|
950
|
+
armIdleTimer();
|
|
951
|
+
|
|
952
|
+
// Stream SSE events. `onChunk` resets the idle watchdog on every
|
|
953
|
+
// byte chunk (so SSE comment pings — which yield zero parsed events
|
|
954
|
+
// — still keep the connection alive).
|
|
955
|
+
for await (const event of streamSSE(response, {
|
|
956
|
+
signal: mergedSignal,
|
|
957
|
+
onChunk: () => armIdleTimer(),
|
|
958
|
+
})) {
|
|
870
959
|
if (onEvent) onEvent(event);
|
|
871
960
|
|
|
872
|
-
// Capture final result
|
|
961
|
+
// Capture final result and tear down the socket immediately on a
|
|
962
|
+
// terminal event. Trailing events (e.g. `test_fixed`, `persisted`)
|
|
963
|
+
// that may arrive after `complete` are intentionally dropped per the
|
|
964
|
+
// plan — the backend keeps the SSE stream open, so waiting for it to
|
|
965
|
+
// close on its own is what causes the ~80s hang we're fixing.
|
|
873
966
|
if (event.event === 'complete' || event.event === 'error') {
|
|
874
967
|
result = event.data;
|
|
968
|
+
reasonRef.current = 'complete';
|
|
969
|
+
controller.abort();
|
|
970
|
+
break;
|
|
875
971
|
}
|
|
876
972
|
}
|
|
877
973
|
|
|
878
974
|
if (!result) {
|
|
975
|
+
// No terminal event arrived AND the stream closed (or was aborted).
|
|
976
|
+
// If we got here via idle timeout, surface that explicitly.
|
|
977
|
+
if (reasonRef.current === 'idle-timeout') {
|
|
978
|
+
throw new Error(`Test run timed out: no SSE events for ${idleTimeoutSec}s`);
|
|
979
|
+
}
|
|
879
980
|
throw new Error('No result received from test execution');
|
|
880
981
|
}
|
|
881
982
|
|
|
882
983
|
return result;
|
|
883
984
|
} catch (error) {
|
|
985
|
+
// If we triggered the abort ourselves on a terminal event, the for-await
|
|
986
|
+
// loop may surface the abort as an AbortError after we've already
|
|
987
|
+
// captured `result`. Swallow it and return the captured result.
|
|
988
|
+
if (
|
|
989
|
+
reasonRef.current === 'complete' &&
|
|
990
|
+
error instanceof Error &&
|
|
991
|
+
error.name === 'AbortError' &&
|
|
992
|
+
result
|
|
993
|
+
) {
|
|
994
|
+
return result;
|
|
995
|
+
}
|
|
996
|
+
// Idle-timeout: the abort came from our watchdog, not from a real
|
|
997
|
+
// network failure. Surface a typed message that tools (and humans)
|
|
998
|
+
// can grep for.
|
|
999
|
+
if (
|
|
1000
|
+
reasonRef.current === 'idle-timeout' &&
|
|
1001
|
+
error instanceof Error &&
|
|
1002
|
+
error.name === 'AbortError'
|
|
1003
|
+
) {
|
|
1004
|
+
throw new Error(`Test run timed out: no SSE events for ${idleTimeoutSec}s`);
|
|
1005
|
+
}
|
|
884
1006
|
if (error instanceof Error) {
|
|
885
1007
|
throw error;
|
|
886
1008
|
}
|
|
887
1009
|
throw new Error('Unknown error running test');
|
|
1010
|
+
} finally {
|
|
1011
|
+
// Defensive teardown: abort is idempotent. Guarantees the socket is
|
|
1012
|
+
// released even if the consumer above threw mid-iteration.
|
|
1013
|
+
controller.abort();
|
|
1014
|
+
clearIdleTimer();
|
|
1015
|
+
if (callerSignal && callerAbortListener) {
|
|
1016
|
+
callerSignal.removeEventListener('abort', callerAbortListener);
|
|
1017
|
+
}
|
|
888
1018
|
}
|
|
889
1019
|
}
|
|
890
1020
|
|