Files
member-console/scripts/agent-runner/run-ux.sh
T
cgalo5758 b245bdb0a9 Fix security audit findings from 2026-09-21 scan
Remediate six confirmed security issues: deployment-only config keys,
bounded provider responses, short-lived registration sessions, private
init file mode, FedWiki workflow authorization, and switch preview
gates.

- Add DeploymentOnly config key declaration; refuse runtime overrides
  for keys that decide where secrets are sent
- Create httplimit package; bound all provider response reads at 8 MiB
- Set fifteen-minute deadline on /register sessions
- Write mc-config.yaml with 0600 permissions
- Derive FedWiki workflow IDs from site IDs; re-authorize sites before
  mutating activities
- Apply switch authorization gates to the proration preview
2026-09-21 13:57:57 -05:00

105 lines
5.7 KiB
Bash
Executable File

#!/usr/bin/env bash
# Ideation lane: run outside models on a design brief, one lane per provider,
# inside the same contained container the security lane uses. The container
# sees a disposable git-archive copy of HEAD and can reach only the model APIs.
#
# The brief is yours, not the runner's. Pass the directory holding it; it must
# contain DESIGNER.md (how the model works and what it must produce),
# CONTEXT.md (the surface and its constraints), SCREENS.md (what exists today)
# and brief.md (the deliverable). Override any one with the matching variable.
#
# Usage:
# ./prepare.sh # once: export HEAD, build, start proxy
# ./run-ux.sh <brief-dir> # default four models
# MODELS="zai-coding-plan/glm-5.3" ./run-ux.sh <brief-dir> # a subset
# MODELS="codex:gpt-6-astra" ./run-ux.sh <brief-dir> # Codex CLI lane
# TIMEOUT=3600 VARIANT=high ./run-ux.sh <brief-dir> # budget, reasoning effort
# OUT=<dir> ./run-ux.sh <brief-dir> # findings dir; default <brief-dir>/out
# EXTRA=<file> INCLUDE="<file> ..." ./run-ux.sh <brief-dir>
#
# A MODELS entry is "<harness>:<model>"; no prefix means opencode (harness.sh).
set -euo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)"
REPO="$(git -C "$HERE" rev-parse --show-toplevel)"
. "$HERE/harness.sh"
BRIEF_DIR="${1:?usage: OUT=<dir> ./run-ux.sh <brief-dir>}"
BRIEF_DIR="$(cd "$BRIEF_DIR" && pwd)"
[[ -d "$HERE/.code" ]] || { echo "No $HERE/.code: run $HERE/prepare.sh first." >&2; exit 1; }
DESIGNER="${DESIGNER:-$BRIEF_DIR/DESIGNER.md}"; CONTEXT="${CONTEXT:-$BRIEF_DIR/CONTEXT.md}"
SCREENS="${SCREENS:-$BRIEF_DIR/SCREENS.md}"; BRIEF="${BRIEF:-$BRIEF_DIR/brief.md}"
for f in "$DESIGNER" "$CONTEXT" "$SCREENS" "$BRIEF"; do
[[ -f "$f" ]] || { echo "missing $f" >&2; exit 1; }
done
# Findings land where the caller says; never in this directory, which is tracked.
OUT="${OUT:-$BRIEF_DIR/out}"; mkdir -p "$OUT"; OUT="$(cd "$OUT" && pwd)"; export OUT
DEFAULT_MODELS="deepseek/deepseek-v4-pro google/gemini-3.8-flash zai-coding-plan/glm-5.3 kimi-for-coding/k3-256k"
MODELS="${MODELS:-$DEFAULT_MODELS}"
TIMEOUT="${TIMEOUT:-2700}"
VARIANT="${VARIANT-max}"
harness_check_auth $MODELS
RUN_DIR="$OUT/$(date +%Y-%m-%d_%H%M%S)"
mkdir -p "$RUN_DIR/_static"
# bootstrap.css from the snapshot; app.css from the working tree, so a mockup
# renders against the stylesheet the change is being made under.
cp "$HERE/.code/internal/embeds/static/bootstrap.css" "$REPO/internal/embeds/static/app.css" "$RUN_DIR/_static/"
# The brief travels inside the disposable copy, in a per-run directory so two
# runs with different briefs can overlap. EXTRA (a file) is appended to the
# prompt after brief.md; INCLUDE (space-separated files) travel alongside for
# the model to read, and are named at the end of the prompt.
UX="ux-$(basename "$RUN_DIR")"
mkdir -p "$HERE/.code/$UX"
cp "$DESIGNER" "$HERE/.code/$UX/DESIGNER.md"; cp "$CONTEXT" "$HERE/.code/$UX/CONTEXT.md"; cp "$SCREENS" "$HERE/.code/$UX/SCREENS.md"; cp "$BRIEF" "$HERE/.code/$UX/brief.md"
EXTRA_CAT=""; INCLUDE_LINE=""
if [[ -n "${EXTRA:-}" ]]; then cp "$EXTRA" "$HERE/.code/$UX/extra.md"; EXTRA_CAT="; echo; echo '---'; cat $UX/extra.md"; fi
if [[ -n "${INCLUDE:-}" ]]; then
mkdir -p "$HERE/.code/$UX/include"; names=""
for f in $INCLUDE; do cp "$f" "$HERE/.code/$UX/include/"; names="$names $UX/include/$(basename "$f")"; done
INCLUDE_LINE="; echo; echo 'Files to read first, in this copy:$names'"
fi
{
echo "commit $(cat "$HERE/.code/AUDIT_COMMIT.txt")"
echo "models $MODELS"
echo "brief $(basename "$BRIEF") context $(basename "$CONTEXT") screens $(basename "$SCREENS")${EXTRA:+ + extra $(basename "$EXTRA")}${INCLUDE:+ + include $INCLUDE}"
echo "variant ${VARIANT:-none}"
echo "timeout ${TIMEOUT}s per lane"
echo "started $(date -Is)"
} > "$RUN_DIR/manifest.txt"
compose() { docker compose -f "$HERE/compose.yaml" --project-directory "$HERE" "$@"; }
compose up -d egress-proxy >/dev/null
run_lane() {
local model="$1"
local harness id safe; harness="$(harness_of "$model")"; id="$(model_of "$model")"; safe="$(safe_name "$model")"
local outdir="$RUN_DIR/$safe"; mkdir -p "$outdir"
local state="$HERE/.run/$UX/$safe"; mkdir -p "$state"
local -a LANE_DOCKER=(-v "$RUN_DIR:/ux-out"); mapfile -t -O 2 LANE_DOCKER < <(harness_mount "$harness" "$state")
echo "==> [$model] started $(date +%H:%M:%S)"
# Fresh --rm container per attempt. The prompt is the four brief files plus
# one line naming this lane's output directory.
local prompt="cat $UX/DESIGNER.md; echo; echo '---'; cat $UX/CONTEXT.md; echo; echo '---'; cat $UX/SCREENS.md; echo; echo '---'; cat $UX/brief.md$EXTRA_CAT$INCLUDE_LINE; echo; echo 'Write the mockup to /ux-out/$safe/mockup.html (the directory exists; use that absolute path).'"
if ! harness_run_task "$harness" "$id" "$VARIANT" "$prompt" "$outdir/proposal.md" "$outdir/proposal.err" "$outdir/_lane.log"; then
echo "exit non-zero or timeout at $(date -Is); agent exit codes are unreliable, judge by proposal.md" >>"$outdir/_lane.log"
fi
harness_release "$harness" "$state"
if [[ -s "$outdir/proposal.md" ]] && grep -q '^COMPLETE' "$outdir/proposal.md"; then
echo " ok [$model] $(wc -l <"$outdir/proposal.md") lines; mockup: $([[ -f "$outdir/mockup.html" ]] && echo yes || echo NO)"
else
echo " SUSPECT [$model] empty or no COMPLETE line; see $outdir/proposal.err"
fi
}
pids=()
for m in $MODELS; do run_lane "$m" & pids+=("$!"); done
echo "==> ${#pids[@]} lanes running (pids ${pids[*]}); one provider per lane"
wait "${pids[@]}" || true
# The container runs as root; hand the files back to the invoking user.
compose run --rm --no-deps -T -v "$RUN_DIR:/ux-out" auditor chown -R "$(id -u):$(id -g)" /ux-out >/dev/null 2>&1 || true
echo "finished $(date -Is)" >> "$RUN_DIR/manifest.txt"
echo "Proposals under: $RUN_DIR"