|
| 1 | +import childProcess from "child_process"; |
1 | 2 | import chokidar from "chokidar";
|
2 | 3 | import dotenv from "dotenv";
|
3 | 4 | import fs from "fs/promises";
|
4 | 5 | import ngrok from "ngrok";
|
5 | 6 | import fetch from "node-fetch";
|
6 |
| -import ora from "ora"; |
| 7 | +import ora, { Ora } from "ora"; |
7 | 8 | import pathModule from "path";
|
| 9 | +import util from "util"; |
8 | 10 | import { z } from "zod";
|
| 11 | +import { CLOUD_API_URL } from "../consts.js"; |
9 | 12 | import { telemetryClient } from "../telemetry/telemetry.js";
|
10 | 13 | import { pathExists, readFile } from "../utils/fileSystem.js";
|
11 | 14 | import { logger } from "../utils/logger.js";
|
12 | 15 | import { resolvePath } from "../utils/parseNameAndPath.js";
|
13 | 16 | import { TriggerApi } from "../utils/triggerApi.js";
|
14 |
| -import { CLOUD_API_URL } from "../consts.js"; |
| 17 | + |
| 18 | +const asyncExecFile = util.promisify(childProcess.execFile); |
15 | 19 |
|
16 | 20 | export const DevCommandOptionsSchema = z.object({
|
17 | 21 | port: z.coerce.number(),
|
@@ -282,23 +286,48 @@ async function resolveEndpointUrl(apiUrl: string, port: number) {
|
282 | 286 |
|
283 | 287 | // Setup tunnel
|
284 | 288 | const tunnelSpinner = ora(`🚇 Creating tunnel`).start();
|
285 |
| - const tunnelUrl = await createTunnel(port); |
| 289 | + |
| 290 | + const tunnelUrl = await createTunnel(port, tunnelSpinner); |
| 291 | + |
286 | 292 | if (tunnelUrl) {
|
287 | 293 | tunnelSpinner.succeed(`🚇 Created tunnel: ${tunnelUrl}`);
|
288 | 294 | }
|
289 | 295 |
|
290 | 296 | return tunnelUrl;
|
291 | 297 | }
|
292 | 298 |
|
293 |
| -async function createTunnel(port: number) { |
| 299 | +async function createTunnel(port: number, spinner: Ora) { |
294 | 300 | try {
|
295 | 301 | return await ngrok.connect(port);
|
296 |
| - } catch (e) { |
297 |
| - logger.error(`Ngrok failed to create a tunnel for port ${port}.\n${e}`); |
| 302 | + } catch (error: any) { |
| 303 | + if ( |
| 304 | + typeof error.message === "string" && |
| 305 | + error.message.includes("`version` property is required") |
| 306 | + ) { |
| 307 | + await upgradeNgrokConfig(spinner); |
| 308 | + |
| 309 | + try { |
| 310 | + return await ngrok.connect(port); |
| 311 | + } catch (retryError) { |
| 312 | + spinner.fail( |
| 313 | + `Ngrok failed to create a tunnel for port ${port} after configuration upgrade.\n${retryError}` |
| 314 | + ); |
| 315 | + return; |
| 316 | + } |
| 317 | + } |
298 | 318 | return;
|
299 | 319 | }
|
300 | 320 | }
|
301 | 321 |
|
| 322 | +async function upgradeNgrokConfig(spinner: Ora) { |
| 323 | + try { |
| 324 | + await asyncExecFile("ngrok", ["config", "upgrade"]); |
| 325 | + spinner.info("Ngrok configuration upgraded successfully."); |
| 326 | + } catch (error) { |
| 327 | + spinner.fail(`Failed to upgrade ngrok configuration.\n${error}`); |
| 328 | + } |
| 329 | +} |
| 330 | + |
302 | 331 | async function refreshEndpoint(apiClient: TriggerApi, endpointId: string, endpointUrl: string) {
|
303 | 332 | try {
|
304 | 333 | const response = await apiClient.registerEndpoint({
|
|
0 commit comments