supbuddy 3.1.17 → 3.1.18
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/bin.js +1 -1
- package/dist/daemon/worker.cjs +96 -16
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -19803,7 +19803,7 @@ function cliBuildKind(env3 = process.env) {
|
|
|
19803
19803
|
return raw === "host" || raw === "npm" ? raw : "dev";
|
|
19804
19804
|
}
|
|
19805
19805
|
function cliVersion(env3 = process.env) {
|
|
19806
|
-
return "3.1.
|
|
19806
|
+
return "3.1.18".trim() || "0.0.0-dev";
|
|
19807
19807
|
}
|
|
19808
19808
|
function isDevBuild(env3) {
|
|
19809
19809
|
return cliBuildKind(env3) === "dev";
|
package/dist/daemon/worker.cjs
CHANGED
|
@@ -45400,6 +45400,8 @@ const executors = {
|
|
|
45400
45400
|
resolversReapplied: false,
|
|
45401
45401
|
resolverError: `${resolvers.error ?? resolvers.outcome}. Missing: [${(resolvers.missing ?? []).join(", ")}]. Those domains will not resolve until the files exist.`
|
|
45402
45402
|
} : {};
|
|
45403
|
+
const anchor = await globalThis.reapplyPfAnchorIfDrifted?.("restart_proxy");
|
|
45404
|
+
const anchorReport = anchor && anchor.ok === false ? { pfAnchorUpdated: false, pfAnchorError: `${anchor.error ?? anchor.outcome}` } : anchor?.outcome === "applied" ? { pfAnchorUpdated: true } : {};
|
|
45403
45405
|
const { getPortForwardingStatus: getPortForwardingStatus2, probe443: probe4432, waitForPort: waitForPort2 } = await __vitePreload(async () => {
|
|
45404
45406
|
const { getPortForwardingStatus: getPortForwardingStatus3, probe443: probe4433, waitForPort: waitForPort3 } = await Promise.resolve().then(() => portForwarding);
|
|
45405
45407
|
return { getPortForwardingStatus: getPortForwardingStatus3, probe443: probe4433, waitForPort: waitForPort3 };
|
|
@@ -45408,14 +45410,14 @@ const executors = {
|
|
|
45408
45410
|
const caddyUp = await waitForPort2(httpsPort).catch(() => false);
|
|
45409
45411
|
const pf = caddyUp ? await getPortForwardingStatus2(httpPort, httpsPort, { probe: probe4432 }).catch(() => null) : null;
|
|
45410
45412
|
if (!caddyUp) {
|
|
45411
|
-
return { ok: true, ...resolverReport, portForwardingChecked: false, note: `Caddy is not accepting on ${httpsPort}, so port forwarding was not verified.` };
|
|
45413
|
+
return { ok: true, ...resolverReport, ...anchorReport, portForwardingChecked: false, note: `Caddy is not accepting on ${httpsPort}, so port forwarding was not verified.` };
|
|
45412
45414
|
}
|
|
45413
45415
|
if (pf?.enabled && pf.enforced === false) {
|
|
45414
45416
|
const repair = globalThis.repairPortForwarding;
|
|
45415
45417
|
const r = repair ? await repair({ prompt: "Supbuddy needs to re-apply the port-forwarding rules." }).catch((e) => ({ ok: false, error: e instanceof Error ? e.message : String(e) })) : { ok: false, error: "pf repair is unavailable in this process" };
|
|
45416
|
-
return { ok: true, ...resolverReport, portForwardingRepaired: r.ok, ...r.ok ? {} : { portForwardingError: r.error } };
|
|
45418
|
+
return { ok: true, ...resolverReport, ...anchorReport, portForwardingRepaired: r.ok, ...r.ok ? {} : { portForwardingError: r.error } };
|
|
45417
45419
|
}
|
|
45418
|
-
return { ok: true, ...resolverReport };
|
|
45420
|
+
return { ok: true, ...resolverReport, ...anchorReport };
|
|
45419
45421
|
} finally {
|
|
45420
45422
|
globalThis.scheduleDoctorAutoScan?.("proxy-restart", PROXY_RESCAN_SETTLE_MS);
|
|
45421
45423
|
}
|
|
@@ -50321,6 +50323,10 @@ function buildPfAnchor(input) {
|
|
|
50321
50323
|
}
|
|
50322
50324
|
return lines.join("\n") + "\n";
|
|
50323
50325
|
}
|
|
50326
|
+
function pfAnchorDrifted(desired, actual) {
|
|
50327
|
+
if (actual === null) return true;
|
|
50328
|
+
return actual !== desired;
|
|
50329
|
+
}
|
|
50324
50330
|
const execAsync$5 = util$1.promisify(child_process.exec);
|
|
50325
50331
|
const execFileAsync$2 = util$1.promisify(child_process.execFile);
|
|
50326
50332
|
const MIGRATE_DIR = path.join(os$1.tmpdir(), "supbuddy-migrate");
|
|
@@ -51351,6 +51357,7 @@ async function applyProjectActivation(projectId, opts) {
|
|
|
51351
51357
|
}
|
|
51352
51358
|
globalThis.applyProjectActivation = applyProjectActivation;
|
|
51353
51359
|
globalThis.reapplyDnsResolversIfDrifted = reapplyDnsResolversIfDrifted;
|
|
51360
|
+
globalThis.reapplyPfAnchorIfDrifted = reapplyPfAnchorIfDrifted;
|
|
51354
51361
|
globalThis.repairPortForwarding = repairPortForwarding;
|
|
51355
51362
|
async function testPortForwarding(_httpsPort) {
|
|
51356
51363
|
return probe443();
|
|
@@ -51589,12 +51596,15 @@ function isPfRulesetRejection(err, stderr) {
|
|
|
51589
51596
|
async function maybeAutoStartProxyForGui() {
|
|
51590
51597
|
if (guiProxyAutoStartDone) return;
|
|
51591
51598
|
const store2 = useStore.getState();
|
|
51592
|
-
if (!store2.settings.autoStart) return;
|
|
51593
51599
|
const status = store2.proxyState.status;
|
|
51594
51600
|
if (status === "running" || status === "starting") {
|
|
51595
51601
|
guiProxyAutoStartDone = true;
|
|
51602
|
+
if (status === "running" && shouldRetryPrivilegedSetup(store2.proxyState, lastPrivilegedError)) {
|
|
51603
|
+
await retryPrivilegedSetupOnce("proxy is running but its privileged setup never ran (no GUI owner at boot)");
|
|
51604
|
+
}
|
|
51596
51605
|
return;
|
|
51597
51606
|
}
|
|
51607
|
+
if (!store2.settings.autoStart) return;
|
|
51598
51608
|
guiProxyAutoStartDone = true;
|
|
51599
51609
|
console.log("[Proxy] GUI host-bridge owner ready — auto-starting proxy (autoStart on)");
|
|
51600
51610
|
try {
|
|
@@ -51604,19 +51614,20 @@ async function maybeAutoStartProxyForGui() {
|
|
|
51604
51614
|
}
|
|
51605
51615
|
const afterFirst = useStore.getState().proxyState;
|
|
51606
51616
|
if (shouldRetryPrivilegedSetup(afterFirst, lastPrivilegedError)) {
|
|
51607
|
-
|
|
51608
|
-
|
|
51617
|
+
await retryPrivilegedSetupOnce("auto-start hit the host-bridge owner race (TTY/ASKPASS)");
|
|
51618
|
+
}
|
|
51619
|
+
}
|
|
51620
|
+
async function retryPrivilegedSetupOnce(reason) {
|
|
51621
|
+
console.warn(`[Proxy] ${reason} — retrying privileged setup once in 2s`);
|
|
51622
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
51623
|
+
try {
|
|
51624
|
+
await handleStartProxy();
|
|
51625
|
+
const after = useStore.getState().proxyState;
|
|
51626
|
+
console.log(
|
|
51627
|
+
after.needsPrivilegedRepair ? "[Proxy] privileged retry still degraded — setup failed again (manual Retry remains available)" : "[Proxy] privileged retry recovered — setup applied"
|
|
51609
51628
|
);
|
|
51610
|
-
|
|
51611
|
-
|
|
51612
|
-
await handleStartProxy();
|
|
51613
|
-
const afterRetry = useStore.getState().proxyState;
|
|
51614
|
-
console.log(
|
|
51615
|
-
afterRetry.needsPrivilegedRepair ? "[Proxy] auto-start retry still degraded — privileged setup failed again (manual Retry remains available)" : "[Proxy] auto-start retry recovered — privileged setup applied"
|
|
51616
|
-
);
|
|
51617
|
-
} catch (e) {
|
|
51618
|
-
console.error("[Proxy] auto-start retry failed:", e);
|
|
51619
|
-
}
|
|
51629
|
+
} catch (e) {
|
|
51630
|
+
console.error("[Proxy] privileged retry failed:", e);
|
|
51620
51631
|
}
|
|
51621
51632
|
}
|
|
51622
51633
|
async function handleStartProxy() {
|
|
@@ -51921,6 +51932,75 @@ async function repairPortForwarding(opts) {
|
|
|
51921
51932
|
await clearProxyFaultIfHealthy("a confirmed pf repair");
|
|
51922
51933
|
return { ok: true, enforced: true };
|
|
51923
51934
|
}
|
|
51935
|
+
async function reapplyPfAnchorIfDrifted(reason) {
|
|
51936
|
+
const store2 = useStore.getState();
|
|
51937
|
+
const { httpPort, httpsPort, lanSharing } = store2.settings;
|
|
51938
|
+
const desired = buildPfAnchor({
|
|
51939
|
+
httpPort,
|
|
51940
|
+
httpsPort,
|
|
51941
|
+
lanSharing,
|
|
51942
|
+
lanInterfaces: lanSharing ? getLanInterfaces() : []
|
|
51943
|
+
});
|
|
51944
|
+
let actual = null;
|
|
51945
|
+
try {
|
|
51946
|
+
actual = fsSync.readFileSync("/etc/pf.anchors/virtual.localhost", "utf-8");
|
|
51947
|
+
} catch {
|
|
51948
|
+
actual = null;
|
|
51949
|
+
}
|
|
51950
|
+
let pfConf = "";
|
|
51951
|
+
try {
|
|
51952
|
+
pfConf = fsSync.readFileSync("/etc/pf.conf", "utf-8");
|
|
51953
|
+
} catch {
|
|
51954
|
+
pfConf = "";
|
|
51955
|
+
}
|
|
51956
|
+
if (!pfConf.includes("virtual.localhost")) return { ok: true, outcome: "not-configured" };
|
|
51957
|
+
if (!pfAnchorDrifted(desired, actual)) return { ok: true, outcome: "no-drift" };
|
|
51958
|
+
console.log(`[Proxy] pf anchor is out of date (${reason}) — rewriting`);
|
|
51959
|
+
const tmpSuffix = `${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
51960
|
+
const tmpAnchor = path.join(os$1.tmpdir(), `supbuddy-pf-anchor-${tmpSuffix}`);
|
|
51961
|
+
const tmpValidation = path.join(os$1.tmpdir(), `supbuddy-pf-validate-${tmpSuffix}`);
|
|
51962
|
+
fsSync.writeFileSync(tmpAnchor, desired);
|
|
51963
|
+
try {
|
|
51964
|
+
const { content: correctedPfConf } = buildCorrectedPfConf(pfConf);
|
|
51965
|
+
fsSync.writeFileSync(tmpValidation, buildValidationPfConf(correctedPfConf, tmpAnchor));
|
|
51966
|
+
try {
|
|
51967
|
+
child_process.execSync(`pfctl -nf "${tmpValidation}"`, { stdio: "pipe" });
|
|
51968
|
+
} catch (validationErr) {
|
|
51969
|
+
const stderr = (validationErr?.stderr?.toString() ?? "").trim();
|
|
51970
|
+
if (isPfRulesetRejection(validationErr, stderr)) {
|
|
51971
|
+
console.error(`[Proxy] refusing to install pf anchor — ruleset rejected: ${stderr}`);
|
|
51972
|
+
return { ok: false, outcome: "invalid", error: stderr || "pf rejected the generated ruleset" };
|
|
51973
|
+
}
|
|
51974
|
+
}
|
|
51975
|
+
if (!process.send) return { ok: false, outcome: "no-bridge", error: "no host bridge in this process" };
|
|
51976
|
+
const res = await defaultElevate$1({
|
|
51977
|
+
command: `cp ${JSON.stringify(tmpAnchor)} /etc/pf.anchors/virtual.localhost && ${buildPfLoadCommand({ restoreBackupOnFailure: false })}`,
|
|
51978
|
+
prompt: "Supbuddy needs to update its port-forwarding rules."
|
|
51979
|
+
}).catch((e) => ({ success: false, error: e instanceof Error ? e.message : String(e) }));
|
|
51980
|
+
if (!res.success) {
|
|
51981
|
+
console.warn(`[Proxy] pf anchor rewrite failed: ${res.error ?? "unknown error"}`);
|
|
51982
|
+
return { ok: false, outcome: "failed", error: res.error };
|
|
51983
|
+
}
|
|
51984
|
+
let after = null;
|
|
51985
|
+
try {
|
|
51986
|
+
after = fsSync.readFileSync("/etc/pf.anchors/virtual.localhost", "utf-8");
|
|
51987
|
+
} catch {
|
|
51988
|
+
after = null;
|
|
51989
|
+
}
|
|
51990
|
+
if (pfAnchorDrifted(desired, after)) {
|
|
51991
|
+
return { ok: false, outcome: "failed", error: "the elevated write reported success but the anchor on disk is unchanged" };
|
|
51992
|
+
}
|
|
51993
|
+
console.log("[Proxy] pf anchor rewritten and reloaded");
|
|
51994
|
+
return { ok: true, outcome: "applied" };
|
|
51995
|
+
} finally {
|
|
51996
|
+
for (const f2 of [tmpAnchor, tmpValidation]) {
|
|
51997
|
+
try {
|
|
51998
|
+
fsSync.unlinkSync(f2);
|
|
51999
|
+
} catch {
|
|
52000
|
+
}
|
|
52001
|
+
}
|
|
52002
|
+
}
|
|
52003
|
+
}
|
|
51924
52004
|
async function clearProxyFaultIfHealthy(reason) {
|
|
51925
52005
|
const store2 = useStore.getState();
|
|
51926
52006
|
if (store2.proxyState.status !== "error") return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "supbuddy",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.18",
|
|
4
4
|
"description": "Run multiple Supabase projects at once on custom local domains with HTTPS. A headless CLI and daemon (proxy, DNS, Supabase/Compose lifecycle, MCP) for macOS and Linux.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"supabase",
|