nikcli-remote 1.0.1 → 1.0.3
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/{chunk-QLGPGAPC.js → chunk-ONRUR3Z7.js} +243 -637
- package/dist/index.cjs +244 -771
- package/dist/index.d.cts +3 -78
- package/dist/index.d.ts +3 -78
- package/dist/index.js +4 -135
- package/dist/{server-ISK4MDQQ.js → server-MURDBK6L.js} +1 -1
- package/package.json +1 -1
- package/src/index.ts +4 -61
- package/src/server.ts +3 -6
- package/src/tunnel.ts +11 -7
- package/src/web-client.ts +236 -593
|
@@ -1548,7 +1548,7 @@ var TunnelManager = class {
|
|
|
1548
1548
|
*/
|
|
1549
1549
|
createLocaltunnelCli(port) {
|
|
1550
1550
|
return new Promise((resolve, reject) => {
|
|
1551
|
-
this.process = spawn2("npx", ["localtunnel", "--port", port.toString()], {
|
|
1551
|
+
this.process = spawn2("npx", ["localtunnel", "--port", port.toString(), "--print-requests", "false"], {
|
|
1552
1552
|
stdio: ["pipe", "pipe", "pipe"],
|
|
1553
1553
|
shell: true
|
|
1554
1554
|
});
|
|
@@ -1565,8 +1565,7 @@ var TunnelManager = class {
|
|
|
1565
1565
|
resolve(match[1]);
|
|
1566
1566
|
}
|
|
1567
1567
|
});
|
|
1568
|
-
this.process.stderr?.on("data", (
|
|
1569
|
-
output += data.toString();
|
|
1568
|
+
this.process.stderr?.on("data", () => {
|
|
1570
1569
|
});
|
|
1571
1570
|
this.process.on("error", (error) => {
|
|
1572
1571
|
clearTimeout(timeout);
|
|
@@ -1587,7 +1586,7 @@ var TunnelManager = class {
|
|
|
1587
1586
|
return new Promise((resolve, reject) => {
|
|
1588
1587
|
this.process = spawn2(
|
|
1589
1588
|
"cloudflared",
|
|
1590
|
-
["tunnel", "--url", `http://localhost:${port}
|
|
1589
|
+
["tunnel", "--url", `http://localhost:${port}`, "--metrics", "localhost:0"],
|
|
1591
1590
|
{
|
|
1592
1591
|
stdio: ["pipe", "pipe", "pipe"]
|
|
1593
1592
|
}
|
|
@@ -1624,7 +1623,7 @@ var TunnelManager = class {
|
|
|
1624
1623
|
*/
|
|
1625
1624
|
createNgrok(port) {
|
|
1626
1625
|
return new Promise((resolve, reject) => {
|
|
1627
|
-
this.process = spawn2("ngrok", ["http", port.toString(), "--log=stdout"], {
|
|
1626
|
+
this.process = spawn2("ngrok", ["http", port.toString(), "--log=stdout", "--log-level=info"], {
|
|
1628
1627
|
stdio: ["pipe", "pipe", "pipe"]
|
|
1629
1628
|
});
|
|
1630
1629
|
let output = "";
|
|
@@ -1640,8 +1639,7 @@ var TunnelManager = class {
|
|
|
1640
1639
|
resolve(match[1]);
|
|
1641
1640
|
}
|
|
1642
1641
|
});
|
|
1643
|
-
this.process.stderr?.on("data", (
|
|
1644
|
-
output += data.toString();
|
|
1642
|
+
this.process.stderr?.on("data", () => {
|
|
1645
1643
|
});
|
|
1646
1644
|
this.process.on("error", (error) => {
|
|
1647
1645
|
clearTimeout(timeout);
|
|
@@ -1656,40 +1654,6 @@ var TunnelManager = class {
|
|
|
1656
1654
|
});
|
|
1657
1655
|
}
|
|
1658
1656
|
};
|
|
1659
|
-
async function checkTunnelAvailability(provider) {
|
|
1660
|
-
try {
|
|
1661
|
-
const { execSync } = await import("child_process");
|
|
1662
|
-
switch (provider) {
|
|
1663
|
-
case "localtunnel":
|
|
1664
|
-
try {
|
|
1665
|
-
await import("./localtunnel-XT32JGNN.js");
|
|
1666
|
-
return true;
|
|
1667
|
-
} catch {
|
|
1668
|
-
execSync("npx localtunnel --version", { stdio: "pipe" });
|
|
1669
|
-
return true;
|
|
1670
|
-
}
|
|
1671
|
-
case "cloudflared":
|
|
1672
|
-
execSync("cloudflared --version", { stdio: "pipe" });
|
|
1673
|
-
return true;
|
|
1674
|
-
case "ngrok":
|
|
1675
|
-
execSync("ngrok version", { stdio: "pipe" });
|
|
1676
|
-
return true;
|
|
1677
|
-
default:
|
|
1678
|
-
return false;
|
|
1679
|
-
}
|
|
1680
|
-
} catch {
|
|
1681
|
-
return false;
|
|
1682
|
-
}
|
|
1683
|
-
}
|
|
1684
|
-
async function findAvailableTunnel() {
|
|
1685
|
-
const providers = ["localtunnel", "cloudflared", "ngrok"];
|
|
1686
|
-
for (const provider of providers) {
|
|
1687
|
-
if (await checkTunnelAvailability(provider)) {
|
|
1688
|
-
return provider;
|
|
1689
|
-
}
|
|
1690
|
-
}
|
|
1691
|
-
return null;
|
|
1692
|
-
}
|
|
1693
1657
|
|
|
1694
1658
|
// src/web-client.ts
|
|
1695
1659
|
function getWebClient() {
|
|
@@ -1697,649 +1661,298 @@ function getWebClient() {
|
|
|
1697
1661
|
<html lang="en">
|
|
1698
1662
|
<head>
|
|
1699
1663
|
<meta charset="UTF-8">
|
|
1700
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
|
1664
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
|
|
1701
1665
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
1702
|
-
<meta name="mobile-web-app-
|
|
1703
|
-
<meta name="theme-color" content="#0d1117">
|
|
1666
|
+
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
|
1704
1667
|
<title>NikCLI Remote</title>
|
|
1668
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@5.3.0/css/xterm.css" />
|
|
1705
1669
|
<style>
|
|
1670
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
1706
1671
|
:root {
|
|
1707
|
-
--bg: #0d1117;
|
|
1672
|
+
--bg-primary: #0d1117;
|
|
1708
1673
|
--bg-secondary: #161b22;
|
|
1709
|
-
--fg: #e6edf3;
|
|
1710
|
-
--fg-muted: #8b949e;
|
|
1711
1674
|
--accent: #58a6ff;
|
|
1712
1675
|
--success: #3fb950;
|
|
1713
|
-
--warning: #d29922;
|
|
1714
|
-
--error: #f85149;
|
|
1715
1676
|
--border: #30363d;
|
|
1716
|
-
--font-mono: 'SF Mono', 'Fira Code', 'Consolas', monospace;
|
|
1717
|
-
}
|
|
1718
|
-
|
|
1719
|
-
* {
|
|
1720
|
-
box-sizing: border-box;
|
|
1721
|
-
margin: 0;
|
|
1722
|
-
padding: 0;
|
|
1723
|
-
-webkit-tap-highlight-color: transparent;
|
|
1724
1677
|
}
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
background: var(--bg);
|
|
1729
|
-
color:
|
|
1730
|
-
font-family: var(--font-mono);
|
|
1731
|
-
font-size: 14px;
|
|
1732
|
-
overflow: hidden;
|
|
1733
|
-
touch-action: manipulation;
|
|
1734
|
-
}
|
|
1735
|
-
|
|
1736
|
-
#app {
|
|
1678
|
+
html, body { height: 100%; overflow: hidden; touch-action: manipulation; }
|
|
1679
|
+
body {
|
|
1680
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
1681
|
+
background: var(--bg-primary);
|
|
1682
|
+
color: #e6edf3;
|
|
1737
1683
|
display: flex;
|
|
1738
1684
|
flex-direction: column;
|
|
1739
|
-
height: 100%;
|
|
1740
|
-
height: 100dvh;
|
|
1741
|
-
}
|
|
1742
|
-
|
|
1743
|
-
/* Header */
|
|
1744
|
-
#header {
|
|
1745
|
-
display: flex;
|
|
1746
|
-
align-items: center;
|
|
1747
|
-
justify-content: space-between;
|
|
1748
|
-
padding: 12px 16px;
|
|
1749
|
-
background: var(--bg-secondary);
|
|
1750
|
-
border-bottom: 1px solid var(--border);
|
|
1751
|
-
flex-shrink: 0;
|
|
1752
|
-
}
|
|
1753
|
-
|
|
1754
|
-
#header h1 {
|
|
1755
|
-
font-size: 16px;
|
|
1756
|
-
font-weight: 600;
|
|
1757
|
-
color: var(--accent);
|
|
1758
|
-
display: flex;
|
|
1759
|
-
align-items: center;
|
|
1760
|
-
gap: 8px;
|
|
1761
|
-
}
|
|
1762
|
-
|
|
1763
|
-
#header h1::before {
|
|
1764
|
-
content: '';
|
|
1765
|
-
width: 10px;
|
|
1766
|
-
height: 10px;
|
|
1767
|
-
background: var(--accent);
|
|
1768
|
-
border-radius: 2px;
|
|
1769
|
-
}
|
|
1770
|
-
|
|
1771
|
-
#status {
|
|
1772
|
-
display: flex;
|
|
1773
|
-
align-items: center;
|
|
1774
|
-
gap: 6px;
|
|
1775
|
-
font-size: 12px;
|
|
1776
|
-
color: var(--fg-muted);
|
|
1777
|
-
}
|
|
1778
|
-
|
|
1779
|
-
#status-dot {
|
|
1780
|
-
width: 8px;
|
|
1781
|
-
height: 8px;
|
|
1782
|
-
border-radius: 50%;
|
|
1783
|
-
background: var(--error);
|
|
1784
|
-
transition: background 0.3s;
|
|
1785
|
-
}
|
|
1786
|
-
|
|
1787
|
-
#status-dot.connected {
|
|
1788
|
-
background: var(--success);
|
|
1789
|
-
}
|
|
1790
|
-
|
|
1791
|
-
#status-dot.connecting {
|
|
1792
|
-
background: var(--warning);
|
|
1793
|
-
animation: pulse 1s infinite;
|
|
1794
|
-
}
|
|
1795
|
-
|
|
1796
|
-
@keyframes pulse {
|
|
1797
|
-
0%, 100% { opacity: 1; }
|
|
1798
|
-
50% { opacity: 0.5; }
|
|
1799
|
-
}
|
|
1800
|
-
|
|
1801
|
-
/* Terminal */
|
|
1802
|
-
#terminal-container {
|
|
1803
|
-
flex: 1;
|
|
1804
|
-
overflow: hidden;
|
|
1805
|
-
position: relative;
|
|
1806
|
-
}
|
|
1807
|
-
|
|
1808
|
-
#terminal {
|
|
1809
|
-
height: 100%;
|
|
1810
|
-
padding: 12px;
|
|
1811
|
-
overflow-y: auto;
|
|
1812
|
-
overflow-x: hidden;
|
|
1813
|
-
font-size: 13px;
|
|
1814
|
-
line-height: 1.5;
|
|
1815
|
-
white-space: pre-wrap;
|
|
1816
|
-
word-break: break-all;
|
|
1817
|
-
-webkit-overflow-scrolling: touch;
|
|
1818
|
-
}
|
|
1819
|
-
|
|
1820
|
-
#terminal::-webkit-scrollbar {
|
|
1821
|
-
width: 6px;
|
|
1822
|
-
}
|
|
1823
|
-
|
|
1824
|
-
#terminal::-webkit-scrollbar-track {
|
|
1825
|
-
background: var(--bg);
|
|
1826
|
-
}
|
|
1827
|
-
|
|
1828
|
-
#terminal::-webkit-scrollbar-thumb {
|
|
1829
|
-
background: var(--border);
|
|
1830
|
-
border-radius: 3px;
|
|
1831
|
-
}
|
|
1832
|
-
|
|
1833
|
-
.cursor {
|
|
1834
|
-
display: inline-block;
|
|
1835
|
-
width: 8px;
|
|
1836
|
-
height: 16px;
|
|
1837
|
-
background: var(--fg);
|
|
1838
|
-
animation: blink 1s step-end infinite;
|
|
1839
|
-
vertical-align: text-bottom;
|
|
1840
|
-
}
|
|
1841
|
-
|
|
1842
|
-
@keyframes blink {
|
|
1843
|
-
50% { opacity: 0; }
|
|
1844
|
-
}
|
|
1845
|
-
|
|
1846
|
-
/* Notifications */
|
|
1847
|
-
#notifications {
|
|
1848
|
-
position: fixed;
|
|
1849
|
-
top: 60px;
|
|
1850
|
-
left: 12px;
|
|
1851
|
-
right: 12px;
|
|
1852
|
-
z-index: 1000;
|
|
1853
|
-
pointer-events: none;
|
|
1854
|
-
}
|
|
1855
|
-
|
|
1856
|
-
.notification {
|
|
1857
|
-
background: var(--bg-secondary);
|
|
1858
|
-
border: 1px solid var(--border);
|
|
1859
|
-
border-radius: 8px;
|
|
1860
|
-
padding: 12px 16px;
|
|
1861
|
-
margin-bottom: 8px;
|
|
1862
|
-
animation: slideIn 0.3s ease;
|
|
1863
|
-
pointer-events: auto;
|
|
1864
|
-
box-shadow: 0 4px 12px rgba(0,0,0,0.4);
|
|
1865
|
-
}
|
|
1866
|
-
|
|
1867
|
-
.notification.success { border-left: 3px solid var(--success); }
|
|
1868
|
-
.notification.error { border-left: 3px solid var(--error); }
|
|
1869
|
-
.notification.warning { border-left: 3px solid var(--warning); }
|
|
1870
|
-
.notification.info { border-left: 3px solid var(--accent); }
|
|
1871
|
-
|
|
1872
|
-
.notification h4 {
|
|
1873
|
-
font-size: 14px;
|
|
1874
|
-
font-weight: 600;
|
|
1875
|
-
margin-bottom: 4px;
|
|
1876
|
-
}
|
|
1877
|
-
|
|
1878
|
-
.notification p {
|
|
1879
|
-
font-size: 12px;
|
|
1880
|
-
color: var(--fg-muted);
|
|
1881
1685
|
}
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
from { transform: translateY(-20px); opacity: 0; }
|
|
1885
|
-
to { transform: translateY(0); opacity: 1; }
|
|
1886
|
-
}
|
|
1887
|
-
|
|
1888
|
-
/* Quick Keys */
|
|
1889
|
-
#quickkeys {
|
|
1890
|
-
display: grid;
|
|
1891
|
-
grid-template-columns: repeat(6, 1fr);
|
|
1892
|
-
gap: 6px;
|
|
1893
|
-
padding: 8px 12px;
|
|
1894
|
-
background: var(--bg-secondary);
|
|
1895
|
-
border-top: 1px solid var(--border);
|
|
1896
|
-
flex-shrink: 0;
|
|
1897
|
-
}
|
|
1898
|
-
|
|
1899
|
-
.qkey {
|
|
1900
|
-
background: var(--bg);
|
|
1901
|
-
border: 1px solid var(--border);
|
|
1902
|
-
border-radius: 6px;
|
|
1903
|
-
padding: 10px 4px;
|
|
1904
|
-
color: var(--fg);
|
|
1905
|
-
font-size: 11px;
|
|
1906
|
-
font-family: var(--font-mono);
|
|
1907
|
-
text-align: center;
|
|
1908
|
-
cursor: pointer;
|
|
1909
|
-
user-select: none;
|
|
1910
|
-
transition: background 0.1s, transform 0.1s;
|
|
1911
|
-
}
|
|
1912
|
-
|
|
1913
|
-
.qkey:active {
|
|
1914
|
-
background: var(--border);
|
|
1915
|
-
transform: scale(0.95);
|
|
1916
|
-
}
|
|
1917
|
-
|
|
1918
|
-
.qkey.wide {
|
|
1919
|
-
grid-column: span 2;
|
|
1920
|
-
}
|
|
1921
|
-
|
|
1922
|
-
.qkey.accent {
|
|
1923
|
-
background: var(--accent);
|
|
1924
|
-
border-color: var(--accent);
|
|
1925
|
-
color: #fff;
|
|
1926
|
-
}
|
|
1927
|
-
|
|
1928
|
-
/* Input */
|
|
1929
|
-
#input-container {
|
|
1930
|
-
padding: 12px;
|
|
1686
|
+
#terminal { flex: 1; overflow: hidden; background: var(--bg-primary); padding: 8px; }
|
|
1687
|
+
#input-area {
|
|
1931
1688
|
background: var(--bg-secondary);
|
|
1932
1689
|
border-top: 1px solid var(--border);
|
|
1690
|
+
padding: 12px 16px;
|
|
1933
1691
|
flex-shrink: 0;
|
|
1692
|
+
padding-bottom: env(safe-area-inset-bottom, 12px);
|
|
1934
1693
|
}
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
gap: 8px;
|
|
1939
|
-
}
|
|
1940
|
-
|
|
1941
|
-
#input {
|
|
1694
|
+
.input-row { display: flex; gap: 8px; align-items: center; }
|
|
1695
|
+
.prompt { color: var(--success); font-family: 'SF Mono', Monaco, monospace; font-size: 14px; white-space: nowrap; }
|
|
1696
|
+
#cmd-input {
|
|
1942
1697
|
flex: 1;
|
|
1943
|
-
background:
|
|
1698
|
+
background: #21262d;
|
|
1944
1699
|
border: 1px solid var(--border);
|
|
1945
|
-
border-radius:
|
|
1946
|
-
padding: 12px
|
|
1947
|
-
color:
|
|
1948
|
-
font-family: var(--font-mono);
|
|
1700
|
+
border-radius: 12px;
|
|
1701
|
+
padding: 12px 16px;
|
|
1702
|
+
color: #e6edf3;
|
|
1949
1703
|
font-size: 16px;
|
|
1704
|
+
font-family: 'SF Mono', Monaco, monospace;
|
|
1950
1705
|
outline: none;
|
|
1951
|
-
|
|
1952
|
-
}
|
|
1953
|
-
|
|
1954
|
-
#input:focus {
|
|
1955
|
-
border-color: var(--accent);
|
|
1706
|
+
-webkit-appearance: none;
|
|
1956
1707
|
}
|
|
1957
|
-
|
|
1958
|
-
#
|
|
1959
|
-
color: var(--fg-muted);
|
|
1960
|
-
}
|
|
1961
|
-
|
|
1962
|
-
#send {
|
|
1708
|
+
#cmd-input:focus { border-color: var(--accent); }
|
|
1709
|
+
#send-btn {
|
|
1963
1710
|
background: var(--accent);
|
|
1964
|
-
color:
|
|
1711
|
+
color: white;
|
|
1965
1712
|
border: none;
|
|
1966
|
-
border-radius:
|
|
1967
|
-
padding: 12px
|
|
1968
|
-
font-size:
|
|
1713
|
+
border-radius: 12px;
|
|
1714
|
+
padding: 12px 24px;
|
|
1715
|
+
font-size: 16px;
|
|
1969
1716
|
font-weight: 600;
|
|
1970
|
-
font-family: var(--font-mono);
|
|
1971
1717
|
cursor: pointer;
|
|
1972
|
-
|
|
1973
|
-
}
|
|
1974
|
-
|
|
1975
|
-
#send:active {
|
|
1976
|
-
opacity: 0.8;
|
|
1977
|
-
transform: scale(0.98);
|
|
1718
|
+
-webkit-tap-highlight-color: transparent;
|
|
1978
1719
|
}
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
background: var(--bg);
|
|
1720
|
+
#send-btn:active { opacity: 0.7; }
|
|
1721
|
+
#status-bar {
|
|
1722
|
+
background: var(--bg-secondary);
|
|
1723
|
+
border-bottom: 1px solid var(--border);
|
|
1724
|
+
padding: 8px 16px;
|
|
1985
1725
|
display: flex;
|
|
1986
|
-
|
|
1726
|
+
justify-content: space-between;
|
|
1987
1727
|
align-items: center;
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
}
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
}
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
}
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
}
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
}
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
@
|
|
2021
|
-
#input-
|
|
2022
|
-
|
|
2023
|
-
}
|
|
2024
|
-
}
|
|
2025
|
-
|
|
2026
|
-
/* Landscape adjustments */
|
|
2027
|
-
@media (max-height: 500px) {
|
|
2028
|
-
#quickkeys {
|
|
2029
|
-
grid-template-columns: repeat(12, 1fr);
|
|
2030
|
-
padding: 6px 8px;
|
|
2031
|
-
}
|
|
2032
|
-
.qkey {
|
|
2033
|
-
padding: 8px 2px;
|
|
2034
|
-
font-size: 10px;
|
|
2035
|
-
}
|
|
2036
|
-
#terminal {
|
|
2037
|
-
font-size: 12px;
|
|
2038
|
-
}
|
|
1728
|
+
font-size: 12px;
|
|
1729
|
+
padding-top: env(safe-area-inset-top, 8px);
|
|
1730
|
+
}
|
|
1731
|
+
.status-row { display: flex; align-items: center; gap: 8px; }
|
|
1732
|
+
.status-dot { width: 8px; height: 8px; border-radius: 50%; background: #8b949e; }
|
|
1733
|
+
.status-dot.connected { background: var(--success); box-shadow: 0 0 8px var(--success); }
|
|
1734
|
+
.status-dot.connecting { background: var(--accent); animation: pulse 1s infinite; }
|
|
1735
|
+
.status-dot.disconnected { background: #f85149; }
|
|
1736
|
+
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
|
|
1737
|
+
#auth-overlay {
|
|
1738
|
+
position: fixed; inset: 0; background: var(--bg-primary);
|
|
1739
|
+
display: flex; flex-direction: column; align-items: center; justify-content: center;
|
|
1740
|
+
padding: 20px; z-index: 100;
|
|
1741
|
+
}
|
|
1742
|
+
#auth-overlay.hidden { display: none; }
|
|
1743
|
+
.auth-title { font-size: 28px; font-weight: 700; margin-bottom: 8px; }
|
|
1744
|
+
.auth-subtitle { color: #8b949e; font-size: 16px; margin-bottom: 24px; }
|
|
1745
|
+
.auth-msg {
|
|
1746
|
+
background: var(--bg-secondary); padding: 20px 28px;
|
|
1747
|
+
border-radius: 16px; border: 1px solid var(--border); text-align: center;
|
|
1748
|
+
}
|
|
1749
|
+
.auth-msg.error { color: #f85149; border-color: #f85149; }
|
|
1750
|
+
.quick-btns {
|
|
1751
|
+
display: flex; gap: 8px; margin-top: 20px; flex-wrap: wrap; justify-content: center;
|
|
1752
|
+
}
|
|
1753
|
+
.quick-btn {
|
|
1754
|
+
background: #21262d; border: 1px solid var(--border); color: #e6edf3;
|
|
1755
|
+
padding: 10px 16px; border-radius: 8px; font-size: 14px;
|
|
1756
|
+
font-family: 'SF Mono', Monaco, monospace; cursor: pointer;
|
|
1757
|
+
}
|
|
1758
|
+
.quick-btn:active { background: #30363d; }
|
|
1759
|
+
.hint { font-size: 12px; color: #8b949e; margin-top: 12px; }
|
|
1760
|
+
@media (max-width: 600px) {
|
|
1761
|
+
#input-area { padding: 10px 12px; }
|
|
1762
|
+
.quick-btn { padding: 8px 12px; font-size: 12px; }
|
|
2039
1763
|
}
|
|
2040
1764
|
</style>
|
|
2041
1765
|
</head>
|
|
2042
1766
|
<body>
|
|
2043
|
-
<div id="
|
|
2044
|
-
<div
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
<header id="header">
|
|
2050
|
-
<h1>NikCLI Remote</h1>
|
|
2051
|
-
<div id="status">
|
|
2052
|
-
<span id="status-dot" class="connecting"></span>
|
|
2053
|
-
<span id="status-text">Connecting</span>
|
|
2054
|
-
</div>
|
|
2055
|
-
</header>
|
|
2056
|
-
|
|
2057
|
-
<div id="terminal-container">
|
|
2058
|
-
<div id="terminal"></div>
|
|
1767
|
+
<div id="auth-overlay">
|
|
1768
|
+
<div class="auth-title">\u{1F4F1} NikCLI Remote</div>
|
|
1769
|
+
<div class="auth-subtitle">Full terminal emulation</div>
|
|
1770
|
+
<div id="auth-msg" class="auth-msg">
|
|
1771
|
+
<div id="auth-text">Connecting...</div>
|
|
2059
1772
|
</div>
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
<button class="
|
|
2065
|
-
<button class="
|
|
2066
|
-
<button class="qkey" data-key="\\x1b[B">\u2193</button>
|
|
2067
|
-
<button class="qkey" data-key="\\x1b[D">\u2190</button>
|
|
2068
|
-
<button class="qkey" data-key="\\x1b[C">\u2192</button>
|
|
2069
|
-
<button class="qkey" data-key="\\x1b">Esc</button>
|
|
2070
|
-
<button class="qkey" data-key="\\x03">^C</button>
|
|
2071
|
-
<button class="qkey" data-key="\\x04">^D</button>
|
|
2072
|
-
<button class="qkey" data-key="\\x1a">^Z</button>
|
|
2073
|
-
<button class="qkey" data-key="\\x0c">^L</button>
|
|
2074
|
-
<button class="qkey wide accent" data-key="\\r">Enter \u23CE</button>
|
|
1773
|
+
<div class="quick-btns">
|
|
1774
|
+
<button class="quick-btn" onclick="send('help')">/help</button>
|
|
1775
|
+
<button class="quick-btn" onclick="send('ls -la')">ls -la</button>
|
|
1776
|
+
<button class="quick-btn" onclick="send('pwd')">pwd</button>
|
|
1777
|
+
<button class="quick-btn" onclick="send('whoami')">whoami</button>
|
|
1778
|
+
<button class="quick-btn" onclick="send('clear')">clear</button>
|
|
2075
1779
|
</div>
|
|
1780
|
+
<div class="hint">Mobile keyboard to type commands</div>
|
|
1781
|
+
</div>
|
|
2076
1782
|
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
</div>
|
|
1783
|
+
<div id="status-bar">
|
|
1784
|
+
<div class="status-row">
|
|
1785
|
+
<span class="status-dot" id="status-dot"></span>
|
|
1786
|
+
<span id="status-text">Disconnected</span>
|
|
2082
1787
|
</div>
|
|
1788
|
+
<span id="session-id" style="color: #8b949e;"></span>
|
|
2083
1789
|
</div>
|
|
2084
1790
|
|
|
2085
|
-
<
|
|
2086
|
-
(function() {
|
|
2087
|
-
'use strict';
|
|
2088
|
-
|
|
2089
|
-
// Parse URL params
|
|
2090
|
-
const params = new URLSearchParams(location.search);
|
|
2091
|
-
const token = params.get('t');
|
|
2092
|
-
const sessionId = params.get('s');
|
|
2093
|
-
|
|
2094
|
-
// DOM elements
|
|
2095
|
-
const terminal = document.getElementById('terminal');
|
|
2096
|
-
const input = document.getElementById('input');
|
|
2097
|
-
const sendBtn = document.getElementById('send');
|
|
2098
|
-
const statusDot = document.getElementById('status-dot');
|
|
2099
|
-
const statusText = document.getElementById('status-text');
|
|
2100
|
-
const authScreen = document.getElementById('auth-screen');
|
|
2101
|
-
const authStatus = document.getElementById('auth-status');
|
|
2102
|
-
const notifications = document.getElementById('notifications');
|
|
2103
|
-
|
|
2104
|
-
// State
|
|
2105
|
-
let ws = null;
|
|
2106
|
-
let reconnectAttempts = 0;
|
|
2107
|
-
const maxReconnectAttempts = 5;
|
|
2108
|
-
let terminalEnabled = true;
|
|
2109
|
-
|
|
2110
|
-
// Connect to WebSocket
|
|
2111
|
-
function connect() {
|
|
2112
|
-
const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
2113
|
-
ws = new WebSocket(protocol + '//' + location.host);
|
|
2114
|
-
|
|
2115
|
-
ws.onopen = function() {
|
|
2116
|
-
setStatus('connecting', 'Authenticating...');
|
|
2117
|
-
ws.send(JSON.stringify({ type: 'auth', token: token }));
|
|
2118
|
-
};
|
|
2119
|
-
|
|
2120
|
-
ws.onmessage = function(event) {
|
|
2121
|
-
try {
|
|
2122
|
-
const msg = JSON.parse(event.data);
|
|
2123
|
-
handleMessage(msg);
|
|
2124
|
-
} catch (e) {
|
|
2125
|
-
console.error('Parse error:', e);
|
|
2126
|
-
}
|
|
2127
|
-
};
|
|
2128
|
-
|
|
2129
|
-
ws.onclose = function() {
|
|
2130
|
-
setStatus('disconnected', 'Disconnected');
|
|
2131
|
-
if (reconnectAttempts < maxReconnectAttempts) {
|
|
2132
|
-
reconnectAttempts++;
|
|
2133
|
-
const delay = Math.min(2000 * reconnectAttempts, 10000);
|
|
2134
|
-
setTimeout(connect, delay);
|
|
2135
|
-
} else {
|
|
2136
|
-
authStatus.textContent = 'Connection failed. Refresh to retry.';
|
|
2137
|
-
authStatus.classList.add('error');
|
|
2138
|
-
authScreen.classList.remove('hidden');
|
|
2139
|
-
}
|
|
2140
|
-
};
|
|
2141
|
-
|
|
2142
|
-
ws.onerror = function() {
|
|
2143
|
-
console.error('WebSocket error');
|
|
2144
|
-
};
|
|
2145
|
-
}
|
|
2146
|
-
|
|
2147
|
-
// Handle incoming message
|
|
2148
|
-
function handleMessage(msg) {
|
|
2149
|
-
switch (msg.type) {
|
|
2150
|
-
case 'auth:required':
|
|
2151
|
-
// Already sent auth on open
|
|
2152
|
-
break;
|
|
2153
|
-
|
|
2154
|
-
case 'auth:success':
|
|
2155
|
-
authScreen.classList.add('hidden');
|
|
2156
|
-
setStatus('connected', 'Connected');
|
|
2157
|
-
reconnectAttempts = 0;
|
|
2158
|
-
terminalEnabled = msg.payload?.terminalEnabled !== false;
|
|
2159
|
-
if (terminalEnabled) {
|
|
2160
|
-
appendOutput('\\x1b[32mConnected to NikCLI\\x1b[0m\\n\\n');
|
|
2161
|
-
}
|
|
2162
|
-
break;
|
|
2163
|
-
|
|
2164
|
-
case 'auth:failed':
|
|
2165
|
-
authStatus.textContent = 'Authentication failed';
|
|
2166
|
-
authStatus.classList.add('error');
|
|
2167
|
-
break;
|
|
2168
|
-
|
|
2169
|
-
case 'terminal:output':
|
|
2170
|
-
if (msg.payload?.data) {
|
|
2171
|
-
appendOutput(msg.payload.data);
|
|
2172
|
-
}
|
|
2173
|
-
break;
|
|
2174
|
-
|
|
2175
|
-
case 'terminal:exit':
|
|
2176
|
-
appendOutput('\\n\\x1b[33m[Process exited with code ' + (msg.payload?.code || 0) + ']\\x1b[0m\\n');
|
|
2177
|
-
break;
|
|
2178
|
-
|
|
2179
|
-
case 'notification':
|
|
2180
|
-
showNotification(msg.payload);
|
|
2181
|
-
break;
|
|
2182
|
-
|
|
2183
|
-
case 'session:end':
|
|
2184
|
-
appendOutput('\\n\\x1b[31m[Session ended]\\x1b[0m\\n');
|
|
2185
|
-
setStatus('disconnected', 'Session ended');
|
|
2186
|
-
break;
|
|
1791
|
+
<div id="terminal"></div>
|
|
2187
1792
|
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
}
|
|
2196
|
-
|
|
2197
|
-
// Append text to terminal with ANSI support
|
|
2198
|
-
function appendOutput(text) {
|
|
2199
|
-
// Simple ANSI to HTML conversion
|
|
2200
|
-
const html = ansiToHtml(text);
|
|
2201
|
-
terminal.innerHTML += html;
|
|
2202
|
-
terminal.scrollTop = terminal.scrollHeight;
|
|
2203
|
-
}
|
|
1793
|
+
<div id="input-area">
|
|
1794
|
+
<form class="input-row" onsubmit="return handleSubmit(event)">
|
|
1795
|
+
<span class="prompt">$</span>
|
|
1796
|
+
<input type="text" id="cmd-input" placeholder="Type command..." autocomplete="off" enterkeyhint="send" inputmode="text">
|
|
1797
|
+
<button type="submit" id="send-btn">Send</button>
|
|
1798
|
+
</form>
|
|
1799
|
+
</div>
|
|
2204
1800
|
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
1801
|
+
<script src="https://cdn.jsdelivr.net/npm/xterm@5.3.0/lib/xterm.js"></script>
|
|
1802
|
+
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.js"></script>
|
|
1803
|
+
<script>
|
|
1804
|
+
let ws = null, term = null, fitAddon = null, connected = false, reconnectAttempts = 0;
|
|
1805
|
+
const token = new URLSearchParams(location.search).get('t') || '';
|
|
1806
|
+
const sessionId = new URLSearchParams(location.search).get('s') || '';
|
|
1807
|
+
|
|
1808
|
+
const authOverlay = document.getElementById('auth-overlay');
|
|
1809
|
+
const authMsg = document.getElementById('auth-msg');
|
|
1810
|
+
const authText = document.getElementById('auth-text');
|
|
1811
|
+
const statusDot = document.getElementById('status-dot');
|
|
1812
|
+
const statusText = document.getElementById('status-text');
|
|
1813
|
+
const sessionSpan = document.getElementById('session-id');
|
|
1814
|
+
const cmdInput = document.getElementById('cmd-input');
|
|
1815
|
+
|
|
1816
|
+
// Initialize xterm.js
|
|
1817
|
+
term = new Terminal({
|
|
1818
|
+
cursorBlink: true,
|
|
1819
|
+
fontSize: 14,
|
|
1820
|
+
fontFamily: '"SF Mono", Monaco, Consolas, monospace',
|
|
1821
|
+
theme: {
|
|
1822
|
+
background: '#0d1117',
|
|
1823
|
+
foreground: '#e6edf3',
|
|
1824
|
+
cursor: '#3fb950',
|
|
1825
|
+
selectionBackground: '#264f78',
|
|
1826
|
+
black: '#484f58',
|
|
1827
|
+
red: '#f85149',
|
|
1828
|
+
green: '#3fb950',
|
|
1829
|
+
yellow: '#d29922',
|
|
1830
|
+
blue: '#58a6ff',
|
|
1831
|
+
magenta: '#a371f7',
|
|
1832
|
+
cyan: '#39c5cf',
|
|
1833
|
+
white: '#e6edf3',
|
|
1834
|
+
brightBlack: '#6e7681',
|
|
1835
|
+
brightRed: '#ffa198',
|
|
1836
|
+
brightGreen: '#7ee787',
|
|
1837
|
+
brightYellow: '#f0883e',
|
|
1838
|
+
brightBlue: '#79c0ff',
|
|
1839
|
+
brightMagenta: '#d2a8ff',
|
|
1840
|
+
brightCyan: '#56d4db',
|
|
1841
|
+
brightWhite: '#f0f6fc'
|
|
1842
|
+
},
|
|
1843
|
+
convertEol: true
|
|
1844
|
+
});
|
|
2213
1845
|
|
|
2214
|
-
|
|
2215
|
-
|
|
1846
|
+
fitAddon = new FitAddon.FitAddon();
|
|
1847
|
+
term.loadAddon(fitAddon);
|
|
1848
|
+
term.open(document.getElementById('terminal'));
|
|
1849
|
+
fitAddon.fit();
|
|
1850
|
+
term.writeln('\x1B[32mInitializing NikCLI Remote...\x1B[0m');
|
|
1851
|
+
term.writeln('\x1B[90mType commands below\x1B[0m');
|
|
1852
|
+
|
|
1853
|
+
// Resize handler
|
|
1854
|
+
window.addEventListener('resize', () => {
|
|
1855
|
+
clearTimeout(window.resizeTimer);
|
|
1856
|
+
window.resizeTimer = setTimeout(() => fitAddon.fit(), 100);
|
|
1857
|
+
});
|
|
2216
1858
|
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
const codes = parts[i].split(';');
|
|
2225
|
-
for (const code of codes) {
|
|
2226
|
-
if (code === '0') {
|
|
2227
|
-
if (currentStyle) {
|
|
2228
|
-
result += '</span>';
|
|
2229
|
-
currentStyle = '';
|
|
2230
|
-
}
|
|
2231
|
-
} else if (code === '1') {
|
|
2232
|
-
currentStyle = 'font-weight:bold;';
|
|
2233
|
-
result += '<span style="' + currentStyle + '">';
|
|
2234
|
-
} else if (ansiColors[code]) {
|
|
2235
|
-
if (currentStyle) result += '</span>';
|
|
2236
|
-
currentStyle = 'color:' + ansiColors[code] + ';';
|
|
2237
|
-
result += '<span style="' + currentStyle + '">';
|
|
2238
|
-
}
|
|
2239
|
-
}
|
|
2240
|
-
}
|
|
2241
|
-
}
|
|
1859
|
+
// Visual viewport for mobile keyboard
|
|
1860
|
+
if (window.visualViewport) {
|
|
1861
|
+
window.visualViewport.addEventListener('resize', () => {
|
|
1862
|
+
clearTimeout(window.resizeTimer);
|
|
1863
|
+
window.resizeTimer = setTimeout(() => fitAddon.fit(), 100);
|
|
1864
|
+
});
|
|
1865
|
+
}
|
|
2242
1866
|
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
1867
|
+
function connect() {
|
|
1868
|
+
const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
1869
|
+
ws = new WebSocket(protocol + '//' + location.host + '/ws');
|
|
2246
1870
|
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
.replace(/>/g, '>')
|
|
2253
|
-
.replace(/"/g, '"')
|
|
2254
|
-
.replace(/\\n/g, '<br>')
|
|
2255
|
-
.replace(/ /g, ' ');
|
|
2256
|
-
}
|
|
1871
|
+
ws.onopen = () => {
|
|
1872
|
+
setStatus('connecting', 'Authenticating...');
|
|
1873
|
+
ws.send(JSON.stringify({ type: 'auth', token }));
|
|
1874
|
+
reconnectAttempts = 0;
|
|
1875
|
+
};
|
|
2257
1876
|
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
state === 'connecting' ? 'connecting' : '';
|
|
2262
|
-
statusText.textContent = text;
|
|
2263
|
-
}
|
|
1877
|
+
ws.onmessage = (e) => {
|
|
1878
|
+
try { handleMessage(JSON.parse(e.data)); } catch (err) { console.error(err); }
|
|
1879
|
+
};
|
|
2264
1880
|
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
}
|
|
1881
|
+
ws.onclose = () => {
|
|
1882
|
+
setStatus('disconnected', 'Disconnected');
|
|
1883
|
+
connected = false;
|
|
1884
|
+
reconnectAttempts++;
|
|
1885
|
+
setTimeout(connect, Math.min(3000, reconnectAttempts * 500));
|
|
1886
|
+
};
|
|
2271
1887
|
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
1888
|
+
ws.onerror = () => setStatus('disconnected', 'Connection error');
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
function handleMessage(msg) {
|
|
1892
|
+
switch (msg.type) {
|
|
1893
|
+
case 'auth:required':
|
|
1894
|
+
ws.send(JSON.stringify({ type: 'auth', token }));
|
|
1895
|
+
break;
|
|
1896
|
+
case 'auth:success':
|
|
1897
|
+
connected = true;
|
|
1898
|
+
authOverlay.classList.add('hidden');
|
|
1899
|
+
setStatus('connected', 'Connected');
|
|
1900
|
+
sessionSpan.textContent = sessionId ? 'Session: ' + sessionId : '';
|
|
1901
|
+
term.writeln('
|
|
1902
|
+
\x1B[32m\u2713 Connected to NikCLI\x1B[0m
|
|
1903
|
+
');
|
|
1904
|
+
break;
|
|
1905
|
+
case 'auth:failed':
|
|
1906
|
+
authMsg.classList.add('error');
|
|
1907
|
+
authText.textContent = 'Authentication failed';
|
|
1908
|
+
break;
|
|
1909
|
+
case 'terminal:output':
|
|
1910
|
+
if (msg.payload?.data) term.write(msg.payload.data);
|
|
1911
|
+
break;
|
|
1912
|
+
case 'terminal:exit':
|
|
1913
|
+
term.writeln('
|
|
1914
|
+
\x1B[33m[Process exited: ' + (msg.payload?.code || 0) + ']\x1B[0m
|
|
1915
|
+
');
|
|
1916
|
+
break;
|
|
2281
1917
|
}
|
|
1918
|
+
}
|
|
2282
1919
|
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
input.value = '';
|
|
2288
|
-
}
|
|
2289
|
-
input.focus();
|
|
2290
|
-
};
|
|
2291
|
-
|
|
2292
|
-
// Event: Enter key in input
|
|
2293
|
-
input.onkeydown = function(e) {
|
|
2294
|
-
if (e.key === 'Enter') {
|
|
2295
|
-
e.preventDefault();
|
|
2296
|
-
sendBtn.click();
|
|
2297
|
-
}
|
|
2298
|
-
};
|
|
1920
|
+
function setStatus(state, text) {
|
|
1921
|
+
statusDot.className = 'status-dot ' + state;
|
|
1922
|
+
statusText.textContent = text;
|
|
1923
|
+
}
|
|
2299
1924
|
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
send(decoded);
|
|
2312
|
-
input.focus();
|
|
2313
|
-
};
|
|
2314
|
-
});
|
|
1925
|
+
function handleSubmit(e) {
|
|
1926
|
+
e?.preventDefault();
|
|
1927
|
+
const value = cmdInput.value.trim();
|
|
1928
|
+
if (!value || !connected) return;
|
|
1929
|
+
cmdInput.value = '';
|
|
1930
|
+
term.write('$ ' + value + '\r
|
|
1931
|
+
');
|
|
1932
|
+
ws.send(JSON.stringify({ type: 'terminal:input', data: value + '\r' }));
|
|
1933
|
+
setTimeout(() => cmdInput.focus(), 50);
|
|
1934
|
+
return false;
|
|
1935
|
+
}
|
|
2315
1936
|
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
1937
|
+
function send(cmd) {
|
|
1938
|
+
if (!connected) return;
|
|
1939
|
+
term.write('$ ' + cmd + '\r
|
|
1940
|
+
');
|
|
1941
|
+
ws.send(JSON.stringify({ type: 'terminal:input', data: cmd + '\r' }));
|
|
1942
|
+
}
|
|
2322
1943
|
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
const rows = Math.floor(terminal.clientHeight / 18);
|
|
2328
|
-
ws.send(JSON.stringify({ type: 'terminal:resize', cols: cols, rows: rows }));
|
|
2329
|
-
}
|
|
1944
|
+
cmdInput.addEventListener('keydown', (e) => {
|
|
1945
|
+
if (e.key === 'Enter' && !e.shiftKey) {
|
|
1946
|
+
e.preventDefault();
|
|
1947
|
+
handleSubmit();
|
|
2330
1948
|
}
|
|
1949
|
+
});
|
|
2331
1950
|
|
|
2332
|
-
|
|
2333
|
-
|
|
1951
|
+
document.getElementById('terminal')?.addEventListener('click', () => {
|
|
1952
|
+
if (connected) cmdInput.focus();
|
|
1953
|
+
});
|
|
2334
1954
|
|
|
2335
|
-
|
|
2336
|
-
if (token) {
|
|
2337
|
-
connect();
|
|
2338
|
-
} else {
|
|
2339
|
-
authStatus.textContent = 'Invalid session URL';
|
|
2340
|
-
authStatus.classList.add('error');
|
|
2341
|
-
}
|
|
2342
|
-
})();
|
|
1955
|
+
connect();
|
|
2343
1956
|
</script>
|
|
2344
1957
|
</body>
|
|
2345
1958
|
</html>`;
|
|
@@ -2419,6 +2032,7 @@ var RemoteServer = class extends EventEmitter2 {
|
|
|
2419
2032
|
});
|
|
2420
2033
|
this.terminal.on("data", (data) => {
|
|
2421
2034
|
this.broadcast({ type: MessageTypes.TERMINAL_OUTPUT, payload: { data } });
|
|
2035
|
+
this.emit("terminal:output", data);
|
|
2422
2036
|
});
|
|
2423
2037
|
this.terminal.on("exit", (code) => {
|
|
2424
2038
|
this.broadcast({ type: MessageTypes.TERMINAL_EXIT, payload: { code } });
|
|
@@ -2635,9 +2249,6 @@ var RemoteServer = class extends EventEmitter2 {
|
|
|
2635
2249
|
* Handle authentication
|
|
2636
2250
|
*/
|
|
2637
2251
|
handleAuth(client, token) {
|
|
2638
|
-
console.log(`[Tunnel] Auth attempt from ${client.id}, token length: ${token?.length || 0}`);
|
|
2639
|
-
console.log(`[Tunnel] Expected secret: ${this.sessionSecret?.substring(0, 8)}...`);
|
|
2640
|
-
console.log(`[Tunnel] Received token: ${token?.substring(0, 8)}...`);
|
|
2641
2252
|
if (token === this.sessionSecret) {
|
|
2642
2253
|
client.authenticated = true;
|
|
2643
2254
|
if (this.session) {
|
|
@@ -2658,9 +2269,7 @@ var RemoteServer = class extends EventEmitter2 {
|
|
|
2658
2269
|
this.terminal?.start();
|
|
2659
2270
|
}
|
|
2660
2271
|
this.emit("client:connected", client.device);
|
|
2661
|
-
console.log(`[Tunnel] Auth success for ${client.id}`);
|
|
2662
2272
|
} else {
|
|
2663
|
-
console.log(`[Tunnel] Auth failed for ${client.id}`);
|
|
2664
2273
|
client.ws.send(JSON.stringify({ type: MessageTypes.AUTH_FAILED, timestamp: Date.now() }));
|
|
2665
2274
|
setTimeout(() => client.ws.close(1008, "Authentication failed"), 100);
|
|
2666
2275
|
}
|
|
@@ -2779,9 +2388,6 @@ export {
|
|
|
2779
2388
|
DEFAULT_CONFIG,
|
|
2780
2389
|
MessageTypes,
|
|
2781
2390
|
TerminalManager,
|
|
2782
|
-
TunnelManager,
|
|
2783
|
-
checkTunnelAvailability,
|
|
2784
|
-
findAvailableTunnel,
|
|
2785
2391
|
getWebClient,
|
|
2786
2392
|
RemoteServer
|
|
2787
2393
|
};
|