@tongil_kim/clautunnel 1.6.0 → 1.6.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/dist/index.js CHANGED
@@ -3000,9 +3000,24 @@ var MobileServerManager = class {
3000
3000
  let needsInstall = false;
3001
3001
  try {
3002
3002
  execSync2("which ngrok", { stdio: "pipe" });
3003
+ try {
3004
+ const configOutput = execSync2("ngrok config check", {
3005
+ stdio: "pipe",
3006
+ timeout: 5e3
3007
+ }).toString();
3008
+ if (!configOutput.toLowerCase().includes("valid")) {
3009
+ issues.push(
3010
+ "ngrok authtoken is not configured.\n 1. Sign up at https://ngrok.com\n 2. Copy your authtoken from https://dashboard.ngrok.com/get-started/your-authtoken\n 3. Run: ngrok config add-authtoken <your-token>"
3011
+ );
3012
+ }
3013
+ } catch {
3014
+ issues.push(
3015
+ "ngrok authtoken is not configured.\n 1. Sign up at https://ngrok.com\n 2. Copy your authtoken from https://dashboard.ngrok.com/get-started/your-authtoken\n 3. Run: ngrok config add-authtoken <your-token>"
3016
+ );
3017
+ }
3003
3018
  } catch {
3004
3019
  issues.push(
3005
- "ngrok is not installed.\n Install: brew install ngrok\n Sign up: https://ngrok.com\n Auth: ngrok config add-authtoken <your-token>"
3020
+ "ngrok is not installed.\n Install: brew install ngrok\n Then configure your account:\n 1. Sign up at https://ngrok.com\n 2. Copy your authtoken from https://dashboard.ngrok.com/get-started/your-authtoken\n 3. Run: ngrok config add-authtoken <your-token>"
3006
3021
  );
3007
3022
  }
3008
3023
  const nodeModulesPath = join5(this.mobileProjectPath, "node_modules");
@@ -3109,26 +3124,51 @@ var MobileServerManager = class {
3109
3124
  async startNgrok() {
3110
3125
  this.ensureLogDir();
3111
3126
  this.ngrokLogStream = createWriteStream(join5(this.logDir, "ngrok.log"));
3127
+ let stderrData = "";
3112
3128
  this.ngrokProcess = spawn2("ngrok", ["http", String(this.expoPort)], {
3113
3129
  stdio: ["ignore", "pipe", "pipe"],
3114
3130
  detached: false
3115
3131
  });
3116
3132
  this.ngrokProcess.stdout?.pipe(this.ngrokLogStream);
3117
- this.ngrokProcess.stderr?.pipe(this.ngrokLogStream);
3133
+ this.ngrokProcess.stderr?.on("data", (chunk) => {
3134
+ stderrData += chunk.toString();
3135
+ this.ngrokLogStream?.write(chunk);
3136
+ });
3118
3137
  this.ngrokProcess.on("error", () => {
3119
3138
  });
3120
3139
  for (let i = 0; i < 10; i++) {
3121
3140
  await this.sleep(1e3);
3141
+ if (this.ngrokProcess.exitCode !== null) {
3142
+ break;
3143
+ }
3122
3144
  const url = await this.getNgrokTunnelUrl();
3123
3145
  if (url) {
3124
3146
  this.tunnelUrl = url;
3125
3147
  return url;
3126
3148
  }
3127
3149
  }
3150
+ this.ngrokError = this.diagnoseNgrokFailure(stderrData);
3128
3151
  this.killProcess(this.ngrokProcess);
3129
3152
  this.ngrokProcess = null;
3130
3153
  return null;
3131
3154
  }
3155
+ /** Last ngrok error diagnosis (available after startNgrok fails) */
3156
+ ngrokError = null;
3157
+ diagnoseNgrokFailure(stderr) {
3158
+ const lower = stderr.toLowerCase();
3159
+ if (lower.includes("authtoken") || lower.includes("err_ngrok_105") || lower.includes("authentication")) {
3160
+ return "ngrok authentication failed.\n Your authtoken may be invalid or expired.\n 1. Get a new token at https://dashboard.ngrok.com/get-started/your-authtoken\n 2. Run: ngrok config add-authtoken <your-token>";
3161
+ }
3162
+ if (lower.includes("tunnel session limit") || lower.includes("err_ngrok_108")) {
3163
+ return "ngrok free plan session limit reached.\n Free accounts allow 1 tunnel at a time.\n Close other ngrok tunnels or upgrade your plan at https://ngrok.com/pricing";
3164
+ }
3165
+ if (lower.includes("tcp dial") || lower.includes("connection refused")) {
3166
+ return `ngrok could not connect to localhost:${this.expoPort}.
3167
+ This is usually a timing issue \u2014 the tunnel started before Expo was ready.`;
3168
+ }
3169
+ return `ngrok tunnel failed to start.
3170
+ Check logs for details: ${join5(this.logDir, "ngrok.log")}`;
3171
+ }
3132
3172
  async startExpo(tunnelUrl) {
3133
3173
  this.ensureLogDir();
3134
3174
  this.expoLogStream = createWriteStream(join5(this.logDir, "expo.log"));
@@ -3219,7 +3259,7 @@ var MobileServerManager = class {
3219
3259
  this.onProgress("Starting ngrok tunnel...");
3220
3260
  const tunnelUrl = await this.startNgrok();
3221
3261
  if (!tunnelUrl) {
3222
- return { started: false, error: "Failed to start ngrok tunnel" };
3262
+ return { started: false, error: this.ngrokError ?? "Failed to start ngrok tunnel" };
3223
3263
  }
3224
3264
  this.onProgress("Starting Expo server...");
3225
3265
  const expoStarted = await this.startExpo(tunnelUrl);