@testrigor/testrigor-cli 2.0.8 → 2.0.9
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 +5 -5
- package/dist/util/trtc.d.ts +13 -4
- package/dist/util/trtc.js +88 -11
- package/dist/util/tunnel.d.ts +6 -0
- package/dist/util/tunnel.js +54 -21
- package/npm-shrinkwrap.json +2 -2
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -168,7 +168,7 @@ EXAMPLES
|
|
|
168
168
|
Config will be stored in ~/.testrigor/testrigor.yml
|
|
169
169
|
```
|
|
170
170
|
|
|
171
|
-
_See code: [src/commands/authenticate.ts](https://github.com/TestRigor/testrigor-cli/blob/v2.0.
|
|
171
|
+
_See code: [src/commands/authenticate.ts](https://github.com/TestRigor/testrigor-cli/blob/v2.0.9/src/commands/authenticate.ts)_
|
|
172
172
|
|
|
173
173
|
## `testrigor clone SUITE`
|
|
174
174
|
|
|
@@ -198,7 +198,7 @@ EXAMPLES
|
|
|
198
198
|
$ testrigor clone <test-suite-id>
|
|
199
199
|
```
|
|
200
200
|
|
|
201
|
-
_See code: [src/commands/clone.ts](https://github.com/TestRigor/testrigor-cli/blob/v2.0.
|
|
201
|
+
_See code: [src/commands/clone.ts](https://github.com/TestRigor/testrigor-cli/blob/v2.0.9/src/commands/clone.ts)_
|
|
202
202
|
|
|
203
203
|
## `testrigor help [COMMAND]`
|
|
204
204
|
|
|
@@ -244,7 +244,7 @@ EXAMPLES
|
|
|
244
244
|
$ testrigor test-suite config --delete
|
|
245
245
|
```
|
|
246
246
|
|
|
247
|
-
_See code: [src/commands/test-suite/config.ts](https://github.com/TestRigor/testrigor-cli/blob/v2.0.
|
|
247
|
+
_See code: [src/commands/test-suite/config.ts](https://github.com/TestRigor/testrigor-cli/blob/v2.0.9/src/commands/test-suite/config.ts)_
|
|
248
248
|
|
|
249
249
|
## `testrigor test-suite run [TESTSUITEID]`
|
|
250
250
|
|
|
@@ -304,7 +304,7 @@ DESCRIPTION
|
|
|
304
304
|
Run all tests of a Test Suite
|
|
305
305
|
```
|
|
306
306
|
|
|
307
|
-
_See code: [src/commands/test-suite/run.ts](https://github.com/TestRigor/testrigor-cli/blob/v2.0.
|
|
307
|
+
_See code: [src/commands/test-suite/run.ts](https://github.com/TestRigor/testrigor-cli/blob/v2.0.9/src/commands/test-suite/run.ts)_
|
|
308
308
|
|
|
309
309
|
## `testrigor test-suite validate-syntax [TESTSUITEID]`
|
|
310
310
|
|
|
@@ -344,7 +344,7 @@ EXAMPLES
|
|
|
344
344
|
$ testrigor test-suite validate-syntax <test-suite-id> --test-cases-path "test_cases/**/*.{yaml,txt}"
|
|
345
345
|
```
|
|
346
346
|
|
|
347
|
-
_See code: [src/commands/test-suite/validate-syntax.ts](https://github.com/TestRigor/testrigor-cli/blob/v2.0.
|
|
347
|
+
_See code: [src/commands/test-suite/validate-syntax.ts](https://github.com/TestRigor/testrigor-cli/blob/v2.0.9/src/commands/test-suite/validate-syntax.ts)_
|
|
348
348
|
<!-- commandsstop -->`, so this README currently stays unchanged.
|
|
349
349
|
|
|
350
350
|
3. Check that the diff only touches the version fields, then build and test from the lockfile:
|
package/dist/util/trtc.d.ts
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
export declare
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export declare function
|
|
1
|
+
export declare const BINARY_SHA256: {
|
|
2
|
+
[key: string]: string;
|
|
3
|
+
};
|
|
4
|
+
export declare function findCachedBinary(dir: string, expectedSha256?: string): Promise<string | undefined>;
|
|
5
|
+
export declare function writeEntry(entryPath: string, entry: NodeJS.ReadableStream, mode?: number, expectedSha256?: string): Promise<void>;
|
|
6
|
+
export interface ExtractOptions {
|
|
7
|
+
expectedSha256?: string;
|
|
8
|
+
binaryName?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function extractZip(dir: string, stream: NodeJS.ReadableStream, options?: ExtractOptions): Promise<string>;
|
|
11
|
+
export declare function extractTar(dir: string, stream: NodeJS.ReadableStream, options?: ExtractOptions): Promise<string>;
|
|
12
|
+
export declare function download(dir: string, expectedSha256?: string): Promise<string>;
|
|
13
|
+
export declare function ensureBinary(dir: string, expectedSha256?: string): Promise<string>;
|
package/dist/util/trtc.js
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BINARY_SHA256 = void 0;
|
|
4
|
+
exports.findCachedBinary = findCachedBinary;
|
|
3
5
|
exports.writeEntry = writeEntry;
|
|
4
6
|
exports.extractZip = extractZip;
|
|
5
7
|
exports.extractTar = extractTar;
|
|
6
8
|
exports.download = download;
|
|
9
|
+
exports.ensureBinary = ensureBinary;
|
|
7
10
|
const tslib_1 = require("tslib");
|
|
8
11
|
const fs_1 = tslib_1.__importDefault(require("@supercharge/fs"));
|
|
12
|
+
const crypto = tslib_1.__importStar(require("crypto"));
|
|
9
13
|
const fs = tslib_1.__importStar(require("fs"));
|
|
10
14
|
const os = tslib_1.__importStar(require("os"));
|
|
11
15
|
const path = tslib_1.__importStar(require("path"));
|
|
@@ -60,6 +64,47 @@ function getUrl() {
|
|
|
60
64
|
}
|
|
61
65
|
throw new Error(`Unsupported platform ${platform} or architecture ${architecture}`);
|
|
62
66
|
}
|
|
67
|
+
// SHA-256 of the trtc binary inside each VERSION archive, keyed by `${FILE_NAME}-${ARCHITECTURE_MAP}`.
|
|
68
|
+
// Bump together with VERSION. A binary that does not match is never spawned.
|
|
69
|
+
exports.BINARY_SHA256 = {
|
|
70
|
+
"darwin-amd64": "44334bfc402e290473cb0cde2b77dc6ad92ab87e20cb60bbd99eda8ebd4341d8",
|
|
71
|
+
"darwin-arm64": "bd09654755f78f8974cb8553c8ac0f1f83afb6ef101599d2ba6c652a82a506bc",
|
|
72
|
+
"linux-amd64": "e96dbe13969340048254f4126d0c229a0022b1dfa471adbd5023e6f01f1fd8b4",
|
|
73
|
+
"linux-arm64": "e70d995325adb783da70e498a17014ea1ace746cd2399ad26a0a4273ca7770ef",
|
|
74
|
+
"windows-amd64": "40ac2237574b5de57cccb43dc62135b7dc3ffe0e5be6c8c214d43492ffc8c26d",
|
|
75
|
+
};
|
|
76
|
+
function getExpectedSha256() {
|
|
77
|
+
const platform = os.platform();
|
|
78
|
+
const architecture = os.arch();
|
|
79
|
+
const expected = exports.BINARY_SHA256[`${FILE_NAME[platform]}-${ARCHITECTURE_MAP[architecture]}`];
|
|
80
|
+
if (expected) {
|
|
81
|
+
return expected;
|
|
82
|
+
}
|
|
83
|
+
throw new Error(`Unsupported platform ${platform} or architecture ${architecture}`);
|
|
84
|
+
}
|
|
85
|
+
function hashFile(file) {
|
|
86
|
+
return new Promise((resolve, reject) => {
|
|
87
|
+
const hash = crypto.createHash("sha256");
|
|
88
|
+
fs.createReadStream(file)
|
|
89
|
+
.on("error", reject)
|
|
90
|
+
.on("data", (chunk) => hash.update(chunk))
|
|
91
|
+
.on("end", () => resolve(hash.digest("hex")));
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
// Returns the binary already extracted in dir when it is executable (not checked on Windows) and
|
|
95
|
+
// matches the pinned hash. A matching hash also proves the version, so no marker file is needed.
|
|
96
|
+
async function findCachedBinary(dir, expectedSha256 = getExpectedSha256()) {
|
|
97
|
+
try {
|
|
98
|
+
const binary = getBinaryPath(dir);
|
|
99
|
+
if (os.platform() !== WIN32_PLATFORM) {
|
|
100
|
+
fs.accessSync(binary, fs.constants.X_OK);
|
|
101
|
+
}
|
|
102
|
+
return (await hashFile(binary)) === expectedSha256 ? binary : undefined;
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
63
108
|
const DOWNLOAD_ATTEMPTS = 3;
|
|
64
109
|
const DOWNLOAD_STALL_TIMEOUT_MS = 20000;
|
|
65
110
|
function errorMessage(error) {
|
|
@@ -69,12 +114,22 @@ function errorMessage(error) {
|
|
|
69
114
|
// destination once fully flushed, so on any OS the binary is never spawned half-written. It also
|
|
70
115
|
// replaces an existing binary with a new file instead of truncating and rewriting it in place,
|
|
71
116
|
// which an OS may refuse or penalise while that binary is running or cached from a previous run.
|
|
72
|
-
|
|
117
|
+
// When expectedSha256 is set, the entry is hashed as it is written and discarded on a mismatch.
|
|
118
|
+
function writeEntry(entryPath, entry, mode, expectedSha256) {
|
|
73
119
|
return new Promise((resolve, reject) => {
|
|
74
120
|
const tmpPath = `${entryPath}.${process.pid}.tmp`;
|
|
75
121
|
const writeStream = fs.createWriteStream(tmpPath);
|
|
122
|
+
const hash = crypto.createHash("sha256");
|
|
76
123
|
writeStream.on("error", reject);
|
|
77
124
|
writeStream.on("close", () => {
|
|
125
|
+
if (expectedSha256 !== undefined) {
|
|
126
|
+
const actualSha256 = hash.digest("hex");
|
|
127
|
+
if (actualSha256 !== expectedSha256) {
|
|
128
|
+
fs.rmSync(tmpPath, { force: true });
|
|
129
|
+
reject(new Error(`Checksum mismatch for ${path.basename(entryPath)}: expected ${expectedSha256}, got ${actualSha256}`));
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
78
133
|
if (mode !== undefined) {
|
|
79
134
|
try {
|
|
80
135
|
fs.chmodSync(tmpPath, mode);
|
|
@@ -92,10 +147,19 @@ function writeEntry(entryPath, entry, mode) {
|
|
|
92
147
|
}
|
|
93
148
|
resolve();
|
|
94
149
|
});
|
|
150
|
+
if (expectedSha256 !== undefined) {
|
|
151
|
+
entry.on("data", (chunk) => hash.update(chunk));
|
|
152
|
+
}
|
|
95
153
|
entry.pipe(writeStream);
|
|
96
154
|
});
|
|
97
155
|
}
|
|
98
|
-
|
|
156
|
+
function expectedHashFor(entryName, options) {
|
|
157
|
+
const binaryName = options.binaryName ?? BINARY_NAME;
|
|
158
|
+
return path.basename(entryName) === binaryName
|
|
159
|
+
? options.expectedSha256
|
|
160
|
+
: undefined;
|
|
161
|
+
}
|
|
162
|
+
async function extractZip(dir, stream, options = {}) {
|
|
99
163
|
const parser = unzip.Parse();
|
|
100
164
|
const writes = [];
|
|
101
165
|
return new Promise((resolve, reject) => {
|
|
@@ -109,13 +173,13 @@ async function extractZip(dir, stream) {
|
|
|
109
173
|
return;
|
|
110
174
|
}
|
|
111
175
|
fs.mkdirSync(path.dirname(entryPath), { recursive: true });
|
|
112
|
-
writes.push(writeEntry(entryPath, entry));
|
|
176
|
+
writes.push(writeEntry(entryPath, entry, undefined, expectedHashFor(entry.path, options)));
|
|
113
177
|
});
|
|
114
178
|
parser.on("finish", () => Promise.all(writes).then(() => resolve(getBinaryPath(dir)), reject));
|
|
115
179
|
stream.pipe(parser);
|
|
116
180
|
});
|
|
117
181
|
}
|
|
118
|
-
async function extractTar(dir, stream) {
|
|
182
|
+
async function extractTar(dir, stream, options = {}) {
|
|
119
183
|
const extract = tar.extract();
|
|
120
184
|
return new Promise((resolve, reject) => {
|
|
121
185
|
stream.on("error", reject);
|
|
@@ -129,7 +193,7 @@ async function extractTar(dir, stream) {
|
|
|
129
193
|
next();
|
|
130
194
|
break;
|
|
131
195
|
case "file":
|
|
132
|
-
writeEntry(entryPath, stream, header.mode || 0o644).then(() => next(), reject);
|
|
196
|
+
writeEntry(entryPath, stream, header.mode || 0o644, expectedHashFor(header.name, options)).then(() => next(), reject);
|
|
133
197
|
return;
|
|
134
198
|
case "symlink":
|
|
135
199
|
// ignore symlinks since they will not work on windows
|
|
@@ -147,7 +211,7 @@ async function extractTar(dir, stream) {
|
|
|
147
211
|
}
|
|
148
212
|
// Aborts the download when no data arrives for DOWNLOAD_STALL_TIMEOUT_MS, since a stalled
|
|
149
213
|
// connection would otherwise never settle.
|
|
150
|
-
async function downloadOnce(dir) {
|
|
214
|
+
async function downloadOnce(dir, expectedSha256) {
|
|
151
215
|
const controller = new AbortController();
|
|
152
216
|
let stallTimer;
|
|
153
217
|
const armStallTimer = () => {
|
|
@@ -164,23 +228,31 @@ async function downloadOnce(dir) {
|
|
|
164
228
|
throw new Error("unexpected null response body");
|
|
165
229
|
}
|
|
166
230
|
const stream = stream_1.Readable.fromWeb(response.body);
|
|
231
|
+
const options = { expectedSha256 };
|
|
167
232
|
const extraction = os.platform() === WIN32_PLATFORM
|
|
168
|
-
? extractZip(dir, stream)
|
|
169
|
-
: extractTar(dir, stream);
|
|
233
|
+
? extractZip(dir, stream, options)
|
|
234
|
+
: extractTar(dir, stream, options);
|
|
170
235
|
// Attached after the extractor has piped the body, so no chunk is consumed before it.
|
|
171
236
|
stream.on("data", armStallTimer);
|
|
172
|
-
|
|
237
|
+
try {
|
|
238
|
+
return await extraction;
|
|
239
|
+
}
|
|
240
|
+
catch (error) {
|
|
241
|
+
// A rejected entry (e.g. a checksum mismatch) leaves the body piped but unread, so close it.
|
|
242
|
+
stream.destroy();
|
|
243
|
+
throw error;
|
|
244
|
+
}
|
|
173
245
|
}
|
|
174
246
|
finally {
|
|
175
247
|
clearTimeout(stallTimer);
|
|
176
248
|
}
|
|
177
249
|
}
|
|
178
|
-
async function download(dir) {
|
|
250
|
+
async function download(dir, expectedSha256 = getExpectedSha256()) {
|
|
179
251
|
let lastError;
|
|
180
252
|
for (let attempt = 1; attempt <= DOWNLOAD_ATTEMPTS; attempt++) {
|
|
181
253
|
try {
|
|
182
254
|
// eslint-disable-next-line no-await-in-loop
|
|
183
|
-
return await downloadOnce(dir);
|
|
255
|
+
return await downloadOnce(dir, expectedSha256);
|
|
184
256
|
}
|
|
185
257
|
catch (error) {
|
|
186
258
|
lastError = error;
|
|
@@ -188,3 +260,8 @@ async function download(dir) {
|
|
|
188
260
|
}
|
|
189
261
|
throw new Error(`Could not download the trtc binary after ${DOWNLOAD_ATTEMPTS} attempts: ${errorMessage(lastError)}`);
|
|
190
262
|
}
|
|
263
|
+
// Reuses the cached binary when it is valid and downloads it otherwise.
|
|
264
|
+
async function ensureBinary(dir, expectedSha256 = getExpectedSha256()) {
|
|
265
|
+
return ((await findCachedBinary(dir, expectedSha256)) ??
|
|
266
|
+
download(dir, expectedSha256));
|
|
267
|
+
}
|
package/dist/util/tunnel.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import * as childProcess from "child_process";
|
|
2
|
+
export declare class PortInUseError extends Error {
|
|
3
|
+
constructor();
|
|
4
|
+
}
|
|
5
|
+
export declare function describeSpawnError(error: NodeJS.ErrnoException, binaryPath: string): string;
|
|
1
6
|
export default class Tunnel {
|
|
2
7
|
private static readonly BINARY_DIRECTORY;
|
|
3
8
|
private running;
|
|
@@ -11,4 +16,5 @@ export default class Tunnel {
|
|
|
11
16
|
stop(): Promise<void>;
|
|
12
17
|
isRunning(): boolean;
|
|
13
18
|
getPort(): number;
|
|
19
|
+
protected launch(binaryPath: string, configPath: string): Promise<childProcess.ChildProcessWithoutNullStreams>;
|
|
14
20
|
}
|
package/dist/util/tunnel.js
CHANGED
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PortInUseError = void 0;
|
|
4
|
+
exports.describeSpawnError = describeSpawnError;
|
|
3
5
|
const tslib_1 = require("tslib");
|
|
4
6
|
const fs_1 = tslib_1.__importDefault(require("@supercharge/fs"));
|
|
5
7
|
const childProcess = tslib_1.__importStar(require("child_process"));
|
|
6
8
|
const path = tslib_1.__importStar(require("path"));
|
|
7
9
|
const trtc_1 = require("./trtc");
|
|
8
10
|
const utils_1 = require("./utils");
|
|
11
|
+
// Only a port conflict is worth retrying with another port, so this bounds that retry alone.
|
|
12
|
+
const MAX_PORT_ATTEMPTS = 40;
|
|
13
|
+
class PortInUseError extends Error {
|
|
14
|
+
constructor() {
|
|
15
|
+
super("Port already used");
|
|
16
|
+
this.name = "PortInUseError";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.PortInUseError = PortInUseError;
|
|
9
20
|
function randomPort(min, max) {
|
|
10
21
|
return Math.floor(Math.random() * (max - min + 1) + min);
|
|
11
22
|
}
|
|
@@ -21,6 +32,18 @@ type = "http_proxy"`;
|
|
|
21
32
|
await fs_1.default.writeFile(configPath, newConfigContent);
|
|
22
33
|
return configPath;
|
|
23
34
|
}
|
|
35
|
+
// Endpoint protection often refuses to run an unknown binary with EPERM or EACCES, so those
|
|
36
|
+
// messages say what to allow-list instead of repeating a bare system error.
|
|
37
|
+
function describeSpawnError(error, binaryPath) {
|
|
38
|
+
const code = error.code ?? "unknown error";
|
|
39
|
+
if (error.code === "EPERM" || error.code === "EACCES") {
|
|
40
|
+
return `Could not run trtc at ${binaryPath} (${code}). Antivirus or endpoint protection may be blocking it; allow-list this path and try again.`;
|
|
41
|
+
}
|
|
42
|
+
if (error.code === "ENOENT") {
|
|
43
|
+
return `trtc is missing at ${binaryPath} (${code}). Delete the trtc folder in the testRigor CLI home (~/.testrigor by default) so it is downloaded again.`;
|
|
44
|
+
}
|
|
45
|
+
return `Could not run trtc at ${binaryPath} (${code}): ${error.message}`;
|
|
46
|
+
}
|
|
24
47
|
async function startTrtc(trtc, configPath) {
|
|
25
48
|
return new Promise((resolve, reject) => {
|
|
26
49
|
if (!fs_1.default.pathExistsSync(trtc)) {
|
|
@@ -30,25 +53,34 @@ async function startTrtc(trtc, configPath) {
|
|
|
30
53
|
const tunnelProcess = childProcess.spawn(trtc, ["-c", configPath], {
|
|
31
54
|
cwd: path.dirname(trtc),
|
|
32
55
|
});
|
|
56
|
+
const rejectPortInUse = () => {
|
|
57
|
+
tunnelProcess.kill("SIGINT");
|
|
58
|
+
reject(new PortInUseError());
|
|
59
|
+
};
|
|
33
60
|
tunnelProcess.stdout.on("data", (data) => {
|
|
34
61
|
const dataString = data.toString();
|
|
35
62
|
if (dataString.includes("start proxy success")) {
|
|
36
63
|
resolve(tunnelProcess);
|
|
37
64
|
}
|
|
38
65
|
else if (dataString.includes("port already used")) {
|
|
39
|
-
|
|
40
|
-
reject(new Error("Port already used"));
|
|
66
|
+
rejectPortInUse();
|
|
41
67
|
}
|
|
42
68
|
});
|
|
43
69
|
tunnelProcess.stderr.on("data", (data) => {
|
|
44
|
-
|
|
70
|
+
const dataString = data.toString();
|
|
71
|
+
if (dataString.includes("port already used")) {
|
|
72
|
+
rejectPortInUse();
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
reject(new Error(`TRTC error: ${dataString}`));
|
|
45
76
|
});
|
|
46
77
|
tunnelProcess.on("error", (error) => {
|
|
47
|
-
reject(error);
|
|
78
|
+
reject(new Error(describeSpawnError(error, trtc)));
|
|
48
79
|
});
|
|
49
80
|
tunnelProcess.on("exit", (code, signal) => {
|
|
50
81
|
if (code !== 0) {
|
|
51
|
-
|
|
82
|
+
const how = code !== null ? `code ${code}` : `signal ${signal}`;
|
|
83
|
+
reject(new Error(`TRTC exited with ${how}`));
|
|
52
84
|
}
|
|
53
85
|
});
|
|
54
86
|
});
|
|
@@ -61,30 +93,28 @@ class Tunnel {
|
|
|
61
93
|
}
|
|
62
94
|
async initialize() {
|
|
63
95
|
const directory = await (0, utils_1.createDirectory)(Tunnel.BINARY_DIRECTORY);
|
|
64
|
-
this.binaryPath = await (0, trtc_1.
|
|
96
|
+
this.binaryPath = await (0, trtc_1.ensureBinary)(directory);
|
|
65
97
|
}
|
|
66
98
|
async start(minPort, maxPort) {
|
|
67
|
-
|
|
99
|
+
const binaryPath = this.binaryPath;
|
|
100
|
+
if (binaryPath == null) {
|
|
68
101
|
throw new Error("Could not download the trtc binary");
|
|
69
102
|
}
|
|
70
|
-
let
|
|
71
|
-
|
|
72
|
-
|
|
103
|
+
for (let attempt = 1; attempt <= MAX_PORT_ATTEMPTS; attempt++) {
|
|
104
|
+
this.port = randomPort(minPort, maxPort);
|
|
105
|
+
this.configPath = await updateConfig(binaryPath, this.port);
|
|
73
106
|
try {
|
|
74
|
-
this.
|
|
75
|
-
this.
|
|
76
|
-
|
|
77
|
-
break;
|
|
107
|
+
this.process = await this.launch(binaryPath, this.configPath);
|
|
108
|
+
this.running = true;
|
|
109
|
+
return;
|
|
78
110
|
}
|
|
79
|
-
catch (
|
|
80
|
-
|
|
81
|
-
|
|
111
|
+
catch (error) {
|
|
112
|
+
if (!(error instanceof PortInUseError)) {
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
82
115
|
}
|
|
83
116
|
}
|
|
84
|
-
|
|
85
|
-
throw new Error(`Could not start trtc process: ${lastError instanceof Error ? lastError.message : String(lastError)}`);
|
|
86
|
-
}
|
|
87
|
-
this.running = true;
|
|
117
|
+
throw new Error(`No free tunnel port found in ${minPort}-${maxPort} after ${MAX_PORT_ATTEMPTS} attempts`);
|
|
88
118
|
}
|
|
89
119
|
async stop() {
|
|
90
120
|
if (this.isRunning()) {
|
|
@@ -97,6 +127,9 @@ class Tunnel {
|
|
|
97
127
|
getPort() {
|
|
98
128
|
return this.port;
|
|
99
129
|
}
|
|
130
|
+
launch(binaryPath, configPath) {
|
|
131
|
+
return startTrtc(binaryPath, configPath);
|
|
132
|
+
}
|
|
100
133
|
}
|
|
101
134
|
Tunnel.BINARY_DIRECTORY = "trtc";
|
|
102
135
|
exports.default = Tunnel;
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@testrigor/testrigor-cli",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.9",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@testrigor/testrigor-cli",
|
|
9
|
-
"version": "2.0.
|
|
9
|
+
"version": "2.0.9",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "MIT",
|
|
12
12
|
"dependencies": {
|
package/oclif.manifest.json
CHANGED