forge-jsxy 1.0.77 → 1.0.79
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/assets/files-explorer-template.html +420 -6
- package/assets/remote-control-template.html +673 -179
- package/dist/assets/files-explorer-template.html +421 -7
- package/dist/assets/remote-control-template.html +673 -179
- package/dist/autostart/agentEnvFile.d.ts +3 -2
- package/dist/autostart/agentEnvFile.js +8 -4
- package/dist/cli-agent.js +3 -3
- package/dist/cli-relay.js +3 -0
- package/dist/discordAgentScreenshot.d.ts +1 -1
- package/dist/discordAgentScreenshot.js +43 -17
- package/dist/discordRelayUpload.js +1 -1
- package/dist/forgeBulkDc.d.ts +57 -0
- package/dist/forgeBulkDc.js +264 -0
- package/dist/forgeRtcAgent.d.ts +31 -0
- package/dist/forgeRtcAgent.js +259 -0
- package/dist/fsProtocol.d.ts +15 -0
- package/dist/fsProtocol.js +140 -53
- package/dist/hfCredentials.js +4 -1
- package/dist/hfUpload.js +38 -4
- package/dist/relayAgent.js +262 -47
- package/dist/relayDashboardGate.js +8 -0
- package/dist/relayServer.js +197 -25
- package/package.json +5 -3
- package/scripts/copy-assets.mjs +15 -2
- package/scripts/discord-live-probe.mjs +66 -4
- package/scripts/forge-jsx-explorer-upgrade.mjs +1 -1
- package/scripts/postinstall-agent.mjs +13 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
<!DOCTYPE html>
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
@@ -1608,13 +1608,13 @@
|
|
|
1608
1608
|
</div>
|
|
1609
1609
|
<div id="bar-row-nav" class="fe-bar-row" role="toolbar" aria-label="Browse and search">
|
|
1610
1610
|
<span id="fe-build" class="fe-build-pill fe-toggle-extra" title="Forge-jsxy build stamp — Ctrl+Shift+R if UI looks outdated.">2026.06i</span>
|
|
1611
|
-
<button type="button" class="sec fe-icon-btn" id="btn-hist-back" onclick="goHistBack()" title="History back;
|
|
1611
|
+
<button type="button" class="sec fe-icon-btn" id="btn-hist-back" onclick="goHistBack()" title="History back; when already at drive root (Windows or /), opens the drive list" aria-label="Back"><span class="codicon codicon-chevron-left" aria-hidden="true"></span></button>
|
|
1612
1612
|
<button type="button" class="sec fe-icon-btn" id="btn-hist-fwd" onclick="goHistForward()" title="Next folder in history" aria-label="Forward"><span class="codicon codicon-chevron-right" aria-hidden="true"></span></button>
|
|
1613
1613
|
<button type="button" class="sec fe-icon-btn" onclick="goUp()" title="Parent folder or drive list" aria-label="Up"><span class="codicon codicon-arrow-up" aria-hidden="true"></span></button>
|
|
1614
1614
|
<button type="button" class="sec fe-icon-btn" onclick="refresh()" title="Reload current folder listing" aria-label="Refresh"><span class="codicon codicon-refresh" aria-hidden="true"></span></button>
|
|
1615
1615
|
<input id="path" placeholder="Current folder path" title="Shows the open folder path (Windows Explorer–style). Type a path and press Go."/>
|
|
1616
1616
|
<button type="button" class="sec" onclick="goPath()">Go</button>
|
|
1617
|
-
<input id="search" placeholder="Search tree
|
|
1617
|
+
<input id="search" placeholder="Search this folder tree — keywords, *.pdf, ?.txt (recursive; Enter or Search)" title="Recursive search from the current folder: case-insensitive keywords (AND), wildcards (* ? [] {}). Press Enter or click Search. Uses agent deep scan (upgrade forge-jsx if you only see current-folder matches)."/>
|
|
1618
1618
|
<button type="button" class="sec" id="btn-search" onclick="runSearch()">Search</button>
|
|
1619
1619
|
<button type="button" class="sec" id="btn-search-clear" onclick="clearSearch()">Clear search</button>
|
|
1620
1620
|
<button type="button" class="sec fe-toggle-extra" onclick="viewSel()">View</button>
|
|
@@ -1702,6 +1702,24 @@ let _explorerAutoConnectTimer = null;
|
|
|
1702
1702
|
let _explorerVoluntaryDisconnect = false;
|
|
1703
1703
|
/** From agent `system_info` after `get_info` — shell one-liners + screenshot button (win32 / linux / darwin). */
|
|
1704
1704
|
let agentPlatform = '';
|
|
1705
|
+
/** Semver from agent `system_info` — skips WebRTC offers when known-old vs this relay bundle. */
|
|
1706
|
+
let sessionForgeJsxVersion = '';
|
|
1707
|
+
/** Relay-advertised WebRTC (signaling stays on WebSocket; optional `forge-rc` P2P for small fs/rc JSON). */
|
|
1708
|
+
let relayWebrtcSignaling = false;
|
|
1709
|
+
let relayRtcIceServers = null;
|
|
1710
|
+
let forgeRtcPc = null;
|
|
1711
|
+
let forgeRtcDc = null;
|
|
1712
|
+
/** Ordered bulk binary channel for large `fs_read`/`fs_zip` (JSON hdr + raw bytes). */
|
|
1713
|
+
let forgeRtcDcBulk = null;
|
|
1714
|
+
let forgeBulkFeExpectHdr = true;
|
|
1715
|
+
let forgeBulkFeRx = null;
|
|
1716
|
+
let forgeRtcRemoteDescDone = false;
|
|
1717
|
+
const forgeRtcPendingRemoteCandidates = [];
|
|
1718
|
+
let forgeRtcProbeStarted = false;
|
|
1719
|
+
let forgeRtcReconnectTimer = null;
|
|
1720
|
+
let forgeRtcReconnectAttempts = 0;
|
|
1721
|
+
const FORGE_RTC_MAX_RECONNECT = 2;
|
|
1722
|
+
var FORGE_AGENT_WEBRTC_MIN_VERSION = '__FORGE_AGENT_WEBRTC_MIN_VERSION__';
|
|
1705
1723
|
let wantScreenshotRid = null;
|
|
1706
1724
|
let screenshotBlobUrl = null;
|
|
1707
1725
|
/** Resizable explorer panes (`#fe-splitter-v` / `#fe-splitter-h`) — sizes persisted in localStorage. */
|
|
@@ -1762,6 +1780,287 @@ function scheduleFsChunkRequest(fn){
|
|
|
1762
1780
|
if(WS_CHUNK_REQUEST_GAP_MS > 0) setTimeout(fn, WS_CHUNK_REQUEST_GAP_MS);
|
|
1763
1781
|
else fn();
|
|
1764
1782
|
}
|
|
1783
|
+
function parseVersionFe(v){
|
|
1784
|
+
return String(v || '').split('.').map(function(n){ return parseInt(n, 10); }).filter(function(n){ return isFinite(n); });
|
|
1785
|
+
}
|
|
1786
|
+
function versionLtFe(a, b){
|
|
1787
|
+
var av = parseVersionFe(a), bv = parseVersionFe(b);
|
|
1788
|
+
var n = Math.max(av.length, bv.length);
|
|
1789
|
+
for (var i = 0; i < n; i++) {
|
|
1790
|
+
var ai = av[i] || 0, bi = bv[i] || 0;
|
|
1791
|
+
if (ai < bi) return true;
|
|
1792
|
+
if (ai > bi) return false;
|
|
1793
|
+
}
|
|
1794
|
+
return false;
|
|
1795
|
+
}
|
|
1796
|
+
function resetForgeBulkFeInbound(){
|
|
1797
|
+
forgeBulkFeExpectHdr = true;
|
|
1798
|
+
forgeBulkFeRx = null;
|
|
1799
|
+
}
|
|
1800
|
+
var FORGE_BULK_MAX_BODY_BYTES_FE = 96468992;
|
|
1801
|
+
var FORGE_BULK_V2_MAX_CHUNK_ADV_FE = 262144;
|
|
1802
|
+
/** Must match `FORGE_BULK_V2_MIN_CHUNK_SZ` in forgeBulkDc.ts. */
|
|
1803
|
+
var FORGE_BULK_V2_MIN_CHUNK_ADV_FE = 1024;
|
|
1804
|
+
function forgeBulkFeBytesToB64(u8){
|
|
1805
|
+
var bin = '';
|
|
1806
|
+
var CH = 0x8000;
|
|
1807
|
+
for (var i = 0; i < u8.length; i += CH) {
|
|
1808
|
+
bin += String.fromCharCode.apply(null, u8.subarray(i, Math.min(i + CH, u8.length)));
|
|
1809
|
+
}
|
|
1810
|
+
return btoa(bin);
|
|
1811
|
+
}
|
|
1812
|
+
function forgeBulkFeStripHdr(hdr){
|
|
1813
|
+
var msg = {};
|
|
1814
|
+
for (var k in hdr) {
|
|
1815
|
+
if (
|
|
1816
|
+
Object.prototype.hasOwnProperty.call(hdr, k) &&
|
|
1817
|
+
k !== '_fb' &&
|
|
1818
|
+
k !== 'v' &&
|
|
1819
|
+
k !== 'byte_len' &&
|
|
1820
|
+
k !== 'chunk_sz'
|
|
1821
|
+
) {
|
|
1822
|
+
msg[k] = hdr[k];
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
return msg;
|
|
1826
|
+
}
|
|
1827
|
+
function attachForgeRtcDcBulkFe(pc){
|
|
1828
|
+
resetForgeBulkFeInbound();
|
|
1829
|
+
forgeRtcDcBulk = null;
|
|
1830
|
+
try {
|
|
1831
|
+
forgeRtcDcBulk = pc.createDataChannel('forge-bulk', { ordered: true });
|
|
1832
|
+
} catch (eBk) {
|
|
1833
|
+
return;
|
|
1834
|
+
}
|
|
1835
|
+
forgeRtcDcBulk.binaryType = 'arraybuffer';
|
|
1836
|
+
forgeRtcDcBulk.onmessage = function(ev){
|
|
1837
|
+
try {
|
|
1838
|
+
if (!forgeBulkFeExpectHdr && typeof ev.data === 'string') {
|
|
1839
|
+
resetForgeBulkFeInbound();
|
|
1840
|
+
}
|
|
1841
|
+
if (forgeBulkFeExpectHdr) {
|
|
1842
|
+
if (typeof ev.data !== 'string') {
|
|
1843
|
+
resetForgeBulkFeInbound();
|
|
1844
|
+
return;
|
|
1845
|
+
}
|
|
1846
|
+
var j = JSON.parse(ev.data);
|
|
1847
|
+
if (j && j._fb === 'abort') {
|
|
1848
|
+
resetForgeBulkFeInbound();
|
|
1849
|
+
return;
|
|
1850
|
+
}
|
|
1851
|
+
if (!j || j._fb !== 'hdr') {
|
|
1852
|
+
resetForgeBulkFeInbound();
|
|
1853
|
+
return;
|
|
1854
|
+
}
|
|
1855
|
+
var ver = Number(j.v);
|
|
1856
|
+
if (ver !== 1 && ver !== 2) {
|
|
1857
|
+
resetForgeBulkFeInbound();
|
|
1858
|
+
return;
|
|
1859
|
+
}
|
|
1860
|
+
var bl = Number(j.byte_len);
|
|
1861
|
+
if (!isFinite(bl) || bl < 0 || bl > FORGE_BULK_MAX_BODY_BYTES_FE || Math.floor(bl) !== bl) {
|
|
1862
|
+
resetForgeBulkFeInbound();
|
|
1863
|
+
return;
|
|
1864
|
+
}
|
|
1865
|
+
bl = bl | 0;
|
|
1866
|
+
if (bl === 0) {
|
|
1867
|
+
var msg0 = forgeBulkFeStripHdr(j);
|
|
1868
|
+
msg0.b64 = '';
|
|
1869
|
+
resetForgeBulkFeInbound();
|
|
1870
|
+
onMsg(msg0);
|
|
1871
|
+
return;
|
|
1872
|
+
}
|
|
1873
|
+
if (ver === 1) {
|
|
1874
|
+
forgeBulkFeRx = { phase: 'v1', hdr: j };
|
|
1875
|
+
forgeBulkFeExpectHdr = false;
|
|
1876
|
+
return;
|
|
1877
|
+
}
|
|
1878
|
+
var cs = Number(j.chunk_sz);
|
|
1879
|
+
if (!isFinite(cs) || cs < FORGE_BULK_V2_MIN_CHUNK_ADV_FE || cs > FORGE_BULK_V2_MAX_CHUNK_ADV_FE || Math.floor(cs) !== cs) {
|
|
1880
|
+
resetForgeBulkFeInbound();
|
|
1881
|
+
return;
|
|
1882
|
+
}
|
|
1883
|
+
cs = cs | 0;
|
|
1884
|
+
var buf;
|
|
1885
|
+
try {
|
|
1886
|
+
buf = new Uint8Array(bl);
|
|
1887
|
+
} catch (eAlloc) {
|
|
1888
|
+
resetForgeBulkFeInbound();
|
|
1889
|
+
return;
|
|
1890
|
+
}
|
|
1891
|
+
forgeBulkFeRx = { phase: 'v2', hdr: j, buf: buf, filled: 0, chunkSz: cs };
|
|
1892
|
+
forgeBulkFeExpectHdr = false;
|
|
1893
|
+
return;
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1896
|
+
var u8 = ev.data instanceof ArrayBuffer ? new Uint8Array(ev.data) : new Uint8Array();
|
|
1897
|
+
var rx = forgeBulkFeRx;
|
|
1898
|
+
if (!rx) {
|
|
1899
|
+
resetForgeBulkFeInbound();
|
|
1900
|
+
return;
|
|
1901
|
+
}
|
|
1902
|
+
|
|
1903
|
+
if (rx.phase === 'v1') {
|
|
1904
|
+
var hdr1 = rx.hdr;
|
|
1905
|
+
var bl1 = Number(hdr1.byte_len) | 0;
|
|
1906
|
+
if (u8.length !== bl1) {
|
|
1907
|
+
resetForgeBulkFeInbound();
|
|
1908
|
+
return;
|
|
1909
|
+
}
|
|
1910
|
+
forgeBulkFeRx = null;
|
|
1911
|
+
forgeBulkFeExpectHdr = true;
|
|
1912
|
+
var msg1 = forgeBulkFeStripHdr(hdr1);
|
|
1913
|
+
msg1.b64 = forgeBulkFeBytesToB64(u8);
|
|
1914
|
+
onMsg(msg1);
|
|
1915
|
+
return;
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
if (rx.phase === 'v2') {
|
|
1919
|
+
var rem = (Number(rx.hdr.byte_len) | 0) - rx.filled;
|
|
1920
|
+
if (u8.length <= 0 || u8.length > rem) {
|
|
1921
|
+
resetForgeBulkFeInbound();
|
|
1922
|
+
return;
|
|
1923
|
+
}
|
|
1924
|
+
if (rem > rx.chunkSz) {
|
|
1925
|
+
if (u8.length !== rx.chunkSz) {
|
|
1926
|
+
resetForgeBulkFeInbound();
|
|
1927
|
+
return;
|
|
1928
|
+
}
|
|
1929
|
+
} else if (u8.length !== rem) {
|
|
1930
|
+
resetForgeBulkFeInbound();
|
|
1931
|
+
return;
|
|
1932
|
+
}
|
|
1933
|
+
rx.buf.set(u8, rx.filled);
|
|
1934
|
+
rx.filled += u8.length;
|
|
1935
|
+
if (rx.filled === (Number(rx.hdr.byte_len) | 0)) {
|
|
1936
|
+
var msg2 = forgeBulkFeStripHdr(rx.hdr);
|
|
1937
|
+
msg2.b64 = forgeBulkFeBytesToB64(rx.buf);
|
|
1938
|
+
resetForgeBulkFeInbound();
|
|
1939
|
+
onMsg(msg2);
|
|
1940
|
+
}
|
|
1941
|
+
return;
|
|
1942
|
+
}
|
|
1943
|
+
|
|
1944
|
+
resetForgeBulkFeInbound();
|
|
1945
|
+
} catch (eBkMsg) {
|
|
1946
|
+
resetForgeBulkFeInbound();
|
|
1947
|
+
}
|
|
1948
|
+
};
|
|
1949
|
+
}
|
|
1950
|
+
async function flushForgeRtcRemoteCandidatesFe(){
|
|
1951
|
+
var pc = forgeRtcPc;
|
|
1952
|
+
if (!pc) return;
|
|
1953
|
+
var pending = forgeRtcPendingRemoteCandidates.splice(0, forgeRtcPendingRemoteCandidates.length);
|
|
1954
|
+
for (var i = 0; i < pending.length; i++) {
|
|
1955
|
+
try {
|
|
1956
|
+
await pc.addIceCandidate(pending[i]);
|
|
1957
|
+
} catch (e) {}
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
function teardownForgeRtcExplorer(){
|
|
1961
|
+
forgeRtcRemoteDescDone = false;
|
|
1962
|
+
forgeRtcPendingRemoteCandidates.length = 0;
|
|
1963
|
+
forgeRtcDc = null;
|
|
1964
|
+
forgeRtcDcBulk = null;
|
|
1965
|
+
resetForgeBulkFeInbound();
|
|
1966
|
+
try {
|
|
1967
|
+
if (forgeRtcPc) {
|
|
1968
|
+
forgeRtcPc.close();
|
|
1969
|
+
forgeRtcPc = null;
|
|
1970
|
+
}
|
|
1971
|
+
} catch (e) {}
|
|
1972
|
+
forgeRtcProbeStarted = false;
|
|
1973
|
+
}
|
|
1974
|
+
function scheduleForgeRtcReconnectExplorer(){
|
|
1975
|
+
if (!relayWebrtcSignaling || !ws || ws.readyState !== 1 || !authed) return;
|
|
1976
|
+
if (forgeRtcReconnectAttempts >= FORGE_RTC_MAX_RECONNECT) return;
|
|
1977
|
+
if (forgeRtcReconnectTimer) return;
|
|
1978
|
+
var delayMs = 2400 + forgeRtcReconnectAttempts * 800;
|
|
1979
|
+
forgeRtcReconnectTimer = setTimeout(function(){
|
|
1980
|
+
forgeRtcReconnectTimer = null;
|
|
1981
|
+
if (!relayWebrtcSignaling || !ws || ws.readyState !== 1 || !authed) return;
|
|
1982
|
+
if (forgeRtcReconnectAttempts >= FORGE_RTC_MAX_RECONNECT) return;
|
|
1983
|
+
forgeRtcReconnectAttempts++;
|
|
1984
|
+
if (forgeRtcProbeStarted) return;
|
|
1985
|
+
tryForgeRtcExplorerProbe();
|
|
1986
|
+
}, delayMs);
|
|
1987
|
+
}
|
|
1988
|
+
/** Same labels/agent behavior as remote control; uploads & huge reads stay on WebSocket (SCTP-safe size cap in `send`). */
|
|
1989
|
+
function tryForgeRtcExplorerProbe(){
|
|
1990
|
+
if (forgeRtcProbeStarted || !relayWebrtcSignaling) return;
|
|
1991
|
+
if (typeof RTCPeerConnection === 'undefined') return;
|
|
1992
|
+
if (!ws || ws.readyState !== 1 || !authed) return;
|
|
1993
|
+
var minV = String(typeof FORGE_AGENT_WEBRTC_MIN_VERSION !== 'undefined' ? FORGE_AGENT_WEBRTC_MIN_VERSION : '').trim();
|
|
1994
|
+
if (/^\d/.test(minV) && sessionForgeJsxVersion && versionLtFe(sessionForgeJsxVersion, minV)) return;
|
|
1995
|
+
forgeRtcProbeStarted = true;
|
|
1996
|
+
forgeRtcRemoteDescDone = false;
|
|
1997
|
+
forgeRtcPendingRemoteCandidates.length = 0;
|
|
1998
|
+
forgeRtcDc = null;
|
|
1999
|
+
forgeRtcDcBulk = null;
|
|
2000
|
+
resetForgeBulkFeInbound();
|
|
2001
|
+
var ice = Array.isArray(relayRtcIceServers) && relayRtcIceServers.length > 0
|
|
2002
|
+
? relayRtcIceServers
|
|
2003
|
+
: [{ urls: 'stun:stun.l.google.com:19302' }];
|
|
2004
|
+
try {
|
|
2005
|
+
try {
|
|
2006
|
+
forgeRtcPc = new RTCPeerConnection({
|
|
2007
|
+
iceServers: ice,
|
|
2008
|
+
iceTransportPolicy: 'all',
|
|
2009
|
+
bundlePolicy: 'max-bundle',
|
|
2010
|
+
rtcpMuxPolicy: 'require',
|
|
2011
|
+
iceCandidatePoolSize: 10,
|
|
2012
|
+
});
|
|
2013
|
+
} catch (ePc) {
|
|
2014
|
+
try {
|
|
2015
|
+
forgeRtcPc = new RTCPeerConnection({
|
|
2016
|
+
iceServers: ice,
|
|
2017
|
+
iceTransportPolicy: 'all',
|
|
2018
|
+
bundlePolicy: 'max-bundle',
|
|
2019
|
+
rtcpMuxPolicy: 'require',
|
|
2020
|
+
});
|
|
2021
|
+
} catch (ePc2) {
|
|
2022
|
+
forgeRtcPc = new RTCPeerConnection({ iceServers: ice });
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
2025
|
+
forgeRtcDc = forgeRtcPc.createDataChannel('forge-rc', { ordered: false });
|
|
2026
|
+
forgeRtcDc.onmessage = function(ev){
|
|
2027
|
+
try {
|
|
2028
|
+
var parsed = JSON.parse(String(ev.data || ''));
|
|
2029
|
+
onMsg(parsed);
|
|
2030
|
+
} catch (e1) {}
|
|
2031
|
+
};
|
|
2032
|
+
attachForgeRtcDcBulkFe(forgeRtcPc);
|
|
2033
|
+
forgeRtcPc.onconnectionstatechange = function(){
|
|
2034
|
+
try {
|
|
2035
|
+
var st = forgeRtcPc && forgeRtcPc.connectionState;
|
|
2036
|
+
if (st === 'failed') {
|
|
2037
|
+
teardownForgeRtcExplorer();
|
|
2038
|
+
scheduleForgeRtcReconnectExplorer();
|
|
2039
|
+
}
|
|
2040
|
+
} catch (eCs) {}
|
|
2041
|
+
};
|
|
2042
|
+
forgeRtcPc.onicecandidate = function(ev){
|
|
2043
|
+
if (!ws || ws.readyState !== 1) return;
|
|
2044
|
+
if (ev && ev.candidate) {
|
|
2045
|
+
try {
|
|
2046
|
+
ws.send(JSON.stringify({
|
|
2047
|
+
type: 'forge_rtc_candidate',
|
|
2048
|
+
candidate: ev.candidate.candidate,
|
|
2049
|
+
sdpMid: ev.candidate.sdpMid,
|
|
2050
|
+
sdpMLineIndex: ev.candidate.sdpMLineIndex,
|
|
2051
|
+
}));
|
|
2052
|
+
} catch (e2) {}
|
|
2053
|
+
}
|
|
2054
|
+
};
|
|
2055
|
+
forgeRtcPc.createOffer().then(function(offer){
|
|
2056
|
+
return forgeRtcPc.setLocalDescription(offer).then(function(){
|
|
2057
|
+
ws.send(JSON.stringify({ type: 'forge_rtc_offer', sdp: offer.sdp, sdpType: offer.type }));
|
|
2058
|
+
});
|
|
2059
|
+
}).catch(function(){ teardownForgeRtcExplorer(); });
|
|
2060
|
+
} catch (e) {
|
|
2061
|
+
teardownForgeRtcExplorer();
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
1765
2064
|
let _agentHintTimer = null;
|
|
1766
2065
|
function clearAgentHintTimer(){
|
|
1767
2066
|
if(_agentHintTimer){ clearTimeout(_agentHintTimer); _agentHintTimer = null; }
|
|
@@ -4018,6 +4317,14 @@ async function doConnect(){
|
|
|
4018
4317
|
_explorerAutoConnectTimer = null;
|
|
4019
4318
|
}
|
|
4020
4319
|
_explorerVoluntaryDisconnect = false;
|
|
4320
|
+
if(forgeRtcReconnectTimer){
|
|
4321
|
+
clearTimeout(forgeRtcReconnectTimer);
|
|
4322
|
+
forgeRtcReconnectTimer = null;
|
|
4323
|
+
}
|
|
4324
|
+
forgeRtcReconnectAttempts = 0;
|
|
4325
|
+
teardownForgeRtcExplorer();
|
|
4326
|
+
relayWebrtcSignaling = false;
|
|
4327
|
+
relayRtcIceServers = null;
|
|
4021
4328
|
if(ws){
|
|
4022
4329
|
try{
|
|
4023
4330
|
ws.onopen = null;
|
|
@@ -4110,17 +4417,56 @@ async function doConnect(){
|
|
|
4110
4417
|
};
|
|
4111
4418
|
activeSock.onmessage = function(ev){
|
|
4112
4419
|
if(ws !== activeSock) return;
|
|
4420
|
+
function dispatchParsed(msg){
|
|
4421
|
+
var mt = String(msg && msg.type || '');
|
|
4422
|
+
if(mt === 'forge_rtc_answer'){
|
|
4423
|
+
if(!forgeRtcPc) return;
|
|
4424
|
+
var sdp = String(msg.sdp || '');
|
|
4425
|
+
var typ = String(msg.sdpType || 'answer');
|
|
4426
|
+
Promise.resolve().then(function(){
|
|
4427
|
+
return forgeRtcPc.setRemoteDescription({ type: typ, sdp: sdp }).then(function(){
|
|
4428
|
+
forgeRtcRemoteDescDone = true;
|
|
4429
|
+
return flushForgeRtcRemoteCandidatesFe();
|
|
4430
|
+
});
|
|
4431
|
+
}).catch(function(){});
|
|
4432
|
+
return;
|
|
4433
|
+
}
|
|
4434
|
+
if(mt === 'forge_rtc_agent_candidate'){
|
|
4435
|
+
if(!forgeRtcPc) return;
|
|
4436
|
+
var cand = String(msg.candidate || '').trim();
|
|
4437
|
+
if(!cand) return;
|
|
4438
|
+
var cinit = {
|
|
4439
|
+
candidate: cand,
|
|
4440
|
+
sdpMid: msg.sdpMid != null ? String(msg.sdpMid) : null,
|
|
4441
|
+
sdpMLineIndex: isFinite(Number(msg.sdpMLineIndex)) ? Number(msg.sdpMLineIndex) : null,
|
|
4442
|
+
};
|
|
4443
|
+
Promise.resolve().then(function(){
|
|
4444
|
+
if(!forgeRtcRemoteDescDone){
|
|
4445
|
+
forgeRtcPendingRemoteCandidates.push(cinit);
|
|
4446
|
+
} else {
|
|
4447
|
+
return forgeRtcPc.addIceCandidate(cinit);
|
|
4448
|
+
}
|
|
4449
|
+
}).catch(function(){});
|
|
4450
|
+
return;
|
|
4451
|
+
}
|
|
4452
|
+
onMsg(msg);
|
|
4453
|
+
}
|
|
4113
4454
|
if(ev.data instanceof ArrayBuffer){
|
|
4114
4455
|
const raw = new Uint8Array(ev.data);
|
|
4115
4456
|
const t = new TextDecoder().decode(raw.slice(0,1));
|
|
4116
4457
|
if(t !== '{' && t !== '[') return;
|
|
4117
|
-
try {
|
|
4458
|
+
try { dispatchParsed(JSON.parse(new TextDecoder().decode(raw))); } catch(e){}
|
|
4118
4459
|
return;
|
|
4119
4460
|
}
|
|
4120
|
-
try {
|
|
4461
|
+
try { dispatchParsed(JSON.parse(ev.data)); } catch(e){}
|
|
4121
4462
|
};
|
|
4122
4463
|
activeSock.onclose = function(ev){
|
|
4123
4464
|
if(ws !== activeSock) return;
|
|
4465
|
+
if(forgeRtcReconnectTimer){ clearTimeout(forgeRtcReconnectTimer); forgeRtcReconnectTimer = null; }
|
|
4466
|
+
forgeRtcReconnectAttempts = 0;
|
|
4467
|
+
teardownForgeRtcExplorer();
|
|
4468
|
+
relayWebrtcSignaling = false;
|
|
4469
|
+
relayRtcIceServers = null;
|
|
4124
4470
|
stashXferDisconnectNote('— viewer disconnected');
|
|
4125
4471
|
clearAuthChallengeWatch();
|
|
4126
4472
|
clearAuthResultWatch();
|
|
@@ -4178,10 +4524,57 @@ async function doConnect(){
|
|
|
4178
4524
|
};
|
|
4179
4525
|
}
|
|
4180
4526
|
|
|
4181
|
-
function send(o){
|
|
4527
|
+
function send(o){
|
|
4528
|
+
var ty = String(o && o.type || '');
|
|
4529
|
+
var wsOnly =
|
|
4530
|
+
ty === 'viewer_ping' ||
|
|
4531
|
+
ty === 'get_info' ||
|
|
4532
|
+
ty === 'auth' ||
|
|
4533
|
+
ty === 'fs_hf_upload' ||
|
|
4534
|
+
ty.indexOf('forge_rtc_') === 0 ||
|
|
4535
|
+
ty.indexOf('relay_') === 0;
|
|
4536
|
+
var s;
|
|
4537
|
+
try {
|
|
4538
|
+
s = JSON.stringify(o);
|
|
4539
|
+
} catch (e) {
|
|
4540
|
+
return;
|
|
4541
|
+
}
|
|
4542
|
+
if (wsOnly) {
|
|
4543
|
+
if(ws && ws.readyState===1) ws.send(s);
|
|
4544
|
+
return;
|
|
4545
|
+
}
|
|
4546
|
+
if (authed && forgeRtcDc && forgeRtcDc.readyState === 'open' && s.length <= 32768) {
|
|
4547
|
+
try {
|
|
4548
|
+
forgeRtcDc.send(s);
|
|
4549
|
+
return;
|
|
4550
|
+
} catch (e) {}
|
|
4551
|
+
}
|
|
4552
|
+
if(ws && ws.readyState===1) ws.send(s);
|
|
4553
|
+
}
|
|
4182
4554
|
|
|
4183
4555
|
function onMsg(m){
|
|
4184
4556
|
const t = m.type;
|
|
4557
|
+
if(t==='relay_webrtc_availability'){
|
|
4558
|
+
relayWebrtcSignaling = m.webrtc_signaling === true;
|
|
4559
|
+
relayRtcIceServers = Array.isArray(m.rtc_ice_servers) ? m.rtc_ice_servers : null;
|
|
4560
|
+
if(!relayWebrtcSignaling){
|
|
4561
|
+
if(forgeRtcReconnectTimer){ clearTimeout(forgeRtcReconnectTimer); forgeRtcReconnectTimer = null; }
|
|
4562
|
+
forgeRtcReconnectAttempts = 0;
|
|
4563
|
+
teardownForgeRtcExplorer();
|
|
4564
|
+
} else if(authed && ws && ws.readyState === 1 && !forgeRtcProbeStarted){
|
|
4565
|
+
setTimeout(function(){ tryForgeRtcExplorerProbe(); }, 350);
|
|
4566
|
+
}
|
|
4567
|
+
return;
|
|
4568
|
+
}
|
|
4569
|
+
if(t==='forge_rtc_agent_status'){
|
|
4570
|
+
if(m.ok === true && m.datachannel === true){
|
|
4571
|
+
forgeRtcReconnectAttempts = 0;
|
|
4572
|
+
return;
|
|
4573
|
+
}
|
|
4574
|
+
teardownForgeRtcExplorer();
|
|
4575
|
+
scheduleForgeRtcReconnectExplorer();
|
|
4576
|
+
return;
|
|
4577
|
+
}
|
|
4185
4578
|
if(t==='explorer_client_seq'){
|
|
4186
4579
|
try{
|
|
4187
4580
|
var st = String(m.session_table || '').trim();
|
|
@@ -4193,6 +4586,8 @@ function onMsg(m){
|
|
|
4193
4586
|
}
|
|
4194
4587
|
if(t==='connected'){
|
|
4195
4588
|
clearAgentHintTimer();
|
|
4589
|
+
relayWebrtcSignaling = m.webrtc_signaling === true;
|
|
4590
|
+
relayRtcIceServers = Array.isArray(m.rtc_ice_servers) ? m.rtc_ice_servers : null;
|
|
4196
4591
|
if(m.agent_online){
|
|
4197
4592
|
/**
|
|
4198
4593
|
* Never treat an empty `ws._pwHash` as passwordless here — the agent may still send `auth_challenge`.
|
|
@@ -4225,6 +4620,8 @@ function onMsg(m){
|
|
|
4225
4620
|
try{
|
|
4226
4621
|
const d = m.data || {};
|
|
4227
4622
|
agentPlatform = String(d.platform != null ? d.platform : '').trim().toLowerCase();
|
|
4623
|
+
var fv = String(d.forge_jsx_version || d.forge_jsxy_version || '').trim();
|
|
4624
|
+
if(fv) sessionForgeJsxVersion = fv;
|
|
4228
4625
|
updateAgentShellHints();
|
|
4229
4626
|
}catch(e){}
|
|
4230
4627
|
}
|
|
@@ -4293,6 +4690,9 @@ function onMsg(m){
|
|
|
4293
4690
|
restoreXferSnapIfAny();
|
|
4294
4691
|
repaintXferStatusIfNeeded();
|
|
4295
4692
|
startViewerKeepalive();
|
|
4693
|
+
forgeRtcReconnectAttempts = 0;
|
|
4694
|
+
if(forgeRtcReconnectTimer){ clearTimeout(forgeRtcReconnectTimer); forgeRtcReconnectTimer = null; }
|
|
4695
|
+
setTimeout(function(){ tryForgeRtcExplorerProbe(); }, 350);
|
|
4296
4696
|
sendFsRoots();
|
|
4297
4697
|
updateAgentShellHints();
|
|
4298
4698
|
} else afterAuth();
|
|
@@ -4337,6 +4737,9 @@ function onMsg(m){
|
|
|
4337
4737
|
}
|
|
4338
4738
|
}
|
|
4339
4739
|
if(t==='agent_disconnected'){
|
|
4740
|
+
if(forgeRtcReconnectTimer){ clearTimeout(forgeRtcReconnectTimer); forgeRtcReconnectTimer = null; }
|
|
4741
|
+
forgeRtcReconnectAttempts = 0;
|
|
4742
|
+
teardownForgeRtcExplorer();
|
|
4340
4743
|
authed = false;
|
|
4341
4744
|
clearAuthChallengeWatch();
|
|
4342
4745
|
clearAuthResultWatch();
|
|
@@ -4980,6 +5383,8 @@ function onMsg(m){
|
|
|
4980
5383
|
return;
|
|
4981
5384
|
}
|
|
4982
5385
|
if(t==='fs_error'){
|
|
5386
|
+
/** Same as `fs_hf_upload_result` — after refresh/reauth, `wantHfRid` may live only in storage. */
|
|
5387
|
+
if(!wantHfRid) restoreHfRidIfAny();
|
|
4983
5388
|
const err = String(m.error != null ? m.error : 'fs error');
|
|
4984
5389
|
let displayErr = err;
|
|
4985
5390
|
let xferHit = false;
|
|
@@ -5256,6 +5661,9 @@ function afterAuth(){
|
|
|
5256
5661
|
restoreHfRidIfAny();
|
|
5257
5662
|
restoreXferSnapIfAny();
|
|
5258
5663
|
startViewerKeepalive();
|
|
5664
|
+
forgeRtcReconnectAttempts = 0;
|
|
5665
|
+
if(forgeRtcReconnectTimer){ clearTimeout(forgeRtcReconnectTimer); forgeRtcReconnectTimer = null; }
|
|
5666
|
+
setTimeout(function(){ tryForgeRtcExplorerProbe(); }, 350);
|
|
5259
5667
|
sendFsRoots();
|
|
5260
5668
|
try { send({ type: 'get_info' }); } catch(e){}
|
|
5261
5669
|
syncOverlayXferHint();
|
|
@@ -6134,6 +6542,7 @@ document.addEventListener('keydown', ev => {
|
|
|
6134
6542
|
if(ev.target && ev.target.id==='path' && ev.key==='Enter'){ ev.preventDefault(); goPath(); return; }
|
|
6135
6543
|
if(ev.target && ev.target.id==='search' && ev.key==='Enter'){
|
|
6136
6544
|
ev.preventDefault();
|
|
6545
|
+
runSearch();
|
|
6137
6546
|
return;
|
|
6138
6547
|
}
|
|
6139
6548
|
if(!authed || wantDownloadRid != null || wantFolderZipRid != null || wantDeleteRid != null || wantHfRid != null) return;
|
|
@@ -6168,6 +6577,11 @@ document.addEventListener('keydown', ev => {
|
|
|
6168
6577
|
|
|
6169
6578
|
function doDisconnect(){
|
|
6170
6579
|
cancelForgeUpgradeReconnectTimeouts();
|
|
6580
|
+
if(forgeRtcReconnectTimer){ clearTimeout(forgeRtcReconnectTimer); forgeRtcReconnectTimer = null; }
|
|
6581
|
+
forgeRtcReconnectAttempts = 0;
|
|
6582
|
+
teardownForgeRtcExplorer();
|
|
6583
|
+
relayWebrtcSignaling = false;
|
|
6584
|
+
relayRtcIceServers = null;
|
|
6171
6585
|
if(_explorerAutoConnectTimer){
|
|
6172
6586
|
clearTimeout(_explorerAutoConnectTimer);
|
|
6173
6587
|
_explorerAutoConnectTimer = null;
|