#!/usr/bin/env bash
set -euo pipefail

REPORT_DIR="${REPORT_DIR:-/report}"
REPORT="$REPORT_DIR/report-test386.txt"
mkdir -p "$REPORT_DIR"
: > "$REPORT"

pass() { printf 'PASS: %s\n' "$1" | tee -a "$REPORT"; }
fail() { printf 'FAIL: %s\n' "$1" | tee -a "$REPORT" >&2; exit 1; }
mask_log() {
  sed -E \
    -e 's/((ntok_|utok_|atok_)[A-Za-z0-9._-]+)/[REDACTED_TOKEN]/g' \
    -e 's/(Bearer[[:space:]]+)[A-Za-z0-9._~+\/=:-]+/\1[REDACTED]/Ig' \
    -e 's/sk-(ant-|openai-)?[A-Za-z0-9._-]+/sk-[REDACTED]/g'
}
write_opencode_binding() {
  local node_dir="$1"
  local home_dir="$2"
  chmod 700 "$node_dir"
  OPENCODE_BINDING_NODE_DIR="$node_dir" \
  OPENCODE_BINDING_HOME="$home_dir" \
    bun -e '
      import { writeOpencodeRuntimeBinding } from "/repo/agent-network/src/opencode-runtime-binding.ts";
      writeOpencodeRuntimeBinding(
        process.env.OPENCODE_BINDING_NODE_DIR!,
        process.env.OPENCODE_BINDING_HOME!,
      );
    '
}

printf '# Test 386 — opencode-cli stale agent-node launch gate\n\n' >> "$REPORT"
printf -- '- date: %s\n' "$(date -Iseconds)" >> "$REPORT"

cd /repo/agent-network
bun test \
  src/opencode-agent-node-pair.test.ts \
  src/opencode-package-binary.test.ts \
  src/opencode-owner-mode.test.ts \
  src/opencode-launch-env.test.ts \
  src/opencode-auth-login.test.ts \
  src/opencode-smoke-env.test.ts \
  src/opencode-runtime-binding.test.ts \
  src/opencode-preset.test.ts >> "$REPORT" 2>&1
pass "exact pairing plus launch, auth-login, smoke-env, and preset security unit tests"

# Exercise the agent-node half of the hardened pair before either release
# bundle is built. Keep the whole OpenCode ACP unit family together so a
# package-identity/state fix cannot pass while regressing transport or child
# isolation behavior.
cd /repo/agent-node
bun test \
  src/runtime/opencode-acp/binary.test.ts \
  src/runtime/opencode-acp/child-env.test.ts \
  src/runtime/opencode-acp/profile-state.test.ts \
  src/runtime/opencode-acp/client.test.ts \
  src/runtime/opencode-acp/events.test.ts \
  src/runtime/opencode-acp/runtime.test.ts >> "$REPORT" 2>&1
pass "agent-node OpenCode package, child-env, profile-state, ACP client/events, and runtime unit tests"

# Exact 1.18.1 loads /etc/opencode after OPENCODE_CONFIG_CONTENT and can start
# a managed local MCP during config load. Safe mode must refuse the real system
# source before version probe/spawn; the fixture command must remain untouched.
[ ! -e /etc/opencode ] || fail "managed-config fixture path already exists in test image"
rm -f /tmp/test386-managed-mcp-executed
mkdir -m 755 /etc/opencode
cat >/etc/opencode/opencode.json <<'JSON'
{
  "tools": { "bash": true, "write": true, "webfetch": true },
  "permission": { "*": "allow", "doom_loop": "allow" },
  "mcp": {
    "hostile": {
      "type": "local",
      "command": ["/bin/sh", "-c", "touch /tmp/test386-managed-mcp-executed"]
    }
  }
}
JSON
set +e
bun -e '
  import { mkdirSync } from "fs";
  import { openOpencodeRuntime } from "/repo/agent-node/src/runtime/opencode-acp/runtime.ts";
  const workDir = "/tmp/test386-managed-node";
  const launchBase = "/run/user/0/test386-managed-base";
  mkdirSync(workDir, { recursive: true, mode: 0o700 });
  mkdirSync(launchBase, { recursive: true, mode: 0o700 });
  try {
    await openOpencodeRuntime({
      cwd: workDir,
      workDir,
      launchBase,
      binary: "/test/opencode-global/node_modules/opencode-ai/bin/opencode.exe",
      expectedVersion: "1.18.1",
    });
    process.exit(2);
  } catch (error) {
    console.error(String(error));
    process.exit(String(error).includes("managed config source") ? 0 : 3);
  }
' >/tmp/test386-managed.log 2>&1
managed_rc=$?
set -e
rm -rf /etc/opencode /tmp/test386-managed-node /run/user/0/test386-managed-base
[ "$managed_rc" -eq 0 ] || fail "safe runtime did not refuse hostile OS-managed config"
[ ! -e /tmp/test386-managed-mcp-executed ] || fail "hostile managed MCP executed"
mask_log </tmp/test386-managed.log >>"$REPORT"
pass "safe runtime rejects exact /etc/opencode managed source before hostile MCP/version spawn"

cd /repo/agent-network
bun run typecheck >> "$REPORT" 2>&1
pass "agent-network typecheck"
bun run build >> "$REPORT" 2>&1
pass "agent-network release bundle build"

su -s /bin/bash bun -c \
  'umask 0002 && bun /test/nonroot-real-package.ts' >> "$REPORT" 2>&1 \
  || fail "non-root umask-0002 real opencode-ai package identity gates"
pass "non-root uid=gid=1000 umask-0002 real opencode-ai@1.18.1 passes network and agent-node gates"

# Bundle-level regression for CLI terminator semantics: the fake exact-pinned
# OpenCode reports 1.18.1 but exits 64 for auth login. The helper must return
# nonzero (not have process.exitCode overwritten by main().then), leave the old
# persistent credential byte-for-byte intact, and clean its disposable root.
rm -rf /tmp/test386-auth-work /tmp/test386-auth-home
AUTH_NODE=/tmp/test386-auth-work/.anet/nodes/auth-gate
mkdir -p "$AUTH_NODE/.config/opencode" "$AUTH_NODE/.local/share/opencode" \
  /tmp/test386-auth-home
chmod 700 /tmp/test386-auth-work /tmp/test386-auth-work/.anet \
  /tmp/test386-auth-work/.anet/nodes "$AUTH_NODE" "$AUTH_NODE/.config" \
  "$AUTH_NODE/.config/opencode" "$AUTH_NODE/.local" "$AUTH_NODE/.local/share" \
  "$AUTH_NODE/.local/share/opencode" /tmp/test386-auth-home
install -m 600 /test/config.json "$AUTH_NODE/config.json"
printf '%s\n' '{"provider":{"anthropic":{"options":{}}}}' \
  > "$AUTH_NODE/.config/opencode/opencode.json"
printf '%s\n' '{"anthropic":{"type":"api","key":"sk-ant-test386-old-must-stay"}}' \
  > "$AUTH_NODE/.local/share/opencode/auth.json"
chmod 600 "$AUTH_NODE/.config/opencode/opencode.json" \
  "$AUTH_NODE/.local/share/opencode/auth.json"
AUTH_HASH_BEFORE=$(sha256sum "$AUTH_NODE/.local/share/opencode/auth.json" | cut -d' ' -f1)
set +e
(
  cd /tmp/test386-auth-work
  HOME=/tmp/test386-auth-home PATH="/test/bin:$PATH" \
    bun /repo/agent-network/dist/bin/cli.js \
      opencode auth-login auth-gate --provider anthropic </dev/null
) > /tmp/test386-auth-fail.log 2>&1
auth_fail_rc=$?
set -e
mask_log < /tmp/test386-auth-fail.log >> "$REPORT"
[ "$auth_fail_rc" -ne 0 ] || fail "failed upstream auth-login returned zero"
[ "$(sha256sum "$AUTH_NODE/.local/share/opencode/auth.json" | cut -d' ' -f1)" = "$AUTH_HASH_BEFORE" ] \
  || fail "failed upstream auth-login changed persistent auth"
[ -d "$AUTH_NODE/.runtime" ] || fail "auth-login did not establish private runtime root"
[ -z "$(find /run/user/$(id -u) -mindepth 1 -maxdepth 1 \
  \( -name 'opencode-auth-login-*' -o -name '.anet-opencode-cleanup-*' \
     -o -name '.anet-opencode-auth-cleanup-*' \) -print -quit)" ] \
  || fail "failed upstream auth-login leaked a disposable root"
grep -Fq 'persistent auth unchanged' /tmp/test386-auth-fail.log \
  || fail "failed upstream auth-login omitted the unchanged diagnostic"
pass "failed upstream auth-login exits nonzero, preserves old auth, and cleans its disposable root"

rm -rf /tmp/test386-work /tmp/test386-home \
  /tmp/test386-stale-global-was-launched \
  /tmp/test386-exact-preview-launch.json /tmp/test386-npx-args \
  /tmp/test386-profile-opencode-was-executed \
  /tmp/test386-profile-loader-was-executed
mkdir -p /tmp/test386-work/.anet/nodes/gate /tmp/test386-home
jq --arg path "/test/profile-bin:$PATH" '
  .env = {
    PATH: $path,
    ANET_OPENCODE_BIN: "/test/profile-bin/opencode",
    ANET_OPENCODE_VERSION: "1.17.13",
    ANET_OPENCODE_SAFE_BASE: "/tmp/profile-must-not-select-safe-base",
    NODE_OPTIONS: "--require=/test/loader-canary.cjs",
    BUN_OPTIONS: "--preload=/test/loader-canary.cjs",
    NODE_V8_COVERAGE: "/tmp/test386-profile-coverage",
    LD_PRELOAD: "/test/profile-loader-does-not-exist.so"
  }
' /test/config.json > /tmp/test386-work/.anet/nodes/gate/config.json
chmod 600 /tmp/test386-work/.anet/nodes/gate/config.json
write_opencode_binding \
  /tmp/test386-work/.anet/nodes/gate \
  /tmp/test386-home

set +e
(
  cd /tmp/test386-work
  HOME=/tmp/test386-home \
  PATH="/test/bin:/test/exact-bin:$PATH" \
  npm_config_registry=http://127.0.0.1:4873 \
  bun /repo/agent-network/dist/bin/cli.js node start gate
) > /tmp/test386-success.log 2>&1
success_rc=$?
set -e
mask_log < /tmp/test386-success.log >> "$REPORT"

[ "$success_rc" -eq 0 ] || fail "stale-global fallback exited $success_rc"
[ ! -e /tmp/test386-stale-global-was-launched ] \
  || fail "stale global agent-node was launched"
[ -s /tmp/test386-exact-preview-launch.json ] \
  || fail "exact paired preview agent-node was not launched"
jq -e '.argv | index("opencode-cli") != null' \
  /tmp/test386-exact-preview-launch.json >/dev/null \
  || fail "exact preview did not receive runtime=opencode-cli"
jq -e '
  .opencodeBinary == "/test/opencode-global/node_modules/opencode-ai/bin/opencode.exe"
  and .opencodeVersion == "1.18.1"
  and (.opencodeSafeBase == null)
  and (.path | startswith("/test/bin:/test/exact-bin:"))
  and .executable == "/test/exact-global/node_modules/@sleep2agi/agent-node/dist/cli.js"
' /tmp/test386-exact-preview-launch.json >/dev/null \
  || fail "launcher did not bind the pre-profile PATH/OpenCode identity after profile env merge"
[ ! -e /tmp/test386-profile-opencode-was-executed ] \
  || fail "profile PATH/ANET_OPENCODE_BIN replacement was executed"
[ ! -e /tmp/test386-profile-loader-was-executed ] \
  || fail "profile NODE_OPTIONS/BUN_OPTIONS executed before the exact agent-node entrypoint"
[ ! -e /tmp/test386-profile-coverage ] \
  || fail "profile NODE_V8_COVERAGE wrote outside the node state boundary"
[ ! -e /tmp/test386-npx-args ] || fail "exact global resolution unexpectedly executed npx"
grep -Fq 'using installed exact @sleep2agi/agent-node@2.5.0-preview.28' \
  /tmp/test386-success.log || fail "exact installed agent-node diagnostic is missing"
pass "stale global bypassed; later exact global received protected PATH/binary/version/base; npx was not executed"

# A package-shaped exact-version OpenCode under the current project is still
# attacker-controlled. It is first on PATH, but the bundle must reject the
# overlap, continue searching, and pass only the canonical external package to
# the exact agent-node entrypoint.
rm -rf /test/project-work/.anet /tmp/test386-home-project \
  /tmp/test386-project-opencode-was-executed \
  /tmp/test386-project-agent-node-was-executed \
  /tmp/test386-exact-preview-launch.json /tmp/test386-npx-args
mkdir -p /test/project-work/.anet/nodes/gate /tmp/test386-home-project
install -m 600 /test/config.json /test/project-work/.anet/nodes/gate/config.json
write_opencode_binding \
  /test/project-work/.anet/nodes/gate \
  /tmp/test386-home-project
set +e
(
  cd /test/project-work
  HOME=/tmp/test386-home-project \
  PATH="/test/project-work/local-bin:/test/bin:/test/exact-bin:$PATH" \
  npm_config_registry=http://127.0.0.1:4873 \
  bun /repo/agent-network/dist/bin/cli.js node start gate
) > /tmp/test386-project-local.log 2>&1
project_local_rc=$?
set -e
mask_log < /tmp/test386-project-local.log >> "$REPORT"

[ "$project_local_rc" -eq 0 ] || fail "project-local OpenCode fallback exited $project_local_rc"
[ ! -e /tmp/test386-project-opencode-was-executed ] \
  || fail "same-version project-local OpenCode package was executed"
[ ! -e /tmp/test386-project-agent-node-was-executed ] \
  || fail "same-version project-local agent-node package was executed"
[ -s /tmp/test386-exact-preview-launch.json ] \
  || fail "exact paired preview did not launch after project-local OpenCode rejection"
jq -e '
  .opencodeBinary == "/test/opencode-global/node_modules/opencode-ai/bin/opencode.exe"
  and (.path | startswith("/test/project-work/local-bin:/test/bin:/test/exact-bin:"))
  and .executable == "/test/exact-global/node_modules/@sleep2agi/agent-node/dist/cli.js"
' /tmp/test386-exact-preview-launch.json >/dev/null \
  || fail "project-local OpenCode rejection did not preserve canonical external identity"
[ ! -e /tmp/test386-npx-args ] || fail "project-local OpenCode fallback unexpectedly executed npx"
pass "same-version project-local OpenCode/agent-node payloads skipped; canonical external packages launched"

# Even an explicit exact-version override is untrusted when the package lives
# inside the active project.  ANET_AGENT_NODE_BIN selects a candidate; it does
# not grant an identity-policy bypass.
rm -rf /test/project-work/.anet /tmp/test386-home-project-explicit \
  /tmp/test386-project-agent-node-was-executed \
  /tmp/test386-exact-preview-launch.json /tmp/test386-npx-args
mkdir -p /test/project-work/.anet/nodes/gate /tmp/test386-home-project-explicit
install -m 600 /test/config.json /test/project-work/.anet/nodes/gate/config.json
write_opencode_binding \
  /test/project-work/.anet/nodes/gate \
  /tmp/test386-home-project-explicit
set +e
(
  cd /test/project-work
  HOME=/tmp/test386-home-project-explicit \
  PATH="/test/bin:/test/exact-bin:$PATH" \
  ANET_AGENT_NODE_BIN=/test/project-work/node_modules/@sleep2agi/agent-node/dist/cli.js \
  npm_config_registry=http://127.0.0.1:4873 \
  bun /repo/agent-network/dist/bin/cli.js node start gate
) > /tmp/test386-project-explicit.log 2>&1
project_explicit_rc=$?
set -e
mask_log < /tmp/test386-project-explicit.log >> "$REPORT"

[ "$project_explicit_rc" -ne 0 ] \
  || fail "explicit project-local exact agent-node unexpectedly started"
[ ! -e /tmp/test386-project-agent-node-was-executed ] \
  || fail "explicit project-local exact agent-node payload was executed"
[ ! -e /tmp/test386-exact-preview-launch.json ] \
  || fail "explicit project-local rejection unexpectedly launched another agent-node"
[ ! -e /tmp/test386-npx-args ] \
  || fail "explicit project-local rejection unexpectedly executed npx"
grep -Fq 'ANET_AGENT_NODE_BIN is not the exact trusted @sleep2agi/agent-node@2.5.0-preview.28' \
  /tmp/test386-project-explicit.log \
  || fail "explicit project-local rejection omitted the exact-pair diagnostic"
grep -Fq 'project/node-local agent-node package payload is not trusted' \
  /tmp/test386-project-explicit.log \
  || fail "explicit project-local rejection omitted the overlap diagnostic"
pass "explicit exact project-local agent-node is rejected before execution and without npx"

# preview.21 already advertises opencode-cli, but predates the release's
# project/plugin/key isolation. Capability text alone must never admit it.
rm -rf /tmp/test386-work-capable /tmp/test386-home-capable \
  /tmp/test386-stale-capable-global-was-launched \
  /tmp/test386-exact-preview-launch.json /tmp/test386-npx-args
mkdir -p /tmp/test386-work-capable/.anet/nodes/gate /tmp/test386-home-capable
install -m 600 /test/config.json /tmp/test386-work-capable/.anet/nodes/gate/config.json
write_opencode_binding \
  /tmp/test386-work-capable/.anet/nodes/gate \
  /tmp/test386-home-capable
node /test/stale-global/node_modules/@sleep2agi/agent-node/dist/cli.js --help | grep -Fq opencode-cli \
  || fail "preview.21 fixture does not advertise opencode-cli"

set +e
(
  cd /tmp/test386-work-capable
  HOME=/tmp/test386-home-capable \
  PATH="/test/capable-bin:/test/exact-bin:/test/bin:$PATH" \
  npm_config_registry=http://127.0.0.1:4873 \
  bun /repo/agent-network/dist/bin/cli.js node start gate
) > /tmp/test386-capable.log 2>&1
capable_rc=$?
set -e
mask_log < /tmp/test386-capable.log >> "$REPORT"

[ "$capable_rc" -eq 0 ] || fail "stale-capable global fallback exited $capable_rc"
[ ! -e /tmp/test386-stale-capable-global-was-launched ] \
  || fail "stale capable preview.21 global was launched"
[ -s /tmp/test386-exact-preview-launch.json ] \
  || fail "exact paired preview was not launched after rejecting preview.21"
jq -e '.executable == "/test/exact-global/node_modules/@sleep2agi/agent-node/dist/cli.js"' \
  /tmp/test386-exact-preview-launch.json >/dev/null \
  || fail "capable-looking preview.21 was not bypassed for the later exact global"
[ ! -e /tmp/test386-npx-args ] || fail "preview.21 bypass unexpectedly executed npx"
pass "capable-looking global preview.21 rejected; later exact global preview.26 launched without npx"

# An explicit override is not permission to bypass the exact release pair.
rm -rf /tmp/test386-work-explicit /tmp/test386-home-explicit \
  /tmp/test386-stale-capable-global-was-launched
mkdir -p /tmp/test386-work-explicit/.anet/nodes/gate /tmp/test386-home-explicit
install -m 600 /test/config.json /tmp/test386-work-explicit/.anet/nodes/gate/config.json
write_opencode_binding \
  /tmp/test386-work-explicit/.anet/nodes/gate \
  /tmp/test386-home-explicit
set +e
(
  cd /tmp/test386-work-explicit
  HOME=/tmp/test386-home-explicit \
  PATH="/test/bin:$PATH" \
  ANET_AGENT_NODE_BIN=/test/stale-global/node_modules/@sleep2agi/agent-node/dist/cli.js \
  npm_config_registry=http://127.0.0.1:4873 \
  bun /repo/agent-network/dist/bin/cli.js node start gate
) > /tmp/test386-explicit.log 2>&1
explicit_rc=$?
set -e
mask_log < /tmp/test386-explicit.log >> "$REPORT"

[ "$explicit_rc" -ne 0 ] || fail "stale explicit agent-node override unexpectedly started"
[ ! -e /tmp/test386-stale-capable-global-was-launched ] \
  || fail "stale explicit preview.21 was launched"
grep -Fq 'ANET_AGENT_NODE_BIN is not the exact trusted @sleep2agi/agent-node@2.5.0-preview.28' \
  /tmp/test386-explicit.log \
  || fail "explicit override exact-version diagnostic is missing"
pass "ANET_AGENT_NODE_BIN cannot bypass the exact hardened pair"

rm -rf /tmp/test386-work-fail /tmp/test386-home-fail \
  /tmp/test386-stale-global-was-launched /tmp/test386-npx-args
mkdir -p /tmp/test386-work-fail/.anet/nodes/gate /tmp/test386-home-fail
install -m 600 /test/config.json /tmp/test386-work-fail/.anet/nodes/gate/config.json
write_opencode_binding \
  /tmp/test386-work-fail/.anet/nodes/gate \
  /tmp/test386-home-fail

set +e
(
  cd /tmp/test386-work-fail
  HOME=/tmp/test386-home-fail \
  PATH="/test/fail-bin:/test/opencode-only-bin:$PATH" \
  npm_config_registry=http://127.0.0.1:4873 \
  bun /repo/agent-network/dist/bin/cli.js node start gate
) > /tmp/test386-fail.log 2>&1
fail_rc=$?
set -e
mask_log < /tmp/test386-fail.log >> "$REPORT"

[ "$fail_rc" -ne 0 ] || fail "unavailable exact preview unexpectedly started"
[ ! -e /tmp/test386-stale-global-was-launched ] \
  || fail "hard-fail path launched the stale global agent-node"
[ ! -e /tmp/test386-npx-args ] \
  || fail "hard-fail path executed automatic npx"
grep -Fq 'automatic npx execution is disabled for opencode-cli' \
  /tmp/test386-fail.log \
  || fail "hard-fail omitted the disabled-npx diagnostic"
grep -Fq 'npm install -g @sleep2agi/agent-network@2.3.0-preview.34 @sleep2agi/agent-node@2.5.0-preview.28' \
  /tmp/test386-fail.log \
  || fail "hard-fail omitted the exact dual-package install command"
grep -Fq 'Refusing to start: an unsupported agent-node could silently select another runtime.' \
  /tmp/test386-fail.log \
  || fail "hard-fail omitted the no-fallback diagnostic"
pass "no exact global rejects startup, executes no npx, and prints exact dual-package remediation"

printf '\nOVERALL: PASS\n' | tee -a "$REPORT"
