#!/usr/bin/env bash
# Exercise the published launch contract after relocation. Network isolation is
# the runner's responsibility (Linux CI uses unshare --net), not a lazy-deps flag.
set -euo pipefail
PAYLOAD="${1:?usage: smoke-payload.sh <payload-dir>}"
command -v cygpath >/dev/null 2>&1 && PAYLOAD="$(cygpath -w "$PAYLOAD")"
node --input-type=module - "$PAYLOAD" <<'JS'
import { mkdtempSync, mkdirSync, readFileSync, realpathSync, renameSync, rmSync } from 'node:fs'
import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path'
import { spawnSync } from 'node:child_process'

const original = resolve(process.argv[2])
// Same filesystem: moving a multi-GB payload should not copy it.
const scratch = mkdtempSync(join(dirname(original), '.payload-smoke-'))
const moved = join(scratch, 'relocated payload')
let relocated = false
try {
  renameSync(original, moved)
  relocated = true
  const manifest = JSON.parse(readFileSync(join(moved, 'manifest.json'), 'utf8'))
  const command = manifest.runtime.commands.hermes
  if (!command) throw new Error('manifest has no hermes command')
  const executable = realpathSync(resolve(moved, command))
  const inside = relative(realpathSync(moved), executable)
  if (isAbsolute(command) || isAbsolute(inside) || inside === '..' || inside.startsWith(`..${sep}`)) {
    throw new Error('manifest command escapes the relocated payload')
  }
  const home = join(scratch, 'home')
  mkdirSync(home)
  const env = { HOME: home, USERPROFILE: home, HERMES_HOME: join(home, '.hermes'),
    PYTHONUTF8: '1', PYTHONDONTWRITEBYTECODE: '1', HERMES_DISABLE_LAZY_INSTALLS: '1',
    UV_OFFLINE: '1', npm_config_offline: 'true' }
  // Keep OS process necessities only; no checkout, Python, PM, or Node overrides.
  for (const [key, value] of Object.entries(process.env)) {
    if (/^(path|systemroot|windir|comspec|pathext|temp|tmp)$/i.test(key)) env[key] = value
  }
  // tools list crosses the real application/config/registry imports, unlike
  // version/help and PM's stdlib bootstrap fast paths.
  for (const args of [['--version'], ['--help'], ['tools', 'list'], ['pm', 'doctor']]) {
    console.log(`— published hermes ${args.join(' ')} (relocated) —`)
    const child = spawnSync(executable, args, {
      cwd: home, env, stdio: 'inherit', timeout: 120000,
    })
    if (child.error) throw child.error
    if (child.status !== 0) throw new Error(`hermes ${args.join(' ')} failed: ${child.status ?? child.signal}`)
  }
  // browser_exec's engine (browser-harness) runs on the store interpreter with only the
  // payload's site dir on PYTHONPATH (tools/browser_use_cli.py); -S drops any site of its own.
  // Telemetry off as at runtime: its detached sender would outlive the check and pin the
  // payload dir on Windows (EPERM on the restore rename).
  console.log('— browser-harness on the store interpreter (relocated) —')
  const harness = spawnSync(resolve(moved, manifest.runtime.storePython),
    ['-S', '-m', 'browser_harness.run', '--version'], {
      cwd: home,
      env: { ...env, ANONYMIZED_TELEMETRY: 'false', PYTHONPATH: resolve(moved, manifest.runtime.sitePackages) },
      stdio: 'inherit', timeout: 120000,
    })
  if (harness.error) throw harness.error
  if (harness.status !== 0) throw new Error(`browser-harness failed: ${harness.status ?? harness.signal}`)
  console.log('SMOKE OK')
} finally {
  // Restore even after a command fails, so its artifact remains inspectable.
  if (relocated) renameSync(moved, original)
  rmSync(scratch, { recursive: true, force: true })
}
JS