wait for the cloudflared tunnel to fully start before exposing its URL

This commit is contained in:
Egor 2025-12-13 21:23:52 -08:00
parent d93a525559
commit b199e9cb24
4 changed files with 40 additions and 9 deletions

View file

@ -1,7 +1,7 @@
{
"name": "gerbil",
"productName": "Gerbil",
"version": "1.16.2",
"version": "1.16.3",
"description": "Run Large Language Models locally",
"main": "out/main/index.js",
"homepage": "./",
@ -67,7 +67,7 @@
"devDependencies": {
"@eslint/js": "^9.39.2",
"@types/mime-types": "^3.0.1",
"@types/node": "^25.0.1",
"@types/node": "^25.0.2",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
"@types/yauzl": "^2.10.3",

View file

@ -92,7 +92,7 @@ export const NetworkTab = () => {
checked={remotetunnel}
onChange={setRemotetunnel}
label="Remote Tunnel"
tooltip="Creates a trycloudflare tunnel. Allows you to access your server from other devices over an internet URL."
tooltip="Creates a cloudflare tunnel. Allows you to access your server from other devices over an internet URL."
/>
<CheckboxWithTooltip

View file

@ -81,6 +81,25 @@ const getTunnelTarget = (frontendPreference: FrontendPreference) => {
}
};
const waitForBackend = async (url: string, timeoutMs = 30000) => {
const startTime = Date.now();
while (Date.now() - startTime < timeoutMs) {
try {
const response = await fetch(url, {
method: 'HEAD',
signal: AbortSignal.timeout(2000),
});
if (response.ok) {
return true;
}
} catch {}
await new Promise((resolve) => setTimeout(resolve, 500));
}
return false;
};
export const startTunnel = async (
frontendPreference: FrontendPreference = 'koboldcpp'
) => {
@ -89,6 +108,17 @@ export const startTunnel = async (
}
try {
const tunnelTarget = getTunnelTarget(frontendPreference);
sendKoboldOutput('Waiting for backend to be ready...');
const backendReady = await waitForBackend(tunnelTarget);
if (!backendReady) {
throw new Error(
'Backend not ready after 30 seconds. Start your backend first before enabling tunnel.'
);
}
sendKoboldOutput('Starting Cloudflare tunnel...');
const bin = getCloudflaredBin();
@ -101,7 +131,6 @@ export const startTunnel = async (
await downloadCloudflared(bin);
}
const tunnelTarget = getTunnelTarget(frontendPreference);
const tunnel = execa(bin, [
'tunnel',
'--url',
@ -163,6 +192,8 @@ export const startTunnel = async (
});
});
await new Promise((resolve) => setTimeout(resolve, 3000));
tunnelUrl = url;
sendKoboldOutput(`Tunnel ready at ${tunnelUrl}`);
sendToRenderer('tunnel-url-changed', tunnelUrl);

View file

@ -1401,12 +1401,12 @@ __metadata:
languageName: node
linkType: hard
"@types/node@npm:*, @types/node@npm:^25.0.1":
version: 25.0.1
resolution: "@types/node@npm:25.0.1"
"@types/node@npm:*, @types/node@npm:^25.0.2":
version: 25.0.2
resolution: "@types/node@npm:25.0.2"
dependencies:
undici-types: "npm:~7.16.0"
checksum: 10c0/1d5ca9f240d0cf8e43d5281c0e6ee96fb22d37dc2e5ef52c6ca71de47957a6128e124990cedf5b14c03d0250737bd78ad370d93bcf1729a75ca4e54384fdd51a
checksum: 10c0/12c4044bf2e46ba3d313ddf6256ee3c88e336a62d129fe788eeab8ff2631b3df51eb31ade4cdc04552fbe51e285f0663c49b60c78acd31da2b9f2c86a84347e3
languageName: node
linkType: hard
@ -3860,7 +3860,7 @@ __metadata:
"@mantine/core": "npm:^8.3.10"
"@mantine/hooks": "npm:^8.3.10"
"@types/mime-types": "npm:^3.0.1"
"@types/node": "npm:^25.0.1"
"@types/node": "npm:^25.0.2"
"@types/react": "npm:^19.2.7"
"@types/react-dom": "npm:^19.2.3"
"@types/yauzl": "npm:^2.10.3"