refactor(demo): drive recording through real cli.py instead of a harness
The previous demo harness called the backend Python API directly, which didn't match what a user typing `./cli.py start <agent>` would actually see. The recording now goes through the real CLI surface: - claude-bottle.demo.json + scripts/demo-setup.sh stage a demo manifest (one bottle, FAKE_TOKEN env, one unreachable git upstream) alongside a dummy SSH identity at ~/.cache/claude-bottle-demo/. - docs/demo.tape types `./cli.py start demo`, answers the y/N preflight, and runs four bash probes via claude's `!` prefix (curl x3 + git push), so the recording shows real preflight output and real probe results. - scripts/demo.sh wraps setup -> cli.py -> teardown for human use; scripts/demo-record.sh does the same around `vhs docs/demo.tape`. - .gitignore picks up claude-bottle.json so a user's local manifest doesn't get tracked alongside .example / .demo siblings. scripts/demo_harness.py is removed -- its behavior is fully replaced by the cli.py + `!` flow. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 183 KiB After Width: | Height: | Size: 2.4 MiB |
+55
-22
@@ -1,38 +1,71 @@
|
||||
# VHS tape — produces docs/demo.gif from scripts/demo.sh.
|
||||
# VHS tape — drives `./cli.py start demo` interactively and runs four
|
||||
# bash probes via claude's `!` prefix. Setup (manifest + dummy SSH key
|
||||
# + image pre-warm) and teardown happen outside the tape; record via
|
||||
# `bash scripts/demo-record.sh`, which wraps both.
|
||||
#
|
||||
# Usage:
|
||||
# brew install vhs # if you don't have it
|
||||
# vhs docs/demo.tape # ~60-90s; writes docs/demo.gif
|
||||
#
|
||||
# Re-record on changes:
|
||||
# - new scenarios → tweak Height below
|
||||
# - faster overall → shorten the Sleep after the trailing summary
|
||||
#
|
||||
# The harness paces itself with its own time.sleep() calls so each
|
||||
# scenario block has time to be read; VHS only needs to capture the
|
||||
# whole run end-to-end.
|
||||
# Re-record when the probe results, manifest, or cli.py preflight
|
||||
# rendering change.
|
||||
|
||||
Output docs/demo.gif
|
||||
|
||||
Set Shell "bash"
|
||||
Set FontSize 14
|
||||
Set Width 1100
|
||||
Set FontSize 13
|
||||
Set Width 1180
|
||||
Set Height 780
|
||||
Set Padding 20
|
||||
Set Theme "Catppuccin Mocha"
|
||||
Set TypingSpeed 60ms
|
||||
Set PlaybackSpeed 1.0
|
||||
Set TypingSpeed 40ms
|
||||
|
||||
Hide
|
||||
Type "clear"
|
||||
Enter
|
||||
Show
|
||||
|
||||
Type "bash scripts/demo.sh"
|
||||
Sleep 500ms
|
||||
# Real cli.py invocation — what a user with claude-bottle.json in cwd
|
||||
# would type. The bottle declares one allowlist (only baked-in
|
||||
# defaults), one git upstream (unreachable on purpose so gitleaks runs
|
||||
# before the gate would forward), and a FAKE_TOKEN env var shaped like
|
||||
# a GitHub PAT.
|
||||
Type "./cli.py start demo"
|
||||
Enter
|
||||
Sleep 8s
|
||||
|
||||
# Confirm the y/N preflight. cli.py reads from /dev/tty.
|
||||
Type "y"
|
||||
Enter
|
||||
|
||||
# Warm-cache run takes ~14s; first-time runs that build images will be
|
||||
# longer, but the wrapper pre-warms quietly so the recording sees a
|
||||
# warm path. Pad a few seconds so the trailing PASS summary holds.
|
||||
Sleep 20s
|
||||
# Wait for the bottle to launch: networks created, pipelock + git-gate
|
||||
# sidecars started, agent container started, claude boots.
|
||||
Sleep 22s
|
||||
|
||||
# Probe 1 — allowlisted HTTPS reaches an allowlisted host via the
|
||||
# bumped TLS tunnel. Baseline: the proxy isn't just blocking everything.
|
||||
Type `! curl --proxy "$HTTPS_PROXY" -sw 'status=%{http_code}\n' -o /dev/null https://raw.githubusercontent.com/git/git/master/README.md`
|
||||
Enter
|
||||
Sleep 5s
|
||||
|
||||
# Probe 2 — non-allowlisted host. Pipelock's host filter refuses to
|
||||
# forward; DLP doesn't even get a chance to run.
|
||||
Type `! curl --proxy "$HTTPS_PROXY" -sw 'status=%{http_code}\n' -o /dev/null http://example.com/`
|
||||
Enter
|
||||
Sleep 5s
|
||||
|
||||
# Probe 3 — allowlisted host BUT body carries a credential pattern.
|
||||
# api.anthropic.com is on the baked-in allowlist, so the host check
|
||||
# passes; the DLP body scanner has to catch the ghp_ pattern.
|
||||
Type `! curl --proxy "$HTTPS_PROXY" -sw 'status=%{http_code}\n' -o /dev/null --data "token=$FAKE_TOKEN" http://api.anthropic.com/dlp-probe`
|
||||
Enter
|
||||
Sleep 5s
|
||||
|
||||
# Probe 4 — git push of a file containing an AKIA-shaped key. The
|
||||
# bottle's ~/.gitconfig rewrites the upstream URL to the git-gate via
|
||||
# `insteadOf`, so this push hits the gate, gitleaks runs in the
|
||||
# pre-receive hook, and rejects the ref before the gate would forward.
|
||||
Type `! cd /tmp && rm -rf r && git init -qb main r && cd r && git config user.email demo@x && git config user.name demo && echo AKIAQRJHK7N5ZPM2VXTL > leak.txt && git add . && git commit -qm leak && git push ssh://git@upstream.invalid/path.git main`
|
||||
Enter
|
||||
Sleep 10s
|
||||
|
||||
# Leave claude. The launcher tears down the container, sidecars, and
|
||||
# networks on session end.
|
||||
Ctrl+D
|
||||
Sleep 4s
|
||||
|
||||
Reference in New Issue
Block a user