@desplega.ai/qa-use 2.14.1 → 2.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/README.md +23 -0
  2. package/dist/lib/env/index.d.ts +13 -0
  3. package/dist/lib/env/index.d.ts.map +1 -1
  4. package/dist/lib/env/index.js +35 -0
  5. package/dist/lib/env/index.js.map +1 -1
  6. package/dist/lib/env/localhost.d.ts +22 -0
  7. package/dist/lib/env/localhost.d.ts.map +1 -0
  8. package/dist/lib/env/localhost.js +49 -0
  9. package/dist/lib/env/localhost.js.map +1 -0
  10. package/dist/lib/env/paths.d.ts +27 -0
  11. package/dist/lib/env/paths.d.ts.map +1 -0
  12. package/dist/lib/env/paths.js +42 -0
  13. package/dist/lib/env/paths.js.map +1 -0
  14. package/dist/lib/env/sessions.d.ts +55 -0
  15. package/dist/lib/env/sessions.d.ts.map +1 -0
  16. package/dist/lib/env/sessions.js +128 -0
  17. package/dist/lib/env/sessions.js.map +1 -0
  18. package/dist/lib/tunnel/errors.d.ts +61 -0
  19. package/dist/lib/tunnel/errors.d.ts.map +1 -0
  20. package/dist/lib/tunnel/errors.js +152 -0
  21. package/dist/lib/tunnel/errors.js.map +1 -0
  22. package/dist/lib/tunnel/index.d.ts.map +1 -1
  23. package/dist/lib/tunnel/index.js +26 -11
  24. package/dist/lib/tunnel/index.js.map +1 -1
  25. package/dist/lib/tunnel/registry.d.ts +182 -0
  26. package/dist/lib/tunnel/registry.d.ts.map +1 -0
  27. package/dist/lib/tunnel/registry.js +561 -0
  28. package/dist/lib/tunnel/registry.js.map +1 -0
  29. package/dist/package.json +1 -1
  30. package/dist/src/cli/commands/browser/_detached.d.ts +27 -0
  31. package/dist/src/cli/commands/browser/_detached.d.ts.map +1 -0
  32. package/dist/src/cli/commands/browser/_detached.js +422 -0
  33. package/dist/src/cli/commands/browser/_detached.js.map +1 -0
  34. package/dist/src/cli/commands/browser/close.d.ts +7 -0
  35. package/dist/src/cli/commands/browser/close.d.ts.map +1 -1
  36. package/dist/src/cli/commands/browser/close.js +101 -5
  37. package/dist/src/cli/commands/browser/close.js.map +1 -1
  38. package/dist/src/cli/commands/browser/create.d.ts +7 -0
  39. package/dist/src/cli/commands/browser/create.d.ts.map +1 -1
  40. package/dist/src/cli/commands/browser/create.js +233 -25
  41. package/dist/src/cli/commands/browser/create.js.map +1 -1
  42. package/dist/src/cli/commands/browser/index.d.ts.map +1 -1
  43. package/dist/src/cli/commands/browser/index.js +3 -0
  44. package/dist/src/cli/commands/browser/index.js.map +1 -1
  45. package/dist/src/cli/commands/browser/run.d.ts.map +1 -1
  46. package/dist/src/cli/commands/browser/run.js +13 -6
  47. package/dist/src/cli/commands/browser/run.js.map +1 -1
  48. package/dist/src/cli/commands/browser/status.d.ts +4 -0
  49. package/dist/src/cli/commands/browser/status.d.ts.map +1 -1
  50. package/dist/src/cli/commands/browser/status.js +85 -3
  51. package/dist/src/cli/commands/browser/status.js.map +1 -1
  52. package/dist/src/cli/commands/doctor.d.ts +45 -0
  53. package/dist/src/cli/commands/doctor.d.ts.map +1 -0
  54. package/dist/src/cli/commands/doctor.js +267 -0
  55. package/dist/src/cli/commands/doctor.js.map +1 -0
  56. package/dist/src/cli/commands/test/run.d.ts.map +1 -1
  57. package/dist/src/cli/commands/test/run.js +29 -18
  58. package/dist/src/cli/commands/test/run.js.map +1 -1
  59. package/dist/src/cli/commands/tunnel/close.d.ts +18 -0
  60. package/dist/src/cli/commands/tunnel/close.d.ts.map +1 -0
  61. package/dist/src/cli/commands/tunnel/close.js +154 -0
  62. package/dist/src/cli/commands/tunnel/close.js.map +1 -0
  63. package/dist/src/cli/commands/tunnel/index.d.ts +6 -0
  64. package/dist/src/cli/commands/tunnel/index.d.ts.map +1 -0
  65. package/dist/src/cli/commands/tunnel/index.js +17 -0
  66. package/dist/src/cli/commands/tunnel/index.js.map +1 -0
  67. package/dist/src/cli/commands/tunnel/ls.d.ts +10 -0
  68. package/dist/src/cli/commands/tunnel/ls.d.ts.map +1 -0
  69. package/dist/src/cli/commands/tunnel/ls.js +89 -0
  70. package/dist/src/cli/commands/tunnel/ls.js.map +1 -0
  71. package/dist/src/cli/commands/tunnel/start.d.ts +15 -0
  72. package/dist/src/cli/commands/tunnel/start.d.ts.map +1 -0
  73. package/dist/src/cli/commands/tunnel/start.js +65 -0
  74. package/dist/src/cli/commands/tunnel/start.js.map +1 -0
  75. package/dist/src/cli/commands/tunnel/status.d.ts +8 -0
  76. package/dist/src/cli/commands/tunnel/status.d.ts.map +1 -0
  77. package/dist/src/cli/commands/tunnel/status.js +58 -0
  78. package/dist/src/cli/commands/tunnel/status.js.map +1 -0
  79. package/dist/src/cli/generated/docs-content.d.ts +1 -1
  80. package/dist/src/cli/generated/docs-content.d.ts.map +1 -1
  81. package/dist/src/cli/generated/docs-content.js +157 -100
  82. package/dist/src/cli/generated/docs-content.js.map +1 -1
  83. package/dist/src/cli/index.js +8 -0
  84. package/dist/src/cli/index.js.map +1 -1
  85. package/dist/src/cli/lib/browser.d.ts +25 -9
  86. package/dist/src/cli/lib/browser.d.ts.map +1 -1
  87. package/dist/src/cli/lib/browser.js +73 -42
  88. package/dist/src/cli/lib/browser.js.map +1 -1
  89. package/dist/src/cli/lib/cli-entry.d.ts +40 -0
  90. package/dist/src/cli/lib/cli-entry.d.ts.map +1 -0
  91. package/dist/src/cli/lib/cli-entry.js +65 -0
  92. package/dist/src/cli/lib/cli-entry.js.map +1 -0
  93. package/dist/src/cli/lib/startup-sweep.d.ts +45 -0
  94. package/dist/src/cli/lib/startup-sweep.d.ts.map +1 -0
  95. package/dist/src/cli/lib/startup-sweep.js +246 -0
  96. package/dist/src/cli/lib/startup-sweep.js.map +1 -0
  97. package/dist/src/cli/lib/tunnel-banner.d.ts +33 -0
  98. package/dist/src/cli/lib/tunnel-banner.d.ts.map +1 -0
  99. package/dist/src/cli/lib/tunnel-banner.js +55 -0
  100. package/dist/src/cli/lib/tunnel-banner.js.map +1 -0
  101. package/dist/src/cli/lib/tunnel-error-hint.d.ts +20 -0
  102. package/dist/src/cli/lib/tunnel-error-hint.d.ts.map +1 -0
  103. package/dist/src/cli/lib/tunnel-error-hint.js +48 -0
  104. package/dist/src/cli/lib/tunnel-error-hint.js.map +1 -0
  105. package/dist/src/cli/lib/tunnel-option.d.ts +27 -0
  106. package/dist/src/cli/lib/tunnel-option.d.ts.map +1 -0
  107. package/dist/src/cli/lib/tunnel-option.js +77 -0
  108. package/dist/src/cli/lib/tunnel-option.js.map +1 -0
  109. package/dist/src/cli/lib/tunnel-resolve.d.ts +42 -0
  110. package/dist/src/cli/lib/tunnel-resolve.d.ts.map +1 -0
  111. package/dist/src/cli/lib/tunnel-resolve.js +72 -0
  112. package/dist/src/cli/lib/tunnel-resolve.js.map +1 -0
  113. package/lib/env/index.ts +51 -0
  114. package/lib/env/localhost.test.ts +63 -0
  115. package/lib/env/localhost.ts +51 -0
  116. package/lib/env/paths.ts +46 -0
  117. package/lib/env/sessions.test.ts +109 -0
  118. package/lib/env/sessions.ts +155 -0
  119. package/lib/tunnel/errors.test.ts +105 -0
  120. package/lib/tunnel/errors.ts +169 -0
  121. package/lib/tunnel/index.ts +26 -11
  122. package/lib/tunnel/registry.test.ts +420 -0
  123. package/lib/tunnel/registry.ts +646 -0
  124. package/package.json +1 -1
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Structured tunnel error classes.
3
+ *
4
+ * The CLI uses these to render triage-hint error messages on tunnel
5
+ * failure. `classifyTunnelFailure` inspects a thrown error from the
6
+ * underlying `@desplega.ai/localtunnel` provider and returns the most
7
+ * specific subclass we can identify.
8
+ *
9
+ * Zero retries live in this layer — classification is strictly about
10
+ * picking the right error shape to hand back up the stack.
11
+ */
12
+ /**
13
+ * Base class for all tunnel-layer failures surfaced to the CLI.
14
+ *
15
+ * Do not throw `TunnelError` directly — throw one of the subclasses
16
+ * below so the CLI can pick the right hint.
17
+ */
18
+ export class TunnelError extends Error {
19
+ target;
20
+ provider;
21
+ cause;
22
+ constructor(message, context = {}) {
23
+ super(message);
24
+ this.name = 'TunnelError';
25
+ this.target = context.target;
26
+ this.provider = context.provider ?? 'localtunnel';
27
+ this.cause = context.cause;
28
+ }
29
+ }
30
+ /** Network / connectivity failure (DNS, timeout, ECONNREFUSED, etc.). */
31
+ export class TunnelNetworkError extends TunnelError {
32
+ constructor(message, context = {}) {
33
+ super(message, context);
34
+ this.name = 'TunnelNetworkError';
35
+ }
36
+ }
37
+ /** Auth failure (bad/expired API key, 401/403 from provider). */
38
+ export class TunnelAuthError extends TunnelError {
39
+ constructor(message, context = {}) {
40
+ super(message, context);
41
+ this.name = 'TunnelAuthError';
42
+ }
43
+ }
44
+ /** Quota / rate-limit / subdomain-clash failure. */
45
+ export class TunnelQuotaError extends TunnelError {
46
+ constructor(message, context = {}) {
47
+ super(message, context);
48
+ this.name = 'TunnelQuotaError';
49
+ }
50
+ }
51
+ /** Fallback when no classification matched. */
52
+ export class TunnelUnknownError extends TunnelError {
53
+ constructor(message, context = {}) {
54
+ super(message, context);
55
+ this.name = 'TunnelUnknownError';
56
+ }
57
+ }
58
+ function extractStatusCode(err) {
59
+ if (!err || typeof err !== 'object')
60
+ return undefined;
61
+ const obj = err;
62
+ if (typeof obj.statusCode === 'number')
63
+ return obj.statusCode;
64
+ if (typeof obj.status === 'number')
65
+ return obj.status;
66
+ // Some providers nest the status in a `response` object.
67
+ const response = obj.response;
68
+ if (response && typeof response === 'object') {
69
+ const respObj = response;
70
+ if (typeof respObj.status === 'number')
71
+ return respObj.status;
72
+ if (typeof respObj.statusCode === 'number')
73
+ return respObj.statusCode;
74
+ }
75
+ return undefined;
76
+ }
77
+ function extractErrorCode(err) {
78
+ if (!err || typeof err !== 'object')
79
+ return undefined;
80
+ const obj = err;
81
+ if (typeof obj.code === 'string')
82
+ return obj.code;
83
+ return undefined;
84
+ }
85
+ function extractMessage(err) {
86
+ if (err instanceof Error)
87
+ return err.message;
88
+ if (typeof err === 'string')
89
+ return err;
90
+ if (err && typeof err === 'object') {
91
+ const obj = err;
92
+ if (typeof obj.message === 'string')
93
+ return obj.message;
94
+ }
95
+ try {
96
+ return String(err);
97
+ }
98
+ catch {
99
+ return 'unknown tunnel error';
100
+ }
101
+ }
102
+ const NETWORK_CODES = new Set([
103
+ 'ECONNREFUSED',
104
+ 'ECONNRESET',
105
+ 'ETIMEDOUT',
106
+ 'ENOTFOUND',
107
+ 'EAI_AGAIN',
108
+ 'EHOSTUNREACH',
109
+ 'ENETUNREACH',
110
+ 'EPIPE',
111
+ 'UND_ERR_CONNECT_TIMEOUT',
112
+ 'UND_ERR_SOCKET',
113
+ ]);
114
+ /**
115
+ * Inspect a thrown tunnel error and return the most specific
116
+ * `TunnelError` subclass we can identify.
117
+ *
118
+ * Heuristics:
119
+ * - Node / undici error codes (`ECONNREFUSED`, `ETIMEDOUT`, …) →
120
+ * `TunnelNetworkError`
121
+ * - HTTP 401/403 → `TunnelAuthError`
122
+ * - HTTP 429 or messages about quota / rate-limit / subdomain in use →
123
+ * `TunnelQuotaError`
124
+ * - Everything else → `TunnelUnknownError`
125
+ */
126
+ export function classifyTunnelFailure(err, context = {}) {
127
+ const ctx = {
128
+ ...context,
129
+ cause: context.cause ?? err,
130
+ };
131
+ const message = extractMessage(err);
132
+ const code = extractErrorCode(err);
133
+ const status = extractStatusCode(err);
134
+ const lowered = message.toLowerCase();
135
+ if (code && NETWORK_CODES.has(code)) {
136
+ return new TunnelNetworkError(message, ctx);
137
+ }
138
+ if (/timeout|timed out|econnrefused|econnreset|enotfound|network/i.test(lowered)) {
139
+ return new TunnelNetworkError(message, ctx);
140
+ }
141
+ if (status === 401 ||
142
+ status === 403 ||
143
+ /\b(401|403|unauthori[sz]ed|forbidden)\b/i.test(lowered)) {
144
+ return new TunnelAuthError(message, ctx);
145
+ }
146
+ if (status === 429 ||
147
+ /quota|rate.?limit|too many|subdomain.*(in use|taken|already)/i.test(lowered)) {
148
+ return new TunnelQuotaError(message, ctx);
149
+ }
150
+ return new TunnelUnknownError(message || 'tunnel failed', ctx);
151
+ }
152
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../lib/tunnel/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAWH;;;;;GAKG;AACH,MAAM,OAAO,WAAY,SAAQ,KAAK;IAC3B,MAAM,CAAU;IAChB,QAAQ,CAAU;IAClB,KAAK,CAAW;IAEzB,YAAY,OAAe,EAAE,UAA8B,EAAE;QAC3D,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;QAC1B,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC7B,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,aAAa,CAAC;QAClD,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;IAC7B,CAAC;CACF;AAED,yEAAyE;AACzE,MAAM,OAAO,kBAAmB,SAAQ,WAAW;IACjD,YAAY,OAAe,EAAE,UAA8B,EAAE;QAC3D,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IACnC,CAAC;CACF;AAED,iEAAiE;AACjE,MAAM,OAAO,eAAgB,SAAQ,WAAW;IAC9C,YAAY,OAAe,EAAE,UAA8B,EAAE;QAC3D,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAChC,CAAC;CACF;AAED,oDAAoD;AACpD,MAAM,OAAO,gBAAiB,SAAQ,WAAW;IAC/C,YAAY,OAAe,EAAE,UAA8B,EAAE;QAC3D,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;CACF;AAED,+CAA+C;AAC/C,MAAM,OAAO,kBAAmB,SAAQ,WAAW;IACjD,YAAY,OAAe,EAAE,UAA8B,EAAE;QAC3D,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IACnC,CAAC;CACF;AAED,SAAS,iBAAiB,CAAC,GAAY;IACrC,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IACtD,MAAM,GAAG,GAAG,GAA8B,CAAC;IAC3C,IAAI,OAAO,GAAG,CAAC,UAAU,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC,UAAU,CAAC;IAC9D,IAAI,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC,MAAM,CAAC;IACtD,yDAAyD;IACzD,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC;IAC9B,IAAI,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC7C,MAAM,OAAO,GAAG,QAAmC,CAAC;QACpD,IAAI,OAAO,OAAO,CAAC,MAAM,KAAK,QAAQ;YAAE,OAAO,OAAO,CAAC,MAAM,CAAC;QAC9D,IAAI,OAAO,OAAO,CAAC,UAAU,KAAK,QAAQ;YAAE,OAAO,OAAO,CAAC,UAAU,CAAC;IACxE,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,gBAAgB,CAAC,GAAY;IACpC,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IACtD,MAAM,GAAG,GAAG,GAA8B,CAAC;IAC3C,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC,IAAI,CAAC;IAClD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,cAAc,CAAC,GAAY;IAClC,IAAI,GAAG,YAAY,KAAK;QAAE,OAAO,GAAG,CAAC,OAAO,CAAC;IAC7C,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC;IACxC,IAAI,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QACnC,MAAM,GAAG,GAAG,GAA8B,CAAC;QAC3C,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ;YAAE,OAAO,GAAG,CAAC,OAAO,CAAC;IAC1D,CAAC;IACD,IAAI,CAAC;QACH,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;IACrB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,sBAAsB,CAAC;IAChC,CAAC;AACH,CAAC;AAED,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC;IAC5B,cAAc;IACd,YAAY;IACZ,WAAW;IACX,WAAW;IACX,WAAW;IACX,cAAc;IACd,aAAa;IACb,OAAO;IACP,yBAAyB;IACzB,gBAAgB;CACjB,CAAC,CAAC;AAEH;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,qBAAqB,CAAC,GAAY,EAAE,UAA8B,EAAE;IAClF,MAAM,GAAG,GAAuB;QAC9B,GAAG,OAAO;QACV,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,GAAG;KAC5B,CAAC;IACF,MAAM,OAAO,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IACpC,MAAM,IAAI,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IACnC,MAAM,MAAM,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC;IACtC,MAAM,OAAO,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAEtC,IAAI,IAAI,IAAI,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,OAAO,IAAI,kBAAkB,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IAC9C,CAAC;IAED,IAAI,8DAA8D,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACjF,OAAO,IAAI,kBAAkB,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IAC9C,CAAC;IAED,IACE,MAAM,KAAK,GAAG;QACd,MAAM,KAAK,GAAG;QACd,0CAA0C,CAAC,IAAI,CAAC,OAAO,CAAC,EACxD,CAAC;QACD,OAAO,IAAI,eAAe,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IAC3C,CAAC;IAED,IACE,MAAM,KAAK,GAAG;QACd,+DAA+D,CAAC,IAAI,CAAC,OAAO,CAAC,EAC7E,CAAC;QACD,OAAO,IAAI,gBAAgB,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IAC5C,CAAC;IAED,OAAO,IAAI,kBAAkB,CAAC,OAAO,IAAI,eAAe,EAAE,GAAG,CAAC,CAAC;AACjE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../lib/tunnel/index.ts"],"names":[],"mappings":"AAGA,OAAO,WAAW,MAAM,0BAA0B,CAAC;AAGnD,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAA8B;IAC7C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAkB;IAEhD;;;;;OAKG;IACH,MAAM,CAAC,8BAA8B,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM;IAa7E,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,aAAa,CAAC;IAuE9E,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAejC,UAAU,IAAI,aAAa,GAAG,IAAI;IAIlC,QAAQ,IAAI,OAAO;IAInB;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IA+BrC,YAAY,IAAI,MAAM,GAAG,IAAI;IAIvB,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC;IAcpC,eAAe,CAAC,kBAAkB,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;CAe3D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../lib/tunnel/index.ts"],"names":[],"mappings":"AAGA,OAAO,WAAW,MAAM,0BAA0B,CAAC;AAInD,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAA8B;IAC7C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAkB;IAEhD;;;;;OAKG;IACH,MAAM,CAAC,8BAA8B,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM;IAa7E,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,aAAa,CAAC;IAqF9E,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAejC,UAAU,IAAI,aAAa,GAAG,IAAI;IAIlC,QAAQ,IAAI,OAAO;IAInB;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IA+BrC,YAAY,IAAI,MAAM,GAAG,IAAI;IAIvB,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC;IAcpC,eAAe,CAAC,kBAAkB,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;CAe3D"}
@@ -3,6 +3,7 @@ import https from 'node:https';
3
3
  import { URL } from 'node:url';
4
4
  import localtunnel from '@desplega.ai/localtunnel';
5
5
  import { getEnv } from '../env/index.js';
6
+ import { classifyTunnelFailure, TunnelError } from './errors.js';
6
7
  export class TunnelManager {
7
8
  session = null;
8
9
  defaultRegion = 'auto';
@@ -42,22 +43,36 @@ export class TunnelManager {
42
43
  if (!subdomain && options.apiKey !== undefined && options.sessionIndex !== undefined) {
43
44
  // Use deterministic subdomain based on API key and session index
44
45
  subdomain = TunnelManager.generateDeterministicSubdomain(options.apiKey, options.sessionIndex);
45
- console.log(`Using deterministic subdomain: ${subdomain}`);
46
+ console.error(`Using deterministic subdomain: ${subdomain}`);
46
47
  }
47
48
  else if (!subdomain) {
48
49
  // Fallback to timestamp-based random subdomain
49
50
  subdomain = `qa-use-${Date.now().toString().slice(-6)}`;
50
- console.log(`Using random subdomain: ${subdomain}`);
51
+ console.error(`Using random subdomain: ${subdomain}`);
51
52
  }
52
- console.log(`Starting tunnel on port ${port} with host ${host} in region ${region}`);
53
- const tunnel = await localtunnel({
54
- port,
55
- host,
56
- subdomain,
57
- local_host: options.localHost || 'localhost',
58
- auth: true,
59
- });
60
- console.log(`Tunnel started at ${tunnel.url}`);
53
+ console.error(`Starting tunnel on port ${port} with host ${host} in region ${region}`);
54
+ let tunnel;
55
+ try {
56
+ tunnel = await localtunnel({
57
+ port,
58
+ host,
59
+ subdomain,
60
+ local_host: options.localHost || 'localhost',
61
+ auth: true,
62
+ });
63
+ }
64
+ catch (err) {
65
+ // Classify provider errors into structured TunnelError subclasses so
66
+ // the CLI can render a triage-hint message. Zero retries here —
67
+ // surface the failure immediately.
68
+ const classified = err instanceof TunnelError
69
+ ? err
70
+ : classifyTunnelFailure(err, {
71
+ target: `${options.localHost || 'localhost'}:${port}`,
72
+ });
73
+ throw classified;
74
+ }
75
+ console.error(`Tunnel started at ${tunnel.url}`);
61
76
  this.session = {
62
77
  tunnel,
63
78
  publicUrl: tunnel.url,
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../lib/tunnel/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../lib/tunnel/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,182 @@
1
+ /**
2
+ * TunnelRegistry — a shared, refcount-managed layer over `TunnelManager`.
3
+ *
4
+ * Goals:
5
+ * - Two CLI commands targeting the same localhost base URL share one
6
+ * remote tunnel (one public URL, one provider connection).
7
+ * - State is visible and editable across sibling processes via
8
+ * `~/.qa-use/tunnels/<hash>.json`, so a SECOND process targeting the
9
+ * same localhost picks up the FIRST process's public URL rather than
10
+ * spinning up its own tunnel.
11
+ * - Last-releaser (in the OWNER process) keeps the tunnel alive for
12
+ * `GRACE_MS` (30 s default) so rapid-fire invocations do not thrash
13
+ * the provider.
14
+ *
15
+ * Cross-process coordination model:
16
+ * - The process that first acquires a given target becomes the OWNER
17
+ * and runs the in-process `TunnelManager`. Its PID is recorded in
18
+ * the registry file.
19
+ * - Later acquirers in OTHER processes read the file, see an alive
20
+ * owner PID, increment refcount under a lockfile, and return an
21
+ * "attach" handle (`isCrossProcessAttach: true`) with the owner's
22
+ * `publicUrl`. They do NOT construct a `TunnelManager`.
23
+ * - Read-modify-write of the record is guarded by a lockfile
24
+ * (`<hash>.lock`, `O_EXCL | O_CREAT`) with bounded retry.
25
+ *
26
+ * TTL grace limitation:
27
+ * - The grace window is bounded by the OWNER process's lifetime. Since
28
+ * `TunnelManager` is a per-process localtunnel client, the remote
29
+ * tunnel dies when the owner exits. For long-lived owners
30
+ * (`tunnel start --hold`, a running `test run`, a detached
31
+ * `browser create` from Phase 4) grace works as designed: a new
32
+ * acquirer within `GRACE_MS` cancels tear-down. For short-lived
33
+ * commands that release-and-exit immediately, grace collapses to
34
+ * zero (the owner process is gone, so there is no tunnel to keep
35
+ * alive anyway).
36
+ *
37
+ * Non-goals (this phase):
38
+ * - Daemonised tunnel hosts. Phase 4 adds detach so short-lived
39
+ * commands can leave a long-lived owner behind.
40
+ * - Retries on provider failure. Zero retries, consistent with Phase 2.
41
+ */
42
+ import { TunnelManager, type TunnelOptions } from './index.js';
43
+ /**
44
+ * How long (ms) to keep a tunnel alive after its refcount hits zero.
45
+ * A new `acquire()` within this window reuses the existing tunnel.
46
+ * Overridable via `QA_USE_TUNNEL_GRACE_MS` env var (primarily a
47
+ * test-friendly knob; production callers should stick with the default).
48
+ */
49
+ export declare const GRACE_MS = 30000;
50
+ /**
51
+ * Max concurrent tunnels per API key. Mirrors the sessionIndex clamp at
52
+ * `lib/tunnel/index.ts:41` so we surface a clear error instead of silently
53
+ * colliding on subdomains.
54
+ */
55
+ export declare const MAX_CONCURRENT_TUNNELS = 10;
56
+ /** On-disk schema for a registry entry. */
57
+ export interface TunnelRecord {
58
+ id: string;
59
+ target: string;
60
+ publicUrl: string;
61
+ pid: number;
62
+ refcount: number;
63
+ ttlExpiresAt: number | null;
64
+ startedAt: number;
65
+ }
66
+ /**
67
+ * Handle returned by `acquire()` / kept in memory by consumers.
68
+ *
69
+ * Consumers must call `registry.release(handle)` exactly once per
70
+ * `acquire()` call.
71
+ */
72
+ export interface TunnelHandle extends TunnelRecord {
73
+ /**
74
+ * True when this handle was acquired from a foreign process's tunnel
75
+ * (the registry file's `pid` !== `process.pid`). The caller MUST NOT
76
+ * attempt to retrieve a `TunnelManager` via `getLiveManager(target)` —
77
+ * there isn't one in this process. Use `handle.publicUrl` directly.
78
+ */
79
+ isCrossProcessAttach: boolean;
80
+ /**
81
+ * Internal marker used to guard double-release. Not persisted.
82
+ */
83
+ _released?: boolean;
84
+ }
85
+ /**
86
+ * Canonical target key — used both as the map key and to derive the
87
+ * filename hash. We lowercase the hostname and drop any path/query so
88
+ * `http://Localhost:3000/foo` and `http://localhost:3000/` dedupe.
89
+ */
90
+ export declare function canonicalTarget(target: string): string;
91
+ /** Filename hash for a target. First 10 hex chars of sha256. */
92
+ export declare function targetHash(target: string): string;
93
+ /**
94
+ * Hook used by tests to inject a fake `TunnelManager` without booting a
95
+ * real tunnel. When unset (the default) the registry constructs a real
96
+ * `TunnelManager`.
97
+ */
98
+ export type TunnelManagerFactory = () => TunnelManager;
99
+ export interface TunnelRegistryOptions {
100
+ /** Override factory (tests). */
101
+ managerFactory?: TunnelManagerFactory;
102
+ /** Override grace window (tests). */
103
+ graceMs?: number;
104
+ /** Override concurrency cap (tests). */
105
+ maxConcurrent?: number;
106
+ /** Options threaded into `TunnelManager.startTunnel(...)`. */
107
+ tunnelOptions?: TunnelOptions;
108
+ }
109
+ /**
110
+ * Acquire / release / list API for tunnels.
111
+ *
112
+ * A singleton is exported as `tunnelRegistry` below for convenience;
113
+ * callers that need isolation (tests) construct their own instance.
114
+ */
115
+ export declare class TunnelRegistry {
116
+ private readonly live;
117
+ private readonly graceMs;
118
+ private readonly maxConcurrent;
119
+ private readonly managerFactory;
120
+ constructor(opts?: TunnelRegistryOptions);
121
+ /**
122
+ * Start a tunnel for `target` (or reuse an existing one — in-process
123
+ * OR in a sibling process), returning a handle. Caller must pair each
124
+ * acquire with exactly one `release(handle)`.
125
+ */
126
+ acquire(target: string, opts?: TunnelOptions): Promise<TunnelHandle>;
127
+ /**
128
+ * Release a handle. Decrements refcount (under lock). When the
129
+ * refcount hits zero AND we are the owner, schedule a tear-down
130
+ * `graceMs` later. A subsequent `acquire()` within the grace window
131
+ * cancels the tear-down.
132
+ *
133
+ * Grace window is bounded by owner process lifetime — short-lived
134
+ * commands exit before grace expires and will tear down immediately.
135
+ */
136
+ release(handle: TunnelHandle): Promise<void>;
137
+ /**
138
+ * Force teardown of a tunnel regardless of refcount. Used by
139
+ * `qa-use tunnel close`. Safe to call when no such tunnel exists.
140
+ *
141
+ * If the owner is this process, tears down the in-memory manager.
142
+ * Otherwise just removes the registry file (and leaves the orphan
143
+ * remote tunnel to die with its owner).
144
+ */
145
+ forceClose(target: string): Promise<void>;
146
+ /**
147
+ * Look up a single entry by canonical target. Scans the on-disk
148
+ * registry; returns `null` if no record exists or the owning pid is
149
+ * dead.
150
+ */
151
+ get(target: string): TunnelRecord | null;
152
+ /**
153
+ * List all live entries. Reconciles against owning PID; stale entries
154
+ * are removed as a side-effect.
155
+ */
156
+ list(): TunnelRecord[];
157
+ /**
158
+ * Returns the live `TunnelManager` instance for `target` IF this process
159
+ * currently owns it. Used by callers that need health-check / WS-URL
160
+ * helpers on the underlying manager. Returns `null` for targets owned
161
+ * by a different process (file visible via `list()` / `get()` but not
162
+ * live in-memory here).
163
+ */
164
+ getLiveManager(target: string): TunnelManager | null;
165
+ /**
166
+ * Look up by filename hash (e.g. output of `tunnel ls`).
167
+ */
168
+ getByHash(hash: string): TunnelRecord | null;
169
+ private writeRecord;
170
+ /**
171
+ * Timer callback: re-check under lock whether we should tear down. A
172
+ * concurrent acquire may have bumped refcount back above zero.
173
+ */
174
+ private maybeTeardown;
175
+ /**
176
+ * Unconditional tear-down. Callers must hold the per-target lock.
177
+ */
178
+ private teardown;
179
+ }
180
+ /** Module-level singleton for CLI use. */
181
+ export declare const tunnelRegistry: TunnelRegistry;
182
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../lib/tunnel/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AASH,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAE/D;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,QAAS,CAAC;AAU/B;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAEzC,2CAA2C;AAC3C,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;GAKG;AACH,MAAM,WAAW,YAAa,SAAQ,YAAY;IAChD;;;;;OAKG;IACH,oBAAoB,EAAE,OAAO,CAAC;IAC9B;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAOtD;AAED,gEAAgE;AAChE,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEjD;AA+GD;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG,MAAM,aAAa,CAAC;AAEvD,MAAM,WAAW,qBAAqB;IACpC,gCAAgC;IAChC,cAAc,CAAC,EAAE,oBAAoB,CAAC;IACtC,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wCAAwC;IACxC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,8DAA8D;IAC9D,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED;;;;;GAKG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAgC;IACrD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAuB;gBAE1C,IAAI,GAAE,qBAA0B;IAM5C;;;;OAIG;IACG,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,aAAkB,GAAG,OAAO,CAAC,YAAY,CAAC;IAsH9E;;;;;;;;OAQG;IACG,OAAO,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAsDlD;;;;;;;OAOG;IACG,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAS/C;;;;OAIG;IACH,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI;IAaxC;;;OAGG;IACH,IAAI,IAAI,YAAY,EAAE;IA2BtB;;;;;;OAMG;IACH,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI;IAMpD;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI;IAe5C,OAAO,CAAC,WAAW;IAKnB;;;OAGG;YACW,aAAa;IA0D3B;;OAEG;YACW,QAAQ;CAoBvB;AAED,0CAA0C;AAC1C,eAAO,MAAM,cAAc,gBAAuB,CAAC"}