fluxy-bot 0.5.70 → 0.5.71
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/package.json +1 -1
- package/supervisor/index.ts +15 -33
- package/supervisor/tunnel.ts +23 -9
package/package.json
CHANGED
package/supervisor/index.ts
CHANGED
|
@@ -784,17 +784,18 @@ export async function startSupervisor() {
|
|
|
784
784
|
config.tunnelUrl = tunnelUrl;
|
|
785
785
|
saveConfig(config);
|
|
786
786
|
|
|
787
|
-
// Wait for
|
|
787
|
+
// Wait for local server to be reachable before telling the relay.
|
|
788
|
+
// Probes localhost directly — avoids macOS issue where server can't
|
|
789
|
+
// reach itself through the Cloudflare tunnel URL.
|
|
788
790
|
let tunnelReady = false;
|
|
789
|
-
log.info(
|
|
791
|
+
log.info('Readiness probe: waiting for local server...');
|
|
790
792
|
for (let i = 0; i < 30; i++) {
|
|
791
793
|
try {
|
|
792
|
-
const res = await fetch(
|
|
794
|
+
const res = await fetch(`http://127.0.0.1:${config.port}/api/health?_cb=${Date.now()}`, {
|
|
793
795
|
signal: AbortSignal.timeout(3000),
|
|
794
|
-
headers: { 'Cache-Control': 'no-cache, no-store' },
|
|
795
796
|
});
|
|
796
797
|
log.info(`Readiness probe #${i + 1}: ${res.status}`);
|
|
797
|
-
if (res.
|
|
798
|
+
if (res.ok) {
|
|
798
799
|
tunnelReady = true;
|
|
799
800
|
break;
|
|
800
801
|
}
|
|
@@ -804,7 +805,7 @@ export async function startSupervisor() {
|
|
|
804
805
|
await new Promise(r => setTimeout(r, 1000));
|
|
805
806
|
}
|
|
806
807
|
if (!tunnelReady) {
|
|
807
|
-
log.warn('
|
|
808
|
+
log.warn('Local server readiness probe timed out — updating relay anyway');
|
|
808
809
|
}
|
|
809
810
|
|
|
810
811
|
// Now register tunnel URL with relay and start heartbeats
|
|
@@ -837,17 +838,16 @@ export async function startSupervisor() {
|
|
|
837
838
|
config.tunnelUrl = tunnelUrl;
|
|
838
839
|
saveConfig(config);
|
|
839
840
|
|
|
840
|
-
// Readiness probe
|
|
841
|
+
// Readiness probe — check local server (not tunnel URL, avoids macOS self-reach issue)
|
|
841
842
|
let tunnelReady = false;
|
|
842
|
-
log.info(
|
|
843
|
+
log.info('Readiness probe: waiting for local server...');
|
|
843
844
|
for (let i = 0; i < 30; i++) {
|
|
844
845
|
try {
|
|
845
|
-
const res = await fetch(
|
|
846
|
+
const res = await fetch(`http://127.0.0.1:${config.port}/api/health?_cb=${Date.now()}`, {
|
|
846
847
|
signal: AbortSignal.timeout(3000),
|
|
847
|
-
headers: { 'Cache-Control': 'no-cache, no-store' },
|
|
848
848
|
});
|
|
849
849
|
log.info(`Readiness probe #${i + 1}: ${res.status}`);
|
|
850
|
-
if (res.
|
|
850
|
+
if (res.ok) {
|
|
851
851
|
tunnelReady = true;
|
|
852
852
|
break;
|
|
853
853
|
}
|
|
@@ -857,7 +857,7 @@ export async function startSupervisor() {
|
|
|
857
857
|
await new Promise(r => setTimeout(r, 1000));
|
|
858
858
|
}
|
|
859
859
|
if (!tunnelReady) {
|
|
860
|
-
log.warn('
|
|
860
|
+
log.warn('Local server readiness probe timed out');
|
|
861
861
|
}
|
|
862
862
|
|
|
863
863
|
console.log('__READY__');
|
|
@@ -880,7 +880,7 @@ export async function startSupervisor() {
|
|
|
880
880
|
lastTick = now; // Update immediately so concurrent ticks don't see a stale gap
|
|
881
881
|
|
|
882
882
|
if (wakeGap || periodicCheck) {
|
|
883
|
-
const alive = await isTunnelAlive(tunnelUrl
|
|
883
|
+
const alive = await isTunnelAlive(tunnelUrl!, config.port);
|
|
884
884
|
if (!alive) {
|
|
885
885
|
log.warn('Tunnel dead, restarting...');
|
|
886
886
|
try {
|
|
@@ -892,26 +892,8 @@ export async function startSupervisor() {
|
|
|
892
892
|
// Quick tunnel: restart and get new URL
|
|
893
893
|
const newUrl = await restartTunnel(config.port);
|
|
894
894
|
|
|
895
|
-
//
|
|
896
|
-
|
|
897
|
-
let tunnelReady = false;
|
|
898
|
-
for (let i = 0; i < 30; i++) {
|
|
899
|
-
try {
|
|
900
|
-
const res = await fetch(newUrl + `/api/health?_cb=${Date.now()}`, {
|
|
901
|
-
signal: AbortSignal.timeout(3000),
|
|
902
|
-
headers: { 'Cache-Control': 'no-cache, no-store' },
|
|
903
|
-
});
|
|
904
|
-
if (res.status !== 502 && res.status !== 503) {
|
|
905
|
-
tunnelReady = true;
|
|
906
|
-
log.info(`Tunnel reachable after ${i + 1} probes`);
|
|
907
|
-
break;
|
|
908
|
-
}
|
|
909
|
-
} catch {}
|
|
910
|
-
await new Promise(r => setTimeout(r, 1000));
|
|
911
|
-
}
|
|
912
|
-
if (!tunnelReady) {
|
|
913
|
-
log.warn('Tunnel readiness probe timed out — updating relay anyway');
|
|
914
|
-
}
|
|
895
|
+
// Brief pause to let cloudflared establish the tunnel
|
|
896
|
+
await new Promise(r => setTimeout(r, 3000));
|
|
915
897
|
|
|
916
898
|
tunnelUrl = newUrl;
|
|
917
899
|
|
package/supervisor/tunnel.ts
CHANGED
|
@@ -116,19 +116,33 @@ export function stopTunnel(): void {
|
|
|
116
116
|
proc = null;
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
119
|
+
/** Check if the cloudflared child process is still running */
|
|
120
|
+
export function isTunnelProcessAlive(): boolean {
|
|
121
|
+
return proc !== null && proc.exitCode === null && proc.signalCode === null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export async function isTunnelAlive(url: string, localPort?: number): Promise<boolean> {
|
|
125
|
+
// 1. Process check — if cloudflared exited, tunnel is definitely dead
|
|
126
|
+
if (!isTunnelProcessAlive()) return false;
|
|
127
|
+
|
|
128
|
+
// 2. Local reachability check — confirm the local server is still responding.
|
|
129
|
+
// We probe localhost directly instead of the tunnel URL because on macOS
|
|
130
|
+
// the server can't reach itself through the Cloudflare tunnel (DNS/firewall).
|
|
131
|
+
// If localhost responds and cloudflared is running, the tunnel is alive.
|
|
132
|
+
if (localPort) {
|
|
122
133
|
try {
|
|
123
|
-
const res = await fetch(
|
|
124
|
-
signal: AbortSignal.timeout(
|
|
125
|
-
headers: { 'Cache-Control': 'no-cache, no-store' },
|
|
134
|
+
const res = await fetch(`http://127.0.0.1:${localPort}/api/health?_cb=${Date.now()}`, {
|
|
135
|
+
signal: AbortSignal.timeout(3000),
|
|
126
136
|
});
|
|
127
|
-
if (res.
|
|
137
|
+
if (res.ok) return true;
|
|
128
138
|
} catch {}
|
|
129
|
-
|
|
139
|
+
// Local server not responding — but cloudflared is alive, so tunnel is still up.
|
|
140
|
+
// The worker may be restarting. Don't kill the tunnel for a local issue.
|
|
141
|
+
return true;
|
|
130
142
|
}
|
|
131
|
-
|
|
143
|
+
|
|
144
|
+
// No local port provided — process alive is enough
|
|
145
|
+
return true;
|
|
132
146
|
}
|
|
133
147
|
|
|
134
148
|
export async function restartTunnel(port: number): Promise<string> {
|