portless 0.15.0 → 0.15.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/README.md +17 -6
- package/dist/{chunk-PCBKLZK2.js → chunk-SD2PIWJU.js} +21 -5
- package/dist/cli.js +333 -166
- package/dist/index.d.ts +8 -1
- package/dist/index.js +3 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -14,9 +14,10 @@ import {
|
|
|
14
14
|
isErrnoException,
|
|
15
15
|
isProcessAlive,
|
|
16
16
|
parseHostname,
|
|
17
|
+
parseHostnames,
|
|
17
18
|
shouldAutoSyncHosts,
|
|
18
19
|
syncHostsFile
|
|
19
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-SD2PIWJU.js";
|
|
20
21
|
|
|
21
22
|
// src/colors.ts
|
|
22
23
|
function supportsColor() {
|
|
@@ -501,15 +502,16 @@ async function generateHostCertAsync(stateDir, hostname) {
|
|
|
501
502
|
fixOwnership(keyPath, certPath);
|
|
502
503
|
return { certPath, keyPath };
|
|
503
504
|
}
|
|
504
|
-
function createSNICallback(stateDir, defaultCert, defaultKey,
|
|
505
|
+
function createSNICallback(stateDir, defaultCert, defaultKey, tlds = "localhost", caCert) {
|
|
505
506
|
const cache = /* @__PURE__ */ new Map();
|
|
506
507
|
const pending = /* @__PURE__ */ new Map();
|
|
508
|
+
const configuredTlds = Array.isArray(tlds) ? tlds : [tlds];
|
|
507
509
|
const defaultCtx = tls.createSecureContext({
|
|
508
510
|
cert: caCert ? Buffer.concat([defaultCert, caCert]) : defaultCert,
|
|
509
511
|
key: defaultKey
|
|
510
512
|
});
|
|
511
513
|
return (servername, cb) => {
|
|
512
|
-
if (servername ===
|
|
514
|
+
if (servername === "localhost" && configuredTlds.includes("localhost")) {
|
|
513
515
|
cb(null, defaultCtx);
|
|
514
516
|
return;
|
|
515
517
|
}
|
|
@@ -1584,8 +1586,25 @@ function validateTld(tld) {
|
|
|
1584
1586
|
}
|
|
1585
1587
|
return null;
|
|
1586
1588
|
}
|
|
1589
|
+
function parseTldList(value, source = "TLD") {
|
|
1590
|
+
const trimmed = value.trim();
|
|
1591
|
+
if (!trimmed) return [];
|
|
1592
|
+
const tlds = [];
|
|
1593
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1594
|
+
for (const rawPart of trimmed.split(",")) {
|
|
1595
|
+
const tld = rawPart.trim().toLowerCase();
|
|
1596
|
+
const err = validateTld(tld);
|
|
1597
|
+
if (err) throw new Error(source === "TLD" ? err : `${source}: ${err}`);
|
|
1598
|
+
if (!seen.has(tld)) {
|
|
1599
|
+
seen.add(tld);
|
|
1600
|
+
tlds.push(tld);
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
return tlds;
|
|
1604
|
+
}
|
|
1587
1605
|
var TLD_FILE = "proxy.tld";
|
|
1588
|
-
|
|
1606
|
+
var TLDS_FILE = "proxy.tlds";
|
|
1607
|
+
function readLegacyTldFromDir(dir) {
|
|
1589
1608
|
try {
|
|
1590
1609
|
const raw = fs3.readFileSync(path3.join(dir, TLD_FILE), "utf-8").trim();
|
|
1591
1610
|
return raw || DEFAULT_TLD;
|
|
@@ -1593,23 +1612,43 @@ function readTldFromDir(dir) {
|
|
|
1593
1612
|
return DEFAULT_TLD;
|
|
1594
1613
|
}
|
|
1595
1614
|
}
|
|
1596
|
-
function
|
|
1597
|
-
|
|
1598
|
-
|
|
1615
|
+
function readTldsFromDir(dir) {
|
|
1616
|
+
try {
|
|
1617
|
+
const raw = fs3.readFileSync(path3.join(dir, TLDS_FILE), "utf-8").trim();
|
|
1618
|
+
const parsed = raw.startsWith("[") ? JSON.parse(raw) : raw.split(/\r?\n/).flatMap((line) => line.split(",")).map((line) => line.trim()).filter(Boolean);
|
|
1619
|
+
if (!Array.isArray(parsed)) return [readLegacyTldFromDir(dir)];
|
|
1620
|
+
const tlds = parsed.flatMap((value) => typeof value === "string" ? parseTldList(value) : []);
|
|
1621
|
+
return tlds.length > 0 ? [...new Set(tlds)] : [DEFAULT_TLD];
|
|
1622
|
+
} catch {
|
|
1623
|
+
return [readLegacyTldFromDir(dir)];
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
function writeTldsFile(dir, tlds) {
|
|
1627
|
+
const uniqueTlds = [...new Set(tlds)];
|
|
1628
|
+
const tldsPath = path3.join(dir, TLDS_FILE);
|
|
1629
|
+
const tldPath = path3.join(dir, TLD_FILE);
|
|
1630
|
+
if (uniqueTlds.length === 1 && uniqueTlds[0] === DEFAULT_TLD) {
|
|
1599
1631
|
try {
|
|
1600
|
-
fs3.unlinkSync(
|
|
1632
|
+
fs3.unlinkSync(tldsPath);
|
|
1633
|
+
} catch {
|
|
1634
|
+
}
|
|
1635
|
+
try {
|
|
1636
|
+
fs3.unlinkSync(tldPath);
|
|
1601
1637
|
} catch {
|
|
1602
1638
|
}
|
|
1603
1639
|
} else {
|
|
1604
|
-
fs3.writeFileSync(
|
|
1640
|
+
fs3.writeFileSync(tldsPath, uniqueTlds.join("\n") + "\n", { mode: 420 });
|
|
1641
|
+
fs3.writeFileSync(tldPath, uniqueTlds[0] ?? DEFAULT_TLD, { mode: 420 });
|
|
1605
1642
|
}
|
|
1606
1643
|
}
|
|
1607
|
-
function
|
|
1644
|
+
function writeTldFile(dir, tld) {
|
|
1645
|
+
writeTldsFile(dir, [tld]);
|
|
1646
|
+
}
|
|
1647
|
+
function getDefaultTlds() {
|
|
1608
1648
|
const val = process.env.PORTLESS_TLD?.trim().toLowerCase();
|
|
1609
|
-
if (!val) return DEFAULT_TLD;
|
|
1610
|
-
const
|
|
1611
|
-
|
|
1612
|
-
return val;
|
|
1649
|
+
if (!val) return [DEFAULT_TLD];
|
|
1650
|
+
const tlds = parseTldList(val, "PORTLESS_TLD");
|
|
1651
|
+
return tlds.length > 0 ? tlds : [DEFAULT_TLD];
|
|
1613
1652
|
}
|
|
1614
1653
|
function isHttpsEnvDisabled() {
|
|
1615
1654
|
const val = process.env.PORTLESS_HTTPS;
|
|
@@ -1628,14 +1667,17 @@ function readPersistedProxyState() {
|
|
|
1628
1667
|
const port = readPortFromDir(dir);
|
|
1629
1668
|
if (port !== null) {
|
|
1630
1669
|
const tls2 = readTlsMarker(dir);
|
|
1631
|
-
const
|
|
1670
|
+
const tlds = readTldsFromDir(dir);
|
|
1671
|
+
const tld = tlds[0] ?? DEFAULT_TLD;
|
|
1632
1672
|
const lanIp = readLanMarker(dir);
|
|
1633
|
-
return { port, tls: tls2, tld, lanMode: lanIp !== null ||
|
|
1673
|
+
return { port, tls: tls2, tld, tlds, lanMode: lanIp !== null || tlds.includes("local") };
|
|
1634
1674
|
}
|
|
1635
1675
|
return null;
|
|
1636
1676
|
}
|
|
1637
1677
|
function buildProxyStartConfig(options) {
|
|
1638
|
-
const
|
|
1678
|
+
const requestedTlds = options.tlds && options.tlds.length > 0 ? [...options.tlds] : [options.tld];
|
|
1679
|
+
const effectiveTlds = options.lanMode ? ["local"] : [...new Set(requestedTlds)];
|
|
1680
|
+
const effectiveTld = effectiveTlds[0] ?? DEFAULT_TLD;
|
|
1639
1681
|
const args = [];
|
|
1640
1682
|
if (options.foreground) {
|
|
1641
1683
|
args.push("--foreground");
|
|
@@ -1661,8 +1703,10 @@ function buildProxyStartConfig(options) {
|
|
|
1661
1703
|
args.push(INTERNAL_LAN_IP_FLAG, options.lanIp);
|
|
1662
1704
|
}
|
|
1663
1705
|
}
|
|
1664
|
-
} else if (effectiveTld !== DEFAULT_TLD) {
|
|
1665
|
-
|
|
1706
|
+
} else if (effectiveTlds.length > 1 || effectiveTld !== DEFAULT_TLD) {
|
|
1707
|
+
for (const tld of effectiveTlds) {
|
|
1708
|
+
args.push("--tld", tld);
|
|
1709
|
+
}
|
|
1666
1710
|
}
|
|
1667
1711
|
if (options.useWildcard) {
|
|
1668
1712
|
args.push("--wildcard");
|
|
@@ -1670,7 +1714,7 @@ function buildProxyStartConfig(options) {
|
|
|
1670
1714
|
if (options.skipTrust) {
|
|
1671
1715
|
args.push("--skip-trust");
|
|
1672
1716
|
}
|
|
1673
|
-
return { effectiveTld, args };
|
|
1717
|
+
return { effectiveTld, effectiveTlds, args };
|
|
1674
1718
|
}
|
|
1675
1719
|
async function discoverState() {
|
|
1676
1720
|
if (process.env.PORTLESS_STATE_DIR) {
|
|
@@ -1679,14 +1723,25 @@ async function discoverState() {
|
|
|
1679
1723
|
const lanIp = readLanMarker(dir2);
|
|
1680
1724
|
if (await isProxyRunning(port) || await isPortListening(port)) {
|
|
1681
1725
|
const tls2 = readTlsMarker(dir2);
|
|
1682
|
-
const
|
|
1683
|
-
|
|
1726
|
+
const tlds3 = readTldsFromDir(dir2);
|
|
1727
|
+
const tld = tlds3[0] ?? DEFAULT_TLD;
|
|
1728
|
+
return {
|
|
1729
|
+
dir: dir2,
|
|
1730
|
+
port,
|
|
1731
|
+
tls: tls2,
|
|
1732
|
+
tld,
|
|
1733
|
+
tlds: tlds3,
|
|
1734
|
+
lanMode: lanIp !== null || tlds3.includes("local"),
|
|
1735
|
+
lanIp
|
|
1736
|
+
};
|
|
1684
1737
|
}
|
|
1738
|
+
const tlds2 = readTldsFromDir(dir2);
|
|
1685
1739
|
return {
|
|
1686
1740
|
dir: dir2,
|
|
1687
1741
|
port,
|
|
1688
1742
|
tls: readTlsMarker(dir2),
|
|
1689
|
-
tld:
|
|
1743
|
+
tld: tlds2[0] ?? DEFAULT_TLD,
|
|
1744
|
+
tlds: tlds2,
|
|
1690
1745
|
lanMode: lanIp !== null,
|
|
1691
1746
|
lanIp: null
|
|
1692
1747
|
};
|
|
@@ -1695,14 +1750,16 @@ async function discoverState() {
|
|
|
1695
1750
|
if (userPort !== null) {
|
|
1696
1751
|
if (await isProxyRunning(userPort)) {
|
|
1697
1752
|
const tls2 = readTlsMarker(USER_STATE_DIR);
|
|
1698
|
-
const
|
|
1753
|
+
const tlds2 = readTldsFromDir(USER_STATE_DIR);
|
|
1754
|
+
const tld = tlds2[0] ?? DEFAULT_TLD;
|
|
1699
1755
|
const lanIp = readLanMarker(USER_STATE_DIR);
|
|
1700
1756
|
return {
|
|
1701
1757
|
dir: USER_STATE_DIR,
|
|
1702
1758
|
port: userPort,
|
|
1703
1759
|
tls: tls2,
|
|
1704
1760
|
tld,
|
|
1705
|
-
|
|
1761
|
+
tlds: tlds2,
|
|
1762
|
+
lanMode: lanIp !== null || tlds2.includes("local"),
|
|
1706
1763
|
lanIp
|
|
1707
1764
|
};
|
|
1708
1765
|
}
|
|
@@ -1711,14 +1768,16 @@ async function discoverState() {
|
|
|
1711
1768
|
if (legacyPort !== null) {
|
|
1712
1769
|
if (await isProxyRunning(legacyPort)) {
|
|
1713
1770
|
const tls2 = readTlsMarker(LEGACY_SYSTEM_STATE_DIR);
|
|
1714
|
-
const
|
|
1771
|
+
const tlds2 = readTldsFromDir(LEGACY_SYSTEM_STATE_DIR);
|
|
1772
|
+
const tld = tlds2[0] ?? DEFAULT_TLD;
|
|
1715
1773
|
const lanIp = readLanMarker(LEGACY_SYSTEM_STATE_DIR);
|
|
1716
1774
|
return {
|
|
1717
1775
|
dir: LEGACY_SYSTEM_STATE_DIR,
|
|
1718
1776
|
port: legacyPort,
|
|
1719
1777
|
tls: tls2,
|
|
1720
1778
|
tld,
|
|
1721
|
-
|
|
1779
|
+
tlds: tlds2,
|
|
1780
|
+
lanMode: lanIp !== null || tlds2.includes("local"),
|
|
1722
1781
|
lanIp
|
|
1723
1782
|
};
|
|
1724
1783
|
}
|
|
@@ -1730,17 +1789,28 @@ async function discoverState() {
|
|
|
1730
1789
|
const dir2 = resolveStateDir(port);
|
|
1731
1790
|
const markerTls = readTlsMarker(dir2);
|
|
1732
1791
|
const tls2 = markerTls || port === getProtocolPort(true);
|
|
1733
|
-
const
|
|
1792
|
+
const tlds2 = readTldsFromDir(dir2);
|
|
1793
|
+
const tld = tlds2[0] ?? DEFAULT_TLD;
|
|
1734
1794
|
const lanIp = readLanMarker(dir2);
|
|
1735
|
-
return {
|
|
1795
|
+
return {
|
|
1796
|
+
dir: dir2,
|
|
1797
|
+
port,
|
|
1798
|
+
tls: tls2,
|
|
1799
|
+
tld,
|
|
1800
|
+
tlds: tlds2,
|
|
1801
|
+
lanMode: lanIp !== null || tlds2.includes("local"),
|
|
1802
|
+
lanIp
|
|
1803
|
+
};
|
|
1736
1804
|
}
|
|
1737
1805
|
}
|
|
1738
1806
|
const dir = resolveStateDir(configuredPort);
|
|
1807
|
+
const tlds = readTldsFromDir(dir);
|
|
1739
1808
|
return {
|
|
1740
1809
|
dir,
|
|
1741
1810
|
port: configuredPort,
|
|
1742
1811
|
tls: readTlsMarker(dir),
|
|
1743
|
-
tld:
|
|
1812
|
+
tld: tlds[0] ?? DEFAULT_TLD,
|
|
1813
|
+
tlds,
|
|
1744
1814
|
lanMode: readLanMarker(dir) !== null,
|
|
1745
1815
|
lanIp: null
|
|
1746
1816
|
};
|
|
@@ -2025,6 +2095,7 @@ var PORTLESS_STATE_FILES = [
|
|
|
2025
2095
|
"proxy.tls",
|
|
2026
2096
|
"proxy.custom-cert",
|
|
2027
2097
|
"proxy.tld",
|
|
2098
|
+
"proxy.tlds",
|
|
2028
2099
|
"proxy.lan",
|
|
2029
2100
|
"ca-key.pem",
|
|
2030
2101
|
"ca.pem",
|
|
@@ -2797,6 +2868,15 @@ var SYSTEMD_SERVICE = "portless.service";
|
|
|
2797
2868
|
var WINDOWS_TASK_NAME = "Portless Proxy";
|
|
2798
2869
|
var INTERNAL_ELEVATED_ENV = "PORTLESS_INTERNAL_SERVICE_ELEVATED";
|
|
2799
2870
|
var SERVICE_ENV_KEYS = /* @__PURE__ */ new Set(["PORTLESS_SYNC_HOSTS"]);
|
|
2871
|
+
function normalizeTlds(tlds) {
|
|
2872
|
+
return [...new Set(tlds.length > 0 ? tlds : [DEFAULT_TLD])];
|
|
2873
|
+
}
|
|
2874
|
+
function primaryTld(tlds) {
|
|
2875
|
+
return tlds[0] ?? DEFAULT_TLD;
|
|
2876
|
+
}
|
|
2877
|
+
function formatTldList(tlds) {
|
|
2878
|
+
return tlds.map((tld) => `.${tld}`).join(", ");
|
|
2879
|
+
}
|
|
2800
2880
|
var DEFAULT_SERVICE_CONFIG = {
|
|
2801
2881
|
proxyPort: DEFAULT_SERVICE_PORT,
|
|
2802
2882
|
useHttps: true,
|
|
@@ -2806,6 +2886,7 @@ var DEFAULT_SERVICE_CONFIG = {
|
|
|
2806
2886
|
lanIp: null,
|
|
2807
2887
|
lanIpExplicit: false,
|
|
2808
2888
|
tld: DEFAULT_TLD,
|
|
2889
|
+
tlds: [DEFAULT_TLD],
|
|
2809
2890
|
useWildcard: false,
|
|
2810
2891
|
extraEnv: {}
|
|
2811
2892
|
};
|
|
@@ -2892,10 +2973,8 @@ function parseServiceInstallConfig(args, env = process.env, options = {}) {
|
|
|
2892
2973
|
config.lanIpExplicit = true;
|
|
2893
2974
|
}
|
|
2894
2975
|
if (env.PORTLESS_TLD) {
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
if (err) throw new Error(`PORTLESS_TLD: ${err}`);
|
|
2898
|
-
config.tld = tld;
|
|
2976
|
+
config.tlds = normalizeTlds(parseTldList(env.PORTLESS_TLD, "PORTLESS_TLD"));
|
|
2977
|
+
config.tld = primaryTld(config.tlds);
|
|
2899
2978
|
}
|
|
2900
2979
|
const envWildcard = parseBooleanEnv(env.PORTLESS_WILDCARD);
|
|
2901
2980
|
if (envWildcard !== null) {
|
|
@@ -2907,6 +2986,7 @@ function parseServiceInstallConfig(args, env = process.env, options = {}) {
|
|
|
2907
2986
|
config.proxyPort = getProtocolPort(config.useHttps);
|
|
2908
2987
|
}
|
|
2909
2988
|
const tokens = args[0] === "service" ? args.slice(2) : args;
|
|
2989
|
+
let tldFlagSeen = false;
|
|
2910
2990
|
for (let i = 0; i < tokens.length; i += 1) {
|
|
2911
2991
|
const token = tokens[i];
|
|
2912
2992
|
switch (token) {
|
|
@@ -2931,10 +3011,10 @@ function parseServiceInstallConfig(args, env = process.env, options = {}) {
|
|
|
2931
3011
|
i += 1;
|
|
2932
3012
|
break;
|
|
2933
3013
|
case "--tld": {
|
|
2934
|
-
const
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
3014
|
+
const tlds = parseTldList(getFlagValue(tokens, i, token));
|
|
3015
|
+
config.tlds = normalizeTlds([...tldFlagSeen ? config.tlds : [], ...tlds]);
|
|
3016
|
+
config.tld = primaryTld(config.tlds);
|
|
3017
|
+
tldFlagSeen = true;
|
|
2938
3018
|
i += 1;
|
|
2939
3019
|
break;
|
|
2940
3020
|
}
|
|
@@ -2974,6 +3054,9 @@ function parseServiceInstallConfig(args, env = process.env, options = {}) {
|
|
|
2974
3054
|
if (!config.lanMode) {
|
|
2975
3055
|
config.lanIp = null;
|
|
2976
3056
|
config.lanIpExplicit = false;
|
|
3057
|
+
} else {
|
|
3058
|
+
config.tlds = ["local"];
|
|
3059
|
+
config.tld = "local";
|
|
2977
3060
|
}
|
|
2978
3061
|
return config;
|
|
2979
3062
|
}
|
|
@@ -3019,6 +3102,7 @@ function buildProxyCommand(entryScript, serviceConfig) {
|
|
|
3019
3102
|
lanIp: serviceConfig.lanIp,
|
|
3020
3103
|
lanIpExplicit: serviceConfig.lanIpExplicit,
|
|
3021
3104
|
tld: serviceConfig.tld,
|
|
3105
|
+
tlds: serviceConfig.tlds,
|
|
3022
3106
|
useWildcard: serviceConfig.useWildcard,
|
|
3023
3107
|
foreground: true,
|
|
3024
3108
|
includePort: true,
|
|
@@ -3041,8 +3125,8 @@ function buildServiceEnv(ctx) {
|
|
|
3041
3125
|
}
|
|
3042
3126
|
if (ctx.config.lanMode) {
|
|
3043
3127
|
env.PORTLESS_TLD = "local";
|
|
3044
|
-
} else if (ctx.config.tld !== DEFAULT_TLD) {
|
|
3045
|
-
env.PORTLESS_TLD = ctx.config.
|
|
3128
|
+
} else if (ctx.config.tlds.length > 1 || ctx.config.tld !== DEFAULT_TLD) {
|
|
3129
|
+
env.PORTLESS_TLD = ctx.config.tlds.join(",");
|
|
3046
3130
|
}
|
|
3047
3131
|
if (ctx.platform === "win32") {
|
|
3048
3132
|
env.USERPROFILE = ctx.user.home;
|
|
@@ -3127,6 +3211,10 @@ function buildServiceSpec(options) {
|
|
|
3127
3211
|
...options.installConfig,
|
|
3128
3212
|
extraEnv: options.installConfig?.extraEnv ?? {}
|
|
3129
3213
|
};
|
|
3214
|
+
installConfig.tlds = installConfig.lanMode ? ["local"] : normalizeTlds(
|
|
3215
|
+
options.installConfig?.tlds ?? (options.installConfig?.tld ? [options.installConfig.tld] : installConfig.tlds)
|
|
3216
|
+
);
|
|
3217
|
+
installConfig.tld = primaryTld(installConfig.tlds);
|
|
3130
3218
|
const stateDir = options.stateDir || installConfig.stateDir || defaultStateDir(options.platform, options.userHome);
|
|
3131
3219
|
const normalizedConfig = {
|
|
3132
3220
|
...installConfig,
|
|
@@ -3617,7 +3705,7 @@ async function printServiceStatus(entryScript, runner) {
|
|
|
3617
3705
|
` Proxy on ${config.proxyPort}: ${status.proxyRunning ? "responding" : "not responding"}`
|
|
3618
3706
|
);
|
|
3619
3707
|
console.log(` HTTPS: ${config.useHttps ? "yes" : "no"}`);
|
|
3620
|
-
console.log(`
|
|
3708
|
+
console.log(` TLDs: ${config.lanMode ? ".local" : formatTldList(config.tlds)}`);
|
|
3621
3709
|
console.log(` LAN mode: ${config.lanMode ? "yes" : "no"}`);
|
|
3622
3710
|
if (config.lanIpExplicit && config.lanIp) {
|
|
3623
3711
|
console.log(` LAN IP: ${config.lanIp}`);
|
|
@@ -3645,7 +3733,7 @@ ${colors_default.bold("Install options:")}
|
|
|
3645
3733
|
--https Enable HTTPS
|
|
3646
3734
|
--lan Enable LAN mode
|
|
3647
3735
|
--ip <address> Pin a specific LAN IP
|
|
3648
|
-
--tld <tld> Use a custom TLD outside LAN mode
|
|
3736
|
+
--tld <tld> Use a custom TLD outside LAN mode, repeatable
|
|
3649
3737
|
--wildcard Allow subdomain fallback
|
|
3650
3738
|
--cert <path> Use a custom TLS certificate
|
|
3651
3739
|
--key <path> Use a custom TLS private key
|
|
@@ -3695,7 +3783,17 @@ var DEBOUNCE_MS = 100;
|
|
|
3695
3783
|
var POLL_INTERVAL_MS = 3e3;
|
|
3696
3784
|
var EXIT_TIMEOUT_MS = 2e3;
|
|
3697
3785
|
var SUDO_SPAWN_TIMEOUT_MS = 3e4;
|
|
3698
|
-
function
|
|
3786
|
+
function normalizeTlds2(tlds) {
|
|
3787
|
+
return [...new Set(tlds.length > 0 ? tlds : [DEFAULT_TLD])];
|
|
3788
|
+
}
|
|
3789
|
+
function primaryTld2(tlds) {
|
|
3790
|
+
return tlds[0] ?? DEFAULT_TLD;
|
|
3791
|
+
}
|
|
3792
|
+
function formatTldList2(tlds) {
|
|
3793
|
+
return tlds.map((tld) => `.${tld}`).join(", ");
|
|
3794
|
+
}
|
|
3795
|
+
function defaultProxyConfig(tlds, useHttps, lanMode) {
|
|
3796
|
+
const effectiveTlds = lanMode ? ["local"] : normalizeTlds2(tlds);
|
|
3699
3797
|
return {
|
|
3700
3798
|
useHttps,
|
|
3701
3799
|
customCertPath: null,
|
|
@@ -3703,13 +3801,14 @@ function defaultProxyConfig(tld, useHttps, lanMode) {
|
|
|
3703
3801
|
lanMode,
|
|
3704
3802
|
lanIp: null,
|
|
3705
3803
|
lanIpExplicit: false,
|
|
3706
|
-
tld:
|
|
3804
|
+
tld: primaryTld2(effectiveTlds),
|
|
3805
|
+
tlds: effectiveTlds,
|
|
3707
3806
|
useWildcard: false
|
|
3708
3807
|
};
|
|
3709
3808
|
}
|
|
3710
3809
|
function resolveProxyConfig(options) {
|
|
3711
3810
|
const config = defaultProxyConfig(
|
|
3712
|
-
options.
|
|
3811
|
+
options.defaultTlds,
|
|
3713
3812
|
options.useHttps,
|
|
3714
3813
|
options.explicit.lanMode ? options.lanMode : options.persistedLanMode
|
|
3715
3814
|
);
|
|
@@ -3730,8 +3829,9 @@ function resolveProxyConfig(options) {
|
|
|
3730
3829
|
if (!options.lanMode) {
|
|
3731
3830
|
config.lanIp = null;
|
|
3732
3831
|
config.lanIpExplicit = false;
|
|
3733
|
-
if (!options.explicit.
|
|
3734
|
-
config.
|
|
3832
|
+
if (!options.explicit.tlds) {
|
|
3833
|
+
config.tlds = normalizeTlds2(options.defaultTlds);
|
|
3834
|
+
config.tld = primaryTld2(config.tlds);
|
|
3735
3835
|
}
|
|
3736
3836
|
}
|
|
3737
3837
|
}
|
|
@@ -3740,8 +3840,9 @@ function resolveProxyConfig(options) {
|
|
|
3740
3840
|
config.lanIp = options.lanIp;
|
|
3741
3841
|
config.lanIpExplicit = true;
|
|
3742
3842
|
}
|
|
3743
|
-
if (options.explicit.
|
|
3744
|
-
config.
|
|
3843
|
+
if (options.explicit.tlds) {
|
|
3844
|
+
config.tlds = normalizeTlds2(options.tlds);
|
|
3845
|
+
config.tld = primaryTld2(config.tlds);
|
|
3745
3846
|
}
|
|
3746
3847
|
if (options.explicit.useWildcard) {
|
|
3747
3848
|
config.useWildcard = options.useWildcard;
|
|
@@ -3751,6 +3852,7 @@ function resolveProxyConfig(options) {
|
|
|
3751
3852
|
config.lanIpExplicit = false;
|
|
3752
3853
|
}
|
|
3753
3854
|
if (config.lanMode) {
|
|
3855
|
+
config.tlds = ["local"];
|
|
3754
3856
|
config.tld = "local";
|
|
3755
3857
|
if (!config.lanIpExplicit) {
|
|
3756
3858
|
config.lanIp = null;
|
|
@@ -3764,15 +3866,17 @@ function resolveProxyConfig(options) {
|
|
|
3764
3866
|
}
|
|
3765
3867
|
function readCurrentProxyConfig(dir) {
|
|
3766
3868
|
const lanIp = readLanMarker(dir);
|
|
3767
|
-
const
|
|
3869
|
+
const tlds = readTldsFromDir(dir);
|
|
3870
|
+
const tld = primaryTld2(tlds);
|
|
3768
3871
|
return {
|
|
3769
3872
|
useHttps: readTlsMarker(dir),
|
|
3770
3873
|
customCertPath: null,
|
|
3771
3874
|
customKeyPath: null,
|
|
3772
|
-
lanMode: lanIp !== null ||
|
|
3875
|
+
lanMode: lanIp !== null || tlds.includes("local"),
|
|
3773
3876
|
lanIp,
|
|
3774
3877
|
lanIpExplicit: false,
|
|
3775
3878
|
tld,
|
|
3879
|
+
tlds,
|
|
3776
3880
|
useWildcard: false
|
|
3777
3881
|
};
|
|
3778
3882
|
}
|
|
@@ -3793,9 +3897,9 @@ function getProxyConfigMismatchMessages(desiredConfig, actualConfig, explicit) {
|
|
|
3793
3897
|
desiredConfig.useHttps ? "requested HTTPS, but the running proxy is using HTTP" : "requested HTTP, but the running proxy is using HTTPS"
|
|
3794
3898
|
);
|
|
3795
3899
|
}
|
|
3796
|
-
if (explicit.
|
|
3900
|
+
if (explicit.tlds && (desiredConfig.tlds.length !== actualConfig.tlds.length || desiredConfig.tlds.some((tld, index) => tld !== actualConfig.tlds[index]))) {
|
|
3797
3901
|
messages.push(
|
|
3798
|
-
`requested
|
|
3902
|
+
`requested ${formatTldList2(desiredConfig.tlds)}, but the running proxy is using ${formatTldList2(actualConfig.tlds)}`
|
|
3799
3903
|
);
|
|
3800
3904
|
}
|
|
3801
3905
|
return messages;
|
|
@@ -3810,6 +3914,7 @@ function formatProxyStartCommand(proxyPort, config) {
|
|
|
3810
3914
|
lanIp: config.lanIpExplicit ? config.lanIp : null,
|
|
3811
3915
|
lanIpExplicit: config.lanIpExplicit,
|
|
3812
3916
|
tld: config.tld,
|
|
3917
|
+
tlds: config.tlds,
|
|
3813
3918
|
useWildcard: config.useWildcard,
|
|
3814
3919
|
includePort: proxyPort !== getDefaultPort(config.useHttps),
|
|
3815
3920
|
proxyPort
|
|
@@ -3903,7 +4008,46 @@ function formatProcessExitSuffix(code, signal) {
|
|
|
3903
4008
|
if (code !== null) return ` (exit ${code})`;
|
|
3904
4009
|
return "";
|
|
3905
4010
|
}
|
|
3906
|
-
function
|
|
4011
|
+
function buildHostnames(name, tlds) {
|
|
4012
|
+
return parseHostnames(name, normalizeTlds2(tlds));
|
|
4013
|
+
}
|
|
4014
|
+
function formatUrls(hostnames, proxyPort, tls2) {
|
|
4015
|
+
return hostnames.map((hostname) => formatUrl(hostname, proxyPort, tls2));
|
|
4016
|
+
}
|
|
4017
|
+
function formatViteAllowedHosts(tlds) {
|
|
4018
|
+
return tlds.map((configuredTld) => `.${configuredTld}`).join(",");
|
|
4019
|
+
}
|
|
4020
|
+
function addRoutes(store, hostnames, port, pid, force = false) {
|
|
4021
|
+
const registered = [];
|
|
4022
|
+
const killedPids = [];
|
|
4023
|
+
try {
|
|
4024
|
+
for (const hostname of hostnames) {
|
|
4025
|
+
const killedPid = store.addRoute(hostname, port, pid, force);
|
|
4026
|
+
registered.push(hostname);
|
|
4027
|
+
if (killedPid !== void 0) {
|
|
4028
|
+
killedPids.push(killedPid);
|
|
4029
|
+
}
|
|
4030
|
+
}
|
|
4031
|
+
} catch (err) {
|
|
4032
|
+
for (const hostname of registered) {
|
|
4033
|
+
try {
|
|
4034
|
+
store.removeRoute(hostname, pid);
|
|
4035
|
+
} catch {
|
|
4036
|
+
}
|
|
4037
|
+
}
|
|
4038
|
+
throw err;
|
|
4039
|
+
}
|
|
4040
|
+
return [...new Set(killedPids)];
|
|
4041
|
+
}
|
|
4042
|
+
function removeRoutes(store, hostnames, ownerPid) {
|
|
4043
|
+
for (const hostname of hostnames) {
|
|
4044
|
+
try {
|
|
4045
|
+
store.removeRoute(hostname, ownerPid);
|
|
4046
|
+
} catch {
|
|
4047
|
+
}
|
|
4048
|
+
}
|
|
4049
|
+
}
|
|
4050
|
+
function startProxyServer(store, proxyPort, tld, tlds, tlsOptions, lanIp, strict, customCert = false) {
|
|
3907
4051
|
store.ensureDir();
|
|
3908
4052
|
const isTls = !!tlsOptions;
|
|
3909
4053
|
const mdnsSupport = isMdnsSupported();
|
|
@@ -3995,6 +4139,7 @@ function startProxyServer(store, proxyPort, tld, tlsOptions, lanIp, strict, cust
|
|
|
3995
4139
|
getRoutes: () => cachedRoutes,
|
|
3996
4140
|
proxyPort,
|
|
3997
4141
|
tld,
|
|
4142
|
+
tlds,
|
|
3998
4143
|
strict,
|
|
3999
4144
|
onError: (msg) => console.error(colors_default.red(msg)),
|
|
4000
4145
|
tls: tlsOptions
|
|
@@ -4033,11 +4178,11 @@ function startProxyServer(store, proxyPort, tld, tlsOptions, lanIp, strict, cust
|
|
|
4033
4178
|
fs9.writeFileSync(store.portFilePath, proxyPort.toString(), { mode: FILE_MODE });
|
|
4034
4179
|
writeTlsMarker(store.dir, isTls);
|
|
4035
4180
|
writeCustomCertMarker(store.dir, isTls && customCert);
|
|
4036
|
-
|
|
4181
|
+
writeTldsFile(store.dir, tlds);
|
|
4037
4182
|
writeLanMarker(store.dir, activeLanIp);
|
|
4038
4183
|
fixOwnership(store.dir, store.pidPath, store.portFilePath);
|
|
4039
4184
|
const proto = isTls ? "HTTPS/2" : "HTTP";
|
|
4040
|
-
const tldLabel = tld !== DEFAULT_TLD ? ` (
|
|
4185
|
+
const tldLabel = tlds.length > 1 || tld !== DEFAULT_TLD ? ` (TLDs: ${formatTldList2(tlds)})` : "";
|
|
4041
4186
|
const modeLabel = strict === false ? " (wildcard)" : "";
|
|
4042
4187
|
console.log(
|
|
4043
4188
|
colors_default.green(`${proto} proxy listening on port ${proxyPort}${tldLabel}${modeLabel}`)
|
|
@@ -4255,28 +4400,29 @@ function listRoutes(store, proxyPort, tls2) {
|
|
|
4255
4400
|
console.log();
|
|
4256
4401
|
}
|
|
4257
4402
|
function resolveProxyDesiredState(lanMode) {
|
|
4258
|
-
const
|
|
4403
|
+
const envTlds = getDefaultTlds();
|
|
4404
|
+
const envTld = primaryTld2(envTlds);
|
|
4259
4405
|
const explicit = {
|
|
4260
4406
|
useHttps: process.env.PORTLESS_HTTPS !== void 0,
|
|
4261
4407
|
customCert: false,
|
|
4262
4408
|
lanMode: process.env.PORTLESS_LAN !== void 0,
|
|
4263
4409
|
lanIp: process.env.PORTLESS_LAN_IP !== void 0,
|
|
4264
|
-
|
|
4410
|
+
tlds: process.env.PORTLESS_TLD !== void 0,
|
|
4265
4411
|
useWildcard: process.env.PORTLESS_WILDCARD !== void 0
|
|
4266
4412
|
};
|
|
4267
4413
|
const desiredConfig = resolveProxyConfig({
|
|
4268
4414
|
persistedLanMode: lanMode,
|
|
4269
4415
|
explicit,
|
|
4270
|
-
|
|
4416
|
+
defaultTlds: envTlds,
|
|
4271
4417
|
useHttps: !isHttpsEnvDisabled(),
|
|
4272
4418
|
customCertPath: null,
|
|
4273
4419
|
customKeyPath: null,
|
|
4274
4420
|
lanMode: isLanEnvEnabled(),
|
|
4275
4421
|
lanIp: process.env.PORTLESS_LAN_IP || null,
|
|
4276
|
-
|
|
4422
|
+
tlds: envTlds,
|
|
4277
4423
|
useWildcard: isWildcardEnvEnabled()
|
|
4278
4424
|
});
|
|
4279
|
-
return { explicit, desiredConfig, envTld };
|
|
4425
|
+
return { explicit, desiredConfig, envTld, envTlds };
|
|
4280
4426
|
}
|
|
4281
4427
|
async function ensureProxyRunning(proxyPort, tls2, desired) {
|
|
4282
4428
|
const { explicit, desiredConfig } = desired;
|
|
@@ -4292,8 +4438,9 @@ async function ensureProxyRunning(proxyPort, tls2, desired) {
|
|
|
4292
4438
|
if (!explicit.useHttps && persisted.tls !== desiredConfig.useHttps) {
|
|
4293
4439
|
startConfig.useHttps = persisted.tls;
|
|
4294
4440
|
}
|
|
4295
|
-
if (!explicit.
|
|
4296
|
-
startConfig.
|
|
4441
|
+
if (!explicit.tlds && (persisted.tlds.length !== desiredConfig.tlds.length || persisted.tlds.some((tld, index) => tld !== desiredConfig.tlds[index]))) {
|
|
4442
|
+
startConfig.tlds = persisted.tlds;
|
|
4443
|
+
startConfig.tld = primaryTld2(persisted.tlds);
|
|
4297
4444
|
}
|
|
4298
4445
|
if (!explicit.lanMode && persisted.lanMode !== desiredConfig.lanMode) {
|
|
4299
4446
|
startConfig.lanMode = persisted.lanMode;
|
|
@@ -4329,6 +4476,7 @@ async function ensureProxyRunning(proxyPort, tls2, desired) {
|
|
|
4329
4476
|
lanIp: startConfig.lanIpExplicit ? startConfig.lanIp : null,
|
|
4330
4477
|
lanIpExplicit: startConfig.lanIpExplicit,
|
|
4331
4478
|
tld: startConfig.tld,
|
|
4479
|
+
tlds: startConfig.tlds,
|
|
4332
4480
|
useWildcard: startConfig.useWildcard,
|
|
4333
4481
|
includePort: startPort !== void 0,
|
|
4334
4482
|
proxyPort: startPort
|
|
@@ -4363,7 +4511,7 @@ async function ensureProxyRunning(proxyPort, tls2, desired) {
|
|
|
4363
4511
|
}
|
|
4364
4512
|
return { started: true, state: discovered };
|
|
4365
4513
|
}
|
|
4366
|
-
async function runApp(initialStore, proxyPort, stateDir, name, commandArgs, tls2,
|
|
4514
|
+
async function runApp(initialStore, proxyPort, stateDir, name, commandArgs, tls2, tlds, force, autoInfo, desiredPort, lanMode = false, lanIp) {
|
|
4367
4515
|
let store = initialStore;
|
|
4368
4516
|
console.log(chalk.blue.bold(`
|
|
4369
4517
|
portless
|
|
@@ -4410,12 +4558,12 @@ portless
|
|
|
4410
4558
|
console.error(colors_default.red(`Error: ${err.message}`));
|
|
4411
4559
|
process.exit(1);
|
|
4412
4560
|
}
|
|
4413
|
-
|
|
4561
|
+
buildHostnames(name, tlds);
|
|
4414
4562
|
const ensureResult = await ensureProxyRunning(proxyPort, tls2, desired);
|
|
4415
4563
|
if (ensureResult.started) {
|
|
4416
4564
|
proxyPort = ensureResult.state.port;
|
|
4417
4565
|
stateDir = ensureResult.state.dir;
|
|
4418
|
-
|
|
4566
|
+
tlds = ensureResult.state.tlds;
|
|
4419
4567
|
tls2 = ensureResult.state.tls;
|
|
4420
4568
|
lanMode = ensureResult.state.lanMode;
|
|
4421
4569
|
lanIp = ensureResult.state.lanIp;
|
|
@@ -4437,20 +4585,22 @@ portless
|
|
|
4437
4585
|
}
|
|
4438
4586
|
lanMode = runningConfig.lanMode;
|
|
4439
4587
|
lanIp = runningConfig.lanIp;
|
|
4588
|
+
tlds = runningConfig.tlds;
|
|
4440
4589
|
console.log(chalk.gray("-- Proxy is running"));
|
|
4441
4590
|
}
|
|
4442
|
-
const
|
|
4443
|
-
|
|
4591
|
+
const hostnames = buildHostnames(name, tlds);
|
|
4592
|
+
const hostname = hostnames[0];
|
|
4593
|
+
if (desired.explicit.tlds && (desired.envTlds.some((envConfiguredTld, index) => envConfiguredTld !== tlds[index]) || desired.envTlds.length !== tlds.length)) {
|
|
4444
4594
|
console.warn(
|
|
4445
4595
|
chalk.yellow(
|
|
4446
|
-
`Warning: PORTLESS_TLD=${desired.
|
|
4596
|
+
`Warning: PORTLESS_TLD=${desired.envTlds.join(",")} but the running proxy uses ${formatTldList2(tlds)}.`
|
|
4447
4597
|
)
|
|
4448
4598
|
);
|
|
4449
4599
|
}
|
|
4450
4600
|
if (lanIp) {
|
|
4451
|
-
console.log(chalk.gray(`-- ${
|
|
4601
|
+
console.log(chalk.gray(`-- ${hostnames.join(", ")} (LAN: ${lanIp})`));
|
|
4452
4602
|
} else {
|
|
4453
|
-
console.log(chalk.gray(`-- ${
|
|
4603
|
+
console.log(chalk.gray(`-- ${hostnames.join(", ")} (auto-resolves to 127.0.0.1)`));
|
|
4454
4604
|
}
|
|
4455
4605
|
if (autoInfo) {
|
|
4456
4606
|
const baseName = autoInfo.prefix ? name.slice(autoInfo.prefix.length + 1) : name;
|
|
@@ -4465,9 +4615,9 @@ portless
|
|
|
4465
4615
|
} else {
|
|
4466
4616
|
console.log(colors_default.green(`-- Using port ${port}`));
|
|
4467
4617
|
}
|
|
4468
|
-
let
|
|
4618
|
+
let killedPids = [];
|
|
4469
4619
|
try {
|
|
4470
|
-
|
|
4620
|
+
killedPids = addRoutes(store, hostnames, port, process.pid, force);
|
|
4471
4621
|
} catch (err) {
|
|
4472
4622
|
if (err instanceof RouteConflictError) {
|
|
4473
4623
|
console.error(colors_default.red(`Error: ${err.message}`));
|
|
@@ -4475,13 +4625,20 @@ portless
|
|
|
4475
4625
|
}
|
|
4476
4626
|
throw err;
|
|
4477
4627
|
}
|
|
4478
|
-
if (
|
|
4479
|
-
console.log(colors_default.yellow(`Killed existing process
|
|
4628
|
+
if (killedPids.length > 0) {
|
|
4629
|
+
console.log(colors_default.yellow(`Killed existing process(es): ${killedPids.join(", ")}`));
|
|
4480
4630
|
}
|
|
4481
4631
|
const finalUrl = formatUrl(hostname, proxyPort, tls2);
|
|
4632
|
+
const allUrls = formatUrls(hostnames, proxyPort, tls2);
|
|
4482
4633
|
console.log(chalk.cyan.bold(`
|
|
4483
4634
|
-> ${finalUrl}
|
|
4484
4635
|
`));
|
|
4636
|
+
for (const extraUrl of allUrls.slice(1)) {
|
|
4637
|
+
console.log(chalk.cyan(` also -> ${extraUrl}`));
|
|
4638
|
+
}
|
|
4639
|
+
if (allUrls.length > 1) {
|
|
4640
|
+
console.log("");
|
|
4641
|
+
}
|
|
4485
4642
|
if (lanIp) {
|
|
4486
4643
|
console.log(chalk.green(` LAN -> ${finalUrl}`));
|
|
4487
4644
|
console.log(chalk.gray(" (accessible from other devices on the same WiFi network)\n"));
|
|
@@ -4594,7 +4751,7 @@ portless
|
|
|
4594
4751
|
} catch {
|
|
4595
4752
|
}
|
|
4596
4753
|
try {
|
|
4597
|
-
store
|
|
4754
|
+
removeRoutes(store, hostnames, process.pid);
|
|
4598
4755
|
} catch {
|
|
4599
4756
|
}
|
|
4600
4757
|
process.exit(1);
|
|
@@ -4628,7 +4785,7 @@ portless
|
|
|
4628
4785
|
PORT: port.toString(),
|
|
4629
4786
|
...hostBind ? { HOST: hostBind } : {},
|
|
4630
4787
|
PORTLESS_URL: finalUrl,
|
|
4631
|
-
__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS:
|
|
4788
|
+
__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS: formatViteAllowedHosts(tlds),
|
|
4632
4789
|
// Note: EXPO_PACKAGER_PROXY_URL is not used — expo-dev-client removed
|
|
4633
4790
|
// baked-in pinging, making this env var ineffective. Expo handles its
|
|
4634
4791
|
// own LAN discovery natively.
|
|
@@ -4648,7 +4805,7 @@ portless
|
|
|
4648
4805
|
} catch {
|
|
4649
4806
|
}
|
|
4650
4807
|
try {
|
|
4651
|
-
store
|
|
4808
|
+
removeRoutes(store, hostnames, process.pid);
|
|
4652
4809
|
} catch {
|
|
4653
4810
|
}
|
|
4654
4811
|
}
|
|
@@ -4941,7 +5098,7 @@ ${colors_default.bold("Options:")}
|
|
|
4941
5098
|
--cert <path> Use a custom TLS certificate
|
|
4942
5099
|
--key <path> Use a custom TLS private key
|
|
4943
5100
|
--foreground Run proxy in foreground (for debugging)
|
|
4944
|
-
--tld <tld> Use a custom TLD instead of .localhost
|
|
5101
|
+
--tld <tld> Use a custom TLD instead of .localhost; repeat for more
|
|
4945
5102
|
--wildcard Allow unregistered subdomains to fall back to parent route
|
|
4946
5103
|
--state-dir <path> Use a custom state directory with service install
|
|
4947
5104
|
--app-port <number> Use a fixed port for the app (skip auto-assignment)
|
|
@@ -4958,7 +5115,7 @@ ${colors_default.bold("Environment variables:")}
|
|
|
4958
5115
|
PORTLESS_HTTPS=0 Disable HTTPS (same as --no-tls)
|
|
4959
5116
|
PORTLESS_LAN=1 Enable LAN mode when set to 1 (set in .bashrc / .zshrc)
|
|
4960
5117
|
PORTLESS_LAN_IP=<address> Pin a specific LAN IP for LAN mode
|
|
4961
|
-
PORTLESS_TLD=<tld>
|
|
5118
|
+
PORTLESS_TLD=<tld>[,<tld>] Use one or more TLDs (e.g. localhost,test)
|
|
4962
5119
|
PORTLESS_WILDCARD=1 Allow unregistered subdomains to fall back to parent route
|
|
4963
5120
|
PORTLESS_SYNC_HOSTS=0 Disable auto-sync of ${HOSTS_DISPLAY} (on by default)
|
|
4964
5121
|
PORTLESS_TAILSCALE=1 Share apps on your Tailscale network (same as --tailscale)
|
|
@@ -4970,7 +5127,7 @@ ${colors_default.bold("Environment variables:")}
|
|
|
4970
5127
|
${colors_default.bold("Child process environment:")}
|
|
4971
5128
|
PORT Ephemeral port the child should listen on
|
|
4972
5129
|
HOST Usually 127.0.0.1 (omitted for Expo in LAN mode)
|
|
4973
|
-
PORTLESS_URL
|
|
5130
|
+
PORTLESS_URL Primary public URL of the app
|
|
4974
5131
|
PORTLESS_LAN Set to 1 when proxy is in LAN mode
|
|
4975
5132
|
PORTLESS_TAILSCALE_URL Tailscale URL of the app (when --tailscale is active)
|
|
4976
5133
|
PORTLESS_NGROK_URL ngrok URL of the app (when --ngrok is active)
|
|
@@ -4996,7 +5153,7 @@ ${colors_default.bold("Reserved names:")}
|
|
|
4996
5153
|
process.exit(0);
|
|
4997
5154
|
}
|
|
4998
5155
|
function printVersion() {
|
|
4999
|
-
console.log("0.15.
|
|
5156
|
+
console.log("0.15.1");
|
|
5000
5157
|
process.exit(0);
|
|
5001
5158
|
}
|
|
5002
5159
|
async function handleTrust() {
|
|
@@ -5271,8 +5428,8 @@ ${colors_default.bold("Examples:")}
|
|
|
5271
5428
|
const name = positional[0];
|
|
5272
5429
|
const worktree = skipWorktree ? null : detectWorktreePrefix();
|
|
5273
5430
|
const effectiveName = worktree ? `${worktree.prefix}.${name}` : name;
|
|
5274
|
-
const { port, tls: tls2,
|
|
5275
|
-
const hostname =
|
|
5431
|
+
const { port, tls: tls2, tlds } = await discoverState();
|
|
5432
|
+
const hostname = buildHostnames(effectiveName, tlds)[0];
|
|
5276
5433
|
const url = formatUrl(hostname, port, tls2);
|
|
5277
5434
|
process.stdout.write(url + "\n");
|
|
5278
5435
|
}
|
|
@@ -5293,7 +5450,7 @@ ${colors_default.bold("Examples:")}
|
|
|
5293
5450
|
`);
|
|
5294
5451
|
process.exit(0);
|
|
5295
5452
|
}
|
|
5296
|
-
const { dir,
|
|
5453
|
+
const { dir, tlds } = await discoverState();
|
|
5297
5454
|
const store = new RouteStore(dir, {
|
|
5298
5455
|
onWarning: (msg) => console.warn(colors_default.yellow(msg))
|
|
5299
5456
|
});
|
|
@@ -5304,15 +5461,15 @@ ${colors_default.bold("Examples:")}
|
|
|
5304
5461
|
console.error(colors_default.cyan(" portless alias --remove <name>"));
|
|
5305
5462
|
process.exit(1);
|
|
5306
5463
|
}
|
|
5307
|
-
const
|
|
5464
|
+
const hostnames2 = buildHostnames(aliasName2, tlds);
|
|
5308
5465
|
const routes = store.loadRoutes();
|
|
5309
|
-
const existing = routes.find((r) => r.hostname
|
|
5466
|
+
const existing = routes.find((r) => hostnames2.includes(r.hostname) && r.pid === 0);
|
|
5310
5467
|
if (!existing) {
|
|
5311
|
-
console.error(colors_default.red(`Error: No alias found for "${
|
|
5468
|
+
console.error(colors_default.red(`Error: No alias found for "${hostnames2.join(", ")}".`));
|
|
5312
5469
|
process.exit(1);
|
|
5313
5470
|
}
|
|
5314
|
-
store
|
|
5315
|
-
console.log(colors_default.green(`Removed alias: ${
|
|
5471
|
+
removeRoutes(store, hostnames2);
|
|
5472
|
+
console.log(colors_default.green(`Removed alias: ${hostnames2.join(", ")}`));
|
|
5316
5473
|
return;
|
|
5317
5474
|
}
|
|
5318
5475
|
const aliasName = args[1];
|
|
@@ -5326,15 +5483,15 @@ ${colors_default.bold("Examples:")}
|
|
|
5326
5483
|
console.error(colors_default.cyan(" portless alias my-postgres 5432"));
|
|
5327
5484
|
process.exit(1);
|
|
5328
5485
|
}
|
|
5329
|
-
const
|
|
5486
|
+
const hostnames = buildHostnames(aliasName, tlds);
|
|
5330
5487
|
const port = parseInt(aliasPort, 10);
|
|
5331
5488
|
if (isNaN(port) || port < 1 || port > 65535) {
|
|
5332
5489
|
console.error(colors_default.red(`Error: Invalid port "${aliasPort}". Must be 1-65535.`));
|
|
5333
5490
|
process.exit(1);
|
|
5334
5491
|
}
|
|
5335
5492
|
const force = args.includes("--force");
|
|
5336
|
-
store
|
|
5337
|
-
console.log(colors_default.green(`Alias registered: ${
|
|
5493
|
+
addRoutes(store, hostnames, port, 0, force);
|
|
5494
|
+
console.log(colors_default.green(`Alias registered: ${hostnames.join(", ")} -> 127.0.0.1:${port}`));
|
|
5338
5495
|
}
|
|
5339
5496
|
async function handleHosts(args) {
|
|
5340
5497
|
if (args[1] === "--help" || args[1] === "-h") {
|
|
@@ -5524,6 +5681,7 @@ ${colors_default.bold("Options:")}
|
|
|
5524
5681
|
port: getDefaultPort(!isHttpsEnvDisabled()),
|
|
5525
5682
|
tls: !isHttpsEnvDisabled(),
|
|
5526
5683
|
tld: DEFAULT_TLD,
|
|
5684
|
+
tlds: [DEFAULT_TLD],
|
|
5527
5685
|
lanMode: isLanEnvEnabled(),
|
|
5528
5686
|
lanIp: null
|
|
5529
5687
|
};
|
|
@@ -5545,12 +5703,14 @@ ${colors_default.bold("Options:")}
|
|
|
5545
5703
|
const proxyUsesCustomCert = proxyTls && readCustomCertMarker(state.dir);
|
|
5546
5704
|
const stateExists = fs9.existsSync(state.dir);
|
|
5547
5705
|
console.log(colors_default.blue.bold("\nportless doctor\n"));
|
|
5548
|
-
console.log(`Version: ${"0.15.
|
|
5706
|
+
console.log(`Version: ${"0.15.1"}`);
|
|
5549
5707
|
console.log(`Node.js: ${process.versions.node}`);
|
|
5550
5708
|
console.log(`Platform: ${process.platform} ${process.arch}`);
|
|
5551
5709
|
console.log(`State dir: ${state.dir}`);
|
|
5552
5710
|
console.log(`Proxy target: ${formatUrl("127.0.0.1", proxyPort, proxyTls)}`);
|
|
5553
|
-
console.log(
|
|
5711
|
+
console.log(
|
|
5712
|
+
`Mode: ${proxyTls ? "HTTPS" : "HTTP"}, ${formatTldList2(state.tlds)}${state.lanMode ? ", LAN" : ""}`
|
|
5713
|
+
);
|
|
5554
5714
|
console.log("");
|
|
5555
5715
|
const nodeMajor = parseInt(process.versions.node.split(".")[0], 10);
|
|
5556
5716
|
if (nodeMajor >= 24) {
|
|
@@ -5822,6 +5982,7 @@ ${colors_default.bold("Usage:")}
|
|
|
5822
5982
|
${colors_default.cyan("portless proxy start --foreground")} Start in foreground (for debugging)
|
|
5823
5983
|
${colors_default.cyan("portless proxy start -p 1355")} Start on a custom port (no sudo)
|
|
5824
5984
|
${colors_default.cyan("portless proxy start --tld test")} Use .test instead of .localhost
|
|
5985
|
+
${colors_default.cyan("portless proxy start --tld localhost --tld test")} Serve both TLDs
|
|
5825
5986
|
${colors_default.cyan("portless proxy start --wildcard")} Allow unregistered subdomains to fall back to parent
|
|
5826
5987
|
${colors_default.cyan("portless proxy stop")} Stop the proxy
|
|
5827
5988
|
|
|
@@ -5883,34 +6044,41 @@ ${colors_default.bold("LAN mode (--lan):")}
|
|
|
5883
6044
|
}
|
|
5884
6045
|
hasExplicitPort = true;
|
|
5885
6046
|
}
|
|
5886
|
-
let
|
|
6047
|
+
let tlds;
|
|
5887
6048
|
try {
|
|
5888
|
-
|
|
6049
|
+
tlds = getDefaultTlds();
|
|
5889
6050
|
} catch (err) {
|
|
5890
6051
|
console.error(colors_default.red(`Error: ${err.message}`));
|
|
5891
6052
|
process.exit(1);
|
|
5892
6053
|
}
|
|
5893
|
-
const
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
6054
|
+
const tldFlagValues = [];
|
|
6055
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
6056
|
+
if (args[i] === "--tld") {
|
|
6057
|
+
const tldValue = args[i + 1];
|
|
6058
|
+
if (!tldValue || tldValue.startsWith("-")) {
|
|
6059
|
+
console.error(colors_default.red("Error: --tld requires a TLD value (e.g. test, localhost)."));
|
|
6060
|
+
process.exit(1);
|
|
6061
|
+
}
|
|
6062
|
+
tldFlagValues.push(tldValue);
|
|
6063
|
+
i += 1;
|
|
5899
6064
|
}
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
6065
|
+
}
|
|
6066
|
+
if (tldFlagValues.length > 0) {
|
|
6067
|
+
try {
|
|
6068
|
+
tlds = normalizeTlds2(tldFlagValues.flatMap((value) => parseTldList(value)));
|
|
6069
|
+
} catch (err) {
|
|
6070
|
+
console.error(colors_default.red(`Error: ${err.message}`));
|
|
5904
6071
|
process.exit(1);
|
|
5905
6072
|
}
|
|
5906
6073
|
}
|
|
6074
|
+
let tld = primaryTld2(tlds);
|
|
5907
6075
|
const useWildcard = args.includes("--wildcard") || isWildcardEnvEnabled();
|
|
5908
6076
|
const explicit = {
|
|
5909
6077
|
useHttps: hasHttpsFlag || hasNoTls || customCertPath !== null || customKeyPath !== null || process.env.PORTLESS_HTTPS !== void 0,
|
|
5910
6078
|
customCert: customCertPath !== null || customKeyPath !== null,
|
|
5911
6079
|
lanMode: process.env.PORTLESS_LAN !== void 0,
|
|
5912
6080
|
lanIp: process.env.PORTLESS_LAN_IP !== void 0,
|
|
5913
|
-
|
|
6081
|
+
tlds: tldFlagValues.length > 0 || process.env.PORTLESS_TLD !== void 0,
|
|
5914
6082
|
useWildcard: args.includes("--wildcard") || process.env.PORTLESS_WILDCARD !== void 0
|
|
5915
6083
|
};
|
|
5916
6084
|
let stateDir = resolveStateDir(proxyPort);
|
|
@@ -5928,13 +6096,13 @@ ${colors_default.bold("LAN mode (--lan):")}
|
|
|
5928
6096
|
const desiredConfig = resolveProxyConfig({
|
|
5929
6097
|
persistedLanMode,
|
|
5930
6098
|
explicit,
|
|
5931
|
-
|
|
6099
|
+
defaultTlds: getDefaultTlds(),
|
|
5932
6100
|
useHttps: wantHttps || !!(customCertPath && customKeyPath),
|
|
5933
6101
|
customCertPath,
|
|
5934
6102
|
customKeyPath,
|
|
5935
6103
|
lanMode: isLanEnvEnabled(),
|
|
5936
6104
|
lanIp: process.env.PORTLESS_LAN_IP || null,
|
|
5937
|
-
|
|
6105
|
+
tlds,
|
|
5938
6106
|
useWildcard
|
|
5939
6107
|
});
|
|
5940
6108
|
const lanMode = desiredConfig.lanMode;
|
|
@@ -5942,31 +6110,35 @@ ${colors_default.bold("LAN mode (--lan):")}
|
|
|
5942
6110
|
customCertPath = desiredConfig.customCertPath;
|
|
5943
6111
|
customKeyPath = desiredConfig.customKeyPath;
|
|
5944
6112
|
tld = desiredConfig.tld;
|
|
6113
|
+
tlds = desiredConfig.tlds;
|
|
5945
6114
|
const desiredWildcard = desiredConfig.useWildcard;
|
|
5946
6115
|
let lanIp = desiredConfig.lanIpExplicit ? desiredConfig.lanIp : null;
|
|
5947
6116
|
if (!hasExplicitPort && runningPort === null) {
|
|
5948
6117
|
proxyPort = getDefaultPort(useHttps);
|
|
5949
6118
|
stateDir = resolveStateDir(proxyPort);
|
|
5950
6119
|
}
|
|
5951
|
-
if (lanMode &&
|
|
5952
|
-
const
|
|
5953
|
-
if (
|
|
6120
|
+
if (lanMode && tldFlagValues.length > 0) {
|
|
6121
|
+
const userTlds = tldFlagValues.join(", ");
|
|
6122
|
+
if (userTlds !== "local") {
|
|
5954
6123
|
console.warn(
|
|
5955
6124
|
chalk.yellow(
|
|
5956
|
-
`Warning: --lan forces .local TLD (mDNS requirement). Ignoring --tld ${
|
|
6125
|
+
`Warning: --lan forces .local TLD (mDNS requirement). Ignoring --tld ${userTlds}.`
|
|
5957
6126
|
)
|
|
5958
6127
|
);
|
|
5959
6128
|
}
|
|
5960
6129
|
}
|
|
5961
|
-
const
|
|
5962
|
-
|
|
5963
|
-
|
|
6130
|
+
for (const configuredTld of tlds) {
|
|
6131
|
+
const riskyReason = RISKY_TLDS.get(configuredTld);
|
|
6132
|
+
if (riskyReason && !lanMode) {
|
|
6133
|
+
console.warn(colors_default.yellow(`Warning: .${configuredTld}: ${riskyReason}`));
|
|
6134
|
+
}
|
|
5964
6135
|
}
|
|
5965
6136
|
const syncDisabled = process.env.PORTLESS_SYNC_HOSTS === "0" || process.env.PORTLESS_SYNC_HOSTS === "false";
|
|
5966
|
-
|
|
6137
|
+
const nonDefaultTlds = tlds.filter((configuredTld) => configuredTld !== DEFAULT_TLD);
|
|
6138
|
+
if (nonDefaultTlds.length > 0 && !lanMode && syncDisabled) {
|
|
5967
6139
|
console.warn(
|
|
5968
6140
|
colors_default.yellow(
|
|
5969
|
-
`Warning:
|
|
6141
|
+
`Warning: ${formatTldList2(nonDefaultTlds)} domains require ${HOSTS_DISPLAY} entries to resolve to 127.0.0.1.`
|
|
5970
6142
|
)
|
|
5971
6143
|
);
|
|
5972
6144
|
console.warn(colors_default.yellow("Hosts sync is disabled. To add entries manually, run:"));
|
|
@@ -6028,6 +6200,7 @@ ${colors_default.bold("LAN mode (--lan):")}
|
|
|
6028
6200
|
lanIp: desiredConfig.lanIpExplicit ? lanIp : null,
|
|
6029
6201
|
lanIpExplicit: desiredConfig.lanIpExplicit,
|
|
6030
6202
|
tld,
|
|
6203
|
+
tlds,
|
|
6031
6204
|
useWildcard: desiredWildcard
|
|
6032
6205
|
};
|
|
6033
6206
|
if (!isWindows && proxyPort < PRIVILEGED_PORT_THRESHOLD && (process.getuid?.() ?? -1) !== 0) {
|
|
@@ -6044,6 +6217,7 @@ ${colors_default.bold("LAN mode (--lan):")}
|
|
|
6044
6217
|
lanIp: desiredConfig.lanIpExplicit ? lanIp : null,
|
|
6045
6218
|
lanIpExplicit: desiredConfig.lanIpExplicit,
|
|
6046
6219
|
tld,
|
|
6220
|
+
tlds,
|
|
6047
6221
|
useWildcard: desiredWildcard,
|
|
6048
6222
|
foreground: isForeground,
|
|
6049
6223
|
includePort: true,
|
|
@@ -6161,7 +6335,7 @@ ${colors_default.bold("LAN mode (--lan):")}
|
|
|
6161
6335
|
cert,
|
|
6162
6336
|
key,
|
|
6163
6337
|
ca,
|
|
6164
|
-
SNICallback: createSNICallback(stateDir, cert, key,
|
|
6338
|
+
SNICallback: createSNICallback(stateDir, cert, key, tlds, ca)
|
|
6165
6339
|
};
|
|
6166
6340
|
}
|
|
6167
6341
|
}
|
|
@@ -6171,6 +6345,7 @@ ${colors_default.bold("LAN mode (--lan):")}
|
|
|
6171
6345
|
store,
|
|
6172
6346
|
proxyPort,
|
|
6173
6347
|
tld,
|
|
6348
|
+
tlds,
|
|
6174
6349
|
tlsOptions,
|
|
6175
6350
|
lanIp,
|
|
6176
6351
|
desiredWildcard ? false : void 0,
|
|
@@ -6199,6 +6374,7 @@ ${colors_default.bold("LAN mode (--lan):")}
|
|
|
6199
6374
|
lanIp: desiredConfig.lanIpExplicit ? lanIp : null,
|
|
6200
6375
|
lanIpExplicit: desiredConfig.lanIpExplicit,
|
|
6201
6376
|
tld,
|
|
6377
|
+
tlds,
|
|
6202
6378
|
useWildcard: desiredWildcard,
|
|
6203
6379
|
foreground: true,
|
|
6204
6380
|
includePort: true,
|
|
@@ -6292,7 +6468,7 @@ async function handleDefaultSingle(cwd, scriptName, appConfig) {
|
|
|
6292
6468
|
}
|
|
6293
6469
|
const worktree = detectWorktreePrefix(cwd);
|
|
6294
6470
|
const effectiveName = worktree ? `${worktree.prefix}.${baseName}` : baseName;
|
|
6295
|
-
const { dir, port, tls: tls2,
|
|
6471
|
+
const { dir, port, tls: tls2, tlds, lanMode, lanIp } = await discoverState();
|
|
6296
6472
|
const store = new RouteStore(dir, {
|
|
6297
6473
|
onWarning: (msg) => console.warn(colors_default.yellow(msg))
|
|
6298
6474
|
});
|
|
@@ -6303,7 +6479,7 @@ async function handleDefaultSingle(cwd, scriptName, appConfig) {
|
|
|
6303
6479
|
effectiveName,
|
|
6304
6480
|
resolved,
|
|
6305
6481
|
tls2,
|
|
6306
|
-
|
|
6482
|
+
tlds,
|
|
6307
6483
|
false,
|
|
6308
6484
|
{ nameSource, prefix: worktree?.prefix, prefixSource: worktree?.source },
|
|
6309
6485
|
appConfig?.appPort,
|
|
@@ -6343,13 +6519,13 @@ function pipeOutput(child, prefix) {
|
|
|
6343
6519
|
prefixStream(child.stdout, process.stdout, prefix);
|
|
6344
6520
|
prefixStream(child.stderr, process.stderr, prefix);
|
|
6345
6521
|
}
|
|
6346
|
-
async function spawnProxiedApp(app, stateDir, proxyPort, tls2,
|
|
6522
|
+
async function spawnProxiedApp(app, stateDir, proxyPort, tls2, tlds, exitCodes) {
|
|
6347
6523
|
const usesPortless = app.commandArgs[0] === "portless";
|
|
6348
6524
|
const pkgEnv = { ...process.env };
|
|
6349
6525
|
pkgEnv.PATH = augmentedPath(pkgEnv, app.pkg.dir);
|
|
6350
6526
|
let env;
|
|
6351
6527
|
let store = null;
|
|
6352
|
-
let
|
|
6528
|
+
let hostnames = [];
|
|
6353
6529
|
let displayUrl;
|
|
6354
6530
|
if (usesPortless) {
|
|
6355
6531
|
env = pkgEnv;
|
|
@@ -6359,17 +6535,17 @@ async function spawnProxiedApp(app, stateDir, proxyPort, tls2, tld, exitCodes) {
|
|
|
6359
6535
|
onWarning: (msg) => console.warn(colors_default.yellow(`[${app.name}] ${msg}`))
|
|
6360
6536
|
});
|
|
6361
6537
|
const appPort = app.appPort ?? await findFreePort();
|
|
6362
|
-
|
|
6363
|
-
const
|
|
6364
|
-
const url =
|
|
6538
|
+
hostnames = buildHostnames(app.name, tlds);
|
|
6539
|
+
const urls = formatUrls(hostnames, proxyPort, tls2);
|
|
6540
|
+
const url = urls[0];
|
|
6365
6541
|
displayUrl = url;
|
|
6366
|
-
|
|
6367
|
-
store.addRoute(hostname, appPort, process.pid);
|
|
6542
|
+
addRoutes(store, hostnames, appPort, process.pid);
|
|
6368
6543
|
env = {
|
|
6369
6544
|
...pkgEnv,
|
|
6370
6545
|
PORT: String(appPort),
|
|
6371
6546
|
HOST: "127.0.0.1",
|
|
6372
|
-
PORTLESS_URL: url
|
|
6547
|
+
PORTLESS_URL: url,
|
|
6548
|
+
__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS: formatViteAllowedHosts(tlds)
|
|
6373
6549
|
};
|
|
6374
6550
|
if (tls2) {
|
|
6375
6551
|
const caPath = path9.join(stateDir, "ca.pem");
|
|
@@ -6381,7 +6557,7 @@ async function spawnProxiedApp(app, stateDir, proxyPort, tls2, tld, exitCodes) {
|
|
|
6381
6557
|
const child = spawnChildProcess(app.commandArgs, env, app.pkg.dir);
|
|
6382
6558
|
pipeOutput(child, chalk.cyan(`[${app.name}]`));
|
|
6383
6559
|
const capturedStore = store;
|
|
6384
|
-
const
|
|
6560
|
+
const capturedHostnames = hostnames;
|
|
6385
6561
|
child.on("exit", (code, signal) => {
|
|
6386
6562
|
exitCodes.set(app.name, code);
|
|
6387
6563
|
if (code !== 0 && code !== null) {
|
|
@@ -6389,14 +6565,11 @@ async function spawnProxiedApp(app, stateDir, proxyPort, tls2, tld, exitCodes) {
|
|
|
6389
6565
|
} else if (signal) {
|
|
6390
6566
|
console.error(colors_default.yellow(`[${app.name}] killed by ${signal}`));
|
|
6391
6567
|
}
|
|
6392
|
-
if (capturedStore &&
|
|
6393
|
-
|
|
6394
|
-
capturedStore.removeRoute(capturedHostname, process.pid);
|
|
6395
|
-
} catch {
|
|
6396
|
-
}
|
|
6568
|
+
if (capturedStore && capturedHostnames.length > 0) {
|
|
6569
|
+
removeRoutes(capturedStore, capturedHostnames, process.pid);
|
|
6397
6570
|
}
|
|
6398
6571
|
});
|
|
6399
|
-
const route = store &&
|
|
6572
|
+
const route = store && hostnames.length > 0 ? { store, hostnames } : null;
|
|
6400
6573
|
return { child, displayUrl, route };
|
|
6401
6574
|
}
|
|
6402
6575
|
function spawnTaskApp(app, exitCodes) {
|
|
@@ -6507,7 +6680,7 @@ async function handleDefaultMulti(wsRoot, globalScript, extraArgs = []) {
|
|
|
6507
6680
|
console.log(chalk.blue.bold(`
|
|
6508
6681
|
portless
|
|
6509
6682
|
`));
|
|
6510
|
-
let { dir, port, tls: tls2,
|
|
6683
|
+
let { dir, port, tls: tls2, tlds } = await discoverState();
|
|
6511
6684
|
if (proxiedApps.length > 0) {
|
|
6512
6685
|
let multiDesired;
|
|
6513
6686
|
try {
|
|
@@ -6521,9 +6694,9 @@ portless
|
|
|
6521
6694
|
dir = ensureResult.state.dir;
|
|
6522
6695
|
port = ensureResult.state.port;
|
|
6523
6696
|
tls2 = ensureResult.state.tls;
|
|
6524
|
-
|
|
6697
|
+
tlds = ensureResult.state.tlds;
|
|
6525
6698
|
} else {
|
|
6526
|
-
({ dir, port, tls: tls2,
|
|
6699
|
+
({ dir, port, tls: tls2, tlds } = await discoverState());
|
|
6527
6700
|
}
|
|
6528
6701
|
if (tls2 && !isCATrusted(dir)) {
|
|
6529
6702
|
await handleTrust();
|
|
@@ -6531,12 +6704,12 @@ portless
|
|
|
6531
6704
|
}
|
|
6532
6705
|
const useTurbo = loaded?.config.turbo !== false && hasTurboConfig(wsRoot);
|
|
6533
6706
|
if (useTurbo) {
|
|
6534
|
-
await runWithTurbo(wsRoot, dir, port, tls2,
|
|
6707
|
+
await runWithTurbo(wsRoot, dir, port, tls2, tlds, scriptName, proxiedApps, taskApps, extraArgs);
|
|
6535
6708
|
} else {
|
|
6536
|
-
await runWithDirectSpawn(dir, port, tls2,
|
|
6709
|
+
await runWithDirectSpawn(dir, port, tls2, tlds, proxiedApps, taskApps);
|
|
6537
6710
|
}
|
|
6538
6711
|
}
|
|
6539
|
-
async function runWithTurbo(wsRoot, stateDir, proxyPort, tls2,
|
|
6712
|
+
async function runWithTurbo(wsRoot, stateDir, proxyPort, tls2, tlds, scriptName, proxiedApps, taskApps, extraArgs = []) {
|
|
6540
6713
|
const store = new RouteStore(stateDir, {
|
|
6541
6714
|
onWarning: (msg) => console.warn(colors_default.yellow(msg))
|
|
6542
6715
|
});
|
|
@@ -6550,17 +6723,17 @@ async function runWithTurbo(wsRoot, stateDir, proxyPort, tls2, tld, scriptName,
|
|
|
6550
6723
|
continue;
|
|
6551
6724
|
}
|
|
6552
6725
|
const appPort = app.appPort ?? await findFreePort();
|
|
6553
|
-
const
|
|
6554
|
-
const
|
|
6555
|
-
const url =
|
|
6726
|
+
const hostnames = buildHostnames(app.name, tlds);
|
|
6727
|
+
const urls = formatUrls(hostnames, proxyPort, tls2);
|
|
6728
|
+
const url = urls[0];
|
|
6556
6729
|
appUrls.push({ label: app.label, url });
|
|
6557
|
-
|
|
6558
|
-
|
|
6559
|
-
routes.push({ hostname });
|
|
6730
|
+
addRoutes(store, hostnames, appPort, process.pid);
|
|
6731
|
+
routes.push({ hostnames });
|
|
6560
6732
|
const entry = {
|
|
6561
6733
|
PORT: String(appPort),
|
|
6562
6734
|
HOST: "127.0.0.1",
|
|
6563
|
-
PORTLESS_URL: url
|
|
6735
|
+
PORTLESS_URL: url,
|
|
6736
|
+
__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS: formatViteAllowedHosts(tlds)
|
|
6564
6737
|
};
|
|
6565
6738
|
if (tls2) {
|
|
6566
6739
|
const caPath = path9.join(stateDir, "ca.pem");
|
|
@@ -6603,11 +6776,8 @@ async function runWithTurbo(wsRoot, stateDir, proxyPort, tls2, tld, scriptName,
|
|
|
6603
6776
|
killTree(turboChild, "SIGKILL");
|
|
6604
6777
|
}
|
|
6605
6778
|
}, SIGKILL_TIMEOUT_MS).unref();
|
|
6606
|
-
for (const {
|
|
6607
|
-
|
|
6608
|
-
store.removeRoute(hostname, process.pid);
|
|
6609
|
-
} catch {
|
|
6610
|
-
}
|
|
6779
|
+
for (const { hostnames } of routes) {
|
|
6780
|
+
removeRoutes(store, hostnames, process.pid);
|
|
6611
6781
|
}
|
|
6612
6782
|
removeManifest();
|
|
6613
6783
|
};
|
|
@@ -6621,7 +6791,7 @@ async function runWithTurbo(wsRoot, stateDir, proxyPort, tls2, tld, scriptName,
|
|
|
6621
6791
|
process.exit(exitCode);
|
|
6622
6792
|
}
|
|
6623
6793
|
}
|
|
6624
|
-
async function runWithDirectSpawn(stateDir, proxyPort, tls2,
|
|
6794
|
+
async function runWithDirectSpawn(stateDir, proxyPort, tls2, tlds, proxiedApps, taskApps) {
|
|
6625
6795
|
const children = [];
|
|
6626
6796
|
const exitCodes = /* @__PURE__ */ new Map();
|
|
6627
6797
|
const appUrls = [];
|
|
@@ -6632,7 +6802,7 @@ async function runWithDirectSpawn(stateDir, proxyPort, tls2, tld, proxiedApps, t
|
|
|
6632
6802
|
stateDir,
|
|
6633
6803
|
proxyPort,
|
|
6634
6804
|
tls2,
|
|
6635
|
-
|
|
6805
|
+
tlds,
|
|
6636
6806
|
exitCodes
|
|
6637
6807
|
);
|
|
6638
6808
|
children.push(child);
|
|
@@ -6667,11 +6837,8 @@ async function runWithDirectSpawn(stateDir, proxyPort, tls2, tld, proxiedApps, t
|
|
|
6667
6837
|
}
|
|
6668
6838
|
}
|
|
6669
6839
|
}, SIGKILL_TIMEOUT_MS).unref();
|
|
6670
|
-
for (const { store,
|
|
6671
|
-
|
|
6672
|
-
store.removeRoute(hostname, process.pid);
|
|
6673
|
-
} catch {
|
|
6674
|
-
}
|
|
6840
|
+
for (const { store, hostnames } of routeEntries) {
|
|
6841
|
+
removeRoutes(store, hostnames, process.pid);
|
|
6675
6842
|
}
|
|
6676
6843
|
};
|
|
6677
6844
|
process.on("SIGINT", cleanup);
|
|
@@ -6730,7 +6897,7 @@ async function handleRunMode(args, globalScript) {
|
|
|
6730
6897
|
}
|
|
6731
6898
|
const worktree = detectWorktreePrefix();
|
|
6732
6899
|
const effectiveName = worktree ? `${worktree.prefix}.${baseName}` : baseName;
|
|
6733
|
-
const { dir, port, tls: tls2,
|
|
6900
|
+
const { dir, port, tls: tls2, tlds, lanMode, lanIp } = await discoverState();
|
|
6734
6901
|
const store = new RouteStore(dir, {
|
|
6735
6902
|
onWarning: (msg) => console.warn(colors_default.yellow(msg))
|
|
6736
6903
|
});
|
|
@@ -6741,7 +6908,7 @@ async function handleRunMode(args, globalScript) {
|
|
|
6741
6908
|
effectiveName,
|
|
6742
6909
|
parsed.commandArgs,
|
|
6743
6910
|
tls2,
|
|
6744
|
-
|
|
6911
|
+
tlds,
|
|
6745
6912
|
parsed.force,
|
|
6746
6913
|
{ nameSource, prefix: worktree?.prefix, prefixSource: worktree?.source },
|
|
6747
6914
|
parsed.appPort,
|
|
@@ -6767,7 +6934,7 @@ async function handleNamedMode(args) {
|
|
|
6767
6934
|
}
|
|
6768
6935
|
const safeName = parsed.name.split(".").map((label) => truncateLabel(label)).join(".");
|
|
6769
6936
|
parseHostname(safeName, DEFAULT_TLD);
|
|
6770
|
-
const { dir, port, tls: tls2,
|
|
6937
|
+
const { dir, port, tls: tls2, tlds, lanMode, lanIp } = await discoverState();
|
|
6771
6938
|
const store = new RouteStore(dir, {
|
|
6772
6939
|
onWarning: (msg) => console.warn(colors_default.yellow(msg))
|
|
6773
6940
|
});
|
|
@@ -6778,7 +6945,7 @@ async function handleNamedMode(args) {
|
|
|
6778
6945
|
safeName,
|
|
6779
6946
|
parsed.commandArgs,
|
|
6780
6947
|
tls2,
|
|
6781
|
-
|
|
6948
|
+
tlds,
|
|
6782
6949
|
parsed.force,
|
|
6783
6950
|
void 0,
|
|
6784
6951
|
parsed.appPort,
|