From df39c6bf1d983a770ecc147bae73a1c0e97d24fc Mon Sep 17 00:00:00 2001 From: sonpiaz Date: Fri, 4 Sep 2026 15:37:37 -0700 Subject: [PATCH] release: v0.3.4 Browser cookies become opt-in. A browser session is the user's identity on that platform, and an agent-driven tool should not reuse it without being told to. ClawHub's review of the skill said the same thing after v0.3.3 cleared the install finding: "can automatically use local browser login cookies when a site blocks anonymous access". - bin/dl-video: WATCH_BROWSER unset (default) or "none" never touches a browser profile; a login wall fails with tag=download-auth and the error explains how to opt in. WATCH_BROWSER=auto tries Chrome, Firefox, Safari, Edge, Brave, Chromium in order; a browser name tries that one. --cookies is unchanged. - SKILL.md, README, docs/cookies.md, docs/platforms.md, docs/exit-codes.md describe the opt-in and tell agents to ask the user before setting it. - Pinned installer references move to v0.3.4; lib/version.sh 0.3.3 -> 0.3.4. Verified: default run against an unreachable URL prints no "retrying with" line; WATCH_BROWSER=auto walks the chain; WATCH_BROWSER=firefox tries only firefox. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_018Rok2mujdEZ2yGw8wVfbr9 --- README.md | 21 +++++++++++++-------- SKILL.md | 10 +++++----- bin/dl-video | 36 +++++++++++++++++++----------------- docs/cookies.md | 22 ++++++++++------------ docs/exit-codes.md | 2 +- docs/platforms.md | 1 + docs/releases.md | 6 +++--- install.sh | 10 +++++----- lib/version.sh | 2 +- 9 files changed, 58 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index e266451..ddc120f 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Eyes and ears for your AI agent. watch-cli composes `yt-dlp` + `ffmpeg` + a Whis watch https://twitter.com/anyone/status/12345 ``` -Works on YouTube, X, LinkedIn, TikTok, Reddit, Vimeo, and Facebook. Login-walled posts (LinkedIn, private X, FB) fall back to your browser cookies automatically. +Works on YouTube, X, LinkedIn, TikTok, Reddit, Vimeo, and Facebook. Login-walled posts (LinkedIn, private X, FB) work with `WATCH_BROWSER=auto`, which reads cookies from a browser you are signed in to. ## What you can build @@ -118,8 +118,8 @@ The agent then picks up `watch ` as a first-class command. Pin a specific version: ```bash -curl -fsSL https://github.com/sonpiaz/watch-cli/releases/download/v0.3.3/install.sh \ - | WATCH_CLI_VERSION=0.3.3 bash +curl -fsSL https://github.com/sonpiaz/watch-cli/releases/download/v0.3.4/install.sh \ + | WATCH_CLI_VERSION=0.3.4 bash ``` Or from a clone: @@ -257,11 +257,16 @@ to see what's behind the alias today. Most YouTube / TikTok / Reddit / Vimeo / public X work without setup. LinkedIn, private X posts, and Facebook need a session. -watch-cli tries every URL anonymously first. On a login wall it -retries with cookies from a signed-in browser on this machine -(Chrome → Firefox → Safari → Edge → Brave → Chromium). Just sign in -normally and re-run. `WATCH_BROWSER=none` turns the browser step off; -cookies are read locally by yt-dlp and never stored or uploaded. +watch-cli fetches every URL anonymously and never reads a browser +session on its own. For a login-walled URL, opt in per run: + +```bash +WATCH_BROWSER=auto watch # any signed-in browser: Chrome → Firefox → Safari → Edge → Brave → Chromium +WATCH_BROWSER=firefox watch # one browser +``` + +Cookies are read from the local browser profile by yt-dlp, sent only to +that platform, and never stored or uploaded. For servers / CI without browsers, pass a manual cookies file: diff --git a/SKILL.md b/SKILL.md index 331b4f7..8dd07c3 100644 --- a/SKILL.md +++ b/SKILL.md @@ -15,12 +15,12 @@ metadata: bins: - watch label: "Install watch-cli (Homebrew)" - - id: release-0.3.3 + - id: release-0.3.4 kind: shell - command: "curl -fsSL https://github.com/sonpiaz/watch-cli/releases/download/v0.3.3/install.sh | WATCH_CLI_VERSION=0.3.3 bash" + command: "curl -fsSL https://github.com/sonpiaz/watch-cli/releases/download/v0.3.4/install.sh | WATCH_CLI_VERSION=0.3.4 bash" bins: - watch - label: "Install watch-cli v0.3.3 (pinned release, SHA256-verified tarball)" + label: "Install watch-cli v0.3.4 (pinned release, SHA256-verified tarball)" --- Watch any social video → get an architecture diagram, working component, runnable notebook, or step-by-step cheat sheet — automatically. @@ -36,7 +36,7 @@ Reach for `watch` whenever the user gives you a video URL and wants you to do so - The user asks to "implement", "clone", "build", or "replicate" what is on screen in a video. - The user asks to "extract architecture from", "diagram", or "turn this paper talk into code" at a video URL. -Supported platforms: YouTube, X / Twitter, LinkedIn, TikTok, Vimeo, Reddit, Facebook. Every URL is fetched anonymously first. Only when the platform answers with a login wall does `watch` retry with the cookies of a browser the user is already signed in to on this machine (Chrome, Firefox, Safari, Edge, Brave, Chromium, in that order); `WATCH_BROWSER=none` turns that step off and `--cookies ` replaces it with an exported cookie file. Cookies are read from the local browser profile by yt-dlp and sent only to the platform that issued them; watch-cli never stores or uploads them. +Supported platforms: YouTube, X / Twitter, LinkedIn, TikTok, Vimeo, Reddit, Facebook. Every URL is fetched anonymously. A login-walled URL fails with `tag=download-auth`; watch-cli does not touch browser sessions on its own. If the user wants to use one, they opt in per run with `WATCH_BROWSER=auto` (or a browser name), which lets yt-dlp read cookies from the local browser profile and send them only to that platform; `--cookies ` uses an exported cookie file instead. Ask the user before setting either; never set them silently. ## What you get back @@ -99,7 +99,7 @@ Pass `--no-cache` only when the source itself has changed. A failed transcriptio - The video download goes to the platform hosting it, through yt-dlp. - The extracted audio track is uploaded to Kyma API for transcription; frames and the video file stay on disk under `~/.watch-cli/archive`. With `--with-local` installed, transcription runs offline through whisper.cpp instead and nothing is uploaded. -- Browser cookies, when used, go only to the platform that set them (see above). +- Browser cookies are never read unless the user sets `WATCH_BROWSER` or passes `--cookies`; when they are, they go only to the platform that set them. - Installation is pinned: the release installer downloads a tagged tarball and verifies its SHA256 against the checksum published on the same GitHub Release. Homebrew does the same through the formula's `sha256`. Transcription runs through Kyma API. Get a key at https://kymaapi.com/?src=skill:watch and set `KYMA_API_KEY`; a one-hour video costs about $0.05. diff --git a/bin/dl-video b/bin/dl-video index ac5c99c..4babcf0 100755 --- a/bin/dl-video +++ b/bin/dl-video @@ -6,8 +6,9 @@ # # Cookie fallback (4 tiers, tried in order): # 1. Anonymous fetch — works for all public videos. -# 2. Auto-detect signed-in browser (Chrome → Firefox → Safari → Edge). -# Override via WATCH_BROWSER=chrome|firefox|safari|edge|brave|chromium. +# 2. Cookies from a signed-in browser, only when the user opts in with +# WATCH_BROWSER=auto (Chrome → Firefox → Safari → Edge → Brave → +# Chromium) or WATCH_BROWSER=. Unset or "none": skipped. # 3. Manual cookies file via --cookies (Netscape format). # 4. Graceful error with guidance — no silent failure. # @@ -147,19 +148,18 @@ if [[ -n "$COOKIES_FILE" ]]; then exit 3 fi -# Tier 2: auto-detect signed-in browser. -# Order: Chrome → Firefox → Safari → Edge → Brave → Chromium. -# Override via env: WATCH_BROWSER=firefox. WATCH_BROWSER=none skips -# this tier entirely, so a login wall fails with tag=download-auth -# instead of touching any browser profile. +# Tier 2: cookies from a signed-in browser, opt-in only. A browser +# session is the user's identity on that platform, so watch-cli never +# reads one unless asked: WATCH_BROWSER=auto tries Chrome → Firefox → +# Safari → Edge → Brave → Chromium, WATCH_BROWSER=firefox tries that +# one. Unset (the default) or "none" skips this tier, and a login wall +# fails with tag=download-auth and a hint instead. declare -a BROWSERS -if [[ "${WATCH_BROWSER:-}" == "none" ]]; then - BROWSERS=() -elif [[ -n "${WATCH_BROWSER:-}" ]]; then - BROWSERS=("$WATCH_BROWSER") -else - BROWSERS=(chrome firefox safari edge brave chromium) -fi +case "${WATCH_BROWSER:-}" in + ""|none) BROWSERS=() ;; + auto) BROWSERS=(chrome firefox safari edge brave chromium) ;; + *) BROWSERS=("$WATCH_BROWSER") ;; +esac # ${arr[@]+"${arr[@]}"} keeps `set -u` happy on an empty array (bash 3.2). for BROWSER in ${BROWSERS[@]+"${BROWSERS[@]}"}; do @@ -177,11 +177,13 @@ TAG="$(classify_failure)" case "$TAG" in download-auth) cat >&2 < +[dl-video] error: download failed for $URL tag=download-auth — re-run with WATCH_BROWSER=auto to use a signed-in browser on this machine, or pass --cookies Three ways to fix: - 1. Sign in to the platform in Chrome (or Firefox/Safari/Edge), then re-run. - watch-cli auto-reads cookies from any signed-in browser. + 1. Sign in to the platform in Chrome (or Firefox/Safari/Edge), then re-run with + WATCH_BROWSER=auto watch + Cookies are read from the local browser profile and sent only to that + platform. watch-cli never does this unless you set WATCH_BROWSER. 2. Export cookies.txt (Netscape format) and pass: watch-cli --cookies ~/path/to/cookies.txt diff --git a/docs/cookies.md b/docs/cookies.md index 8bcd7db..5ccf274 100644 --- a/docs/cookies.md +++ b/docs/cookies.md @@ -16,8 +16,9 @@ For these, watch-cli needs to act as a signed-in user. ## Option 1 — sign in to a browser (easiest) **You don't need to copy cookies by hand.** If you're already signed in -to the platform in **any** of these browsers, watch-cli reads the live -session directly. No F12, no DevTools, no extensions: +to the platform in **any** of these browsers, `WATCH_BROWSER=auto` +lets watch-cli read the live session directly. No F12, no DevTools, no +extensions. Nothing is read unless you set the variable: - Chrome (default first try) - Firefox @@ -26,23 +27,20 @@ session directly. No F12, no DevTools, no extensions: - Brave - Chromium -No setup needed — just sign in normally and re-run `watch `. - -To force a specific browser: - ```bash -WATCH_BROWSER=firefox watch +WATCH_BROWSER=auto watch ``` -To never touch a browser profile (servers, shared machines, or when you -would rather see a login wall fail than have a session reused): +To use a specific browser: ```bash -WATCH_BROWSER=none watch +WATCH_BROWSER=firefox watch ``` -The download then stops at `tag=download-auth` for login-walled URLs; -pass `--cookies ` if you still need them. +With `WATCH_BROWSER` unset (the default, and what `none` also means), +a login-walled URL stops at `tag=download-auth` and the error tells you +how to opt in; pass `--cookies ` if you would rather not touch a +browser profile at all. ### Why this works diff --git a/docs/exit-codes.md b/docs/exit-codes.md index 0b3ff53..67204fc 100644 --- a/docs/exit-codes.md +++ b/docs/exit-codes.md @@ -43,7 +43,7 @@ parser around its stderr. Documented exit codes are the first step: | `0` | success | The pipeline completed without errors. In `watch`, both frame extraction and transcribe succeeded. | (no error output) | Consume the stdout payload. | | `1` | general error | An uncategorized error that doesn't fit one of the more specific codes below. Should be rare; if you see it often, the case probably deserves its own row. | `[watch] error: ` | Log the stderr, treat as a real failure. | | `2` | missing dependency | A required binary (`yt-dlp`, `ffmpeg`, `ffprobe`, `jq`, `curl`, `python3`) is not on `PATH`. | `[watch] error: ffmpeg not found on PATH. Install via 'brew install ffmpeg' or 'apt install ffmpeg'.` | Install the missing tool, then retry. Do not auto-retry. | -| `3` | download failed | `yt-dlp` returned a non-zero exit. The pipeline cannot continue without a video file. The stderr line includes a `tag=…` token so callers can grep without needing extra exit codes (see below). | `[watch] error: download failed for tag=download-auth — sign in to the platform in your browser and re-run, or pass --cookies ` | Inspect the `tag=…` value. Auth → retry with cookies. Region → try VPN. Network → wait and retry. Other → file an issue. | +| `3` | download failed | `yt-dlp` returned a non-zero exit. The pipeline cannot continue without a video file. The stderr line includes a `tag=…` token so callers can grep without needing extra exit codes (see below). | `[watch] error: download failed for tag=download-auth — re-run with WATCH_BROWSER=auto to use a signed-in browser on this machine, or pass --cookies ` | Inspect the `tag=…` value. Auth → retry with cookies. Region → try VPN. Network → wait and retry. Other → file an issue. | | `4` | transcribe failed | Frame extraction succeeded but the transcribe step failed. Output is partial: `frame_paths` is populated, `transcript` is `null`. See *Partial success* below. The stderr line includes a `tag=…` token. | `[watch] error: transcribe failed tag=transcribe-quota — top up Kyma credit at https://kymaapi.com/billing?src=cli:watch or set GROQ_API_KEY for BYOK` | Read the partial output and decide. Quota → top up. Timeout → retry with shorter audio. Silent-audio → expected, fall through to frames only. | | `64` | usage error | Bad invocation: missing required argument, unknown flag, malformed URL, or `-h` / `--help` was passed in a context where the caller wants a non-zero. Mirrors `sysexits.h` `EX_USAGE`. | `usage: watch [frame-count] [--cookies ] [--format text\|json] [--pipe] [--no-cache] tag=usage-error` | Do not retry. Fix the invocation. | diff --git a/docs/platforms.md b/docs/platforms.md index 5497f00..5ce8206 100644 --- a/docs/platforms.md +++ b/docs/platforms.md @@ -2,6 +2,7 @@ `watch-cli` works on any platform `yt-dlp` understands. The table below covers the ones we have explicitly tested and the cookie tier they need. +"Auto browser" means `WATCH_BROWSER=auto` (opt-in; never read by default). | Platform | No cookie | Auto browser | Manual `--cookies` | |---|---|---|---| diff --git a/docs/releases.md b/docs/releases.md index f9d435e..7a63c5a 100644 --- a/docs/releases.md +++ b/docs/releases.md @@ -287,8 +287,8 @@ tagged release tarball. curl -fsSL https://github.com/sonpiaz/watch-cli/releases/latest/download/install.sh | bash # pin a specific version -curl -fsSL https://github.com/sonpiaz/watch-cli/releases/download/v0.3.3/install.sh \ - | WATCH_CLI_VERSION=0.3.3 bash +curl -fsSL https://github.com/sonpiaz/watch-cli/releases/download/v0.3.4/install.sh \ + | WATCH_CLI_VERSION=0.3.4 bash ``` `releases/latest/download/` is a GitHub-managed redirect that @@ -350,7 +350,7 @@ The implementer must verify end to end before declaring done: against `watch-cli.tar.gz.sha256`, unpack and run `./watch-cli-0.3.0/install.sh` on a clean container. `watch --version` prints `watch-cli v0.3.0`. -3. **Pinned install.** Run the `WATCH_CLI_VERSION=0.3.3` curl form (the variable goes on the `bash` side of the pipe) +3. **Pinned install.** Run the `WATCH_CLI_VERSION=0.3.4` curl form (the variable goes on the `bash` side of the pipe) on a second clean container. Same result. 4. **Latest install.** Run the `releases/latest/download/install.sh` form. Resolves to v0.3.0. diff --git a/install.sh b/install.sh index 0871bff..2511c1b 100755 --- a/install.sh +++ b/install.sh @@ -3,8 +3,8 @@ # Usage: # curl -fsSL https://github.com/sonpiaz/watch-cli/releases/latest/download/install.sh | bash # or pin a version: -# curl -fsSL https://github.com/sonpiaz/watch-cli/releases/download/v0.3.3/install.sh \ -# | WATCH_CLI_VERSION=0.3.3 bash +# curl -fsSL https://github.com/sonpiaz/watch-cli/releases/download/v0.3.4/install.sh \ +# | WATCH_CLI_VERSION=0.3.4 bash # or, from a clone: # ./install.sh # @@ -52,12 +52,12 @@ watch-cli installer Usage: curl -fsSL https://github.com/sonpiaz/watch-cli/releases/latest/download/install.sh | bash - curl -fsSL https://github.com/sonpiaz/watch-cli/releases/download/v0.3.3/install.sh \ - | WATCH_CLI_VERSION=0.3.3 bash + curl -fsSL https://github.com/sonpiaz/watch-cli/releases/download/v0.3.4/install.sh \ + | WATCH_CLI_VERSION=0.3.4 bash ./install.sh [--with-skill] [--with-mcp] [--with-local] Env: - WATCH_CLI_VERSION Pin to a specific release (e.g. 0.3.3). Set it on + WATCH_CLI_VERSION Pin to a specific release (e.g. 0.3.4). Set it on the bash side of the pipe, not on curl. Unset → install the latest release. diff --git a/lib/version.sh b/lib/version.sh index ed9d04b..3a509bf 100644 --- a/lib/version.sh +++ b/lib/version.sh @@ -12,5 +12,5 @@ # titled `release: vX.Y.Z`, merge, then tag the merge commit `vX.Y.Z` # and push. The tag fires the release workflow. -export WATCH_CLI_VERSION="0.3.3" +export WATCH_CLI_VERSION="0.3.4" export WATCH_CLI_VERSION_STRING="watch-cli v${WATCH_CLI_VERSION}"