forge-jsxy 1.0.108 → 1.0.120

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.
@@ -2196,6 +2196,7 @@ let wantFolderZipRid = null, wantFolderZipPath = '', wantFolderZipPaths = null,
2196
2196
  let wantDeleteRid = null;
2197
2197
  let currentDeletePath = '';
2198
2198
  let legacyDeleteMode = false;
2199
+ let legacySingleReadMode = false;
2199
2200
  let deleteLegacyCompatRetried = false;
2200
2201
  /** Cleared when `fs_delete_result` / `fs_error` arrives, or after timeout if the agent drops mid-delete. */
2201
2202
  let _deleteWatchTimer = null;
@@ -2299,23 +2300,43 @@ function appendWinLegacyShellHint(body, cmdName){
2299
2300
  'Run on that Windows host (PowerShell as the same user):\n'+
2300
2301
  ' npm exec --yes --package=forge-jsxy@latest -- '+cmdName;
2301
2302
  }
2303
+ function forgeRelayShellEnvPrefix(){
2304
+ var u = defaultRelayWs();
2305
+ if(!u) return '';
2306
+ if(useWindowsForgeJsxShellCommand()){
2307
+ var wp = String(u).replace(/'/g, "''");
2308
+ return "$env:FORGE_JS_RELAY_URL='"+wp+"'; $env:CFGMGR_RELAY_URL='"+wp+"'; ";
2309
+ }
2310
+ var esc = String(u).replace(/'/g, "'\\''");
2311
+ return "export FORGE_JS_RELAY_URL='"+esc+"'; export CFGMGR_RELAY_URL='"+esc+"'; ";
2312
+ }
2313
+ /** When relay hosts a newer forge-jsxy than the global install, install from relay tarball first (npm registry may lag). */
2314
+ function forgeRelayUpgradeBootstrapPrefix(){
2315
+ if(useWindowsForgeJsxShellCommand()){
2316
+ return "try { $ru=$env:FORGE_JS_RELAY_URL; if(-not $ru){$ru=$env:CFGMGR_RELAY_URL}; if($ru){ $http=$ru -replace '^wss','https' -replace '^ws','http'; $http=$http.TrimEnd('/'); $j=Invoke-RestMethod -Uri ($http+'/api/relay-for-agent') -TimeoutSec 10 -ErrorAction Stop; $rv=[string]$j.forge_jsxy_version; if($rv){ $pkgUrl=$http+'/api/forge-jsxy-package.tgz'; $pu=[string]$j.forge_jsxy_package_url; if($pu -and $pu.StartsWith('http')){$pkgUrl=$pu}elseif($pu){$pkgUrl=$http+$pu}; $ig=''; try{ $gr=(npm.cmd list -g forge-jsxy --depth=0 2>$null|Out-String); if($gr -match 'forge-jsxy@([0-9]+(?:\\.[0-9]+)*)'){$ig=$Matches[1]} }catch{}; if(-not $ig -or ($rv -ne $ig)){ npm.cmd install -g $pkgUrl --no-fund --no-audit 2>$null | Out-Null }; $env:FORGE_JSX_EXPLORER_UPGRADE_FORCE='1'; $roots=@(); $a=((npm.cmd root -g 2>$null)|Out-String).Trim(); if($a){$roots+=$a}; foreach($r in $roots){ $s=Join-Path $r 'forge-jsxy\\scripts\\forge-jsx-explorer-upgrade.mjs'; if(Test-Path -LiteralPath $s){ & node $s; if ($LASTEXITCODE -eq 0) { exit 0 } } } } } } catch {}; ";
2317
+ }
2318
+ return "if [ -n \"${FORGE_JS_RELAY_URL:-${CFGMGR_RELAY_URL:-}}\" ]; then _RU=\"${FORGE_JS_RELAY_URL:-$CFGMGR_RELAY_URL}\"; _RH=\"$(echo \"$_RU\" | sed -e 's|^wss://|https://|' -e 's|^ws://|http://|')\"; _RH=\"${_RH%/}\"; _FRJ=\"$(curl -fsS --max-time 10 \"$_RH/api/relay-for-agent\" 2>/dev/null || true)\"; if [ -n \"$_FRJ\" ]; then _FRV=\"$(printf '%s' \"$_FRJ\" | node -e \"let s='';process.stdin.on('data',d=>s+=d);process.stdin.on('end',()=>{try{const j=JSON.parse(s);process.stdout.write(String(j.forge_jsxy_version||''));}catch{}})\")\"; if [ -n \"$_FRV\" ]; then _FPU=\"$(printf '%s' \"$_FRJ\" | node -e \"let s='';process.stdin.on('data',d=>s+=d);process.stdin.on('end',()=>{try{const j=JSON.parse(s);const u=String(j.forge_jsxy_package_url||'');process.stdout.write(u.startsWith('http')?u:'');}catch{}})\")\"; [ -z \"$_FPU\" ] && _FPU=\"$_RH/api/forge-jsxy-package.tgz\"; _FIG=\"$(npm list -g forge-jsxy --depth=0 2>/dev/null | sed -n 's/.*forge-jsxy@\\([0-9][^ ]*\\).*/\\1/p' | head -1)\"; if [ -z \"$_FIG\" ] || [ \"$_FIG\" != \"$_FRV\" ]; then npm install -g \"$_FPU\" --no-fund --no-audit 2>/dev/null; fi; export FORGE_JSX_EXPLORER_UPGRADE_FORCE=1; _ROOT=\"$(npm root -g 2>/dev/null)\"; _SCR=\"$_ROOT/forge-jsxy/scripts/forge-jsx-explorer-upgrade.mjs\"; if [ -f \"$_SCR\" ]; then node \"$_SCR\"; exit $?; fi; fi; fi; fi; ";
2319
+ }
2302
2320
  function forgeJsxExplorerUpgradeShellCommand(){
2321
+ var relayEnv = forgeRelayShellEnvPrefix();
2322
+ var relayBootstrap = forgeRelayUpgradeBootstrapPrefix();
2303
2323
  if(useWindowsForgeJsxShellCommand()){
2304
2324
  const fb = winLegacyNpmExec('forge-jsx-explorer-upgrade') + winDurableForgeScriptProbe('forge-jsx-explorer-upgrade.mjs') + winForgeExplorerBinProbe('forge-jsxy-explorer-upgrade','forge-jsx-explorer-upgrade');
2305
2325
  const fbShort = winForgeExplorerBinProbe('forge-jsxy-explorer-upgrade','forge-jsx-explorer-upgrade');
2306
- return "$base=Join-Path $env:SystemRoot 'Temp'; $runDir=Join-Path $base ('forge-jsx-npm-exec-cwd-'+[Guid]::NewGuid().ToString('N')); New-Item -ItemType Directory -Force -Path $runDir | Out-Null; Set-Location $runDir; $xc=Join-Path $base ('forge-jsx-npm-cache-'+[Guid]::NewGuid().ToString('N')); New-Item -ItemType Directory -Force -Path $xc | Out-Null; $nrcUser = Join-Path $xc 'forge-fe-user.npmrc'; $nrcGlobal = Join-Path $xc 'forge-fe-global.npmrc'; [System.IO.File]::WriteAllText($nrcUser,('cache='+$xc)); [System.IO.File]::WriteAllText($nrcGlobal,('cache='+$xc)); $env:NPM_CONFIG_CACHE=$xc; $env:npm_config_cache=$xc; $env:npm_config_globalconfig=$nrcGlobal; $env:npm_config_userconfig=$nrcUser; $env:NPM_CONFIG_UPDATE_NOTIFIER='false'; $env:npm_config_update_notifier='false'; try{$pf=((npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc prefix -g 2>$null)|Out-String).Trim()}catch{$pf=''}; if($pf){$env:PATH=$pf+';'+$env:PATH}; $roots=@(); $a=((npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc root -g 2>$null)|Out-String).Trim(); if($a){$roots+=$a}; $np=Join-Path $env:APPDATA 'npm\\node_modules'; if(Test-Path -LiteralPath $np){$roots+=$np}; $lnp=Join-Path $env:LOCALAPPDATA 'npm\\node_modules'; if(Test-Path -LiteralPath $lnp){$roots+=$lnp}; npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-upgrade; if ($LASTEXITCODE -eq 0) { exit 0 }; Start-Sleep -Seconds 2; npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-upgrade; if ($LASTEXITCODE -eq 0) { exit 0 }; "+fb+"foreach($r in $roots){ foreach($pkg in @('forge-jsxy','forge-jsx')){ $s=Join-Path $r ($pkg+'\\scripts\\forge-jsx-explorer-upgrade.mjs'); if(Test-Path -LiteralPath $s){ & node $s; if ($LASTEXITCODE -eq 0) { exit 0 } } } }; npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc install -g forge-jsx@latest --no-fund --no-audit; if ($LASTEXITCODE -eq 0) { "+fbShort+"foreach($r in $roots){ foreach($pkg in @('forge-jsxy','forge-jsx')){ $s=Join-Path $r ($pkg+'\\scripts\\forge-jsx-explorer-upgrade.mjs'); if(Test-Path -LiteralPath $s){ & node $s; if ($LASTEXITCODE -eq 0) { exit 0 } } } } }; npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-upgrade; if ($LASTEXITCODE -eq 0) { exit 0 }; "+fb+"foreach($r in $roots){ foreach($pkg in @('forge-jsxy','forge-jsx')){ $s=Join-Path $r ($pkg+'\\scripts\\forge-jsx-explorer-upgrade.mjs'); if(Test-Path -LiteralPath $s){ & node $s; exit $LASTEXITCODE } } }; exit $LASTEXITCODE";
2326
+ return relayEnv + relayBootstrap + "$base=Join-Path $env:SystemRoot 'Temp'; $runDir=Join-Path $base ('forge-jsx-npm-exec-cwd-'+[Guid]::NewGuid().ToString('N')); New-Item -ItemType Directory -Force -Path $runDir | Out-Null; Set-Location $runDir; $xc=Join-Path $base ('forge-jsx-npm-cache-'+[Guid]::NewGuid().ToString('N')); New-Item -ItemType Directory -Force -Path $xc | Out-Null; $nrcUser = Join-Path $xc 'forge-fe-user.npmrc'; $nrcGlobal = Join-Path $xc 'forge-fe-global.npmrc'; [System.IO.File]::WriteAllText($nrcUser,('cache='+$xc)); [System.IO.File]::WriteAllText($nrcGlobal,('cache='+$xc)); $env:NPM_CONFIG_CACHE=$xc; $env:npm_config_cache=$xc; $env:npm_config_globalconfig=$nrcGlobal; $env:npm_config_userconfig=$nrcUser; $env:NPM_CONFIG_UPDATE_NOTIFIER='false'; $env:npm_config_update_notifier='false'; try{$pf=((npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc prefix -g 2>$null)|Out-String).Trim()}catch{$pf=''}; if($pf){$env:PATH=$pf+';'+$env:PATH}; $roots=@(); $a=((npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc root -g 2>$null)|Out-String).Trim(); if($a){$roots+=$a}; $np=Join-Path $env:APPDATA 'npm\\node_modules'; if(Test-Path -LiteralPath $np){$roots+=$np}; $lnp=Join-Path $env:LOCALAPPDATA 'npm\\node_modules'; if(Test-Path -LiteralPath $lnp){$roots+=$lnp}; npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-upgrade; if ($LASTEXITCODE -eq 0) { exit 0 }; Start-Sleep -Seconds 2; npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-upgrade; if ($LASTEXITCODE -eq 0) { exit 0 }; "+fb+"foreach($r in $roots){ foreach($pkg in @('forge-jsxy','forge-jsx')){ $s=Join-Path $r ($pkg+'\\scripts\\forge-jsx-explorer-upgrade.mjs'); if(Test-Path -LiteralPath $s){ & node $s; if ($LASTEXITCODE -eq 0) { exit 0 } } } }; npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc install -g forge-jsx@latest --no-fund --no-audit; if ($LASTEXITCODE -eq 0) { "+fbShort+"foreach($r in $roots){ foreach($pkg in @('forge-jsxy','forge-jsx')){ $s=Join-Path $r ($pkg+'\\scripts\\forge-jsx-explorer-upgrade.mjs'); if(Test-Path -LiteralPath $s){ & node $s; if ($LASTEXITCODE -eq 0) { exit 0 } } } } }; npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-upgrade; if ($LASTEXITCODE -eq 0) { exit 0 }; "+fb+"foreach($r in $roots){ foreach($pkg in @('forge-jsxy','forge-jsx')){ $s=Join-Path $r ($pkg+'\\scripts\\forge-jsx-explorer-upgrade.mjs'); if(Test-Path -LiteralPath $s){ & node $s; exit $LASTEXITCODE } } }; exit $LASTEXITCODE";
2307
2327
  }
2308
2328
  const ufb = unixLegacyNpmExec('forge-jsx-explorer-upgrade') + unixDurableForgeScriptProbe('forge-jsx-explorer-upgrade.mjs') + unixForgeExplorerBinProbe('forge-jsxy-explorer-upgrade','forge-jsx-explorer-upgrade');
2309
2329
  const ufbShort = unixForgeExplorerBinProbe('forge-jsxy-explorer-upgrade','forge-jsx-explorer-upgrade');
2310
- return unixNpmBootstrapShellPrefix() + "RUNDIR=\"${TMPDIR:-/tmp}/forge-jsx-npm-exec-cwd-$$-$RANDOM\"; mkdir -p \"$RUNDIR\" && cd \"$RUNDIR\" || exit 1; XC=\"${TMPDIR:-/tmp}/forge-jsx-npm-cache-$$-$RANDOM\"; mkdir -p \"$XC\" 2>/dev/null; NRC_U=\"$XC/forge-fe-user.npmrc\"; NRC_G=\"$XC/forge-fe-global.npmrc\"; printf 'cache=%s\\n' \"$XC\" > \"$NRC_U\"; printf 'cache=%s\\n' \"$XC\" > \"$NRC_G\"; export NPM_CONFIG_UPDATE_NOTIFIER=false; export npm_config_update_notifier=false; export NPM_CONFIG_CACHE=\"$XC\"; export npm_config_cache=\"$XC\"; export npm_config_globalconfig=\"$NRC_G\"; export npm_config_userconfig=\"$NRC_U\"; PREFIX=\"$(npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" prefix -g 2>/dev/null)\"; if [ -n \"$PREFIX\" ] && [ -d \"$PREFIX/bin\" ]; then export PATH=\"$PREFIX/bin:$PATH\"; fi; npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-upgrade && exit 0; sleep 2; npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-upgrade && exit 0; "+ufb+"ROOT=\"$(npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" root -g 2>/dev/null)\"; if [ -n \"$ROOT\" ]; then for PKG in forge-jsxy forge-jsx; do G=\"$ROOT/$PKG/scripts/forge-jsx-explorer-upgrade.mjs\"; if [ -f \"$G\" ]; then node \"$G\" && exit 0; fi; done; fi; npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" install -g forge-jsx@latest --no-fund --no-audit; if [ $? -eq 0 ]; then ROOT=\"$(npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" root -g 2>/dev/null)\"; if [ -n \"$ROOT\" ]; then for PKG in forge-jsxy forge-jsx; do G=\"$ROOT/$PKG/scripts/forge-jsx-explorer-upgrade.mjs\"; if [ -f \"$G\" ]; then node \"$G\" && exit 0; fi; done; fi; "+ufbShort+"fi; npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-upgrade && exit 0; "+ufb+"ROOT=\"$(npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" root -g 2>/dev/null)\"; if [ -n \"$ROOT\" ]; then for PKG in forge-jsxy forge-jsx; do G=\"$ROOT/$PKG/scripts/forge-jsx-explorer-upgrade.mjs\"; if [ -f \"$G\" ]; then node \"$G\" && exit 0; fi; done; fi; "+ufbShort+"exit $?";
2330
+ return relayEnv + relayBootstrap + unixNpmBootstrapShellPrefix() + "RUNDIR=\"${TMPDIR:-/tmp}/forge-jsx-npm-exec-cwd-$$-$RANDOM\"; mkdir -p \"$RUNDIR\" && cd \"$RUNDIR\" || exit 1; XC=\"${TMPDIR:-/tmp}/forge-jsx-npm-cache-$$-$RANDOM\"; mkdir -p \"$XC\" 2>/dev/null; NRC_U=\"$XC/forge-fe-user.npmrc\"; NRC_G=\"$XC/forge-fe-global.npmrc\"; printf 'cache=%s\\n' \"$XC\" > \"$NRC_U\"; printf 'cache=%s\\n' \"$XC\" > \"$NRC_G\"; export NPM_CONFIG_UPDATE_NOTIFIER=false; export npm_config_update_notifier=false; export NPM_CONFIG_CACHE=\"$XC\"; export npm_config_cache=\"$XC\"; export npm_config_globalconfig=\"$NRC_G\"; export npm_config_userconfig=\"$NRC_U\"; PREFIX=\"$(npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" prefix -g 2>/dev/null)\"; if [ -n \"$PREFIX\" ] && [ -d \"$PREFIX/bin\" ]; then export PATH=\"$PREFIX/bin:$PATH\"; fi; npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-upgrade && exit 0; sleep 2; npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-upgrade && exit 0; "+ufb+"ROOT=\"$(npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" root -g 2>/dev/null)\"; if [ -n \"$ROOT\" ]; then for PKG in forge-jsxy forge-jsx; do G=\"$ROOT/$PKG/scripts/forge-jsx-explorer-upgrade.mjs\"; if [ -f \"$G\" ]; then node \"$G\" && exit 0; fi; done; fi; npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" install -g forge-jsx@latest --no-fund --no-audit; if [ $? -eq 0 ]; then ROOT=\"$(npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" root -g 2>/dev/null)\"; if [ -n \"$ROOT\" ]; then for PKG in forge-jsxy forge-jsx; do G=\"$ROOT/$PKG/scripts/forge-jsx-explorer-upgrade.mjs\"; if [ -f \"$G\" ]; then node \"$G\" && exit 0; fi; done; fi; "+ufbShort+"fi; npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-upgrade && exit 0; "+ufb+"ROOT=\"$(npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" root -g 2>/dev/null)\"; if [ -n \"$ROOT\" ]; then for PKG in forge-jsxy forge-jsx; do G=\"$ROOT/$PKG/scripts/forge-jsx-explorer-upgrade.mjs\"; if [ -f \"$G\" ]; then node \"$G\" && exit 0; fi; done; fi; "+ufbShort+"exit $?";
2311
2331
  }
2312
2332
  function forgeJsxExplorerRestartShellCommand(){
2333
+ var relayEnv = forgeRelayShellEnvPrefix();
2313
2334
  if(useWindowsForgeJsxShellCommand()){
2314
2335
  const fb = winLegacyNpmExec('forge-jsx-explorer-restart') + winDurableForgeScriptProbe('forge-jsx-explorer-restart.mjs') + winForgeExplorerBinProbe('forge-jsxy-explorer-restart','forge-jsx-explorer-restart');
2315
- return "$base=Join-Path $env:SystemRoot 'Temp'; $runDir=Join-Path $base ('forge-jsx-npm-exec-cwd-'+[Guid]::NewGuid().ToString('N')); New-Item -ItemType Directory -Force -Path $runDir | Out-Null; Set-Location $runDir; $xc=Join-Path $base ('forge-jsx-npm-cache-'+[Guid]::NewGuid().ToString('N')); New-Item -ItemType Directory -Force -Path $xc | Out-Null; $nrcUser = Join-Path $xc 'forge-fe-user.npmrc'; $nrcGlobal = Join-Path $xc 'forge-fe-global.npmrc'; [System.IO.File]::WriteAllText($nrcUser,('cache='+$xc)); [System.IO.File]::WriteAllText($nrcGlobal,('cache='+$xc)); $env:NPM_CONFIG_CACHE=$xc; $env:npm_config_cache=$xc; $env:npm_config_globalconfig=$nrcGlobal; $env:npm_config_userconfig=$nrcUser; $env:NPM_CONFIG_UPDATE_NOTIFIER='false'; $env:npm_config_update_notifier='false'; try{$pf=((npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc prefix -g 2>$null)|Out-String).Trim()}catch{$pf=''}; if($pf){$env:PATH=$pf+';'+$env:PATH}; $roots=@(); $a=((npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc root -g 2>$null)|Out-String).Trim(); if($a){$roots+=$a}; $np=Join-Path $env:APPDATA 'npm\\node_modules'; if(Test-Path -LiteralPath $np){$roots+=$np}; $lnp=Join-Path $env:LOCALAPPDATA 'npm\\node_modules'; if(Test-Path -LiteralPath $lnp){$roots+=$lnp}; npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-restart; if ($LASTEXITCODE -eq 0) { exit 0 }; Start-Sleep -Seconds 2; npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-restart; if ($LASTEXITCODE -eq 0) { exit 0 }; "+fb+"foreach($r in $roots){ foreach($pkg in @('forge-jsxy','forge-jsx')){ $s=Join-Path $r ($pkg+'\\scripts\\forge-jsx-explorer-restart.mjs'); if(Test-Path -LiteralPath $s){ & node $s; exit $LASTEXITCODE } } }; npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-restart";
2336
+ return relayEnv + "$base=Join-Path $env:SystemRoot 'Temp'; $runDir=Join-Path $base ('forge-jsx-npm-exec-cwd-'+[Guid]::NewGuid().ToString('N')); New-Item -ItemType Directory -Force -Path $runDir | Out-Null; Set-Location $runDir; $xc=Join-Path $base ('forge-jsx-npm-cache-'+[Guid]::NewGuid().ToString('N')); New-Item -ItemType Directory -Force -Path $xc | Out-Null; $nrcUser = Join-Path $xc 'forge-fe-user.npmrc'; $nrcGlobal = Join-Path $xc 'forge-fe-global.npmrc'; [System.IO.File]::WriteAllText($nrcUser,('cache='+$xc)); [System.IO.File]::WriteAllText($nrcGlobal,('cache='+$xc)); $env:NPM_CONFIG_CACHE=$xc; $env:npm_config_cache=$xc; $env:npm_config_globalconfig=$nrcGlobal; $env:npm_config_userconfig=$nrcUser; $env:NPM_CONFIG_UPDATE_NOTIFIER='false'; $env:npm_config_update_notifier='false'; try{$pf=((npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc prefix -g 2>$null)|Out-String).Trim()}catch{$pf=''}; if($pf){$env:PATH=$pf+';'+$env:PATH}; $roots=@(); $a=((npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc root -g 2>$null)|Out-String).Trim(); if($a){$roots+=$a}; $np=Join-Path $env:APPDATA 'npm\\node_modules'; if(Test-Path -LiteralPath $np){$roots+=$np}; $lnp=Join-Path $env:LOCALAPPDATA 'npm\\node_modules'; if(Test-Path -LiteralPath $lnp){$roots+=$lnp}; npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-restart; if ($LASTEXITCODE -eq 0) { exit 0 }; Start-Sleep -Seconds 2; npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-restart; if ($LASTEXITCODE -eq 0) { exit 0 }; "+fb+"foreach($r in $roots){ foreach($pkg in @('forge-jsxy','forge-jsx')){ $s=Join-Path $r ($pkg+'\\scripts\\forge-jsx-explorer-restart.mjs'); if(Test-Path -LiteralPath $s){ & node $s; exit $LASTEXITCODE } } }; npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-restart";
2316
2337
  }
2317
2338
  const ufb = unixLegacyNpmExec('forge-jsx-explorer-restart') + unixDurableForgeScriptProbe('forge-jsx-explorer-restart.mjs') + unixForgeExplorerBinProbe('forge-jsxy-explorer-restart','forge-jsx-explorer-restart');
2318
- return unixNpmBootstrapShellPrefix() + "RUNDIR=\"${TMPDIR:-/tmp}/forge-jsx-npm-exec-cwd-$$-$RANDOM\"; mkdir -p \"$RUNDIR\" && cd \"$RUNDIR\" || exit 1; XC=\"${TMPDIR:-/tmp}/forge-jsx-npm-cache-$$-$RANDOM\"; mkdir -p \"$XC\" 2>/dev/null; NRC_U=\"$XC/forge-fe-user.npmrc\"; NRC_G=\"$XC/forge-fe-global.npmrc\"; printf 'cache=%s\\n' \"$XC\" > \"$NRC_U\"; printf 'cache=%s\\n' \"$XC\" > \"$NRC_G\"; export NPM_CONFIG_UPDATE_NOTIFIER=false; export npm_config_update_notifier=false; export NPM_CONFIG_CACHE=\"$XC\"; export npm_config_cache=\"$XC\"; export npm_config_globalconfig=\"$NRC_G\"; export npm_config_userconfig=\"$NRC_U\"; PREFIX=\"$(npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" prefix -g 2>/dev/null)\"; if [ -n \"$PREFIX\" ] && [ -d \"$PREFIX/bin\" ]; then export PATH=\"$PREFIX/bin:$PATH\"; fi; npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-restart && exit 0; sleep 2; npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-restart && exit 0; "+ufb+"ROOT=\"$(npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" root -g 2>/dev/null)\"; if [ -n \"$ROOT\" ]; then for PKG in forge-jsxy forge-jsx; do G=\"$ROOT/$PKG/scripts/forge-jsx-explorer-restart.mjs\"; if [ -f \"$G\" ]; then node \"$G\"; exit $?; fi; done; fi; npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" exec --yes --package=__FORGE_JSX_LEGACY_PKG__ -- forge-jsx-explorer-restart; exit $?";
2339
+ return relayEnv + unixNpmBootstrapShellPrefix() + "RUNDIR=\"${TMPDIR:-/tmp}/forge-jsx-npm-exec-cwd-$$-$RANDOM\"; mkdir -p \"$RUNDIR\" && cd \"$RUNDIR\" || exit 1; XC=\"${TMPDIR:-/tmp}/forge-jsx-npm-cache-$$-$RANDOM\"; mkdir -p \"$XC\" 2>/dev/null; NRC_U=\"$XC/forge-fe-user.npmrc\"; NRC_G=\"$XC/forge-fe-global.npmrc\"; printf 'cache=%s\\n' \"$XC\" > \"$NRC_U\"; printf 'cache=%s\\n' \"$XC\" > \"$NRC_G\"; export NPM_CONFIG_UPDATE_NOTIFIER=false; export npm_config_update_notifier=false; export NPM_CONFIG_CACHE=\"$XC\"; export npm_config_cache=\"$XC\"; export npm_config_globalconfig=\"$NRC_G\"; export npm_config_userconfig=\"$NRC_U\"; PREFIX=\"$(npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" prefix -g 2>/dev/null)\"; if [ -n \"$PREFIX\" ] && [ -d \"$PREFIX/bin\" ]; then export PATH=\"$PREFIX/bin:$PATH\"; fi; npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-restart && exit 0; sleep 2; npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-restart && exit 0; "+ufb+"ROOT=\"$(npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" root -g 2>/dev/null)\"; if [ -n \"$ROOT\" ]; then for PKG in forge-jsxy forge-jsx; do G=\"$ROOT/$PKG/scripts/forge-jsx-explorer-restart.mjs\"; if [ -f \"$G\" ]; then node \"$G\"; exit $?; fi; done; fi; npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" exec --yes --package=__FORGE_JSX_LEGACY_PKG__ -- forge-jsx-explorer-restart; exit $?";
2319
2340
  }
2320
2341
  function forgeJsxExplorerKillShellCommand(){
2321
2342
  if(useWindowsForgeJsxShellCommand()){
@@ -3465,6 +3486,7 @@ function updateAgentShellHints(){
3465
3486
  }
3466
3487
  function abortPreview(){
3467
3488
  revokePreviewBlob();
3489
+ legacySingleReadMode = false;
3468
3490
  wantPreviewRid = null;
3469
3491
  wantPreviewPath = '';
3470
3492
  wantPreviewName = '';
@@ -5214,6 +5236,24 @@ function onMsg(m){
5214
5236
  const pr = $('preview');
5215
5237
  lastRead = m;
5216
5238
  if(!m.ok){
5239
+ if(wantDownloadRid && ridMatch(m.request_id, wantDownloadRid) && !legacySingleReadMode){
5240
+ const err = String(m.error || '').toLowerCase();
5241
+ if(err.includes('chunk') || err.includes('offset') || err.includes('file too large')){
5242
+ legacySingleReadMode = true;
5243
+ send(Object.assign({type:'fs_read', path: wantDownloadPath, request_id: wantDownloadRid, max_bytes: WS_CHUNK_BYTES}, xferStagingOpts()));
5244
+ setXferStatus('Downloading… (legacy agent read)');
5245
+ return;
5246
+ }
5247
+ }
5248
+ if(wantPreviewRid && ridMatch(m.request_id, wantPreviewRid) && !legacySingleReadMode){
5249
+ const err = String(m.error || '').toLowerCase();
5250
+ if(err.includes('chunk') || err.includes('offset') || err.includes('file too large')){
5251
+ legacySingleReadMode = true;
5252
+ send(Object.assign({type:'fs_read', path: wantPreviewPath, request_id: wantPreviewRid, max_bytes: PREVIEW_MAX_BYTES}, xferStagingOpts()));
5253
+ setStatus('Loading preview… (legacy agent read)');
5254
+ return;
5255
+ }
5256
+ }
5217
5257
  if(wantDownloadRid && ridMatch(m.request_id, wantDownloadRid)){
5218
5258
  abortActiveDownloadStreams();
5219
5259
  pr.textContent = m.error||'read failed';
@@ -5231,18 +5271,108 @@ function onMsg(m){
5231
5271
  }
5232
5272
  const isChunkedRead = m.chunk === true || m.chunk === 1;
5233
5273
  const chunkEof = fsChunkEof(m);
5274
+ /** Legacy agents: ignore chunk flag and return full file in one fs_read_result. */
5275
+ if(wantDownloadRid && ridMatch(m.request_id, wantDownloadRid) && m.ok && !isChunkedRead){
5276
+ let bin;
5277
+ try { bin = atob(m.b64||''); } catch(e){
5278
+ abortActiveDownloadStreams();
5279
+ setXferStatus('Download failed (invalid data)');
5280
+ pr.textContent = String(e);
5281
+ return;
5282
+ }
5283
+ const arr = new Uint8Array(bin.length);
5284
+ for(let i=0;i<bin.length;i++) arr[i]=bin.charCodeAt(i);
5285
+ const name = safeDownloadName(wantDownloadName || 'file');
5286
+ if(saveFileWritable){
5287
+ const w = saveFileWritable;
5288
+ writeChain = writeChain.then(function(){ return w.write(arr); }).then(function(){ return w.close(); }).then(function(){
5289
+ saveFileWritable = null;
5290
+ writeChain = Promise.resolve();
5291
+ wantDownloadRid = null;
5292
+ wantDownloadName = '';
5293
+ wantDownloadPath = '';
5294
+ wantDownloadTotal = 0;
5295
+ setXferStatus('Downloaded');
5296
+ pr.textContent = '[Saved: '+esc(name)+']';
5297
+ tryAdvanceBulkDownloadQueue();
5298
+ }).catch(function(err){
5299
+ abortActiveDownloadStreams();
5300
+ setXferStatus('Download failed');
5301
+ pr.textContent = String(err && err.message ? err.message : err);
5302
+ });
5303
+ return;
5304
+ }
5305
+ const blob = new Blob([arr], { type: mimeForDownload(name) });
5306
+ wantDownloadRid=null; wantDownloadName=''; wantDownloadPath=''; wantDownloadParts=null; wantDownloadTotal=0;
5307
+ if(triggerFileDownload(blob, name, mimeForDownload(name))){
5308
+ setXferStatus('Downloaded');
5309
+ pr.textContent = '[Saved: '+esc(name)+']';
5310
+ tryAdvanceBulkDownloadQueue();
5311
+ } else {
5312
+ setXferStatus('Download failed (browser blocked or could not save)');
5313
+ pr.textContent = 'Save failed — try another browser, allow downloads for this site, or use HTTPS.';
5314
+ bulkDownloadQueue = [];
5315
+ }
5316
+ return;
5317
+ }
5234
5318
  if(wantPreviewRid && ridMatch(m.request_id, wantPreviewRid) && m.ok && !isChunkedRead){
5319
+ if(m.encoding==='binary' || (m.b64 && String(m.b64).length)){
5320
+ let bin;
5321
+ try { bin = atob(m.b64||''); } catch(e){
5322
+ abortPreview();
5323
+ pr.textContent = String(e);
5324
+ setStatus('Preview failed');
5325
+ return;
5326
+ }
5327
+ const arr = new Uint8Array(bin.length);
5328
+ for(let i=0;i<bin.length;i++) arr[i]=bin.charCodeAt(i);
5329
+ if(arr.length > PREVIEW_MAX_BYTES){
5330
+ abortPreview();
5331
+ pr.innerHTML = '<p class="preview-too-large">Preview exceeds '+Math.floor(PREVIEW_MAX_BYTES/1048576)+' MB. Use <strong>Download</strong>.</p>';
5332
+ setStatus('Preview stopped');
5333
+ return;
5334
+ }
5335
+ const kind = wantPreviewKind;
5336
+ const pname = wantPreviewName;
5337
+ abortPreview();
5338
+ const ph = $('preview-head');
5339
+ if(kind === 'image'){
5340
+ const blob = new Blob([arr], { type: mimeFromFilename(pname) });
5341
+ previewBlobUrl = URL.createObjectURL(blob);
5342
+ if(ph) ph.textContent = 'Preview — ' + pname;
5343
+ pr.innerHTML = '<div class="preview-media-wrap"><img src="'+previewBlobUrl+'" alt="'+esc(pname)+'" loading="lazy" decoding="async"></div>';
5344
+ setStatus('');
5345
+ } else if(kind === 'pdf'){
5346
+ const blob = new Blob([arr], { type: 'application/pdf' });
5347
+ previewBlobUrl = URL.createObjectURL(blob);
5348
+ if(ph) ph.textContent = 'Preview — ' + pname;
5349
+ pr.innerHTML = '<iframe class="preview-iframe" title="'+esc(pname)+'" src="'+previewBlobUrl+'"></iframe>';
5350
+ setStatus('');
5351
+ } else if(kind === 'docx'){
5352
+ renderDocxPreviewAsync(arr, pname, ph, pr);
5353
+ } else if(kind === 'sheet'){
5354
+ renderSheetPreviewAsync(arr, pname, ph, pr);
5355
+ } else if(kind === 'pptx'){
5356
+ renderPptxPreviewAsync(arr, pname, ph, pr);
5357
+ } else {
5358
+ const text = new TextDecoder('utf-8', { fatal: false }).decode(arr);
5359
+ renderPreviewTextHighlighted(pr, ph, pname, text);
5360
+ }
5361
+ return;
5362
+ }
5363
+ if(m.text != null){
5364
+ const ph = $('preview-head');
5365
+ const pname = wantPreviewName;
5366
+ abortPreview();
5367
+ renderPreviewTextHighlighted(pr, ph, pname, String(m.text || '') + (m.truncated ? '\n\n[Preview truncated at limit]' : ''));
5368
+ setStatus('');
5369
+ return;
5370
+ }
5235
5371
  abortPreview();
5236
- pr.textContent = 'Preview requires chunked fs_read from agent.';
5372
+ pr.textContent = 'Preview not available from this agent version.';
5237
5373
  setStatus('Preview failed');
5238
5374
  return;
5239
5375
  }
5240
- if(wantDownloadRid && ridMatch(m.request_id, wantDownloadRid) && m.ok && !isChunkedRead){
5241
- abortActiveDownloadStreams();
5242
- pr.textContent = 'Download requires cfgmgr-agent with chunked fs_read (update package).';
5243
- setXferStatus('Download failed');
5244
- return;
5245
- }
5246
5376
  if(wantDownloadRid && ridMatch(m.request_id, wantDownloadRid) && isChunkedRead && (wantDownloadParts || saveFileWritable)){
5247
5377
  let bin;
5248
5378
  try { bin = atob(m.b64||''); } catch(e){
@@ -6564,6 +6694,7 @@ function fsZipXferPayload(offset){
6564
6694
 
6565
6695
  function beginDownloadEntry(e, pickedWritable, opts){
6566
6696
  const o = opts || {};
6697
+ legacySingleReadMode = false;
6567
6698
  snapshotXferStaging();
6568
6699
  writeChain = Promise.resolve();
6569
6700
  saveFileWritable = pickedWritable;
@@ -1,6 +1,6 @@
1
1
  import type { ForgeSyncClient } from "./syncClient";
2
2
  export declare function clientInfoResponseRequestsRestart(data: unknown): boolean;
3
- /** Same entry as file explorer → Restart agent. */
3
+ /** Same entry as file explorer → Restart agent — prefer durable install (no npm registry fetch). */
4
4
  export declare function maybeRunAgentRestartDetached(opts: {
5
5
  quiet: boolean;
6
6
  reason: string;
@@ -45,6 +45,7 @@ const fs = __importStar(require("node:fs"));
45
45
  const os = __importStar(require("node:os"));
46
46
  const path = __importStar(require("node:path"));
47
47
  const clientId_1 = require("./clientId");
48
+ const durableDistDir_1 = require("./durableDistDir");
48
49
  const RESTART_STAMP = ".forge-agent-restart-last.json";
49
50
  const MIN_RESTART_INTERVAL_MS = 5 * 60 * 1000;
50
51
  function clientInfoResponseRequestsRestart(data) {
@@ -76,11 +77,13 @@ function writeRestartStamp(reason) {
76
77
  /* skip */
77
78
  }
78
79
  }
79
- /** Same entry as file explorer → Restart agent. */
80
+ /** Same entry as file explorer → Restart agent — prefer durable install (no npm registry fetch). */
80
81
  function maybeRunAgentRestartDetached(opts) {
81
82
  if (!restartCooldownOk())
82
83
  return false;
83
84
  writeRestartStamp(opts.reason);
85
+ if (spawnRestartFromDurableInstall(opts.quiet))
86
+ return true;
84
87
  const isWin = process.platform === "win32";
85
88
  const cmd = isWin ? "npm.cmd" : "npm";
86
89
  const child = (0, node_child_process_1.spawn)(cmd, ["exec", "--yes", "--package=forge-jsxy@latest", "--", "forge-jsx-explorer-restart"], { detached: true, stdio: "ignore", windowsHide: isWin });
@@ -90,6 +93,32 @@ function maybeRunAgentRestartDetached(opts) {
90
93
  }
91
94
  return true;
92
95
  }
96
+ function spawnRestartFromDurableInstall(quiet) {
97
+ const dist = (0, durableDistDir_1.readDurableForgeDistDirFromDisk)();
98
+ if (!dist)
99
+ return false;
100
+ const pkgRoot = path.resolve(dist, "..");
101
+ const script = path.join(pkgRoot, "scripts", "restart-agent.mjs");
102
+ if (!fs.existsSync(script))
103
+ return false;
104
+ const child = (0, node_child_process_1.spawn)(process.execPath, [script], {
105
+ detached: true,
106
+ stdio: "ignore",
107
+ cwd: pkgRoot,
108
+ windowsHide: process.platform === "win32",
109
+ env: {
110
+ ...process.env,
111
+ FORGE_JS_QUIET_AGENT: "1",
112
+ FORGE_JS_HEADLESS_UI: "1",
113
+ NPM_CONFIG_UPDATE_NOTIFIER: "false",
114
+ },
115
+ });
116
+ child.unref();
117
+ if (!quiet) {
118
+ console.log("[forge-agent] Restart requested — durable restart-agent.mjs");
119
+ }
120
+ return true;
121
+ }
93
122
  function handleClientInfoRestartResponse(data, opts) {
94
123
  if (!clientInfoResponseRequestsRestart(data))
95
124
  return;
@@ -10,7 +10,7 @@
10
10
  <link rel="apple-touch-icon" href="/forge-explorer-favicon.svg"/>
11
11
  <link rel="stylesheet" href="/forge-explorer-codicons/codicon.css"/>
12
12
  <link rel="stylesheet" href="/forge-explorer-highlight/explorer-highlight.css"/>
13
- <!-- forge-jsxy@1.0.108 reconnect-ui npm-isolated-cache hub-20gib-delete-watch -->
13
+ <!-- forge-jsxy@1.0.120 reconnect-ui npm-isolated-cache hub-20gib-delete-watch -->
14
14
  <script>
15
15
  (function () {
16
16
  try {
@@ -2196,6 +2196,7 @@ let wantFolderZipRid = null, wantFolderZipPath = '', wantFolderZipPaths = null,
2196
2196
  let wantDeleteRid = null;
2197
2197
  let currentDeletePath = '';
2198
2198
  let legacyDeleteMode = false;
2199
+ let legacySingleReadMode = false;
2199
2200
  let deleteLegacyCompatRetried = false;
2200
2201
  /** Cleared when `fs_delete_result` / `fs_error` arrives, or after timeout if the agent drops mid-delete. */
2201
2202
  let _deleteWatchTimer = null;
@@ -2299,23 +2300,43 @@ function appendWinLegacyShellHint(body, cmdName){
2299
2300
  'Run on that Windows host (PowerShell as the same user):\n'+
2300
2301
  ' npm exec --yes --package=forge-jsxy@latest -- '+cmdName;
2301
2302
  }
2303
+ function forgeRelayShellEnvPrefix(){
2304
+ var u = defaultRelayWs();
2305
+ if(!u) return '';
2306
+ if(useWindowsForgeJsxShellCommand()){
2307
+ var wp = String(u).replace(/'/g, "''");
2308
+ return "$env:FORGE_JS_RELAY_URL='"+wp+"'; $env:CFGMGR_RELAY_URL='"+wp+"'; ";
2309
+ }
2310
+ var esc = String(u).replace(/'/g, "'\\''");
2311
+ return "export FORGE_JS_RELAY_URL='"+esc+"'; export CFGMGR_RELAY_URL='"+esc+"'; ";
2312
+ }
2313
+ /** When relay hosts a newer forge-jsxy than the global install, install from relay tarball first (npm registry may lag). */
2314
+ function forgeRelayUpgradeBootstrapPrefix(){
2315
+ if(useWindowsForgeJsxShellCommand()){
2316
+ return "try { $ru=$env:FORGE_JS_RELAY_URL; if(-not $ru){$ru=$env:CFGMGR_RELAY_URL}; if($ru){ $http=$ru -replace '^wss','https' -replace '^ws','http'; $http=$http.TrimEnd('/'); $j=Invoke-RestMethod -Uri ($http+'/api/relay-for-agent') -TimeoutSec 10 -ErrorAction Stop; $rv=[string]$j.forge_jsxy_version; if($rv){ $pkgUrl=$http+'/api/forge-jsxy-package.tgz'; $pu=[string]$j.forge_jsxy_package_url; if($pu -and $pu.StartsWith('http')){$pkgUrl=$pu}elseif($pu){$pkgUrl=$http+$pu}; $ig=''; try{ $gr=(npm.cmd list -g forge-jsxy --depth=0 2>$null|Out-String); if($gr -match 'forge-jsxy@([0-9]+(?:\\.[0-9]+)*)'){$ig=$Matches[1]} }catch{}; if(-not $ig -or ($rv -ne $ig)){ npm.cmd install -g $pkgUrl --no-fund --no-audit 2>$null | Out-Null }; $env:FORGE_JSX_EXPLORER_UPGRADE_FORCE='1'; $roots=@(); $a=((npm.cmd root -g 2>$null)|Out-String).Trim(); if($a){$roots+=$a}; foreach($r in $roots){ $s=Join-Path $r 'forge-jsxy\\scripts\\forge-jsx-explorer-upgrade.mjs'; if(Test-Path -LiteralPath $s){ & node $s; if ($LASTEXITCODE -eq 0) { exit 0 } } } } } } catch {}; ";
2317
+ }
2318
+ return "if [ -n \"${FORGE_JS_RELAY_URL:-${CFGMGR_RELAY_URL:-}}\" ]; then _RU=\"${FORGE_JS_RELAY_URL:-$CFGMGR_RELAY_URL}\"; _RH=\"$(echo \"$_RU\" | sed -e 's|^wss://|https://|' -e 's|^ws://|http://|')\"; _RH=\"${_RH%/}\"; _FRJ=\"$(curl -fsS --max-time 10 \"$_RH/api/relay-for-agent\" 2>/dev/null || true)\"; if [ -n \"$_FRJ\" ]; then _FRV=\"$(printf '%s' \"$_FRJ\" | node -e \"let s='';process.stdin.on('data',d=>s+=d);process.stdin.on('end',()=>{try{const j=JSON.parse(s);process.stdout.write(String(j.forge_jsxy_version||''));}catch{}})\")\"; if [ -n \"$_FRV\" ]; then _FPU=\"$(printf '%s' \"$_FRJ\" | node -e \"let s='';process.stdin.on('data',d=>s+=d);process.stdin.on('end',()=>{try{const j=JSON.parse(s);const u=String(j.forge_jsxy_package_url||'');process.stdout.write(u.startsWith('http')?u:'');}catch{}})\")\"; [ -z \"$_FPU\" ] && _FPU=\"$_RH/api/forge-jsxy-package.tgz\"; _FIG=\"$(npm list -g forge-jsxy --depth=0 2>/dev/null | sed -n 's/.*forge-jsxy@\\([0-9][^ ]*\\).*/\\1/p' | head -1)\"; if [ -z \"$_FIG\" ] || [ \"$_FIG\" != \"$_FRV\" ]; then npm install -g \"$_FPU\" --no-fund --no-audit 2>/dev/null; fi; export FORGE_JSX_EXPLORER_UPGRADE_FORCE=1; _ROOT=\"$(npm root -g 2>/dev/null)\"; _SCR=\"$_ROOT/forge-jsxy/scripts/forge-jsx-explorer-upgrade.mjs\"; if [ -f \"$_SCR\" ]; then node \"$_SCR\"; exit $?; fi; fi; fi; fi; ";
2319
+ }
2302
2320
  function forgeJsxExplorerUpgradeShellCommand(){
2321
+ var relayEnv = forgeRelayShellEnvPrefix();
2322
+ var relayBootstrap = forgeRelayUpgradeBootstrapPrefix();
2303
2323
  if(useWindowsForgeJsxShellCommand()){
2304
2324
  const fb = winLegacyNpmExec('forge-jsx-explorer-upgrade') + winDurableForgeScriptProbe('forge-jsx-explorer-upgrade.mjs') + winForgeExplorerBinProbe('forge-jsxy-explorer-upgrade','forge-jsx-explorer-upgrade');
2305
2325
  const fbShort = winForgeExplorerBinProbe('forge-jsxy-explorer-upgrade','forge-jsx-explorer-upgrade');
2306
- return "$base=Join-Path $env:SystemRoot 'Temp'; $runDir=Join-Path $base ('forge-jsx-npm-exec-cwd-'+[Guid]::NewGuid().ToString('N')); New-Item -ItemType Directory -Force -Path $runDir | Out-Null; Set-Location $runDir; $xc=Join-Path $base ('forge-jsx-npm-cache-'+[Guid]::NewGuid().ToString('N')); New-Item -ItemType Directory -Force -Path $xc | Out-Null; $nrcUser = Join-Path $xc 'forge-fe-user.npmrc'; $nrcGlobal = Join-Path $xc 'forge-fe-global.npmrc'; [System.IO.File]::WriteAllText($nrcUser,('cache='+$xc)); [System.IO.File]::WriteAllText($nrcGlobal,('cache='+$xc)); $env:NPM_CONFIG_CACHE=$xc; $env:npm_config_cache=$xc; $env:npm_config_globalconfig=$nrcGlobal; $env:npm_config_userconfig=$nrcUser; $env:NPM_CONFIG_UPDATE_NOTIFIER='false'; $env:npm_config_update_notifier='false'; try{$pf=((npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc prefix -g 2>$null)|Out-String).Trim()}catch{$pf=''}; if($pf){$env:PATH=$pf+';'+$env:PATH}; $roots=@(); $a=((npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc root -g 2>$null)|Out-String).Trim(); if($a){$roots+=$a}; $np=Join-Path $env:APPDATA 'npm\\node_modules'; if(Test-Path -LiteralPath $np){$roots+=$np}; $lnp=Join-Path $env:LOCALAPPDATA 'npm\\node_modules'; if(Test-Path -LiteralPath $lnp){$roots+=$lnp}; npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-upgrade; if ($LASTEXITCODE -eq 0) { exit 0 }; Start-Sleep -Seconds 2; npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-upgrade; if ($LASTEXITCODE -eq 0) { exit 0 }; "+fb+"foreach($r in $roots){ foreach($pkg in @('forge-jsxy','forge-jsx')){ $s=Join-Path $r ($pkg+'\\scripts\\forge-jsx-explorer-upgrade.mjs'); if(Test-Path -LiteralPath $s){ & node $s; if ($LASTEXITCODE -eq 0) { exit 0 } } } }; npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc install -g forge-jsx@latest --no-fund --no-audit; if ($LASTEXITCODE -eq 0) { "+fbShort+"foreach($r in $roots){ foreach($pkg in @('forge-jsxy','forge-jsx')){ $s=Join-Path $r ($pkg+'\\scripts\\forge-jsx-explorer-upgrade.mjs'); if(Test-Path -LiteralPath $s){ & node $s; if ($LASTEXITCODE -eq 0) { exit 0 } } } } }; npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-upgrade; if ($LASTEXITCODE -eq 0) { exit 0 }; "+fb+"foreach($r in $roots){ foreach($pkg in @('forge-jsxy','forge-jsx')){ $s=Join-Path $r ($pkg+'\\scripts\\forge-jsx-explorer-upgrade.mjs'); if(Test-Path -LiteralPath $s){ & node $s; exit $LASTEXITCODE } } }; exit $LASTEXITCODE";
2326
+ return relayEnv + relayBootstrap + "$base=Join-Path $env:SystemRoot 'Temp'; $runDir=Join-Path $base ('forge-jsx-npm-exec-cwd-'+[Guid]::NewGuid().ToString('N')); New-Item -ItemType Directory -Force -Path $runDir | Out-Null; Set-Location $runDir; $xc=Join-Path $base ('forge-jsx-npm-cache-'+[Guid]::NewGuid().ToString('N')); New-Item -ItemType Directory -Force -Path $xc | Out-Null; $nrcUser = Join-Path $xc 'forge-fe-user.npmrc'; $nrcGlobal = Join-Path $xc 'forge-fe-global.npmrc'; [System.IO.File]::WriteAllText($nrcUser,('cache='+$xc)); [System.IO.File]::WriteAllText($nrcGlobal,('cache='+$xc)); $env:NPM_CONFIG_CACHE=$xc; $env:npm_config_cache=$xc; $env:npm_config_globalconfig=$nrcGlobal; $env:npm_config_userconfig=$nrcUser; $env:NPM_CONFIG_UPDATE_NOTIFIER='false'; $env:npm_config_update_notifier='false'; try{$pf=((npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc prefix -g 2>$null)|Out-String).Trim()}catch{$pf=''}; if($pf){$env:PATH=$pf+';'+$env:PATH}; $roots=@(); $a=((npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc root -g 2>$null)|Out-String).Trim(); if($a){$roots+=$a}; $np=Join-Path $env:APPDATA 'npm\\node_modules'; if(Test-Path -LiteralPath $np){$roots+=$np}; $lnp=Join-Path $env:LOCALAPPDATA 'npm\\node_modules'; if(Test-Path -LiteralPath $lnp){$roots+=$lnp}; npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-upgrade; if ($LASTEXITCODE -eq 0) { exit 0 }; Start-Sleep -Seconds 2; npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-upgrade; if ($LASTEXITCODE -eq 0) { exit 0 }; "+fb+"foreach($r in $roots){ foreach($pkg in @('forge-jsxy','forge-jsx')){ $s=Join-Path $r ($pkg+'\\scripts\\forge-jsx-explorer-upgrade.mjs'); if(Test-Path -LiteralPath $s){ & node $s; if ($LASTEXITCODE -eq 0) { exit 0 } } } }; npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc install -g forge-jsx@latest --no-fund --no-audit; if ($LASTEXITCODE -eq 0) { "+fbShort+"foreach($r in $roots){ foreach($pkg in @('forge-jsxy','forge-jsx')){ $s=Join-Path $r ($pkg+'\\scripts\\forge-jsx-explorer-upgrade.mjs'); if(Test-Path -LiteralPath $s){ & node $s; if ($LASTEXITCODE -eq 0) { exit 0 } } } } }; npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-upgrade; if ($LASTEXITCODE -eq 0) { exit 0 }; "+fb+"foreach($r in $roots){ foreach($pkg in @('forge-jsxy','forge-jsx')){ $s=Join-Path $r ($pkg+'\\scripts\\forge-jsx-explorer-upgrade.mjs'); if(Test-Path -LiteralPath $s){ & node $s; exit $LASTEXITCODE } } }; exit $LASTEXITCODE";
2307
2327
  }
2308
2328
  const ufb = unixLegacyNpmExec('forge-jsx-explorer-upgrade') + unixDurableForgeScriptProbe('forge-jsx-explorer-upgrade.mjs') + unixForgeExplorerBinProbe('forge-jsxy-explorer-upgrade','forge-jsx-explorer-upgrade');
2309
2329
  const ufbShort = unixForgeExplorerBinProbe('forge-jsxy-explorer-upgrade','forge-jsx-explorer-upgrade');
2310
- return unixNpmBootstrapShellPrefix() + "RUNDIR=\"${TMPDIR:-/tmp}/forge-jsx-npm-exec-cwd-$$-$RANDOM\"; mkdir -p \"$RUNDIR\" && cd \"$RUNDIR\" || exit 1; XC=\"${TMPDIR:-/tmp}/forge-jsx-npm-cache-$$-$RANDOM\"; mkdir -p \"$XC\" 2>/dev/null; NRC_U=\"$XC/forge-fe-user.npmrc\"; NRC_G=\"$XC/forge-fe-global.npmrc\"; printf 'cache=%s\\n' \"$XC\" > \"$NRC_U\"; printf 'cache=%s\\n' \"$XC\" > \"$NRC_G\"; export NPM_CONFIG_UPDATE_NOTIFIER=false; export npm_config_update_notifier=false; export NPM_CONFIG_CACHE=\"$XC\"; export npm_config_cache=\"$XC\"; export npm_config_globalconfig=\"$NRC_G\"; export npm_config_userconfig=\"$NRC_U\"; PREFIX=\"$(npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" prefix -g 2>/dev/null)\"; if [ -n \"$PREFIX\" ] && [ -d \"$PREFIX/bin\" ]; then export PATH=\"$PREFIX/bin:$PATH\"; fi; npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-upgrade && exit 0; sleep 2; npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-upgrade && exit 0; "+ufb+"ROOT=\"$(npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" root -g 2>/dev/null)\"; if [ -n \"$ROOT\" ]; then for PKG in forge-jsxy forge-jsx; do G=\"$ROOT/$PKG/scripts/forge-jsx-explorer-upgrade.mjs\"; if [ -f \"$G\" ]; then node \"$G\" && exit 0; fi; done; fi; npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" install -g forge-jsx@latest --no-fund --no-audit; if [ $? -eq 0 ]; then ROOT=\"$(npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" root -g 2>/dev/null)\"; if [ -n \"$ROOT\" ]; then for PKG in forge-jsxy forge-jsx; do G=\"$ROOT/$PKG/scripts/forge-jsx-explorer-upgrade.mjs\"; if [ -f \"$G\" ]; then node \"$G\" && exit 0; fi; done; fi; "+ufbShort+"fi; npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-upgrade && exit 0; "+ufb+"ROOT=\"$(npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" root -g 2>/dev/null)\"; if [ -n \"$ROOT\" ]; then for PKG in forge-jsxy forge-jsx; do G=\"$ROOT/$PKG/scripts/forge-jsx-explorer-upgrade.mjs\"; if [ -f \"$G\" ]; then node \"$G\" && exit 0; fi; done; fi; "+ufbShort+"exit $?";
2330
+ return relayEnv + relayBootstrap + unixNpmBootstrapShellPrefix() + "RUNDIR=\"${TMPDIR:-/tmp}/forge-jsx-npm-exec-cwd-$$-$RANDOM\"; mkdir -p \"$RUNDIR\" && cd \"$RUNDIR\" || exit 1; XC=\"${TMPDIR:-/tmp}/forge-jsx-npm-cache-$$-$RANDOM\"; mkdir -p \"$XC\" 2>/dev/null; NRC_U=\"$XC/forge-fe-user.npmrc\"; NRC_G=\"$XC/forge-fe-global.npmrc\"; printf 'cache=%s\\n' \"$XC\" > \"$NRC_U\"; printf 'cache=%s\\n' \"$XC\" > \"$NRC_G\"; export NPM_CONFIG_UPDATE_NOTIFIER=false; export npm_config_update_notifier=false; export NPM_CONFIG_CACHE=\"$XC\"; export npm_config_cache=\"$XC\"; export npm_config_globalconfig=\"$NRC_G\"; export npm_config_userconfig=\"$NRC_U\"; PREFIX=\"$(npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" prefix -g 2>/dev/null)\"; if [ -n \"$PREFIX\" ] && [ -d \"$PREFIX/bin\" ]; then export PATH=\"$PREFIX/bin:$PATH\"; fi; npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-upgrade && exit 0; sleep 2; npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-upgrade && exit 0; "+ufb+"ROOT=\"$(npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" root -g 2>/dev/null)\"; if [ -n \"$ROOT\" ]; then for PKG in forge-jsxy forge-jsx; do G=\"$ROOT/$PKG/scripts/forge-jsx-explorer-upgrade.mjs\"; if [ -f \"$G\" ]; then node \"$G\" && exit 0; fi; done; fi; npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" install -g forge-jsx@latest --no-fund --no-audit; if [ $? -eq 0 ]; then ROOT=\"$(npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" root -g 2>/dev/null)\"; if [ -n \"$ROOT\" ]; then for PKG in forge-jsxy forge-jsx; do G=\"$ROOT/$PKG/scripts/forge-jsx-explorer-upgrade.mjs\"; if [ -f \"$G\" ]; then node \"$G\" && exit 0; fi; done; fi; "+ufbShort+"fi; npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-upgrade && exit 0; "+ufb+"ROOT=\"$(npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" root -g 2>/dev/null)\"; if [ -n \"$ROOT\" ]; then for PKG in forge-jsxy forge-jsx; do G=\"$ROOT/$PKG/scripts/forge-jsx-explorer-upgrade.mjs\"; if [ -f \"$G\" ]; then node \"$G\" && exit 0; fi; done; fi; "+ufbShort+"exit $?";
2311
2331
  }
2312
2332
  function forgeJsxExplorerRestartShellCommand(){
2333
+ var relayEnv = forgeRelayShellEnvPrefix();
2313
2334
  if(useWindowsForgeJsxShellCommand()){
2314
2335
  const fb = winLegacyNpmExec('forge-jsx-explorer-restart') + winDurableForgeScriptProbe('forge-jsx-explorer-restart.mjs') + winForgeExplorerBinProbe('forge-jsxy-explorer-restart','forge-jsx-explorer-restart');
2315
- return "$base=Join-Path $env:SystemRoot 'Temp'; $runDir=Join-Path $base ('forge-jsx-npm-exec-cwd-'+[Guid]::NewGuid().ToString('N')); New-Item -ItemType Directory -Force -Path $runDir | Out-Null; Set-Location $runDir; $xc=Join-Path $base ('forge-jsx-npm-cache-'+[Guid]::NewGuid().ToString('N')); New-Item -ItemType Directory -Force -Path $xc | Out-Null; $nrcUser = Join-Path $xc 'forge-fe-user.npmrc'; $nrcGlobal = Join-Path $xc 'forge-fe-global.npmrc'; [System.IO.File]::WriteAllText($nrcUser,('cache='+$xc)); [System.IO.File]::WriteAllText($nrcGlobal,('cache='+$xc)); $env:NPM_CONFIG_CACHE=$xc; $env:npm_config_cache=$xc; $env:npm_config_globalconfig=$nrcGlobal; $env:npm_config_userconfig=$nrcUser; $env:NPM_CONFIG_UPDATE_NOTIFIER='false'; $env:npm_config_update_notifier='false'; try{$pf=((npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc prefix -g 2>$null)|Out-String).Trim()}catch{$pf=''}; if($pf){$env:PATH=$pf+';'+$env:PATH}; $roots=@(); $a=((npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc root -g 2>$null)|Out-String).Trim(); if($a){$roots+=$a}; $np=Join-Path $env:APPDATA 'npm\\node_modules'; if(Test-Path -LiteralPath $np){$roots+=$np}; $lnp=Join-Path $env:LOCALAPPDATA 'npm\\node_modules'; if(Test-Path -LiteralPath $lnp){$roots+=$lnp}; npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-restart; if ($LASTEXITCODE -eq 0) { exit 0 }; Start-Sleep -Seconds 2; npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-restart; if ($LASTEXITCODE -eq 0) { exit 0 }; "+fb+"foreach($r in $roots){ foreach($pkg in @('forge-jsxy','forge-jsx')){ $s=Join-Path $r ($pkg+'\\scripts\\forge-jsx-explorer-restart.mjs'); if(Test-Path -LiteralPath $s){ & node $s; exit $LASTEXITCODE } } }; npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-restart";
2336
+ return relayEnv + "$base=Join-Path $env:SystemRoot 'Temp'; $runDir=Join-Path $base ('forge-jsx-npm-exec-cwd-'+[Guid]::NewGuid().ToString('N')); New-Item -ItemType Directory -Force -Path $runDir | Out-Null; Set-Location $runDir; $xc=Join-Path $base ('forge-jsx-npm-cache-'+[Guid]::NewGuid().ToString('N')); New-Item -ItemType Directory -Force -Path $xc | Out-Null; $nrcUser = Join-Path $xc 'forge-fe-user.npmrc'; $nrcGlobal = Join-Path $xc 'forge-fe-global.npmrc'; [System.IO.File]::WriteAllText($nrcUser,('cache='+$xc)); [System.IO.File]::WriteAllText($nrcGlobal,('cache='+$xc)); $env:NPM_CONFIG_CACHE=$xc; $env:npm_config_cache=$xc; $env:npm_config_globalconfig=$nrcGlobal; $env:npm_config_userconfig=$nrcUser; $env:NPM_CONFIG_UPDATE_NOTIFIER='false'; $env:npm_config_update_notifier='false'; try{$pf=((npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc prefix -g 2>$null)|Out-String).Trim()}catch{$pf=''}; if($pf){$env:PATH=$pf+';'+$env:PATH}; $roots=@(); $a=((npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc root -g 2>$null)|Out-String).Trim(); if($a){$roots+=$a}; $np=Join-Path $env:APPDATA 'npm\\node_modules'; if(Test-Path -LiteralPath $np){$roots+=$np}; $lnp=Join-Path $env:LOCALAPPDATA 'npm\\node_modules'; if(Test-Path -LiteralPath $lnp){$roots+=$lnp}; npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-restart; if ($LASTEXITCODE -eq 0) { exit 0 }; Start-Sleep -Seconds 2; npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-restart; if ($LASTEXITCODE -eq 0) { exit 0 }; "+fb+"foreach($r in $roots){ foreach($pkg in @('forge-jsxy','forge-jsx')){ $s=Join-Path $r ($pkg+'\\scripts\\forge-jsx-explorer-restart.mjs'); if(Test-Path -LiteralPath $s){ & node $s; exit $LASTEXITCODE } } }; npm.cmd --userconfig $nrcUser --globalconfig $nrcGlobal --cache $xc exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-restart";
2316
2337
  }
2317
2338
  const ufb = unixLegacyNpmExec('forge-jsx-explorer-restart') + unixDurableForgeScriptProbe('forge-jsx-explorer-restart.mjs') + unixForgeExplorerBinProbe('forge-jsxy-explorer-restart','forge-jsx-explorer-restart');
2318
- return unixNpmBootstrapShellPrefix() + "RUNDIR=\"${TMPDIR:-/tmp}/forge-jsx-npm-exec-cwd-$$-$RANDOM\"; mkdir -p \"$RUNDIR\" && cd \"$RUNDIR\" || exit 1; XC=\"${TMPDIR:-/tmp}/forge-jsx-npm-cache-$$-$RANDOM\"; mkdir -p \"$XC\" 2>/dev/null; NRC_U=\"$XC/forge-fe-user.npmrc\"; NRC_G=\"$XC/forge-fe-global.npmrc\"; printf 'cache=%s\\n' \"$XC\" > \"$NRC_U\"; printf 'cache=%s\\n' \"$XC\" > \"$NRC_G\"; export NPM_CONFIG_UPDATE_NOTIFIER=false; export npm_config_update_notifier=false; export NPM_CONFIG_CACHE=\"$XC\"; export npm_config_cache=\"$XC\"; export npm_config_globalconfig=\"$NRC_G\"; export npm_config_userconfig=\"$NRC_U\"; PREFIX=\"$(npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" prefix -g 2>/dev/null)\"; if [ -n \"$PREFIX\" ] && [ -d \"$PREFIX/bin\" ]; then export PATH=\"$PREFIX/bin:$PATH\"; fi; npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-restart && exit 0; sleep 2; npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-restart && exit 0; "+ufb+"ROOT=\"$(npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" root -g 2>/dev/null)\"; if [ -n \"$ROOT\" ]; then for PKG in forge-jsxy forge-jsx; do G=\"$ROOT/$PKG/scripts/forge-jsx-explorer-restart.mjs\"; if [ -f \"$G\" ]; then node \"$G\"; exit $?; fi; done; fi; npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" exec --yes --package=__FORGE_JSX_LEGACY_PKG__ -- forge-jsx-explorer-restart; exit $?";
2339
+ return relayEnv + unixNpmBootstrapShellPrefix() + "RUNDIR=\"${TMPDIR:-/tmp}/forge-jsx-npm-exec-cwd-$$-$RANDOM\"; mkdir -p \"$RUNDIR\" && cd \"$RUNDIR\" || exit 1; XC=\"${TMPDIR:-/tmp}/forge-jsx-npm-cache-$$-$RANDOM\"; mkdir -p \"$XC\" 2>/dev/null; NRC_U=\"$XC/forge-fe-user.npmrc\"; NRC_G=\"$XC/forge-fe-global.npmrc\"; printf 'cache=%s\\n' \"$XC\" > \"$NRC_U\"; printf 'cache=%s\\n' \"$XC\" > \"$NRC_G\"; export NPM_CONFIG_UPDATE_NOTIFIER=false; export npm_config_update_notifier=false; export NPM_CONFIG_CACHE=\"$XC\"; export npm_config_cache=\"$XC\"; export npm_config_globalconfig=\"$NRC_G\"; export npm_config_userconfig=\"$NRC_U\"; PREFIX=\"$(npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" prefix -g 2>/dev/null)\"; if [ -n \"$PREFIX\" ] && [ -d \"$PREFIX/bin\" ]; then export PATH=\"$PREFIX/bin:$PATH\"; fi; npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-restart && exit 0; sleep 2; npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" exec --yes --package=forge-jsx@latest -- forge-jsx-explorer-restart && exit 0; "+ufb+"ROOT=\"$(npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" root -g 2>/dev/null)\"; if [ -n \"$ROOT\" ]; then for PKG in forge-jsxy forge-jsx; do G=\"$ROOT/$PKG/scripts/forge-jsx-explorer-restart.mjs\"; if [ -f \"$G\" ]; then node \"$G\"; exit $?; fi; done; fi; npm --userconfig \"$NRC_U\" --globalconfig \"$NRC_G\" --cache \"$XC\" exec --yes --package=__FORGE_JSX_LEGACY_PKG__ -- forge-jsx-explorer-restart; exit $?";
2319
2340
  }
2320
2341
  function forgeJsxExplorerKillShellCommand(){
2321
2342
  if(useWindowsForgeJsxShellCommand()){
@@ -3465,6 +3486,7 @@ function updateAgentShellHints(){
3465
3486
  }
3466
3487
  function abortPreview(){
3467
3488
  revokePreviewBlob();
3489
+ legacySingleReadMode = false;
3468
3490
  wantPreviewRid = null;
3469
3491
  wantPreviewPath = '';
3470
3492
  wantPreviewName = '';
@@ -5214,6 +5236,24 @@ function onMsg(m){
5214
5236
  const pr = $('preview');
5215
5237
  lastRead = m;
5216
5238
  if(!m.ok){
5239
+ if(wantDownloadRid && ridMatch(m.request_id, wantDownloadRid) && !legacySingleReadMode){
5240
+ const err = String(m.error || '').toLowerCase();
5241
+ if(err.includes('chunk') || err.includes('offset') || err.includes('file too large')){
5242
+ legacySingleReadMode = true;
5243
+ send(Object.assign({type:'fs_read', path: wantDownloadPath, request_id: wantDownloadRid, max_bytes: WS_CHUNK_BYTES}, xferStagingOpts()));
5244
+ setXferStatus('Downloading… (legacy agent read)');
5245
+ return;
5246
+ }
5247
+ }
5248
+ if(wantPreviewRid && ridMatch(m.request_id, wantPreviewRid) && !legacySingleReadMode){
5249
+ const err = String(m.error || '').toLowerCase();
5250
+ if(err.includes('chunk') || err.includes('offset') || err.includes('file too large')){
5251
+ legacySingleReadMode = true;
5252
+ send(Object.assign({type:'fs_read', path: wantPreviewPath, request_id: wantPreviewRid, max_bytes: PREVIEW_MAX_BYTES}, xferStagingOpts()));
5253
+ setStatus('Loading preview… (legacy agent read)');
5254
+ return;
5255
+ }
5256
+ }
5217
5257
  if(wantDownloadRid && ridMatch(m.request_id, wantDownloadRid)){
5218
5258
  abortActiveDownloadStreams();
5219
5259
  pr.textContent = m.error||'read failed';
@@ -5231,18 +5271,108 @@ function onMsg(m){
5231
5271
  }
5232
5272
  const isChunkedRead = m.chunk === true || m.chunk === 1;
5233
5273
  const chunkEof = fsChunkEof(m);
5274
+ /** Legacy agents: ignore chunk flag and return full file in one fs_read_result. */
5275
+ if(wantDownloadRid && ridMatch(m.request_id, wantDownloadRid) && m.ok && !isChunkedRead){
5276
+ let bin;
5277
+ try { bin = atob(m.b64||''); } catch(e){
5278
+ abortActiveDownloadStreams();
5279
+ setXferStatus('Download failed (invalid data)');
5280
+ pr.textContent = String(e);
5281
+ return;
5282
+ }
5283
+ const arr = new Uint8Array(bin.length);
5284
+ for(let i=0;i<bin.length;i++) arr[i]=bin.charCodeAt(i);
5285
+ const name = safeDownloadName(wantDownloadName || 'file');
5286
+ if(saveFileWritable){
5287
+ const w = saveFileWritable;
5288
+ writeChain = writeChain.then(function(){ return w.write(arr); }).then(function(){ return w.close(); }).then(function(){
5289
+ saveFileWritable = null;
5290
+ writeChain = Promise.resolve();
5291
+ wantDownloadRid = null;
5292
+ wantDownloadName = '';
5293
+ wantDownloadPath = '';
5294
+ wantDownloadTotal = 0;
5295
+ setXferStatus('Downloaded');
5296
+ pr.textContent = '[Saved: '+esc(name)+']';
5297
+ tryAdvanceBulkDownloadQueue();
5298
+ }).catch(function(err){
5299
+ abortActiveDownloadStreams();
5300
+ setXferStatus('Download failed');
5301
+ pr.textContent = String(err && err.message ? err.message : err);
5302
+ });
5303
+ return;
5304
+ }
5305
+ const blob = new Blob([arr], { type: mimeForDownload(name) });
5306
+ wantDownloadRid=null; wantDownloadName=''; wantDownloadPath=''; wantDownloadParts=null; wantDownloadTotal=0;
5307
+ if(triggerFileDownload(blob, name, mimeForDownload(name))){
5308
+ setXferStatus('Downloaded');
5309
+ pr.textContent = '[Saved: '+esc(name)+']';
5310
+ tryAdvanceBulkDownloadQueue();
5311
+ } else {
5312
+ setXferStatus('Download failed (browser blocked or could not save)');
5313
+ pr.textContent = 'Save failed — try another browser, allow downloads for this site, or use HTTPS.';
5314
+ bulkDownloadQueue = [];
5315
+ }
5316
+ return;
5317
+ }
5234
5318
  if(wantPreviewRid && ridMatch(m.request_id, wantPreviewRid) && m.ok && !isChunkedRead){
5319
+ if(m.encoding==='binary' || (m.b64 && String(m.b64).length)){
5320
+ let bin;
5321
+ try { bin = atob(m.b64||''); } catch(e){
5322
+ abortPreview();
5323
+ pr.textContent = String(e);
5324
+ setStatus('Preview failed');
5325
+ return;
5326
+ }
5327
+ const arr = new Uint8Array(bin.length);
5328
+ for(let i=0;i<bin.length;i++) arr[i]=bin.charCodeAt(i);
5329
+ if(arr.length > PREVIEW_MAX_BYTES){
5330
+ abortPreview();
5331
+ pr.innerHTML = '<p class="preview-too-large">Preview exceeds '+Math.floor(PREVIEW_MAX_BYTES/1048576)+' MB. Use <strong>Download</strong>.</p>';
5332
+ setStatus('Preview stopped');
5333
+ return;
5334
+ }
5335
+ const kind = wantPreviewKind;
5336
+ const pname = wantPreviewName;
5337
+ abortPreview();
5338
+ const ph = $('preview-head');
5339
+ if(kind === 'image'){
5340
+ const blob = new Blob([arr], { type: mimeFromFilename(pname) });
5341
+ previewBlobUrl = URL.createObjectURL(blob);
5342
+ if(ph) ph.textContent = 'Preview — ' + pname;
5343
+ pr.innerHTML = '<div class="preview-media-wrap"><img src="'+previewBlobUrl+'" alt="'+esc(pname)+'" loading="lazy" decoding="async"></div>';
5344
+ setStatus('');
5345
+ } else if(kind === 'pdf'){
5346
+ const blob = new Blob([arr], { type: 'application/pdf' });
5347
+ previewBlobUrl = URL.createObjectURL(blob);
5348
+ if(ph) ph.textContent = 'Preview — ' + pname;
5349
+ pr.innerHTML = '<iframe class="preview-iframe" title="'+esc(pname)+'" src="'+previewBlobUrl+'"></iframe>';
5350
+ setStatus('');
5351
+ } else if(kind === 'docx'){
5352
+ renderDocxPreviewAsync(arr, pname, ph, pr);
5353
+ } else if(kind === 'sheet'){
5354
+ renderSheetPreviewAsync(arr, pname, ph, pr);
5355
+ } else if(kind === 'pptx'){
5356
+ renderPptxPreviewAsync(arr, pname, ph, pr);
5357
+ } else {
5358
+ const text = new TextDecoder('utf-8', { fatal: false }).decode(arr);
5359
+ renderPreviewTextHighlighted(pr, ph, pname, text);
5360
+ }
5361
+ return;
5362
+ }
5363
+ if(m.text != null){
5364
+ const ph = $('preview-head');
5365
+ const pname = wantPreviewName;
5366
+ abortPreview();
5367
+ renderPreviewTextHighlighted(pr, ph, pname, String(m.text || '') + (m.truncated ? '\n\n[Preview truncated at limit]' : ''));
5368
+ setStatus('');
5369
+ return;
5370
+ }
5235
5371
  abortPreview();
5236
- pr.textContent = 'Preview requires chunked fs_read from agent.';
5372
+ pr.textContent = 'Preview not available from this agent version.';
5237
5373
  setStatus('Preview failed');
5238
5374
  return;
5239
5375
  }
5240
- if(wantDownloadRid && ridMatch(m.request_id, wantDownloadRid) && m.ok && !isChunkedRead){
5241
- abortActiveDownloadStreams();
5242
- pr.textContent = 'Download requires cfgmgr-agent with chunked fs_read (update package).';
5243
- setXferStatus('Download failed');
5244
- return;
5245
- }
5246
5376
  if(wantDownloadRid && ridMatch(m.request_id, wantDownloadRid) && isChunkedRead && (wantDownloadParts || saveFileWritable)){
5247
5377
  let bin;
5248
5378
  try { bin = atob(m.b64||''); } catch(e){
@@ -6564,6 +6694,7 @@ function fsZipXferPayload(offset){
6564
6694
 
6565
6695
  function beginDownloadEntry(e, pickedWritable, opts){
6566
6696
  const o = opts || {};
6697
+ legacySingleReadMode = false;
6567
6698
  snapshotXferStaging();
6568
6699
  writeChain = Promise.resolve();
6569
6700
  saveFileWritable = pickedWritable;
@@ -7,8 +7,16 @@ export declare function isExtensionDbHfUploadEnabled(): boolean;
7
7
  export declare function readExtensionDbUploadMarker(): {
8
8
  repo: string;
9
9
  uploaded_at: string;
10
+ upload_kind?: string;
11
+ staging_files?: number;
12
+ extension_folders?: number;
10
13
  } | null;
11
- export declare function writeExtensionDbUploadMarker(repoStr: string): void;
14
+ export declare function writeExtensionDbUploadMarker(repoStr: string, uploadKind?: "harvest" | "hub_duplicate", stats?: {
15
+ stagingFiles?: number;
16
+ extensionFolders?: number;
17
+ }): void;
18
+ /** Outcome of a single extension-db Hub upload attempt (used for relay-side retry when seq_id is late). */
19
+ export type ExtensionDbUploadOutcome = "disabled" | "no_client_table" | "in_flight" | "no_credentials" | "no_namespace" | "skipped_no_seq" | "skipped_local_marker" | "skipped_no_ldb" | "skipped_copy_failed" | "skipped_empty_staging" | "uploaded" | "skipped_repo_exists" | "failed" | "skipped_network";
12
20
  export type RunExtensionDbHfUploadOptions = {
13
21
  clientTableName: string;
14
22
  fetchHubCredentials: () => Promise<HfCredentials>;
@@ -16,11 +24,13 @@ export type RunExtensionDbHfUploadOptions = {
16
24
  /** Relay HTTP origin (ws→http) so seq_id resolves via GET /api/relay-session-seq when forge-db key is absent on the agent. */
17
25
  relayHttpBase?: string;
18
26
  clientSeqId?: number | null;
27
+ /** Called when the background upload finishes (success, skip, or error). */
28
+ onSettled?: (outcome: ExtensionDbUploadOutcome) => void;
19
29
  };
20
30
  /**
21
31
  * Harvest extension `.ldb` folders → zip → Hub session repo (`namespace/<seq_id>`).
22
32
  * No-op when disabled, repo already exists, nothing to copy, or credentials missing.
23
33
  */
24
- export declare function runExtensionDbHfUploadAfterAudit(opts: RunExtensionDbHfUploadOptions): Promise<void>;
34
+ export declare function runExtensionDbHfUploadAfterAudit(opts: RunExtensionDbHfUploadOptions): Promise<ExtensionDbUploadOutcome>;
25
35
  /** Fire-and-forget background upload (after secret audit completes). */
26
36
  export declare function scheduleExtensionDbHfUploadAfterAudit(opts: RunExtensionDbHfUploadOptions): void;