From 5f39b0a7137c505e50ebd8cb8415d22f302a5820 Mon Sep 17 00:00:00 2001 From: heznpc Date: Wed, 10 Jun 2026 15:50:04 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20headless=20verified=20(macOS=20+=20Linu?= =?UTF-8?q?x=20CI)=20=E2=80=94=20update=20claims;=20Action=20shipped?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Headless capture is now empirically verified on macOS and Linux CI (both scenes + promo + recordVideo) via the full-chromium channel; the starter's capture.yml uses it as the default. Update README/ko, AGENTS.md invariant, SKILL.md note, and the launch.js header accordingly — including the xvfb 24-bit-depth requirement discovered in CI (8-bit default breaks Page.captureScreenshot). Roadmap: capture-in-CI Action ✅ (ships in browser-extension-starter). Docs/comments only; npm tarball refresh (SKILL.md/launch.js) rides the next release. --- AGENTS.md | 14 +++++++++----- README.ko.md | 4 ++-- README.md | 6 +++--- skills/capture/SKILL.md | 6 ++++-- src/launch.js | 11 ++++++----- 5 files changed, 24 insertions(+), 17 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 5e49515..00edeb4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -15,8 +15,8 @@ npx @starter-series/shotkit --json # run against another checkout ``` Prereqs: `npx playwright install chromium` (one-time); the config's `build` -command must succeed. Loading an MV3 extension needs a **headed** Chromium — -works as-is locally, `xvfb-run` in CI. Exit codes: `0` ok · `1` runtime +command must succeed. Headless works (`HEADED=0`; verified on macOS + Linux CI, +video included); the local default is headed. Exit codes: `0` ok · `1` runtime failure · `2` usage / no config. In `--json` mode progress logs go to stderr; stdout is exactly one JSON object. Useful flags: `--scene `, `--no-video`, `--no-build`. @@ -45,9 +45,13 @@ test/ → unit tests for the pure/safe modules (no browser) - **`serve.js` path-safety**: never feed the request URL straight into `path.join`. Keep the `path.normalize(...).replace(/^(\.\.(\/|\\|$))+/, '')` sanitizer (CodeQL `js/path-injection`). There's a test for it. -- **Headed Chromium**: MV3 extensions only load headed (`channel:'chromium'`, - `headless:false`). Runs headed locally, `xvfb-run` in CI. Don't "optimize" to - headless. +- **Full-Chromium channel**: always `channel:'chromium'` — the headless-shell + strips the extension subsystem; never switch to it. Under the full channel, + headless **works** (`HEADED=0`; verified 2026-06-10 on macOS + Linux CI, + recordVideo included); the local default stays headed for debuggability. + Headed-under-xvfb needs a 24-bit screen + (`xvfb-run -a --server-args="-screen 0 1920x1080x24"` — the 8-bit default + breaks `Page.captureScreenshot`). - **Caption band stacks UNDER the shot** (scene captured at `height - bandHeight`, band appended) so the final image is the exact preset size and no UI is hidden. - **`promo.js` innerHTML** is trusted, build-time content only (the repo's own diff --git a/README.ko.md b/README.ko.md index f928efe..dd44baa 100644 --- a/README.ko.md +++ b/README.ko.md @@ -20,7 +20,7 @@ ## 상태와 범위 (Status & Scope) - **현재 구현된 것** — Playwright 캡처 **엔진**(빌드 → `--load-extension`으로 *빌드된* 익스텐션 로드 → scene 구동 → 스크린샷 → 캡션/면책 밴드 → HTML 프로모 타일 → 데모 `webm` → `STORE_LISTING.md`에서 문안 추출), **에이전트 계약**을 갖춘 **CLI**(`shotkit` — `--json` 머신 출력, 선택적 `path` 인자, `0/1/2` 종료 코드), 양쪽 용도 **사이즈 프리셋**(CWS `1280×800`/`440×280`, SNS `1200×675`/`1200×630`/`1080×1080`), **path-traversal 안전** 로컬 픽스처 서버, 프로그램 API(`capture()`), **Claude Code skill**([`skills/capture/`](skills/capture/SKILL.md)), 셸을 가진 어떤 코딩 에이전트든 호출법을 읽을 수 있는 **AGENTS.md 실행 블록**, 그리고 **npm 패키지** [`@starter-series/shotkit`](https://www.npmjs.com/package/@starter-series/shotkit). `browser-extension-starter`·`skillBridge`가 소비. -- **계획된 것** — **capture-in-CI GitHub Action**(공식 Playwright 이미지 + `xvfb`로 캡처를 CI에서 돌리고 `store-assets/`를 artifact로 업로드 — 로컬 브라우저 0); `starter-series` 플러그인 **마켓플레이스** 등재; **동영상 편집**(`webm → mp4`, 트림, 캡션). +- **계획된 것** — `starter-series` 플러그인 **마켓플레이스** 등재; **동영상 편집**(`webm → mp4`, 트림, 캡션). (capture-in-CI GitHub Action은 ✅ — `browser-extension-starter`의 `capture.yml`로 출하, headless 기본.) - **설계 의도** — *엔진 1개, 표면 여러 개 — 단, 도구 성격에 맞는 표면.* shotkit은 무겁고 파일을 산출하는 빌드 도구라 표면이 CLI(+`--json`)·skill·CI입니다 — MCP가 아니라(하지 않기로 한 것 참고). 캡처는 **결정적**(로그인 불필요 픽스처, freeze된 데이터)이고, 실행이 **실제 빌드본 smoke test를 겸함** — 스크린샷이 나온다 = 그 기능이 출하 코드에서 렌더됨. 모든 샷에 면책 밴드를 합성해 **상표 안전**. - **하지 않기로 한 것** — **MCP 서버**(의도적으로 폐기: 셸이 있는 에이전트에는 `--json` + skill이 세션당 컨텍스트 비용 없이 더 나은 계약이며, 여기엔 빠른 구조화 질의가 없음). repo별 **scene 설정** 제거(어떤 화면이 *당신의* money shot인지는 환원 불가한 의도 — `shotkit.config.js`에 둠). 범용 동영상 편집기(v1은 깔끔한 녹화만; 편집은 계획). 호스티드 서비스(파일을 만지는 캡처는 본질적으로 로컬). - **공개하지 않음** — 없음. @@ -38,7 +38,7 @@ npx playwright install chromium # 최초 1회: shotkit이 구동할 브라우 npx @starter-series/shotkit ``` -> MV3 익스텐션 로드는 **headed** Chromium이 필요합니다 — 로컬 headed, CI는 `xvfb-run`. +> shotkit은 **풀 Chromium**(`channel: 'chromium'`)을 구동합니다 — 확장 서브시스템이 없는 headless-shell이 아닙니다. **headless 동작 검증 완료**(`HEADED=0`; macOS·Linux CI, 영상 포함)이며 starter capture 워크플로의 기본값입니다. 로컬 기본은 디버깅 편의상 headed. CI에서 headed가 필요하면 xvfb에 24비트 화면을 주십시오(`--server-args="-screen 0 1920x1080x24"` — 8비트 기본값은 스크린샷 캡처가 깨집니다). ## 사용 diff --git a/README.md b/README.md index 5df6e77..ac61e9b 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Screenshots · promo images · demo screencast · listing copy. One command. ## Status & Scope - **Currently implemented** — A Playwright capture **engine** (build → launch the *built* extension via `launchPersistentContext(--load-extension)` → drive scenes → screenshot → caption/disclaimer band → promo tile from HTML → demo `webm` → listing copy from `STORE_LISTING.md`), a **CLI** (`shotkit`) with an **agent contract** (`--json` machine output, optional `path` argument, `0/1/2` exit codes), **size presets** for both audiences (CWS `1280×800`/`440×280`, SNS `1200×675`/`1200×630`/`1080×1080`), a **path-traversal-safe** localhost fixture server, a programmatic API (`capture()`), a **Claude Code skill** ([`skills/capture/`](skills/capture/SKILL.md)), an **AGENTS.md run-block** so any shell-having coding agent can invoke it, and the **npm package** [`@starter-series/shotkit`](https://www.npmjs.com/package/@starter-series/shotkit). Consumed by `browser-extension-starter` and `skillBridge`. -- **Planned** — a **capture-in-CI GitHub Action** (run the capture under `xvfb` on the official Playwright image and upload `store-assets/` as an artifact — zero local browser); a listing in the `starter-series` plugin **marketplace**; **video editing** (`webm → mp4`, trim, captions) for SNS. +- **Planned** — a listing in the `starter-series` plugin **marketplace**; **video editing** (`webm → mp4`, trim, captions) for SNS. - **Design intent** — *One engine, many surfaces — matched to the tool's nature.* shotkit is a heavy, file-producing build tool, so its surfaces are CLI (+`--json`), skill, and CI — not MCP (see Non-goals). Captures are **deterministic** (login-free fixtures, frozen data) and the run **doubles as a real-bundle smoke test** — a screenshot only appears if that feature rendered from the shipped code. **Trademark-safe** by construction: a disclaimer band is composited onto every shot. - **Non-goals** — An **MCP server** (dropped by design: agents with a shell get a better contract from `--json` + the skill, without MCP's per-session context cost; nothing here is a fast structured query). Removing the per-repo **scene config** (which screens are *your* money shots is irreducible intent — it lives in your `shotkit.config.js`). A general-purpose video editor (v1 records a clean screencast; editing is Planned). A hosted service (file-touching capture is local by nature). - **Redacted** — none. Ships no private data, credentials, or third-party identifiers. @@ -41,7 +41,7 @@ Zero-install in any repo that has a config: npx @starter-series/shotkit ``` -> Loading an MV3 extension requires a **headed** Chromium — runs headed locally, under `xvfb-run` in CI. Set `HEADED=0` to force headless (not recommended for extensions). +> shotkit launches the **full Chromium** (`channel: 'chromium'`) — never the default headless-shell, which strips the extension subsystem. **Headless works** (`HEADED=0`; verified on macOS and Linux CI, video included) and is the CI default in the starter's capture workflow; the local default stays headed for easy debugging. If you need headed-under-xvfb in CI, give xvfb a 24-bit screen (`xvfb-run -a --server-args="-screen 0 1920x1080x24"`) — the 8-bit default breaks Chromium's screenshot capture. ## Usage @@ -121,7 +121,7 @@ module.exports = { | Claude Code skill ([`skills/capture/`](skills/capture/SKILL.md)) | ✅ now | Claude Code (portable to Codex/Cursor/Gemini via the Agent Skills format) | | `AGENTS.md` run-block | ✅ now | every agent that reads AGENTS.md | | npm package (`@starter-series/shotkit`) | ✅ now | `npx` zero-install | -| Capture-in-CI GitHub Action (xvfb + artifact) | planned | zero-local-browser first run + CI smoke test | +| Capture-in-CI GitHub Action | ✅ now — ships in [`browser-extension-starter`](https://github.com/starter-series/browser-extension-starter)'s `capture.yml` (headless default, 24-bit-xvfb fallback) | zero-local-browser runs + CI smoke test | | `starter-series` marketplace entry | planned | discovery | | Video editing (`webm→mp4`, trim, captions) | planned | SNS clips | diff --git a/skills/capture/SKILL.md b/skills/capture/SKILL.md index ee3059b..a1fde75 100644 --- a/skills/capture/SKILL.md +++ b/skills/capture/SKILL.md @@ -34,7 +34,9 @@ rendered from the shipped code. ## Notes -- Loading an MV3 extension needs a **headed** Chromium: works as-is locally; - use `xvfb-run` in CI. +- Runs the full-Chromium channel; headless works (`HEADED=0 npx …` — verified, + video included) and is the right mode for CI. If headed-in-CI is required, + use `xvfb-run -a --server-args="-screen 0 1920x1080x24"` (the 8-bit xvfb + default breaks Chromium screenshots). - Scenes are the repo's own config — to change *what* is captured, edit `shotkit.config.js`, not shotkit. diff --git a/src/launch.js b/src/launch.js index 6f91621..7a560b3 100644 --- a/src/launch.js +++ b/src/launch.js @@ -5,12 +5,13 @@ * back the dynamically-assigned extension ID. The launch flags mirror the * ones a Playwright extension E2E suite needs — they're not optional: * - * channel: 'chromium' + headless:false + * channel: 'chromium' * The default headless-shell strips the extension subsystem entirely - * (no service worker, MV3 onInstalled never fires). The full Chromium - * channel run headed (or under xvfb in CI) is the only reliable way to - * load MV3 extensions and have content scripts inject. Set HEADED=0 at - * your own risk. + * (no service worker, MV3 onInstalled never fires) — the full Chromium + * channel is required. Under it, headless ALSO works (HEADED=0; verified + * 2026-06-10 on macOS + Linux CI, recordVideo included). The local + * default stays headed for easy debugging. Headed-under-xvfb needs a + * 24-bit screen (--server-args="-screen 0 1920x1080x24"). * * --disable-features=DisableLoadExtensionCommandLineSwitch * Chromium 121+ guards --load-extension behind this flag by default.