Leon4gr45 commited on
Commit
e03e325
·
verified ·
1 Parent(s): 2aba7ef

Delete files * with huggingface_hub

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .hfignore +0 -15
  2. Agent.md +0 -66
  3. Dockerfile +0 -102
  4. README.md +0 -51
  5. __pycache__/pinchtab_browser.cpython-312.pyc +0 -0
  6. app.py +0 -0
  7. config/config.json +0 -15
  8. entrypoint.sh +0 -12
  9. lambda.png +0 -3
  10. openresearcher-title.svg +0 -12
  11. or-logo1.png +0 -3
  12. pinchtab/.devcontainer/devcontainer.json +0 -15
  13. pinchtab/.dockerignore +0 -42
  14. pinchtab/.github/DEFINITION_OF_DONE.md +0 -53
  15. pinchtab/.github/DOCUMENTATION_REVIEW.md +0 -398
  16. pinchtab/.github/GOVERNANCE.md +0 -85
  17. pinchtab/.github/ISSUE_TEMPLATE/bug_report.md +0 -31
  18. pinchtab/.github/ISSUE_TEMPLATE/feature_request.md +0 -20
  19. pinchtab/.github/LABELING_GUIDE.md +0 -246
  20. pinchtab/.github/PULL_REQUEST_TEMPLATE.md +0 -30
  21. pinchtab/.github/RELEASING.md +0 -44
  22. pinchtab/.github/workflows/branch-naming.yml +0 -50
  23. pinchtab/.github/workflows/dashboard.yml +0 -107
  24. pinchtab/.github/workflows/docs-verify.yml +0 -33
  25. pinchtab/.github/workflows/e2e-recent.yml +0 -317
  26. pinchtab/.github/workflows/go-verify.yml +0 -191
  27. pinchtab/.github/workflows/npm-verify.yml +0 -157
  28. pinchtab/.github/workflows/publish-skill.yml +0 -58
  29. pinchtab/.github/workflows/release.yml +0 -238
  30. pinchtab/.gitignore +0 -50
  31. pinchtab/.goreleaser.yml +0 -61
  32. pinchtab/.markdownlint.json +0 -26
  33. pinchtab/.pre-commit-config.yaml +0 -40
  34. pinchtab/CODE_OF_CONDUCT.md +0 -46
  35. pinchtab/CONTRIBUTING.md +0 -21
  36. pinchtab/DEFINITION_OF_DONE.md +0 -5
  37. pinchtab/DEVELOPMENT.md +0 -7
  38. pinchtab/Dockerfile +0 -70
  39. pinchtab/LICENSE +0 -21
  40. pinchtab/README.md +0 -369
  41. pinchtab/RELEASE.md +0 -224
  42. pinchtab/SECURITY.md +0 -27
  43. pinchtab/TESTING.md +0 -176
  44. pinchtab/THIRD_PARTY_LICENSES.md +0 -98
  45. pinchtab/assets/docs-no-background-256.png +0 -0
  46. pinchtab/assets/favicon.png +0 -0
  47. pinchtab/assets/pinchtab-headless.png +0 -3
  48. pinchtab/cmd/pinchtab/build_test.go +0 -111
  49. pinchtab/cmd/pinchtab/cmd_bridge.go +0 -47
  50. pinchtab/cmd/pinchtab/cmd_bridge_test.go +0 -36
.hfignore DELETED
@@ -1,15 +0,0 @@
1
- .git/
2
- .github/
3
- __pycache__/
4
- *.pyc
5
- *.pyo
6
- *.pyd
7
- .env
8
- .venv
9
- venv/
10
- node_modules/
11
- pinchtab/dashboard/node_modules/
12
- pinchtab/dashboard/dist/
13
- pinchtab/pinchtab
14
- .cache/
15
- **/.cache/
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Agent.md DELETED
@@ -1,66 +0,0 @@
1
- # Agent Deployment Information
2
-
3
- ## 1. Deployment Configuration
4
-
5
- ### Target Space
6
- - **Profile:** `Leon4gr45`
7
- - **Space:** `openweb`
8
- - **Full Identifier:** `Leon4gr45/openweb`
9
- - **Frontend Port:** `7860` (mandatory for all Hugging Face Spaces)
10
-
11
- ### Deployment Method
12
- - **Docker SDK** — Chosen for flexibility to run both the Python app and the Pinchtab Go binary.
13
-
14
- ### HF Token
15
- - The environment variable **`HF_TOKEN`** is used for deployment and monitoring.
16
-
17
- ### Required Files
18
- - `Dockerfile`
19
- - `README.md` with Hugging Face YAML frontmatter:
20
- ```yaml
21
- ---
22
- title: OpenResearcher
23
- sdk: docker
24
- app_port: 7860
25
- ---
26
- ```
27
- - `.hfignore`
28
- - `Agent.md`
29
-
30
- ---
31
-
32
- ## 2. API Exposure and Documentation
33
-
34
- ### Mandatory Endpoints
35
- - **`/health`**: Returns HTTP 200 when the app is ready.
36
- - **`/api-docs`**: Documents all available API endpoints.
37
-
38
- ### Functional Endpoints
39
- - **GET `/health`**
40
- - Purpose: Health check for Hugging Face Space.
41
- - Response: `{"status": "ok"}`
42
- - **GET `/api-docs`**
43
- - Purpose: API Documentation.
44
- - Response: HTML documentation.
45
- - **POST `/predict`** (via Gradio)
46
- - Purpose: Main research agent interface.
47
-
48
- ---
49
-
50
- ## 3. Deployment Workflow
51
-
52
- ### Standard Deployment Command
53
- ```bash
54
- hf upload Leon4gr45/openweb --repo-type=space
55
- ```
56
-
57
- ### Monitoring Logs
58
- Build logs:
59
- ```bash
60
- curl -N -H "Authorization: Bearer $HF_TOKEN" "https://huggingface.co/api/spaces/Leon4gr45/openweb/logs/build"
61
- ```
62
-
63
- Run logs:
64
- ```bash
65
- curl -N -H "Authorization: Bearer $HF_TOKEN" "https://huggingface.co/api/spaces/Leon4gr45/openweb/logs/run"
66
- ```
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Dockerfile DELETED
@@ -1,102 +0,0 @@
1
- # Stage 1: Build the React dashboard with Bun.
2
- FROM oven/bun:1 AS dashboard-builder
3
- WORKDIR /app/pinchtab/dashboard
4
- COPY pinchtab/dashboard/package.json pinchtab/dashboard/bun.lock ./
5
- RUN bun install --frozen-lockfile
6
- COPY pinchtab/dashboard/ .
7
- RUN bun run build
8
-
9
- # Stage 2: Compile the Go binary.
10
- FROM golang:1.25-alpine AS go-builder
11
- RUN apk add --no-cache git
12
- WORKDIR /app/pinchtab
13
- COPY pinchtab/go.mod pinchtab/go.sum ./
14
- RUN go mod download
15
- COPY pinchtab/ .
16
- # Create missing embedded files if they don't exist
17
- RUN touch internal/assets/stealth.js internal/assets/readability.js
18
- COPY --from=dashboard-builder /app/pinchtab/dashboard/dist/ internal/dashboard/dashboard/
19
- RUN mv internal/dashboard/dashboard/index.html internal/dashboard/dashboard/dashboard.html || true
20
- RUN go build -o /pinchtab_bin ./cmd/pinchtab
21
-
22
- # Final stage
23
- FROM python:3.12-slim
24
-
25
- # Install system dependencies
26
- RUN apt-get update && apt-get install -y \
27
- chromium \
28
- chromium-driver \
29
- libnss3 \
30
- libasound2 \
31
- libatk1.0-0 \
32
- libcairo2 \
33
- libcups2 \
34
- libdbus-1-3 \
35
- libexpat1 \
36
- libfontconfig1 \
37
- libgbm1 \
38
- libgcc1 \
39
- libglib2.0-0 \
40
- libgtk-3-0 \
41
- libnspr4 \
42
- libpango-1.0-0 \
43
- libpangocairo-1.0-0 \
44
- libstdc++6 \
45
- libx11-6 \
46
- libx11-xcb1 \
47
- libxcb1 \
48
- libxcomposite1 \
49
- libxcursor1 \
50
- libxdamage1 \
51
- libxext6 \
52
- libxfixes3 \
53
- libxi6 \
54
- libxrandr2 \
55
- libxrender1 \
56
- libxss1 \
57
- libxtst6 \
58
- ca-certificates \
59
- fonts-liberation \
60
- lsb-release \
61
- xdg-utils \
62
- wget \
63
- curl \
64
- git \
65
- rustc \
66
- cargo \
67
- pkg-config \
68
- libssl-dev \
69
- && rm -rf /var/lib/apt/lists/*
70
-
71
- WORKDIR /app
72
-
73
- # Copy and install Python dependencies
74
- RUN pip install --no-cache-dir --upgrade pip setuptools wheel
75
- COPY requirements.txt .
76
- RUN pip install --no-cache-dir -r requirements.txt
77
-
78
- # Copy the rest of the application
79
- COPY . .
80
-
81
- # Copy the Pinchtab binary
82
- COPY --from=go-builder /pinchtab_bin /usr/local/bin/pinchtab
83
-
84
- # Expose ports (FastAPI/Gradio)
85
- EXPOSE 7860
86
- # Pinchtab
87
- EXPOSE 9867
88
-
89
- # Create a non-root user
90
- RUN useradd -m -u 1000 user
91
- USER user
92
- ENV HOME=/home/user \
93
- PATH=/home/user/.local/bin:$PATH \
94
- PYTHONPATH=/app
95
-
96
- # Setup entrypoint
97
- # Copy as root, chmod as root, then everything else is fine
98
- USER root
99
- RUN chmod +x entrypoint.sh
100
- USER user
101
-
102
- ENTRYPOINT ["./entrypoint.sh"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
README.md DELETED
@@ -1,51 +0,0 @@
1
- ---
2
- title: OpenResearcher
3
- emoji: 🏃
4
- colorFrom: purple
5
- colorTo: green
6
- sdk: docker
7
- app_port: 7860
8
- pinned: false
9
- ---
10
-
11
- OpenResearcher DeepSearch agent. Inference runs locally in this Space via
12
- ZeroGPU by default; a cloud OpenAI-compatible endpoint is used only as a
13
- backup if local inference fails. An "Inference Mode" switch in the sidebar
14
- (Auto / Local only / Cloud only) lets you override this per request. The
15
- agent also plans an explicit research goal (with a 1-5 vagueness rating) up
16
- front and stops as soon as that goal is met, instead of always running to
17
- the round limit.
18
-
19
- ## Configuration (Space secrets, all optional)
20
-
21
- | Secret | Description |
22
- |---|---|
23
- | `MODEL_NAME` | Local model to run via ZeroGPU. Defaults to `OpenResearcher/Nemotron-3-Nano-30B-A3B`. |
24
- | `OPENAI_BASE_URL` | Cloud backup, used only if local inference errors out. Base URL of an OpenAI-compatible API; `/chat/completions` is appended automatically. |
25
- | `OPENAI_MODEL` | Model name for the cloud backup. Defaults to `MODEL_NAME`. |
26
- | `OPENAI_API_KEY` | Bearer token for the cloud backup. |
27
- | `JINA_MCP_BASE_URL` | Self-hosted Jina MCP server. Used exclusively for web search (`search_web` tool) and as the primary scraping backend (`read_url` tool). Defaults to `https://leon4gr45-jina.hf.space`. Its own API key is configured on that server, so none is needed here. |
28
- | `MAX_CONTEXT_TOKENS` | Fallback input budget (approx. tokens), only used if the context length can't be auto-detected. Defaults to `100000`. |
29
-
30
- The ReAct loop's conversation history is kept under a per-model token
31
- budget automatically. That budget is auto-detected once per run: for the
32
- local model, straight from its loaded config (`max_position_embeddings`
33
- etc.); for the cloud backup, by querying `{OPENAI_BASE_URL}/models` and
34
- reading off whichever context-length field the server reports (e.g. vLLM's
35
- `max_model_len`, OpenRouter's `context_length` - the plain OpenAI API
36
- itself doesn't expose this, so falls back to `MAX_CONTEXT_TOKENS` there).
37
- In Auto mode, the tighter of the two detected limits is used, since either
38
- backend might end up serving the request. Older rounds get truncated
39
- (recent rounds stay verbatim) before each generation call, and again as an
40
- emergency retry if a "context length exceeded" error still slips through.
41
-
42
- Web search uses only Jina's `search_web` tool. (A self-hosted SearXNG
43
- instance was tried, but Hugging Face's abuse-detector auto-paused that
44
- Space over a process-name/cmdline match on "searxng" — not viable here.)
45
- Page fetching tries Jina's `read_url` first, then falls back to a direct
46
- HTTP fetch, then to Serper's scrape API if a key is provided in the UI.
47
-
48
- This Space requires ZeroGPU (or another GPU) hardware to run local model
49
- inference.
50
-
51
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
__pycache__/pinchtab_browser.cpython-312.pyc DELETED
Binary file (7.21 kB)
 
app.py DELETED
The diff for this file is too large to render. See raw diff
 
config/config.json DELETED
@@ -1,15 +0,0 @@
1
- {
2
- "server": {
3
- "bind": "0.0.0.0",
4
- "port": "9867"
5
- },
6
- "instanceDefaults": {
7
- "mode": "headless"
8
- },
9
- "multiInstance": {
10
- "strategy": "simple"
11
- },
12
- "browser": {
13
- "binary": "/usr/bin/google-chrome"
14
- }
15
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
entrypoint.sh DELETED
@@ -1,12 +0,0 @@
1
- #!/bin/bash
2
-
3
- # Start Pinchtab in the background
4
- # Using 'server' command and custom config
5
- export PINCHTAB_CONFIG=/app/config/config.json
6
- pinchtab server &
7
-
8
- # Wait for Pinchtab to start (optional but helpful)
9
- # sleep 5
10
-
11
- # Start the Python application
12
- python app.py
 
 
 
 
 
 
 
 
 
 
 
 
 
lambda.png DELETED

Git LFS Details

  • SHA256: fab6f3889e9ffd3609fc20fe7f8be8c971110fe044ab6ad17f8b6b2665d212aa
  • Pointer size: 131 Bytes
  • Size of remote file: 222 kB
openresearcher-title.svg DELETED
or-logo1.png DELETED

Git LFS Details

  • SHA256: 4d6288904dea6e09ad8ec22bb342309c3bb774a96fc94698798f496649558418
  • Pointer size: 131 Bytes
  • Size of remote file: 618 kB
pinchtab/.devcontainer/devcontainer.json DELETED
@@ -1,15 +0,0 @@
1
- {
2
- "name": "PinchTab",
3
- "image": "mcr.microsoft.com/devcontainers/go:1.23",
4
- "features": {
5
- "ghcr.io/devcontainers/features/docker-in-docker:2": {}
6
- },
7
- "postCreateCommand": "docker compose up -d && go build -o pinchtab ./cmd/pinchtab && echo 'Done! Run: ./pinchtab health'",
8
- "forwardPorts": [9867],
9
- "portsAttributes": {
10
- "9867": {
11
- "label": "PinchTab",
12
- "onAutoForward": "notify"
13
- }
14
- }
15
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pinchtab/.dockerignore DELETED
@@ -1,42 +0,0 @@
1
- # Binaries
2
- pinchtab
3
- browser-bridge
4
-
5
- # Test coverage
6
- coverage.out
7
- *.test
8
-
9
- # Git
10
- .git/
11
- .github/
12
-
13
- # Tests (E2E fixtures, scenarios, results)
14
- tests/
15
-
16
- # Local state
17
- .pinchtab/
18
- .openclaw/
19
-
20
- # macOS
21
- .DS_Store
22
-
23
- # Editor
24
- .vscode/
25
- *.swp
26
-
27
- # Docs (not needed in image)
28
- docs/
29
-
30
- # Development
31
- node_modules/
32
- dashboard/node_modules/
33
- *.log
34
-
35
- # Build caches
36
- .gocache/
37
- .gomodcache/
38
- tmp/
39
-
40
- # Markdown (README, RELEASE, etc.)
41
- *.md
42
- !dashboard/**/*.md
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pinchtab/.github/DEFINITION_OF_DONE.md DELETED
@@ -1,53 +0,0 @@
1
- # Definition of Done (PR Checklist)
2
-
3
- ## Automated ✅ (CI/GitHub enforces these)
4
- These run automatically via `ci.yml`. If your PR fails them, fix and re-push.
5
- - [ ] Go formatting & linting passes (gofmt, vet, golangci-lint)
6
- - [ ] Unit + E2E tests pass (`go test ./...` and `./dev e2e`)
7
- - [ ] Build succeeds (`go build`)
8
- - [ ] CodeQL security scan passes
9
- - [ ] Branch naming follows convention
10
-
11
- ## Manual — Code Quality (Required)
12
- - [ ] **Error handling explicit** — All errors wrapped with `%w`, no silent failures
13
- - [ ] **No regressions** — Verify stealth, token efficiency, session persistence work (test locally if unsure)
14
- - [ ] **SOLID principles** — Functions do one thing, testable, no unnecessary deps
15
- - [ ] **No redundant comments** — Comments explain *why* or *context*, not *what* the code does
16
- - ❌ Bad: `// Loop through items` above `for _, item := range items`
17
- - ❌ Bad: `// Return error` above `return err`
18
- - ✅ Good: `// Prioritize chromium-browser on ARM64 (Raspberry Pi default)`
19
- - ✅ Good: `// Chrome may not be installed in CI, so empty result is valid`
20
-
21
- ## Manual — Testing (Required)
22
- - [ ] **New/changed functionality has tests** — Same-package unit tests preferred
23
- - [ ] **Docker E2E tests run locally** — If you modified handlers/bridge/tabs, run: `./dev e2e` (Docker curl E2E) and/or `./dev e2e cli` (Docker CLI E2E)
24
- - [ ] **npm commands work** (if npm wrapper touched):
25
- - `npm pack` in `/npm/` produces valid tarball
26
- - `npm install -g pinchtab` (or from local tarball) succeeds
27
- - `pinchtab --version` + basic commands work after install
28
-
29
- ## Manual — Documentation (Required)
30
- - [ ] **README.md updated** — If user-facing changes (CLI, API, env vars, install)
31
- - [ ] **/docs/ updated** — If API/architecture/perf changed (optional for small fixes)
32
-
33
- ## Manual — Review (Required)
34
- - [ ] **PR description explains what + why** — Especially stealth/perf/compatibility impact
35
- - [ ] **Commits are atomic** — Logical grouping, good messages
36
- - [ ] **No breaking changes to npm** — Unless explicitly major version bump
37
-
38
- ## Conditional (Only if applicable)
39
- - [ ] Headed-mode tested (if dashboard/UI changes)
40
- - [ ] Breaking changes documented in PR description (if any)
41
-
42
- ---
43
-
44
- ## Quick Checklist (Copy/Paste for PRs)
45
- ```markdown
46
- ## Definition of Done
47
- - [ ] Unit + Docker E2E tests added & passing
48
- - [ ] Error handling explicit (wrapped with %w)
49
- - [ ] No regressions in stealth/perf/persistence
50
- - [ ] No redundant comments (explain why, not what)
51
- - [ ] README/docs updated (if user-facing)
52
- - [ ] npm install works (if npm changes)
53
- ```
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pinchtab/.github/DOCUMENTATION_REVIEW.md DELETED
@@ -1,398 +0,0 @@
1
- # Documentation Review Guide
2
-
3
- ## Purpose
4
-
5
- Code is the source of truth. This guide helps agents audit, validate, and maintain documentation to ensure it stays in sync with the codebase.
6
-
7
- Use this document when asking agents to review documentation for accuracy, completeness, and consistency.
8
-
9
- ---
10
-
11
- ## Quick Summary
12
-
13
- When asked to review documentation, an agent should:
14
-
15
- 1. **Verify all examples match current code behavior**
16
- 2. **Check doc structure against actual codebase**
17
- 3. **Update or remove outdated content**
18
- 4. **Report findings and improvements**
19
-
20
- ---
21
-
22
- ## Detailed Review Process
23
-
24
- ### Phase 1: Code-to-Docs Validation
25
-
26
- #### 1.1 Check All Examples
27
-
28
- For **every code example** in `/docs`:
29
-
30
- - [ ] **Run the example** (if it's executable)
31
- - Does it produce the expected output?
32
- - Does it work with current API/CLI?
33
- - Are environment variables correct?
34
-
35
- - [ ] **Verify API endpoints**
36
- - Do endpoints still exist? (`GET /snapshot`, `POST /action`, etc.)
37
- - Do request/response formats match?
38
- - Are all parameters documented?
39
- - Are deprecated endpoints removed?
40
-
41
- - [ ] **Verify CLI commands**
42
- - Do commands still exist? (`pinchtab config set`, `pinchtab health`, etc.)
43
- - Do they work as documented?
44
- - Are flags/options correct?
45
- - Are deprecated commands removed?
46
-
47
- - [ ] **Verify configuration values**
48
- - Do env vars still exist? (`CHROME_EXTENSION_PATHS`, `BRIDGE_PORT`, etc.)
49
- - Are default values correct?
50
- - Are deprecated config options removed?
51
-
52
- #### 1.2 Check Content Accuracy
53
-
54
- For **every paragraph, section, and claim** in `/docs`:
55
-
56
- - [ ] **API behavior** — Does it accurately describe current behavior?
57
- - [ ] **Performance notes** — Are benchmarks/timings still valid?
58
- - [ ] **Stealth/security claims** — Do they match current implementation?
59
- - [ ] **Feature availability** — Is feature still implemented?
60
- - [ ] **Limitations** — Are noted limitations still present?
61
- - [ ] **Requirements** — Are tool versions, Chrome versions, etc. correct?
62
-
63
- #### 1.3 Cross-Check Against Code
64
-
65
- Use the code as reference:
66
-
67
- ```
68
- For each doc section:
69
- 1. Find the corresponding code file(s)
70
- 2. Compare doc description with actual implementation
71
- 3. If different → docs are WRONG
72
- 4. If missing → docs are INCOMPLETE
73
- 5. If outdated → docs are STALE
74
- ```
75
-
76
- **Example:**
77
- - Doc says: "fill action with refs sets the input value"
78
- - Code says: `FillByNodeID()` exists → docs are CORRECT ✅
79
- - Code says: no `FillByNodeID()` → docs are WRONG ❌
80
-
81
- ---
82
-
83
- ### Phase 2: Structure Review
84
-
85
- #### 2.1 Check Documentation Structure
86
-
87
- **File structure should match codebase organization:**
88
-
89
- ```
90
- docs/
91
- ├── api/
92
- │ ├── endpoints/
93
- │ │ ├── navigation.md
94
- │ │ ├── snapshot.md
95
- │ │ ├── actions.md
96
- │ │ ├── find.md
97
- │ │ ├── evaluate.md
98
- │ │ └── ...
99
- │ └── ...
100
- ├── cli/
101
- │ ├── management.md
102
- │ ├── config.md
103
- │ └── ...
104
- ├── architecture/
105
- │ └── ...
106
- └── index.json
107
- ```
108
-
109
- **Verify:**
110
- - [ ] All major API endpoints documented?
111
- - [ ] All CLI commands documented?
112
- - [ ] Organization makes sense?
113
- - [ ] Groups reflect actual functionality?
114
- - [ ] No duplicate content?
115
- - [ ] No orphaned docs (not linked from index)?
116
-
117
- #### 2.2 Check index.json
118
-
119
- **Verify index.json structure:**
120
-
121
- ```json
122
- {
123
- "sections": [
124
- {
125
- "title": "API",
126
- "path": "api/",
127
- "items": [
128
- {
129
- "title": "Endpoints",
130
- "path": "api/endpoints/",
131
- "items": [
132
- { "title": "Navigation", "path": "api/endpoints/navigation.md" },
133
- { "title": "Snapshot", "path": "api/endpoints/snapshot.md" },
134
- ...
135
- ]
136
- }
137
- ]
138
- }
139
- ]
140
- }
141
- ```
142
-
143
- **Checks:**
144
- - [ ] All sections map to actual directories?
145
- - [ ] All items map to actual files?
146
- - [ ] No broken paths?
147
- - [ ] Nesting depth reasonable (not too deep)?
148
- - [ ] Titles are accurate and clear?
149
- - [ ] Order makes sense (API before examples)?
150
-
151
- ---
152
-
153
- ### Phase 3: Report Findings
154
-
155
- #### If Changes Were Made
156
-
157
- **Create a PR with:**
158
-
159
- 1. **Summary of fixes**
160
- ```markdown
161
- ## Documentation Fixes
162
-
163
- - Updated /docs/api/endpoints/fill.md to document FillByNodeID support (fixes #114)
164
- - Removed deprecated /docs/api/endpoints/old-nav.md (no longer exists in code)
165
- - Fixed /docs/cli/config.md examples to use new config set syntax
166
- - Updated Chrome requirement from 144 to 145+ for extension support
167
- ```
168
-
169
- 2. **List of possible improvements** (as comments in PR)
170
- ```markdown
171
- ## Suggested Improvements
172
-
173
- - [ ] /docs/architecture/ could use a "bridge lifecycle" diagram
174
- - [ ] /docs/api/endpoints/actions.md could add more examples
175
- - [ ] /docs/cli/config.md could show YAML format examples
176
- ```
177
-
178
- 3. **PR description**
179
- ```markdown
180
- ## Documentation Review — Code Alignment
181
-
182
- Verified all examples, API endpoints, CLI commands, and config values against current code.
183
-
184
- ### Changes Made
185
- - X examples updated to match current API
186
- - X endpoints documented/updated
187
- - X CLI commands verified
188
- - X deprecated content removed
189
-
190
- ### No Changes Needed
191
- - API behavior accurate
192
- - Structure organized
193
- - Examples working
194
-
195
- ### Improvements Suggested
196
- See comments in PR for enhancement requests.
197
- ```
198
-
199
- #### If No Changes Needed but Improvements Found
200
-
201
- **Create a GitHub issue:**
202
-
203
- ```markdown
204
- Title: docs: enhancement - add examples for [feature]
205
-
206
- Body:
207
- ## Suggestion
208
-
209
- The documentation could be improved by:
210
-
211
- 1. Adding example for feature X (current docs only show basic usage)
212
- 2. Adding "Common Patterns" section to endpoint Y
213
- 3. Adding troubleshooting section to CLI guide
214
-
215
- ## Reasoning
216
-
217
- Users asking "how do I..." suggests these are common use cases.
218
-
219
- ## References
220
-
221
- - See /docs/api/endpoints/[endpoint].md
222
- - See /docs/cli/[command].md
223
- ```
224
-
225
- ---
226
-
227
- ## Checklist for Agents
228
-
229
- When given the task "Review documentation", verify:
230
-
231
- ### Code Accuracy
232
- - [ ] All examples are tested and working
233
- - [ ] All API endpoints documented correctly
234
- - [ ] All CLI commands documented correctly
235
- - [ ] All config options documented correctly
236
- - [ ] No deprecated content remains
237
- - [ ] All current features documented
238
-
239
- ### Structure
240
- - [ ] index.json paths are correct
241
- - [ ] Directory structure makes sense
242
- - [ ] No orphaned docs
243
- - [ ] Grouping still makes sense
244
- - [ ] Navigation depth reasonable
245
-
246
- ### Output
247
- - [ ] If changes: Create PR with summary + improvements list
248
- - [ ] If no changes but improvements: Create GitHub issue with enhancement request
249
- - [ ] Always: Note which docs were spot-checked and results
250
-
251
- ---
252
-
253
- ## Common Documentation Mistakes to Fix
254
-
255
- ❌ **Outdated examples:**
256
- ```bash
257
- # OLD (deprecated)
258
- pinchtab nav https://pinchtab.com # ← This command was removed
259
-
260
- # NEW (correct)
261
- curl -X POST http://localhost:9867/navigate \
262
- -d '{"url":"https://pinchtab.com"}'
263
- ```
264
-
265
- ❌ **Wrong API format:**
266
- ```javascript
267
- // OLD (incorrect)
268
- response = await fetch('/action', {
269
- body: JSON.stringify({action: 'click', ref: 'e5'})
270
- })
271
-
272
- // NEW (correct)
273
- response = await fetch('/action', {
274
- body: JSON.stringify({kind: 'click', ref: 'e5'})
275
- })
276
- ```
277
-
278
- ❌ **Missing new features:**
279
- ```markdown
280
- <!-- Missing in docs -->
281
- <!-- Code has FillByNodeID support for fills with refs -->
282
- <!-- Docs should say: "fill supports both selectors and refs" -->
283
- ```
284
-
285
- ❌ **Incorrect config examples:**
286
- ```bash
287
- # OLD (deprecated env var name)
288
- export BRIDGE_NAV_TIMEOUT=30
289
-
290
- # NEW (correct)
291
- export PINCHTAB_NAVIGATE_TIMEOUT=30
292
- ```
293
-
294
- ---
295
-
296
- ## Documentation File Locations
297
-
298
- **Root docs location:** `/docs`
299
-
300
- **Key files to check:**
301
-
302
- - `/docs/index.json` — Navigation/structure
303
- - `/docs/README.md` — Overview
304
- - `/docs/api/endpoints/*.md` — API documentation
305
- - `/docs/cli/*.md` — CLI documentation
306
- - `/docs/configuration.md` — Configuration reference
307
- - `/docs/architecture/*.md` — Architecture/design docs
308
- - `/docs/examples/*.md` — Usage examples
309
-
310
- ---
311
-
312
- ## When to Ask for Documentation Review
313
-
314
- **Ask for review:**
315
- - After major feature additions (new endpoints, CLI commands)
316
- - After refactoring (config structure changes, API changes)
317
- - After bug fixes affecting documented behavior
318
- - Periodically (every quarter) for general sync-up
319
- - Before major releases (ensure nothing is stale)
320
-
321
- **Example requests:**
322
-
323
- > "Review documentation against current code. Check all API examples, CLI commands, and config values. Update docs to match code or remove outdated content. Create a PR with a summary of changes."
324
-
325
- > "Audit /docs for accuracy. Update any incorrect examples, remove deprecated features, fix broken index.json paths. Create an issue with improvement suggestions if no changes needed."
326
-
327
- ---
328
-
329
- ## Quality Gates
330
-
331
- A documentation review is **complete** when:
332
-
333
- ✅ All examples are tested and working
334
- ✅ All API endpoints match code implementation
335
- ✅ All CLI commands match code implementation
336
- ✅ All config options match code implementation
337
- ✅ No deprecated content remains
338
- ✅ index.json structure is accurate
339
- ✅ Documentation structure reflects codebase
340
- ✅ Either: PR with fixes submitted, OR Issue with improvements created
341
-
342
- ---
343
-
344
- ## Example Review Output (PR)
345
-
346
- ```markdown
347
- ## Documentation Review — Code Alignment
348
-
349
- All examples, endpoints, and CLI commands verified against current codebase.
350
-
351
- ### ✅ Changes Made (3 files)
352
-
353
- 1. **docs/api/endpoints/actions.md**
354
- - Updated fill example to show ref support (new in PR #119)
355
- - Added FillByNodeID code path documentation
356
-
357
- 2. **docs/cli/config.md**
358
- - Fixed config set examples (new syntax with PR #120)
359
- - Added YAML output format example
360
-
361
- 3. **docs/api/endpoints/find.md**
362
- - Added Find endpoint documentation (new in feat/allocation-strategies)
363
-
364
- ### ✅ Verified (No Changes Needed)
365
-
366
- - All navigate endpoint examples working
367
- - All snapshot filters documented correctly
368
- - All action kinds (click, type, press, etc.) verified
369
- - Config sections (server, chrome, orchestrator) accurate
370
- - Environment variable names current
371
-
372
- ### 💡 Suggested Improvements
373
-
374
- See comments below for enhancement requests (low priority):
375
- - [ ] Add "common patterns" section to actions.md
376
- - [ ] Add troubleshooting FAQ to cli/config.md
377
- - [ ] Create /docs/examples/multi-step-workflow.md
378
-
379
- ### Review Stats
380
-
381
- - Examples tested: 12/12 ✅
382
- - Endpoints checked: 8/8 ✅
383
- - CLI commands checked: 5/5 ✅
384
- - Config sections checked: 4/4 ✅
385
- - Deprecated content found: 0 ✅
386
- - Structure issues found: 0 ✅
387
- ```
388
-
389
- ---
390
-
391
- ## Related Documents
392
-
393
- - [DEFINITION_OF_DONE.md](./DEFINITION_OF_DONE.md) — PR checklist
394
- - [LABELING_GUIDE.md](./LABELING_GUIDE.md) — Issue labeling guide
395
-
396
- ---
397
-
398
- Last updated: 2026-03-04
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pinchtab/.github/GOVERNANCE.md DELETED
@@ -1,85 +0,0 @@
1
- # GitHub Governance Documents
2
-
3
- This directory contains pinchtab's governance and workflow documents referenced by developers and automated agents.
4
-
5
- ## Documents
6
-
7
- ### [DEFINITION_OF_DONE.md](./DEFINITION_OF_DONE.md)
8
- **Purpose:** PR checklist for code quality, testing, and documentation before merging.
9
-
10
- **For:** All contributors submitting PRs
11
- **How to use:** Check this list before submitting a PR, or ask agents to verify PRs against it.
12
-
13
- **Key sections:**
14
- - Automated checks (CI enforces)
15
- - Manual code quality requirements
16
- - Testing requirements
17
- - Documentation requirements
18
- - Quick checklist for copy/paste
19
-
20
- ---
21
-
22
- ### [LABELING_GUIDE.md](./LABELING_GUIDE.md)
23
- **Purpose:** Reference guide for issue and PR labeling to maintain consistent triage.
24
-
25
- **For:** Maintainers triaging issues, agents reviewing tickets
26
- **How to use:** Point agents to this guide when asking them to review and label open issues.
27
-
28
- **Key sections:**
29
- - 3-tier labeling system (Type → Status → Priority)
30
- - Decision tree for triage
31
- - Guidelines for agents
32
- - Examples of well-labeled issues
33
- - Common mistakes to avoid
34
-
35
- ---
36
-
37
- ### [DOCUMENTATION_REVIEW.md](./DOCUMENTATION_REVIEW.md)
38
- **Purpose:** Guide for auditing documentation to ensure it stays in sync with code (code is source of truth).
39
-
40
- **For:** Agents maintaining documentation, quality assurance
41
- **How to use:** Point agents to this guide when asking them to audit docs for accuracy and consistency.
42
-
43
- **Key sections:**
44
- - Code-to-docs validation (examples, endpoints, commands, config)
45
- - Structure review (organization, index.json, grouping)
46
- - Output requirements (PR with fixes or GitHub issue with improvements)
47
- - Common documentation mistakes to fix
48
- - Quality gates for completion
49
-
50
- ---
51
-
52
- ## Quick Reference
53
-
54
- | Document | Audit For | Used By |
55
- |----------|-----------|---------|
56
- | DEFINITION_OF_DONE.md | PR quality before merge | Developers, agents, reviewers |
57
- | LABELING_GUIDE.md | Consistent issue triage | Maintainers, agents |
58
- | DOCUMENTATION_REVIEW.md | Documentation accuracy vs code | Agents, QA |
59
-
60
- ---
61
-
62
- ## How Agents Use These
63
-
64
- When asking an agent to help with PRs, issues, or documentation:
65
-
66
- **For PR review:**
67
- > "Review this PR against `.github/DEFINITION_OF_DONE.md` and ensure it meets all requirements."
68
-
69
- **For issue triage:**
70
- > "Review open issues and apply labels according to `.github/LABELING_GUIDE.md`. Ensure Type + Status + Priority are consistent."
71
-
72
- **For documentation audit:**
73
- > "Review documentation against current code using `.github/DOCUMENTATION_REVIEW.md`. Verify all examples match code, update or remove outdated content, report changes in a PR or improvements in a GitHub issue."
74
-
75
- ---
76
-
77
- ## Maintenance
78
-
79
- - **DEFINITION_OF_DONE.md** — Update when code quality standards change
80
- - **LABELING_GUIDE.md** — Update when new labels are added or workflow changes
81
- - This **README.md** — Update when new governance documents are added
82
-
83
- ---
84
-
85
- Last updated: 2026-03-04
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pinchtab/.github/ISSUE_TEMPLATE/bug_report.md DELETED
@@ -1,31 +0,0 @@
1
- ---
2
- name: Bug report
3
- about: Create a report to help us improve
4
- title: ''
5
- labels: bug
6
- assignees: ''
7
-
8
- ---
9
-
10
- **Describe the bug**
11
- A clear and concise description of what the bug is.
12
-
13
- **To Reproduce**
14
- Steps to reproduce the behavior:
15
- 1. Start Pinchtab with `...`
16
- 2. Send request to `/...` with body `...`
17
- 3. See error
18
-
19
- **Expected behavior**
20
- A clear and concise description of what you expected to happen.
21
-
22
- **Screenshots/Logs**
23
- If applicable, add screenshots or console output to help explain your problem.
24
-
25
- **Environment (please complete the following information):**
26
- - OS: [e.g. macOS, Ubuntu]
27
- - Browser Version: [e.g. Chrome 120]
28
- - Go Version: [e.g. 1.25.0]
29
-
30
- **Additional context**
31
- Add any other context about the problem here.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pinchtab/.github/ISSUE_TEMPLATE/feature_request.md DELETED
@@ -1,20 +0,0 @@
1
- ---
2
- name: Feature request
3
- about: Suggest an idea for this project
4
- title: ''
5
- labels: enhancement
6
- assignees: ''
7
-
8
- ---
9
-
10
- **Is your feature request related to a problem? Please describe.**
11
- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12
-
13
- **Describe the solution you'd like**
14
- A clear and concise description of what you want to happen.
15
-
16
- **Describe alternatives you've considered**
17
- A clear and concise description of any alternative solutions or features you've considered.
18
-
19
- **Additional context**
20
- Add any other context or screenshots about the feature request here.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pinchtab/.github/LABELING_GUIDE.md DELETED
@@ -1,246 +0,0 @@
1
- # Labeling Guide
2
-
3
- This document describes the labeling system used for issues and PRs in pinchtab. Use this guide when triaging tickets or asking agents to review open issues.
4
-
5
- ---
6
-
7
- ## Quick Reference
8
-
9
- Every issue should have:
10
- 1. **Exactly one Type label** (bug, enhancement, documentation, question, etc.)
11
- 2. **One Status label** (ready, in-progress, blocked, fixed-unreleased, needs-investigation)
12
- 3. **One Priority label** (high, medium, low) — optional for documentation or questions
13
-
14
- ---
15
-
16
- ## Tier 1: Issue Type (Mandatory)
17
-
18
- Choose **exactly one**:
19
-
20
- | Label | Color | Usage |
21
- |-------|-------|-------|
22
- | `bug` | 🔴 Red | Something isn't working correctly |
23
- | `enhancement` | 🔵 Cyan | New feature or capability request |
24
- | `documentation` | 🔵 Blue | Improvements/additions to README, docs, or code comments |
25
- | `question` | 🟣 Purple | Request for clarification or information |
26
- | `good first issue` | 🟣 Purple | Good for newcomers (in addition to type label) |
27
- | `help wanted` | 🟢 Green | Needs extra attention or expertise |
28
- | `dependencies` | 🔵 Blue | Dependency updates (PR label) |
29
- | `invalid` | 🟡 Yellow | Doesn't seem right; requires clarification |
30
- | `duplicate` | ⚪ Gray | Already exists (close and reference original) |
31
- | `wontfix` | ⚪ White | Deliberate decision not to fix |
32
-
33
- ### Examples
34
-
35
- - **Bug:** "fill action silently no-ops when using refs" → `bug`
36
- - **Enhancement:** "Add CHROME_EXTENSION_PATHS support" → `enhancement`
37
- - **Documentation:** "Update README with new CLI commands" → `documentation`
38
- - **Question:** "How do I configure stealth mode?" → `question`
39
-
40
- ---
41
-
42
- ## Tier 2: Status (Recommended)
43
-
44
- Choose **one** to show current state:
45
-
46
- | Label | Color | Meaning | When to Use |
47
- |-------|-------|---------|------------|
48
- | `status: ready` | 🔵 Blue | Ready to start work | Issue is fully understood, no blockers, waiting for someone to pick it up |
49
- | `status: in-progress` | 🟡 Yellow | Actively being worked on | Someone has claimed the issue and is working on a fix/feature |
50
- | `status: blocked` | 🔴 Red | Blocked by something | Waiting on external dependency, another issue, or decision |
51
- | `status: fixed-unreleased` | 🟢 Green | Fix merged, not in release | PR is merged but feature/fix isn't in a released version yet |
52
- | `status: needs-investigation` | 🔴 Red | Needs debugging/research | Not enough info; requires investigation before work can start |
53
-
54
- ### Workflow
55
-
56
- ```
57
- New Issue
58
- ↓
59
- [Triage] Add Type + Status
60
- ↓
61
- status: needs-investigation (if unclear) or status: ready (if clear)
62
- ↓
63
- Developer picks it up
64
- ↓
65
- status: in-progress
66
- ↓
67
- PR submitted
68
- ↓
69
- PR merged
70
- ↓
71
- status: fixed-unreleased (for bugs) or remove status (for features)
72
- ↓
73
- Release cut
74
- ↓
75
- Remove status label (feature is released)
76
- ```
77
-
78
- ---
79
-
80
- ## Tier 3: Priority (Optional, for bugs/enhancements)
81
-
82
- Choose **one** to indicate urgency:
83
-
84
- | Label | Color | Level | When to Use |
85
- |-------|-------|-------|------------|
86
- | `priority: high` | 🔴 Red | Critical; blocks users | Security vulnerability, critical bug, high-demand feature |
87
- | `priority: medium` | 🟡 Yellow | Normal; important for roadmap | Important bug or feature; should be done soon |
88
- | `priority: low` | 🟢 Green | Nice to have; can defer | Minor improvement, polish, or edge case |
89
-
90
- ### Examples
91
-
92
- - **High:** "SafePath() fails to block path traversal" (security) → `bug` + `priority: high`
93
- - **Medium:** "Snapshot doesn't work inside iframes" (affects users) → `bug` + `priority: medium`
94
- - **Low:** "Consider migrating to PINCHTAB_* env vars" (nice refactor) → `enhancement` + `priority: low`
95
-
96
- ---
97
-
98
- ## Special Labels
99
-
100
- ### `good first issue`
101
- Add this **in addition to the type label** for issues that are good entry points for new contributors.
102
-
103
- **Criteria:**
104
- - Clear problem statement
105
- - Solution is straightforward
106
- - Doesn't require deep knowledge of codebase
107
- - Estimated effort: <4 hours
108
-
109
- **Example:** "documentation: Add /find endpoint example to README" → `documentation` + `good first issue`
110
-
111
- ### `help wanted`
112
- Indicates the issue needs expertise or has been stalled.
113
-
114
- **When to use:**
115
- - Needs specific expertise (e.g., "needs Windows testing")
116
- - Issue has been open >2 weeks without progress
117
- - Complex problem that benefits from external input
118
-
119
- ---
120
-
121
- ## Decision Tree for Triage
122
-
123
- ```
124
- New issue arrives
125
- ↓
126
- 1. Is it a valid issue?
127
- NO → `invalid` + close
128
- YES ↓
129
- 2. What is it?
130
- → Bug? `bug` + assess severity → `priority: high|medium|low`
131
- → New feature? `enhancement` + assess importance → `priority: high|medium|low`
132
- → Docs missing? `documentation` + no priority needed
133
- → Unclear? `question` + no priority needed
134
- ↓
135
- 3. Can we start work immediately?
136
- YES → `status: ready`
137
- NO ↓
138
- 4. Why not?
139
- → Need info → `status: needs-investigation`
140
- → Waiting on something → `status: blocked`
141
- → Already fixed → `status: fixed-unreleased`
142
- ```
143
-
144
- ---
145
-
146
- ## Guidelines for Agents
147
-
148
- When reviewing open issues, follow this checklist:
149
-
150
- - [ ] **Each issue has exactly one Type label** (bug, enhancement, documentation, question)
151
- - [ ] **Bugs have Priority labels** (high, medium, low)
152
- - [ ] **Enhancements have Priority labels** (high, medium, low)
153
- - [ ] **Each issue has a Status label** (ready, in-progress, blocked, fixed-unreleased, needs-investigation)
154
- - [ ] **No duplicate labels** (e.g., two type labels on one issue)
155
- - [ ] **Status matches reality** (e.g., `status: in-progress` has an active PR)
156
- - [ ] **Stale issues reviewed** (e.g., `status: blocked` for >2 weeks should note why)
157
-
158
- ---
159
-
160
- ## Current Label Inventory
161
-
162
- **Type labels (10):**
163
- - bug, enhancement, documentation, question, good first issue, help wanted, dependencies, invalid, duplicate, wontfix
164
-
165
- **Status labels (5):**
166
- - status: ready, status: in-progress, status: blocked, status: fixed-unreleased, status: needs-investigation
167
-
168
- **Priority labels (3):**
169
- - priority: high, priority: medium, priority: low
170
-
171
- **Code labels (1):**
172
- - javascript
173
-
174
- ---
175
-
176
- ## Examples of Well-Labeled Issues
177
-
178
- ### Example 1: Security Bug
179
- ```
180
- Title: SafePath() fails to block path traversal on Windows
181
- Labels: bug, priority: high, status: ready
182
- ```
183
- **Why:** Critical security issue, ready to work on, high priority.
184
-
185
- ### Example 2: Feature Request (Ready)
186
- ```
187
- Title: feat: Resource Pool with pluggable allocation strategies
188
- Labels: enhancement, priority: medium, status: ready
189
- ```
190
- **Why:** Enhancement, medium priority (important for roadmap), ready to start.
191
-
192
- ### Example 3: Feature (Blocked)
193
- ```
194
- Title: feat: Semantic Element Selection via NLP (/find endpoint)
195
- Labels: enhancement, priority: medium, status: blocked
196
- ```
197
- **Why:** Enhancement, medium priority, but blocked (perhaps waiting on design decision).
198
-
199
- ### Example 4: Bug (Not Ready)
200
- ```
201
- Title: click and humanClick fail to trigger Bootstrap dropdown
202
- Labels: bug, priority: medium, status: needs-investigation
203
- ```
204
- **Why:** Bug, medium priority, but needs investigation (reproduction steps unclear).
205
-
206
- ### Example 5: Already Fixed
207
- ```
208
- Title: Installation issue
209
- Labels: bug, priority: high, status: fixed-unreleased
210
- ```
211
- **Why:** Critical bug, but fix is already merged and waiting for release.
212
-
213
- ---
214
-
215
- ## Common Mistakes
216
-
217
- ❌ **Don't do this:**
218
-
219
- | Mistake | Why | Fix |
220
- |---------|-----|-----|
221
- | Two Type labels on one issue | Ambiguous; what is it really? | Choose one type only |
222
- | Status label on closed issue | Status should reflect open work | Remove status label when closing |
223
- | No Priority on bugs | Can't triage effectively | Add priority: high/medium/low to all bugs |
224
- | `status: in-progress` with no PR | Misleading; is someone actually working on it? | Only use when work is actively underway |
225
- | Forgetting Status labels | No visibility into progress | Always add a status label during triage |
226
-
227
- ---
228
-
229
- ## Related Documents
230
-
231
- - [DEFINITION_OF_DONE.md](./DEFINITION_OF_DONE.md) — Checklist for PRs before merge
232
- - [CONTRIBUTING.md](../CONTRIBUTING.md) — Contribution guidelines (if present)
233
-
234
- ---
235
-
236
- ## Last Updated
237
-
238
- 2026-03-04
239
-
240
- ---
241
-
242
- ## Summary
243
-
244
- **For agents:** Use this guide to understand and apply labels when triaging issues. Ensure every issue has Type + Status + Priority (if applicable).
245
-
246
- **For maintainers:** Review labels during triage and ensure consistency. Use the Decision Tree above as a quick reference.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pinchtab/.github/PULL_REQUEST_TEMPLATE.md DELETED
@@ -1,30 +0,0 @@
1
- ## What Changed?
2
- <!-- Brief description of the change -->
3
-
4
- ## Why?
5
- <!-- Motivation, issue being fixed, or feature request -->
6
-
7
- ## Testing
8
- <!-- How did you test this? Include command examples if applicable -->
9
-
10
- - [ ] Unit and/or Docker E2E tests added or updated
11
- - [ ] Manual testing completed (describe below)
12
-
13
- ## Checklist
14
- See [DEFINITION_OF_DONE.md](./DEFINITION_OF_DONE.md) for the full checklist.
15
-
16
- **Automated (CI enforces):**
17
- - [ ] gofmt + golangci-lint passes
18
- - [ ] All tests pass
19
- - [ ] Build succeeds
20
-
21
- **Manual:**
22
- - [ ] Error handling explicit (wrapped with `%w`)
23
- - [ ] No regressions in stealth/performance/persistence
24
- - [ ] README/CHANGELOG updated (if user-facing)
25
- - [ ] npm install works (if npm changes)
26
-
27
- ## Impact
28
- <!-- Any performance, compatibility, or breaking changes? -->
29
-
30
- ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pinchtab/.github/RELEASING.md DELETED
@@ -1,44 +0,0 @@
1
- # Releasing PinchTab
2
-
3
- This is the release checklist for the GitHub tag-driven release pipeline.
4
-
5
- ## One-time setup
6
-
7
- - Create the public tap repository `pinchtab/homebrew-tap`.
8
- - Add the `HOMEBREW_TAP_GITHUB_TOKEN` Actions secret to `pinchtab/pinchtab`.
9
- - The token must have write access to `pinchtab/homebrew-tap`.
10
-
11
- ## What the release workflow does
12
-
13
- Pushing a tag like `v0.7.0` triggers [release.yml](/Users/luigi/dev/prj/giago/pt-bosch/.github/workflows/release.yml), which:
14
-
15
- 1. Builds release binaries and creates the GitHub release via GoReleaser.
16
- 2. Publishes the npm package.
17
- 3. Builds and publishes container images.
18
- 4. Generates a Homebrew formula PR against `pinchtab/homebrew-tap`.
19
-
20
- ## Release steps
21
-
22
- 1. Verify the branch you want to release is merged to `main`.
23
- 2. Push the release tag:
24
-
25
- ```bash
26
- git tag v0.7.0
27
- git push origin v0.7.0
28
- ```
29
-
30
- 3. Watch the `Release` workflow in GitHub Actions.
31
- 4. Confirm GoReleaser opens a PR in `pinchtab/homebrew-tap`.
32
- 5. Merge that PR.
33
-
34
- After the tap PR is merged, users can install with:
35
-
36
- ```bash
37
- brew install pinchtab/tap/pinchtab
38
- ```
39
-
40
- ## Notes
41
-
42
- - The Homebrew formula is not published until the tap PR is merged.
43
- - No extra automation is required in `homebrew-tap` unless you want auto-merge.
44
- - The release workflow can also be run manually with `workflow_dispatch` and a tag input.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pinchtab/.github/workflows/branch-naming.yml DELETED
@@ -1,50 +0,0 @@
1
- name: Branch Naming
2
-
3
- on:
4
- pull_request:
5
- types: [opened, synchronize, reopened]
6
-
7
- permissions:
8
- contents: read
9
-
10
- jobs:
11
- check-branch-name:
12
- name: Branch Name Convention
13
- runs-on: ubuntu-latest
14
- if: github.event.pull_request.draft == false
15
- steps:
16
- - name: Check branch name
17
- run: |
18
- BRANCH="${GITHUB_HEAD_REF}"
19
- echo "Branch: $BRANCH"
20
-
21
- # Allow long-running branches
22
- if [[ "$BRANCH" =~ ^(main|develop|staging)$ ]]; then
23
- echo "✅ Long-running branch: $BRANCH"
24
- exit 0
25
- fi
26
-
27
- # Allow dependabot branches
28
- if [[ "$BRANCH" =~ ^dependabot/ ]]; then
29
- echo "✅ Dependabot branch: $BRANCH"
30
- exit 0
31
- fi
32
-
33
- # Enforce prefix/description pattern
34
- PATTERN="^(feature|feat|fix|hotfix|docs|chore|refactor|test)/[a-z0-9][a-z0-9-]*$"
35
- if [[ ! "$BRANCH" =~ $PATTERN ]]; then
36
- echo "❌ Branch name '$BRANCH' doesn't match convention."
37
- echo ""
38
- echo "Expected format: <prefix>/<description>"
39
- echo " Prefixes: feature, feat, fix, hotfix, docs, chore, refactor, test"
40
- echo " Description: lowercase, hyphens, no special chars"
41
- echo ""
42
- echo "Examples:"
43
- echo " feature/42-add-search-filter"
44
- echo " fix/chrome-orphan-cleanup"
45
- echo " hotfix/critical-api-fix"
46
- echo " docs/update-docker-guide"
47
- exit 1
48
- fi
49
-
50
- echo "✅ Branch name '$BRANCH' follows convention."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pinchtab/.github/workflows/dashboard.yml DELETED
@@ -1,107 +0,0 @@
1
- name: Dashboard
2
-
3
- on:
4
- push:
5
- branches: [main]
6
- paths:
7
- - 'dashboard/**'
8
- - 'internal/api/types/**'
9
- - 'scripts/build-dashboard.sh'
10
- - '.github/workflows/dashboard.yml'
11
- pull_request:
12
- paths:
13
- - 'dashboard/**'
14
- - 'internal/api/types/**'
15
- - 'scripts/build-dashboard.sh'
16
- - '.github/workflows/dashboard.yml'
17
- workflow_dispatch:
18
-
19
- env:
20
- FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
21
-
22
- permissions:
23
- contents: read
24
-
25
- defaults:
26
- run:
27
- working-directory: dashboard
28
-
29
- jobs:
30
- check:
31
- name: Lint, Type Check & Test
32
- runs-on: ubuntu-latest
33
- if: github.event.pull_request.draft == false
34
- steps:
35
- - uses: actions/checkout@v5
36
-
37
- - name: Setup Go
38
- uses: actions/setup-go@v6
39
- with:
40
- go-version: '1.26'
41
-
42
- - name: Setup Bun
43
- uses: oven-sh/setup-bun@v2
44
- with:
45
- bun-version: latest
46
-
47
- - name: Install tygo
48
- run: go install github.com/gzuidhof/tygo@latest
49
-
50
- - name: Install dependencies
51
- run: bun install --frozen-lockfile
52
-
53
- - name: Generate dashboard types
54
- run: |
55
- $HOME/go/bin/tygo generate
56
- npx prettier --write src/generated/types.ts
57
- git diff --exit-code -- src/generated/types.ts
58
-
59
- - name: TypeScript check
60
- run: bun run typecheck
61
-
62
- - name: ESLint
63
- run: bun run lint
64
-
65
- - name: Prettier check
66
- run: bun run format:check
67
-
68
- - name: Run tests
69
- run: bun run test:run
70
-
71
- build:
72
- name: Build
73
- runs-on: ubuntu-latest
74
- needs: check
75
- steps:
76
- - uses: actions/checkout@v5
77
-
78
- - name: Setup Go
79
- uses: actions/setup-go@v6
80
- with:
81
- go-version: '1.26'
82
-
83
- - name: Setup Bun
84
- uses: oven-sh/setup-bun@v2
85
- with:
86
- bun-version: latest
87
-
88
- - name: Install tygo
89
- run: go install github.com/gzuidhof/tygo@latest
90
-
91
- - name: Install dependencies
92
- run: bun install --frozen-lockfile
93
-
94
- - name: Generate dashboard types
95
- run: |
96
- $HOME/go/bin/tygo generate
97
- npx prettier --write src/generated/types.ts
98
-
99
- - name: Build
100
- run: bun run build
101
-
102
- - name: Upload build artifacts
103
- uses: actions/upload-artifact@v4
104
- with:
105
- name: dashboard-dist
106
- path: dashboard/dist/
107
- retention-days: 7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pinchtab/.github/workflows/docs-verify.yml DELETED
@@ -1,33 +0,0 @@
1
- name: Documentation Verification
2
-
3
- on:
4
- push:
5
- branches: [main]
6
- paths:
7
- - 'docs/**'
8
- - '.github/workflows/docs-verify.yml'
9
- pull_request:
10
- branches: [main]
11
- paths:
12
- - 'docs/**'
13
- - '.github/workflows/docs-verify.yml'
14
-
15
- env:
16
- FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
17
-
18
- permissions:
19
- contents: read
20
-
21
- jobs:
22
- docs-validate:
23
- name: Validate Documentation
24
- runs-on: ubuntu-latest
25
- if: github.event.pull_request.draft == false
26
- steps:
27
- - uses: actions/checkout@v5
28
-
29
- - name: Check docs.json references
30
- run: |
31
- echo "🔍 Validating docs.json..."
32
- ./scripts/check-docs-json.sh
33
- shell: bash
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pinchtab/.github/workflows/e2e-recent.yml DELETED
@@ -1,317 +0,0 @@
1
- name: E2E Recent Tests
2
-
3
- permissions:
4
- contents: read
5
-
6
- on:
7
- push:
8
- branches: [main]
9
- paths-ignore:
10
- - '**.md'
11
- - 'docs/**'
12
- - 'LICENSE'
13
- - '.gitignore'
14
- - 'skill/**'
15
- - 'plugin/**'
16
- pull_request:
17
- branches: [main]
18
- paths-ignore:
19
- - '**.md'
20
- - 'docs/**'
21
- - 'LICENSE'
22
- - '.gitignore'
23
- - 'skill/**'
24
- - 'plugin/**'
25
- workflow_dispatch:
26
-
27
- env:
28
- FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
29
-
30
- jobs:
31
- recent:
32
- name: E2E Recent Tests
33
- runs-on: ubuntu-latest
34
- if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
35
- timeout-minutes: 10
36
-
37
- steps:
38
- - name: Checkout
39
- uses: actions/checkout@v5
40
-
41
- - name: Set up Go
42
- uses: actions/setup-go@v6
43
- with:
44
- go-version-file: go.mod
45
- cache: true
46
-
47
- - name: Set up Docker Buildx
48
- uses: docker/setup-buildx-action@v3
49
-
50
- - name: Run recent E2E tests
51
- id: e2e
52
- run: |
53
- set +e
54
- mkdir -p tests/e2e/results
55
-
56
- ./dev e2e recent 2>&1 | tee e2e-output.log
57
- EXIT_CODE=${PIPESTATUS[0]}
58
-
59
- PASSED=$(grep -oP 'Passed:\s*\K\d+' e2e-output.log | tail -1 || true)
60
- FAILED=$(grep -oP 'Failed:\s*\K\d+' e2e-output.log | tail -1 || true)
61
- FAILURES=$(grep -E '✗.*failed' e2e-output.log | sed 's/.*✗ /- /' | head -10 || true)
62
- PASSED=${PASSED:-0}
63
- FAILED=${FAILED:-0}
64
-
65
- echo "passed=$PASSED" >> $GITHUB_OUTPUT
66
- echo "failed=$FAILED" >> $GITHUB_OUTPUT
67
-
68
- {
69
- echo 'failures<<EOF'
70
- echo "$FAILURES"
71
- echo 'EOF'
72
- } >> $GITHUB_OUTPUT
73
-
74
- exit $EXIT_CODE
75
- env:
76
- DOCKER_BUILDKIT: 1
77
- COMPOSE_DOCKER_CLI_BUILD: 1
78
-
79
- - name: Dump pinchtab logs on failure
80
- if: failure()
81
- run: |
82
- echo "==== pinchtab logs ===="
83
- docker compose -f tests/e2e/docker-compose.yml logs pinchtab || true
84
- echo ""
85
- echo "==== filtered Chrome/extension lines ===="
86
- docker compose -f tests/e2e/docker-compose.yml logs pinchtab 2>/dev/null | grep -Ei 'chrome|extension|devtools|load-extension|disable-extensions|headless|warning|error' || true
87
- echo ""
88
- echo "==== instance inventory ===="
89
- INSTANCES_JSON=$(curl -sf http://localhost:9999/instances || true)
90
- if [ -n "$INSTANCES_JSON" ]; then
91
- echo "$INSTANCES_JSON"
92
- if command -v jq >/dev/null 2>&1; then
93
- echo "$INSTANCES_JSON" | jq -r '.[].id' | while read -r inst_id; do
94
- [ -z "$inst_id" ] && continue
95
- echo ""
96
- echo "==== logs for $inst_id ===="
97
- curl -sf "http://localhost:9999/instances/$inst_id/logs" || true
98
- done
99
- fi
100
- else
101
- echo "unable to fetch /instances from localhost:9999"
102
- fi
103
-
104
- - name: Post summary
105
- if: always()
106
- run: |
107
- PASSED="${{ steps.e2e.outputs.passed }}"
108
- FAILED="${{ steps.e2e.outputs.failed }}"
109
- PASSED="${PASSED:-0}"
110
- FAILED="${FAILED:-0}"
111
-
112
- if [ "${{ steps.e2e.outcome }}" = "success" ]; then
113
- echo "## ✅ E2E Recent Tests Passed" >> $GITHUB_STEP_SUMMARY
114
- if [ "$PASSED" = "0" ]; then
115
- echo "All tests passed" >> $GITHUB_STEP_SUMMARY
116
- else
117
- echo "**$PASSED** tests passed" >> $GITHUB_STEP_SUMMARY
118
- fi
119
- else
120
- echo "## ❌ E2E Recent Tests Failed" >> $GITHUB_STEP_SUMMARY
121
- echo "**Passed:** $PASSED" >> $GITHUB_STEP_SUMMARY
122
- echo "**Failed:** $FAILED" >> $GITHUB_STEP_SUMMARY
123
- echo '```' >> $GITHUB_STEP_SUMMARY
124
- echo "${{ steps.e2e.outputs.failures }}" >> $GITHUB_STEP_SUMMARY
125
- echo '```' >> $GITHUB_STEP_SUMMARY
126
- fi
127
-
128
- - name: Report check annotation
129
- if: always()
130
- run: |
131
- PASSED="${{ steps.e2e.outputs.passed }}"
132
- FAILED="${{ steps.e2e.outputs.failed }}"
133
- PASSED="${PASSED:-0}"
134
- FAILED="${FAILED:-0}"
135
-
136
- if [ "${{ steps.e2e.outcome }}" = "success" ]; then
137
- if [ "$PASSED" = "0" ]; then
138
- echo "::notice title=E2E Recent Tests::✅ All tests passed"
139
- else
140
- echo "::notice title=E2E Recent Tests::✅ ${PASSED} tests passed"
141
- fi
142
- else
143
- echo "::error title=E2E Recent Tests::❌ ${FAILED} failed, ${PASSED} passed"
144
- fi
145
-
146
- e2e-curl:
147
- name: E2E Curl Tests
148
- needs: recent
149
- runs-on: ubuntu-latest
150
- timeout-minutes: 15
151
-
152
- steps:
153
- - name: Checkout
154
- uses: actions/checkout@v5
155
-
156
- - name: Set up Go
157
- uses: actions/setup-go@v6
158
- with:
159
- go-version-file: go.mod
160
- cache: true
161
-
162
- - name: Set up Docker Buildx
163
- uses: docker/setup-buildx-action@v3
164
-
165
- - name: Run E2E curl tests
166
- id: e2e
167
- run: |
168
- set +e
169
- mkdir -p tests/e2e/results
170
- ./dev e2e curl 2>&1 | tee e2e-output.log
171
- EXIT_CODE=${PIPESTATUS[0]}
172
-
173
- PASSED=$(grep -oP 'Passed:\s*\K\d+' e2e-output.log | tail -1 || true)
174
- FAILED=$(grep -oP 'Failed:\s*\K\d+' e2e-output.log | tail -1 || true)
175
- FAILURES=$(grep -E '✗.*failed' e2e-output.log | sed 's/.*✗ /- /' | head -10 || true)
176
- PASSED=${PASSED:-0}
177
- FAILED=${FAILED:-0}
178
-
179
- echo "passed=$PASSED" >> $GITHUB_OUTPUT
180
- echo "failed=$FAILED" >> $GITHUB_OUTPUT
181
- {
182
- echo 'failures<<EOF'
183
- echo "$FAILURES"
184
- echo 'EOF'
185
- } >> $GITHUB_OUTPUT
186
-
187
- exit $EXIT_CODE
188
- env:
189
- DOCKER_BUILDKIT: 1
190
- COMPOSE_DOCKER_CLI_BUILD: 1
191
-
192
- - name: Post summary
193
- if: always()
194
- run: |
195
- PASSED="${{ steps.e2e.outputs.passed }}"
196
- FAILED="${{ steps.e2e.outputs.failed }}"
197
- PASSED="${PASSED:-0}"
198
- FAILED="${FAILED:-0}"
199
-
200
- if [ "${{ steps.e2e.outcome }}" = "success" ]; then
201
- echo "## ✅ E2E Curl Tests Passed" >> $GITHUB_STEP_SUMMARY
202
- if [ "$PASSED" = "0" ]; then
203
- echo "All tests passed" >> $GITHUB_STEP_SUMMARY
204
- else
205
- echo "**$PASSED** tests passed" >> $GITHUB_STEP_SUMMARY
206
- fi
207
- else
208
- echo "## ❌ E2E Curl Tests Failed" >> $GITHUB_STEP_SUMMARY
209
- echo "**Passed:** $PASSED" >> $GITHUB_STEP_SUMMARY
210
- echo "**Failed:** $FAILED" >> $GITHUB_STEP_SUMMARY
211
- echo '```' >> $GITHUB_STEP_SUMMARY
212
- echo "${{ steps.e2e.outputs.failures }}" >> $GITHUB_STEP_SUMMARY
213
- echo '```' >> $GITHUB_STEP_SUMMARY
214
- fi
215
-
216
- - name: Report check annotation
217
- if: always()
218
- run: |
219
- PASSED="${{ steps.e2e.outputs.passed }}"
220
- FAILED="${{ steps.e2e.outputs.failed }}"
221
- PASSED="${PASSED:-0}"
222
- FAILED="${FAILED:-0}"
223
- if [ "${{ steps.e2e.outcome }}" = "success" ]; then
224
- if [ "$PASSED" = "0" ]; then
225
- echo "::notice title=E2E Curl Tests::✅ All tests passed"
226
- else
227
- echo "::notice title=E2E Curl Tests::✅ ${PASSED} tests passed"
228
- fi
229
- else
230
- echo "::error title=E2E Curl Tests::❌ ${FAILED} failed, ${PASSED} passed"
231
- fi
232
-
233
- e2e-cli:
234
- name: E2E CLI Tests
235
- needs: recent
236
- runs-on: ubuntu-latest
237
- timeout-minutes: 15
238
-
239
- steps:
240
- - name: Checkout
241
- uses: actions/checkout@v5
242
-
243
- - name: Set up Go
244
- uses: actions/setup-go@v6
245
- with:
246
- go-version-file: go.mod
247
- cache: true
248
-
249
- - name: Set up Docker Buildx
250
- uses: docker/setup-buildx-action@v3
251
-
252
- - name: Run CLI E2E tests
253
- id: e2e
254
- run: |
255
- set +e
256
- ./dev e2e cli 2>&1 | tee e2e-output.log
257
- EXIT_CODE=${PIPESTATUS[0]}
258
-
259
- PASSED=$(grep -oP 'Passed:\s*\K\d+' e2e-output.log | tail -1 || true)
260
- FAILED=$(grep -oP 'Failed:\s*\K\d+' e2e-output.log | tail -1 || true)
261
- FAILURES=$(grep -E '✗.*failed' e2e-output.log | sed 's/.*✗ /- /' | head -10 || true)
262
- PASSED=${PASSED:-0}
263
- FAILED=${FAILED:-0}
264
-
265
- echo "passed=$PASSED" >> $GITHUB_OUTPUT
266
- echo "failed=$FAILED" >> $GITHUB_OUTPUT
267
- {
268
- echo 'failures<<EOF'
269
- echo "$FAILURES"
270
- echo 'EOF'
271
- } >> $GITHUB_OUTPUT
272
-
273
- exit $EXIT_CODE
274
- env:
275
- DOCKER_BUILDKIT: 1
276
- COMPOSE_DOCKER_CLI_BUILD: 1
277
-
278
- - name: Post summary
279
- if: always()
280
- run: |
281
- PASSED="${{ steps.e2e.outputs.passed }}"
282
- FAILED="${{ steps.e2e.outputs.failed }}"
283
- PASSED="${PASSED:-0}"
284
- FAILED="${FAILED:-0}"
285
-
286
- if [ "${{ steps.e2e.outcome }}" = "success" ]; then
287
- echo "## ✅ E2E CLI Tests Passed" >> $GITHUB_STEP_SUMMARY
288
- if [ "$PASSED" = "0" ]; then
289
- echo "All tests passed" >> $GITHUB_STEP_SUMMARY
290
- else
291
- echo "**$PASSED** tests passed" >> $GITHUB_STEP_SUMMARY
292
- fi
293
- else
294
- echo "## ❌ E2E CLI Tests Failed" >> $GITHUB_STEP_SUMMARY
295
- echo "**Passed:** $PASSED" >> $GITHUB_STEP_SUMMARY
296
- echo "**Failed:** $FAILED" >> $GITHUB_STEP_SUMMARY
297
- echo '```' >> $GITHUB_STEP_SUMMARY
298
- echo "${{ steps.e2e.outputs.failures }}" >> $GITHUB_STEP_SUMMARY
299
- echo '```' >> $GITHUB_STEP_SUMMARY
300
- fi
301
-
302
- - name: Report check annotation
303
- if: always()
304
- run: |
305
- PASSED="${{ steps.e2e.outputs.passed }}"
306
- FAILED="${{ steps.e2e.outputs.failed }}"
307
- PASSED="${PASSED:-0}"
308
- FAILED="${FAILED:-0}"
309
- if [ "${{ steps.e2e.outcome }}" = "success" ]; then
310
- if [ "$PASSED" = "0" ]; then
311
- echo "::notice title=E2E CLI Tests::✅ All tests passed"
312
- else
313
- echo "::notice title=E2E CLI Tests::✅ ${PASSED} tests passed"
314
- fi
315
- else
316
- echo "::error title=E2E CLI Tests::❌ ${FAILED} failed, ${PASSED} passed"
317
- fi
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pinchtab/.github/workflows/go-verify.yml DELETED
@@ -1,191 +0,0 @@
1
- name: Go Verification
2
-
3
- on:
4
- push:
5
- branches: [main]
6
- paths-ignore:
7
- - '**.md'
8
- - 'docs/**'
9
- - 'LICENSE'
10
- - '.gitignore'
11
- - 'skill/**'
12
- - 'plugin/**'
13
- pull_request:
14
- branches: [main]
15
- paths-ignore:
16
- - '**.md'
17
- - 'docs/**'
18
- - 'LICENSE'
19
- - '.gitignore'
20
- - 'skill/**'
21
- - 'plugin/**'
22
-
23
- env:
24
- FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
25
-
26
- permissions:
27
- contents: read
28
-
29
- jobs:
30
- # Check which files changed to conditionally run jobs
31
- changes:
32
- name: Detect Changes
33
- runs-on: ubuntu-latest
34
- if: github.event.pull_request.draft == false
35
- outputs:
36
- go: ${{ steps.filter.outputs.go }}
37
- steps:
38
- - uses: actions/checkout@v5
39
- - uses: dorny/paths-filter@v3
40
- id: filter
41
- with:
42
- filters: |
43
- go:
44
- - '**.go'
45
- - 'go.mod'
46
- - 'go.sum'
47
- - 'cmd/**'
48
- - 'internal/**'
49
-
50
- build:
51
- name: Build & Vet
52
- needs: changes
53
- runs-on: ubuntu-latest
54
- steps:
55
- - name: Check for Go changes
56
- id: check
57
- run: |
58
- if [ "${{ needs.changes.outputs.go }}" = "true" ]; then
59
- echo "Go files detected"
60
- echo "has_go_changes=true" >> $GITHUB_OUTPUT
61
- else
62
- echo "⏭️ No Go files changed - docs/workflow-only changes detected"
63
- echo "has_go_changes=false" >> $GITHUB_OUTPUT
64
- fi
65
-
66
- - uses: actions/checkout@v5
67
-
68
- - uses: actions/setup-go@v6
69
- if: steps.check.outputs.has_go_changes == 'true'
70
- with:
71
- go-version: '1.26'
72
-
73
- - name: Download deps
74
- if: steps.check.outputs.has_go_changes == 'true'
75
- run: go mod download
76
-
77
- - name: Format check
78
- if: steps.check.outputs.has_go_changes == 'true'
79
- run: |
80
- unformatted=$(gofmt -l .)
81
- if [ -n "$unformatted" ]; then
82
- echo "Files not formatted:"
83
- echo "$unformatted"
84
- exit 1
85
- fi
86
-
87
- - name: Vet
88
- if: steps.check.outputs.has_go_changes == 'true'
89
- run: go vet ./...
90
-
91
- - name: Build
92
- if: steps.check.outputs.has_go_changes == 'true'
93
- run: go build -o pinchtab ./cmd/pinchtab
94
-
95
- - name: Test with coverage
96
- if: steps.check.outputs.has_go_changes == 'true'
97
- run: go test ./... -v -count=1 -coverprofile=coverage.out -covermode=atomic
98
-
99
- - name: Coverage summary
100
- if: steps.check.outputs.has_go_changes == 'true'
101
- run: |
102
- coverage=$(go tool cover -func=coverage.out | tail -1)
103
- echo "### Coverage" >> $GITHUB_STEP_SUMMARY
104
- echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
105
- echo "$coverage" >> $GITHUB_STEP_SUMMARY
106
- echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
107
-
108
- lint:
109
- name: Lint
110
- needs: changes
111
- runs-on: ubuntu-latest
112
- steps:
113
- - name: Check for Go changes
114
- id: check
115
- run: |
116
- if [ "${{ needs.changes.outputs.go }}" = "true" ]; then
117
- echo "has_go_changes=true" >> $GITHUB_OUTPUT
118
- else
119
- echo "⏭️ No Go files changed, skipping lint"
120
- echo "has_go_changes=false" >> $GITHUB_OUTPUT
121
- fi
122
-
123
- - uses: actions/checkout@v5
124
-
125
- - uses: actions/setup-go@v6
126
- if: steps.check.outputs.has_go_changes == 'true'
127
- with:
128
- go-version: '1.26'
129
-
130
- - uses: oven-sh/setup-bun@v2
131
- if: needs.changes.outputs.go == 'true'
132
- with:
133
- bun-version: latest
134
-
135
- - name: Install tygo
136
- if: needs.changes.outputs.go == 'true'
137
- run: go install github.com/gzuidhof/tygo@latest
138
-
139
- - name: Build dashboard
140
- if: needs.changes.outputs.go == 'true'
141
- run: |
142
- cd dashboard && bun install --frozen-lockfile && cd ..
143
- ./scripts/build-dashboard.sh
144
-
145
- - name: golangci-lint
146
- if: steps.check.outputs.has_go_changes == 'true'
147
- uses: golangci/golangci-lint-action@v7
148
- with:
149
- version: v2.9.0
150
-
151
- security:
152
- name: Security Scan
153
- needs: changes
154
- runs-on: ubuntu-latest
155
- steps:
156
- - name: Check for Go changes
157
- id: check
158
- run: |
159
- if [ "${{ needs.changes.outputs.go }}" = "true" ]; then
160
- echo "has_go_changes=true" >> $GITHUB_OUTPUT
161
- else
162
- echo "⏭️ No Go files changed, skipping security scan"
163
- echo "has_go_changes=false" >> $GITHUB_OUTPUT
164
- fi
165
-
166
- - uses: actions/checkout@v5
167
-
168
- - uses: actions/setup-go@v6
169
- if: steps.check.outputs.has_go_changes == 'true'
170
- with:
171
- go-version: '1.26'
172
-
173
- - name: Install gosec
174
- if: steps.check.outputs.has_go_changes == 'true'
175
- run: go install github.com/securego/gosec/v2/cmd/gosec@latest
176
-
177
- - name: Run gosec
178
- if: steps.check.outputs.has_go_changes == 'true'
179
- run: gosec -exclude=G301,G302,G304,G306,G404,G107,G115,G703,G704,G705,G706 -fmt=json -out=gosec-results.json ./... || true
180
-
181
- - name: Check critical findings
182
- if: steps.check.outputs.has_go_changes == 'true'
183
- run: |
184
- # Fail on G112 (Slowloris), G204 (command injection)
185
- ISSUES=$(cat gosec-results.json | jq '[.Issues[] | select(.rule_id == "G112" or .rule_id == "G204")] | length')
186
- echo "Critical issues: $ISSUES"
187
- cat gosec-results.json | jq '.Stats'
188
- if [ "$ISSUES" -gt 0 ]; then
189
- cat gosec-results.json | jq '.Issues[] | select(.rule_id == "G112" or .rule_id == "G204")'
190
- exit 1
191
- fi
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pinchtab/.github/workflows/npm-verify.yml DELETED
@@ -1,157 +0,0 @@
1
- name: npm Package Verification
2
-
3
- on:
4
- pull_request:
5
- paths:
6
- - 'npm/**'
7
- - '.github/workflows/npm-verify.yml'
8
- push:
9
- tags:
10
- - 'v*'
11
-
12
- env:
13
- FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
14
-
15
- permissions:
16
- contents: read
17
-
18
- jobs:
19
- verify:
20
- name: Verify npm Package
21
- runs-on: ubuntu-latest
22
- steps:
23
- - uses: actions/checkout@v5
24
-
25
- - uses: actions/setup-node@v5
26
- with:
27
- node-version: '22'
28
-
29
- - name: Install dependencies
30
- working-directory: npm
31
- run: npm ci
32
-
33
- - name: Lint (ESLint)
34
- working-directory: npm
35
- run: npm run lint
36
-
37
- - name: Format Check (Prettier)
38
- working-directory: npm
39
- run: npm run format:check
40
-
41
- - name: Build TypeScript
42
- working-directory: npm
43
- run: tsc
44
-
45
- - name: Run tests
46
- working-directory: npm
47
- run: npm test
48
-
49
- - name: Check for source .ts files in package
50
- working-directory: npm
51
- run: |
52
- # Create the tarball and check its actual contents
53
- npm pack > /dev/null 2>&1
54
- TARBALL=$(ls -t pinchtab-*.tgz | head -1)
55
- SOURCES=$(tar -tzf "$TARBALL" | grep -E "\.ts$" | grep -v "\.d\.ts" || true)
56
- if [ -n "$SOURCES" ]; then
57
- echo "❌ ERROR: Source .ts files in package:"
58
- echo "$SOURCES"
59
- exit 1
60
- fi
61
- echo "✅ No source .ts files in tarball"
62
- rm "$TARBALL"
63
-
64
- - name: Check for test files in package
65
- working-directory: npm
66
- run: |
67
- TESTS=$(npm pack --dry-run 2>&1 | grep "dist/tests" || true)
68
- if [ -n "$TESTS" ]; then
69
- echo "❌ ERROR: Test files in package:"
70
- echo "$TESTS"
71
- exit 1
72
- fi
73
- echo "✅ No test files in package"
74
-
75
- - name: Check for source maps in package
76
- working-directory: npm
77
- run: |
78
- MAPS=$(npm pack --dry-run 2>&1 | grep "\.map$" || true)
79
- if [ -n "$MAPS" ]; then
80
- echo "❌ ERROR: Source maps in package:"
81
- echo "$MAPS"
82
- exit 1
83
- fi
84
- echo "✅ No source maps in package"
85
-
86
- - name: Verify required files exist
87
- working-directory: npm
88
- run: |
89
- REQUIRED=(
90
- "dist/src/index.js"
91
- "dist/src/index.d.ts"
92
- "scripts/postinstall.js"
93
- "bin/pinchtab"
94
- "LICENSE"
95
- )
96
-
97
- PACK_OUTPUT=$(npm pack --dry-run 2>&1)
98
-
99
- for file in "${REQUIRED[@]}"; do
100
- if echo "$PACK_OUTPUT" | grep -q "$file"; then
101
- echo "✅ $file"
102
- else
103
- echo "❌ Missing: $file"
104
- exit 1
105
- fi
106
- done
107
-
108
- - name: Check postinstall.js syntax
109
- working-directory: npm
110
- run: |
111
- node -c scripts/postinstall.js
112
- echo "✅ postinstall.js valid JavaScript"
113
-
114
- - name: Verify package metadata
115
- working-directory: npm
116
- run: |
117
- node -e "
118
- const pkg = require('./package.json');
119
- const checks = [
120
- { name: 'name', value: pkg.name === 'pinchtab' },
121
- { name: 'version', value: !!pkg.version },
122
- { name: 'files array', value: Array.isArray(pkg.files) && pkg.files.length > 0 },
123
- { name: 'postinstall script', value: !!pkg.scripts.postinstall },
124
- { name: 'bin.pinchtab', value: !!pkg.bin.pinchtab }
125
- ];
126
-
127
- let failed = false;
128
- checks.forEach(check => {
129
- if (check.value) {
130
- console.log('✅', check.name);
131
- } else {
132
- console.log('❌', check.name, 'missing or invalid');
133
- failed = true;
134
- }
135
- });
136
-
137
- if (failed) process.exit(1);
138
- "
139
-
140
- - name: Check package size
141
- working-directory: npm
142
- run: |
143
- SIZE=$(npm pack --dry-run 2>&1 | tail -1 | awk '{print $NF}' | tr -d 'B')
144
- echo "📦 Package size: ${SIZE}B"
145
- if [ "$SIZE" -gt 100000 ]; then
146
- echo "⚠️ Warning: Package is large (>100KB)"
147
- fi
148
-
149
- - name: Security audit
150
- working-directory: npm
151
- run: npm audit --audit-level=moderate || true
152
-
153
- - name: List package contents
154
- working-directory: npm
155
- run: |
156
- echo "📦 Package will contain:"
157
- npm pack --dry-run 2>&1 | grep "notice" | awk '{print " " $3 " (" $2 ")"}'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pinchtab/.github/workflows/publish-skill.yml DELETED
@@ -1,58 +0,0 @@
1
- name: Publish Skill
2
-
3
- on:
4
- workflow_call:
5
- inputs:
6
- version:
7
- required: true
8
- type: string
9
- secrets:
10
- CLAWHUB_TOKEN:
11
- required: true
12
- workflow_dispatch:
13
- inputs:
14
- version:
15
- description: 'Skill version (e.g. 0.2.0, without v prefix)'
16
- required: true
17
-
18
- env:
19
- FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
20
-
21
- permissions:
22
- contents: read
23
-
24
- jobs:
25
- publish:
26
- name: Publish to ClawHub
27
- runs-on: ubuntu-latest
28
- steps:
29
- - uses: actions/checkout@v5
30
-
31
- - uses: actions/setup-node@v5
32
- with:
33
- node-version: '22'
34
-
35
- - name: Install ClawHub CLI
36
- run: npm i -g clawhub
37
-
38
- - name: Authenticate
39
- run: clawhub login --token "$CLAWHUB_TOKEN" --no-browser
40
- env:
41
- CLAWHUB_TOKEN: ${{ secrets.CLAWHUB_TOKEN }}
42
-
43
- - name: Publish skill
44
- run: |
45
- if [ -n "${{ inputs.version }}" ]; then
46
- VERSION="${{ inputs.version }}"
47
- elif [ -n "${{ github.event.inputs.version }}" ]; then
48
- VERSION="${{ github.event.inputs.version }}"
49
- else
50
- VERSION=${GITHUB_REF_NAME#v}
51
- fi
52
- VERSION=${VERSION#v}
53
- clawhub publish skills/pinchtab \
54
- --slug pinchtab \
55
- --name "Pinchtab" \
56
- --version "$VERSION" \
57
- --changelog "Release v$VERSION" \
58
- --tags latest
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pinchtab/.github/workflows/release.yml DELETED
@@ -1,238 +0,0 @@
1
- name: Release
2
-
3
- # Automatic release pipeline triggered on tag push (v0.7.0, etc.)
4
- #
5
- # Pipeline:
6
- # 1. Checkout tag
7
- # 2. Build Go binary + create GitHub release (goreleaser)
8
- # 3. Publish npm package (with postinstall binary download support)
9
- # 4. Build & push Docker images
10
- # See .github/RELEASING.md for the operational checklist.
11
- #
12
- # Secrets required:
13
- # - NPM_TOKEN: npm authentication (https://docs.npmjs.com/creating-and-viewing-access-tokens)
14
- # - DOCKERHUB_USER / DOCKERHUB_TOKEN: Docker Hub credentials
15
- # - GITHUB_TOKEN: Automatic (GitHub Actions)
16
- # - HOMEBREW_TAP_GITHUB_TOKEN: PAT with write access to pinchtab/homebrew-tap
17
- #
18
- # To create a release:
19
- # git tag v0.7.0
20
- # git push origin v0.7.0
21
-
22
- on:
23
- push:
24
- tags: ['v*']
25
- workflow_dispatch:
26
- inputs:
27
- tag:
28
- description: 'Tag to release (e.g. v0.2.0). Leave empty for dry-run from ref.'
29
- required: false
30
- ref:
31
- description: 'Git ref for dry-run testing (branch, tag, or SHA). Defaults to main.'
32
- required: false
33
- default: 'main'
34
- dry_run:
35
- description: 'Build release artifacts without publishing them.'
36
- required: false
37
- type: boolean
38
- default: false
39
-
40
- env:
41
- FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
42
- GORELEASER_VERSION: v2.14.3
43
-
44
- permissions:
45
- contents: write
46
-
47
- jobs:
48
- release:
49
- name: Release Binaries
50
- runs-on: ubuntu-latest
51
- permissions:
52
- contents: write
53
- steps:
54
- - name: Validate workflow inputs
55
- if: ${{ github.event_name == 'workflow_dispatch' }}
56
- run: |
57
- if [ "${{ inputs.dry_run }}" != "true" ] && [ -z "${{ github.event.inputs.tag }}" ]; then
58
- echo "tag is required unless dry_run=true" >&2
59
- exit 1
60
- fi
61
-
62
- - uses: actions/checkout@v5
63
- with:
64
- fetch-depth: 0
65
- ref: ${{ github.event.inputs.tag || github.event.inputs.ref || github.ref }}
66
-
67
- - uses: actions/setup-go@v6
68
- with:
69
- go-version: '1.26'
70
-
71
- - uses: oven-sh/setup-bun@v2
72
- with:
73
- bun-version: latest
74
-
75
- - name: Install dashboard dependencies
76
- working-directory: dashboard
77
- run: bun install --frozen-lockfile
78
-
79
- - name: Run GoReleaser
80
- run: |
81
- if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ "${{ inputs.dry_run }}" = "true" ]; then
82
- ARGS="release --clean --snapshot"
83
- else
84
- ARGS="release --clean"
85
- fi
86
-
87
- curl -sfL https://goreleaser.com/static/run | VERSION="$GORELEASER_VERSION" bash -s -- $ARGS
88
- env:
89
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
90
- HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }}
91
-
92
- npm:
93
- name: Publish to npm
94
- runs-on: ubuntu-latest
95
- needs: release
96
- permissions:
97
- contents: read
98
- steps:
99
- - uses: actions/checkout@v5
100
- with:
101
- ref: ${{ github.event.inputs.tag || github.event.inputs.ref || github.ref }}
102
-
103
- - uses: actions/setup-node@v5
104
- with:
105
- node-version: '22'
106
- registry-url: 'https://registry.npmjs.org'
107
-
108
- - name: Extract version
109
- id: version
110
- run: |
111
- if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ "${{ inputs.dry_run }}" = "true" ] && [ -z "${{ github.event.inputs.tag }}" ]; then
112
- VERSION=$(jq -r .version npm/package.json)
113
- else
114
- TAG="${{ github.event.inputs.tag || github.ref_name }}"
115
- VERSION=${TAG#v} # Remove 'v' prefix
116
- fi
117
- echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
118
-
119
- - name: Update npm package version
120
- if: ${{ github.event_name != 'workflow_dispatch' || !inputs.dry_run }}
121
- working-directory: npm
122
- run: |
123
- CURRENT=$(jq -r .version package.json)
124
- DESIRED=${{ steps.version.outputs.version }}
125
- if [ "$CURRENT" != "$DESIRED" ]; then
126
- npm version "$DESIRED" --no-git-tag-version
127
- else
128
- echo "Version already correct: $CURRENT"
129
- fi
130
-
131
- - name: Install dependencies
132
- working-directory: npm
133
- run: npm ci --ignore-scripts
134
-
135
- - name: Build TypeScript
136
- working-directory: npm
137
- run: npm run build
138
-
139
- - name: Dry-run npm publish
140
- if: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run }}
141
- working-directory: npm
142
- run: npm publish --dry-run
143
- env:
144
- NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
145
-
146
- - name: Publish to npm
147
- if: ${{ github.event_name != 'workflow_dispatch' || !inputs.dry_run }}
148
- working-directory: npm
149
- run: npm publish
150
- env:
151
- NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
152
-
153
- docker:
154
- name: Docker Image
155
- runs-on: ubuntu-latest
156
- needs: release
157
- permissions:
158
- contents: read
159
- packages: write
160
- steps:
161
- - uses: actions/checkout@v5
162
- with:
163
- ref: ${{ github.event.inputs.tag || github.event.inputs.ref || github.ref }}
164
-
165
- - name: Extract Docker metadata
166
- id: meta
167
- run: |
168
- if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ "${{ inputs.dry_run }}" = "true" ]; then
169
- if [ -n "${{ github.event.inputs.tag }}" ]; then
170
- TAG="${{ github.event.inputs.tag }}"
171
- VERSION="${TAG#v}-dryrun-${GITHUB_SHA::7}"
172
- else
173
- TAG="dry-run-${GITHUB_SHA::7}"
174
- VERSION="$(jq -r .version npm/package.json)-dryrun-${GITHUB_SHA::7}"
175
- fi
176
- PUSH="false"
177
- else
178
- TAG="${{ github.event.inputs.tag || github.ref_name }}"
179
- VERSION="${TAG#v}"
180
- PUSH="true"
181
- fi
182
-
183
- {
184
- echo "tag=$TAG"
185
- echo "version=$VERSION"
186
- echo "push=$PUSH"
187
- } >> "$GITHUB_OUTPUT"
188
-
189
- - name: Docker dry-run note
190
- if: ${{ steps.meta.outputs.push != 'true' }}
191
- run: |
192
- echo "Running a multi-arch Docker build without pushing to GHCR or Docker Hub." >> "$GITHUB_STEP_SUMMARY"
193
-
194
- - uses: docker/setup-qemu-action@v3
195
- - uses: docker/setup-buildx-action@v3
196
-
197
- - uses: docker/login-action@v3
198
- if: ${{ steps.meta.outputs.push == 'true' }}
199
- with:
200
- username: ${{ secrets.DOCKERHUB_USER }}
201
- password: ${{ secrets.DOCKERHUB_TOKEN }}
202
-
203
- - uses: docker/login-action@v3
204
- if: ${{ steps.meta.outputs.push == 'true' }}
205
- with:
206
- registry: ghcr.io
207
- username: ${{ github.actor }}
208
- password: ${{ secrets.GITHUB_TOKEN }}
209
-
210
- - uses: docker/build-push-action@v6
211
- with:
212
- context: .
213
- file: ./Dockerfile
214
- platforms: linux/amd64,linux/arm64
215
- push: ${{ steps.meta.outputs.push == 'true' }}
216
- provenance: false
217
- labels: |
218
- org.opencontainers.image.source=https://github.com/pinchtab/pinchtab
219
- org.opencontainers.image.version=${{ steps.meta.outputs.version }}
220
- tags: |
221
- ghcr.io/pinchtab/pinchtab:latest
222
- ghcr.io/pinchtab/pinchtab:${{ steps.meta.outputs.tag }}
223
- ghcr.io/pinchtab/pinchtab:${{ steps.meta.outputs.version }}
224
- pinchtab/pinchtab:latest
225
- pinchtab/pinchtab:${{ steps.meta.outputs.tag }}
226
- pinchtab/pinchtab:${{ steps.meta.outputs.version }}
227
-
228
- skill:
229
- name: Publish Skill
230
- needs:
231
- - npm
232
- - docker
233
- if: ${{ github.event_name != 'workflow_dispatch' || !inputs.dry_run }}
234
- uses: ./.github/workflows/publish-skill.yml
235
- with:
236
- version: ${{ github.event.inputs.tag || github.ref_name }}
237
- secrets:
238
- CLAWHUB_TOKEN: ${{ secrets.CLAWHUB_TOKEN }}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pinchtab/.gitignore DELETED
@@ -1,50 +0,0 @@
1
- node_modules/
2
- .venv/
3
- plugins/*/.venv/
4
- __pycache__/
5
- *.pyc
6
- browser-bridge
7
- /pinchtab
8
- coverage.out
9
-
10
- # Build artifacts and test output
11
- dist/
12
- *.js
13
- !scripts/**/*.js
14
- !dashboard/eslint.config.js
15
- !dashboard/vite.config.ts
16
- !npm/bin/pinchtab
17
- # React dashboard build output (generated by scripts/build-dashboard.sh)
18
- # Go code serves a built-in fallback when these files are absent.
19
- internal/dashboard/dashboard/assets/
20
- internal/dashboard/dashboard/dashboard.html
21
-
22
- # npm package
23
- npm/node_modules/
24
- # package-lock.json is committed for reproducible installs via npm ci
25
-
26
- # OpenClaw workspace files (should never be committed)
27
- .openclaw/
28
- SOUL.md
29
- HEARTBEAT.md
30
- IDENTITY.md
31
- USER.md
32
- BOOTSTRAP.md
33
- TOOLS.md
34
- MEMORY.md
35
- AGENTS.md
36
- *.test
37
- memory/
38
- pinchtab-small
39
-
40
- # Local development files
41
- MY_PROJECT_NOTES.md
42
- debug.log
43
- pinchtab.exe
44
-
45
- .gomodcache/
46
- .gocache
47
- .DS_Store
48
- # E2E test results
49
- tests/e2e/results/*
50
- !tests/e2e/results/.gitkeep
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pinchtab/.goreleaser.yml DELETED
@@ -1,61 +0,0 @@
1
- version: 2
2
-
3
- before:
4
- hooks:
5
- - ./scripts/build-dashboard.sh
6
-
7
- builds:
8
- - main: ./cmd/pinchtab
9
- ldflags:
10
- - -s -w -X main.version={{.Version}}
11
- goos:
12
- - linux
13
- - darwin
14
- - windows
15
- goarch:
16
- - amd64
17
- - arm64
18
-
19
- archives:
20
- # Output bare binaries directly (no archives)
21
- # Note: goreleaser auto-adds .exe for Windows, so don't include it in the template
22
- - format: binary
23
- name_template: "pinchtab-{{ .Os }}-{{ .Arch }}"
24
-
25
- checksum:
26
- name_template: checksums.txt
27
-
28
- brews:
29
- - name: pinchtab
30
- repository:
31
- owner: pinchtab
32
- name: homebrew-tap
33
- branch: "brew-{{ .ProjectName }}-{{ .Version }}"
34
- token: "{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}"
35
- pull_request:
36
- enabled: true
37
- base:
38
- owner: pinchtab
39
- name: homebrew-tap
40
- branch: main
41
- directory: Formula
42
- homepage: "https://pinchtab.com"
43
- description: "High-performance browser automation bridge and multi-instance orchestrator for AI agents"
44
- license: "MIT"
45
- test: |
46
- system "#{bin}/pinchtab version"
47
- install: |
48
- bin.install Dir["pinchtab*"].first => "pinchtab"
49
-
50
- changelog:
51
- sort: asc
52
- filters:
53
- exclude:
54
- - '^docs:'
55
- - '^ci:'
56
- - '^chore:'
57
-
58
- release:
59
- github:
60
- owner: pinchtab
61
- name: pinchtab
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pinchtab/.markdownlint.json DELETED
@@ -1,26 +0,0 @@
1
- {
2
- "default": true,
3
- "line-length": {
4
- "line_length": 200,
5
- "heading_line_length": 200,
6
- "code_block_line_length": 200,
7
- "code_blocks": false
8
- },
9
- "no-hard-tabs": false,
10
- "no-bare-urls": false,
11
- "no-inline-html": false,
12
- "blanks-around-headings": false,
13
- "blanks-around-lists": false,
14
- "blanks-around-fences": false,
15
- "no-emphasis-as-heading": false,
16
- "no-multiple-blank-lines": {
17
- "maximum": 2
18
- },
19
- "first-line-heading": false,
20
- "heading-start-left": true,
21
- "heading-increment": true,
22
- "no-duplicate-heading": {
23
- "allow_different_nesting": true
24
- },
25
- "fenced-code-language": false
26
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pinchtab/.pre-commit-config.yaml DELETED
@@ -1,40 +0,0 @@
1
- repos:
2
- # General file checks
3
- - repo: https://github.com/pre-commit/pre-commit-hooks
4
- rev: v4.5.0
5
- hooks:
6
- - id: trailing-whitespace
7
- - id: end-of-file-fixer
8
- - id: check-yaml
9
- - id: check-json
10
- exclude: 'tsconfig\..+\.json$'
11
- - id: check-merge-conflict
12
- - id: check-added-large-files
13
- args: ['--maxkb=500']
14
-
15
- # Go formatting
16
- - repo: https://github.com/golangci/golangci-lint
17
- rev: v2.1.6
18
- hooks:
19
- - id: golangci-lint
20
- args: ['--timeout=5m']
21
-
22
- # Markdown linting (relaxed for docs)
23
- - repo: https://github.com/igorshubovych/markdownlint-cli
24
- rev: v0.37.0
25
- hooks:
26
- - id: markdownlint
27
- args: ['--config', '.markdownlint.json']
28
- # Exclude test files, auto-generated docs, repo root, and skill docs
29
- exclude: |
30
- (?x)^(
31
- tests/.*\.md|
32
- \.github/.*\.md|
33
- skill/.*\.md|
34
- docs/references/.*\.json|
35
- README\.md|
36
- CONTRIBUTING\.md|
37
- CODE_OF_CONDUCT\.md|
38
- SECURITY\.md|
39
- RELEASE\.md
40
- )$
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pinchtab/CODE_OF_CONDUCT.md DELETED
@@ -1,46 +0,0 @@
1
- # Contributor Covenant Code of Conduct
2
-
3
- ## Our Pledge
4
-
5
- In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
6
-
7
- ## Our Standards
8
-
9
- Examples of behavior that contributes to creating a positive environment include:
10
-
11
- * Using welcoming and inclusive language
12
- * Being respectful of differing viewpoints and experiences
13
- * Gracefully accepting constructive criticism
14
- * Focusing on what is best for the community
15
- * Showing empathy towards other community members
16
-
17
- Examples of unacceptable behavior by participants include:
18
-
19
- * The use of sexualized language or imagery and unwelcome sexual attention or advances
20
- * Trolling, insulting/derogatory comments, and personal or political attacks
21
- * Public or private harassment
22
- * Publishing others' private information, such as a physical or electronic address, without explicit permission
23
- * Other conduct which could reasonably be considered inappropriate in a professional setting
24
-
25
- ## Our Responsibilities
26
-
27
- Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28
-
29
- Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30
-
31
- ## Scope
32
-
33
- This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34
-
35
- ## Enforcement
36
-
37
- Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at admin@pinchtab.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38
-
39
- Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40
-
41
- ## Attribution
42
-
43
- This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44
-
45
- [homepage]: http://contributor-covenant.org
46
- [version]: http://contributor-covenant.org/version/1/4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pinchtab/CONTRIBUTING.md DELETED
@@ -1,21 +0,0 @@
1
- # Contributing to PinchTab
2
-
3
- This repository keeps one detailed contributor guide:
4
-
5
- - [docs/guides/contributing.md](docs/guides/contributing.md)
6
-
7
- Use that guide for setup, build commands, tests, hooks, and the day-to-day development workflow.
8
-
9
- ## Quick Start
10
-
11
- ```bash
12
- git clone https://github.com/pinchtab/pinchtab.git
13
- cd pinchtab
14
- ./dev doctor
15
- ./dev check
16
- ./dev test unit
17
- ```
18
-
19
- ## Pull Requests
20
-
21
- Please keep GitHub's "Allow edits from maintainers" option enabled on your PRs. It makes small fixes, rebases, and merge-conflict resolution much faster.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pinchtab/DEFINITION_OF_DONE.md DELETED
@@ -1,5 +0,0 @@
1
- # Definition of Done
2
-
3
- Canonical file: [`.github/DEFINITION_OF_DONE.md`](./.github/DEFINITION_OF_DONE.md)
4
-
5
- Use the `.github` copy as the source of truth for PR review, templates, and agent instructions.
 
 
 
 
 
 
pinchtab/DEVELOPMENT.md DELETED
@@ -1,7 +0,0 @@
1
- # Development Setup
2
-
3
- This document has been consolidated into the main contributor guide:
4
-
5
- - [docs/guides/contributing.md](docs/guides/contributing.md)
6
-
7
- Use that page as the source of truth for environment setup, building, testing, hooks, and contributor workflow.
 
 
 
 
 
 
 
 
pinchtab/Dockerfile DELETED
@@ -1,70 +0,0 @@
1
- # Stage 1: Build the React dashboard with Bun.
2
- # The compiled assets are copied into the Go embed directory in stage 2.
3
- FROM oven/bun:1 AS dashboard
4
- WORKDIR /build
5
- COPY dashboard/package.json dashboard/bun.lock ./
6
- RUN bun install --frozen-lockfile
7
- COPY dashboard/ .
8
- RUN bun run build
9
-
10
- # Stage 2: Compile the Go binary.
11
- # Dashboard dist is embedded via Go's embed package.
12
- # Vite always outputs index.html; rename to dashboard.html so it doesn't
13
- # collide with http.FileServer's automatic index.html handling at /dashboard/.
14
- FROM golang:1.26-alpine AS builder
15
- RUN apk add --no-cache git
16
- WORKDIR /build
17
- COPY go.mod go.sum ./
18
- RUN go mod download
19
- COPY . .
20
- COPY --from=dashboard /build/dist/ internal/dashboard/dashboard/
21
- RUN mv internal/dashboard/dashboard/index.html internal/dashboard/dashboard/dashboard.html
22
- RUN go build -ldflags="-s -w" -o pinchtab ./cmd/pinchtab
23
-
24
- # Stage 3: Minimal runtime image with Chromium.
25
- # Only the compiled binary and entrypoint script are copied in.
26
- #
27
- # Security model:
28
- # - Chrome runs with --no-sandbox (set by entrypoint) because containers don't
29
- # have user namespaces for sandboxing
30
- # - Container provides isolation via cgroups, seccomp, dropped capabilities,
31
- # read-only filesystem, and non-root user
32
- # - This matches best practices for headless Chrome in containerized environments
33
- FROM alpine:3.21
34
-
35
- LABEL org.opencontainers.image.source="https://github.com/pinchtab/pinchtab"
36
- LABEL org.opencontainers.image.description="High-performance browser automation bridge"
37
-
38
- # Chromium and its runtime dependencies for headless operation
39
- RUN apk add --no-cache \
40
- chromium \
41
- nss \
42
- freetype \
43
- harfbuzz \
44
- ca-certificates \
45
- ttf-freefont \
46
- dumb-init
47
-
48
- # Non-root user; /data is the persistent volume mount point
49
- RUN adduser -D -h /data -g '' pinchtab && \
50
- mkdir -p /data && \
51
- chown pinchtab:pinchtab /data
52
-
53
- COPY --from=builder /build/pinchtab /usr/local/bin/pinchtab
54
- COPY --chmod=0755 docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
55
-
56
- USER pinchtab
57
- WORKDIR /data
58
-
59
- # HOME and XDG_CONFIG_HOME point into the persistent volume so config
60
- # and Chrome profiles survive container restarts.
61
- ENV HOME=/data \
62
- XDG_CONFIG_HOME=/data/.config
63
-
64
- EXPOSE 9867
65
-
66
- HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
67
- CMD wget -q -O /dev/null http://localhost:9867/health || exit 1
68
-
69
- ENTRYPOINT ["/usr/bin/dumb-init", "--"]
70
- CMD ["/usr/local/bin/docker-entrypoint.sh", "pinchtab"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pinchtab/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Luigi Agosti
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pinchtab/README.md DELETED
@@ -1,369 +0,0 @@
1
- <p align="center">
2
- <img src="assets/pinchtab-headless.png" alt="PinchTab" width="200"/>
3
- </p>
4
-
5
- <p align="center">
6
- <strong>PinchTab</strong><br/>
7
- <strong>Browser control for AI agents</strong><br/>
8
- 12MB Go binary • HTTP API • Token-efficient
9
- </p>
10
-
11
-
12
- <table align="center">
13
- <tr>
14
- <td align="center" valign="middle">
15
- <a href="https://pinchtab.com/docs"><img src="assets/docs-no-background-256.png" alt="Full Documentation" width="92"/></a>
16
- </td>
17
- <td align="left" valign="middle">
18
- <a href="https://github.com/pinchtab/pinchtab/releases/latest"><img src="https://img.shields.io/github/v/release/pinchtab/pinchtab?style=flat-square&color=FFD700" alt="Release"/></a><br/>
19
- <a href="https://github.com/pinchtab/pinchtab/actions/workflows/go-verify.yml"><img src="https://img.shields.io/github/actions/workflow/status/pinchtab/pinchtab/go-verify.yml?branch=main&style=flat-square&label=Build" alt="Build"/></a><br/>
20
- <img src="https://img.shields.io/badge/Go-1.25+-00ADD8?style=flat-square&logo=go&logoColor=white" alt="Go 1.25+"/><br/>
21
- <a href="LICENSE"><img src="https://img.shields.io/badge/license-Apache%202.0-blue?style=flat-square" alt="License"/></a>
22
- </td>
23
- </tr>
24
- </table>
25
-
26
- ---
27
-
28
- ## What is PinchTab?
29
-
30
- PinchTab is a **standalone HTTP server** that gives AI agents direct control over Chrome.
31
-
32
- For day-to-day local use, the server is typically installed as a user-level daemon, allowing agent tools to reuse the same browser control plane running in the background.
33
-
34
- ```bash
35
- curl -fsSL https://pinchtab.com/install.sh | bash
36
- # or
37
- pinchtab daemon install
38
- ```
39
-
40
- This installs the control-plane server and starts a default headless Chrome instance, ready to accept requests from agents or manual API calls.
41
-
42
-
43
- If you prefer not to run a daemon, or if you're on Windows, you can instead run:
44
-
45
- `pinchtab server` — runs the control-plane server directly
46
- `pinchtab bridge` — runs a single browser instance as a lightweight runtime
47
-
48
- PinchTab also provides a CLI with an interactive entry point for local setup and common tasks:
49
-
50
- `pinchtab`
51
-
52
- ## Security
53
-
54
- PinchTab defaults to a **local-first security posture**:
55
-
56
- - `server.bind = 127.0.0.1`
57
- - sensitive endpoint families are disabled by default
58
- - `attach` is disabled by default
59
- - IDPI is enabled with a **local-only website allowlist**
60
-
61
- > [!CAUTION]
62
- > By default, IDPI restricts browsing to **locally hosted websites only**.
63
- > This prevents agents from navigating the public internet until you explicitly allow it.
64
- > The restriction exists to make the security implications of browser automation clear before enabling wider access.
65
-
66
- See the full guide: [docs/guides/security.md](docs/guides/security.md)
67
-
68
- ## What can you use it for
69
-
70
- ### Headless navigation
71
-
72
- With the daemon installed and an agent skill configured, an agent can execute tasks like:
73
-
74
- ```
75
- "What are the main news about aliens on news.com?"
76
- ```
77
-
78
- PinchTab exposes browser tools that allow agents to navigate pages, extract structured content, and interact with the DOM without wasting tokens on raw HTML or images.
79
-
80
- ### Headed navigation
81
-
82
- In addition to headless automation, PinchTab supports headed Chrome profiles.
83
-
84
- You can create profiles configured with authentication, cookies, extensions, or specific environments. Each profile can have a name and description.
85
-
86
- For example, an agent request like:
87
-
88
- ```
89
- "Log into my work profile and download the weekly report"
90
- ```
91
-
92
- can automatically select the appropriate profile and perform the action.
93
-
94
- ### Local container isolation
95
-
96
- If you prefer stronger isolation, PinchTab can run inside Docker.
97
-
98
- This allows agents to control browsers in a sandboxed environment, reducing risk when running automation tasks locally.
99
-
100
- ### Distributed automation
101
-
102
- PinchTab can manage multiple Chrome instances (headless or headed) across containers or remote machines.
103
-
104
- Typical use cases include:
105
-
106
- - QA automation
107
- - testing environments
108
- - distributed browsing tasks
109
- - development tooling
110
-
111
- You can connect to multiple PinchTab servers, or attach to Chrome instances running in remote debug mode.
112
-
113
- ## Process Model
114
-
115
- PinchTab is server-first:
116
- 1. install the daemon or run `pinchtab server` for the full control plane
117
- 2. let the server manage profiles and instances
118
- 3. let each managed instance run behind a lightweight `pinchtab bridge` runtime
119
-
120
- In practice:
121
- - Server — the main product entry point and control plane
122
- - Bridge — the runtime that manages a single browser instance
123
- - Attach — an advanced mode for registering externally managed Chrome instances
124
-
125
- ### Primary Usage
126
-
127
- The primary user journey is:
128
-
129
- 1. install Pinchtab
130
- 2. install and start the daemon with `pinchtab daemon install`
131
- 3. point your agent or tool at `http://localhost:9867`
132
- 4. let PinchTab act as your local browser service
133
-
134
- That is the default “replace the browser runtime” scenario.
135
- Most users should not need to think about `pinchtab bridge` directly, and only need `pinchtab` when they want the local interactive menu.
136
-
137
- ### Key Features
138
-
139
- - **CLI or Curl** — Control via command-line or HTTP API
140
- - **Token-efficient** — 800 tokens/page with text extraction (5-13x cheaper than screenshots)
141
- - **Headless or Headed** — Run without a window or with visible Chrome
142
- - **Multi-instance** — Run multiple parallel Chrome processes with isolated profiles
143
- - **Self-contained** — ~15MB binary, no external dependencies
144
- - **Accessibility-first** — Stable element refs instead of fragile coordinates
145
- - **ARM64-optimized** — First-class Raspberry Pi support with automatic Chromium detection
146
-
147
- ---
148
-
149
- ## Quick Start
150
-
151
- ### Installation
152
-
153
- **macOS / Linux:**
154
- ```bash
155
- curl -fsSL https://pinchtab.com/install.sh | bash
156
- ```
157
-
158
- **Homebrew (macOS / Linux):**
159
- ```bash
160
- brew install pinchtab/tap/pinchtab
161
- ```
162
-
163
- **npm:**
164
- ```bash
165
- npm install -g pinchtab
166
- ```
167
-
168
- ### Shell Completion
169
-
170
- Generate and install shell completions after `pinchtab` is on your `PATH`:
171
-
172
- ```bash
173
- # Generate and install zsh completions
174
- pinchtab completion zsh > "${fpath[1]}/_pinchtab"
175
-
176
- # Generate bash completions
177
- pinchtab completion bash > /etc/bash_completion.d/pinchtab
178
-
179
- # Generate fish completions
180
- pinchtab completion fish > ~/.config/fish/completions/pinchtab.fish
181
- ```
182
-
183
- **Docker:**
184
- ```bash
185
- docker run -d \
186
- --name pinchtab \
187
- -p 127.0.0.1:9867:9867 \
188
- -v pinchtab-data:/data \
189
- --shm-size=2g \
190
- pinchtab/pinchtab
191
- ```
192
-
193
- The bundled container persists its managed config at `/data/.config/pinchtab/config.json`.
194
- If you want to supply your own config file instead, mount it and point `PINCHTAB_CONFIG` at it:
195
-
196
- ```bash
197
- docker run -d \
198
- --name pinchtab \
199
- -p 127.0.0.1:9867:9867 \
200
- -e PINCHTAB_CONFIG=/config/config.json \
201
- -v "$PWD/config.json:/config/config.json:ro" \
202
- -v pinchtab-data:/data \
203
- --shm-size=2g \
204
- pinchtab/pinchtab
205
- ```
206
-
207
- ### Use It
208
-
209
- **Terminal 1 — Start the server:**
210
- ```bash
211
- pinchtab server
212
- ```
213
-
214
- **Recommended for daily local use — install the daemon once:**
215
- ```bash
216
- pinchtab daemon install
217
- pinchtab daemon
218
- ```
219
-
220
- That keeps PinchTab running in the background so your agent tools can reuse it without an open terminal.
221
-
222
- **Terminal 2 — Control the browser:**
223
- ```bash
224
- # Navigate
225
- pinchtab nav https://pinchtab.com
226
-
227
- # Get page structure
228
- pinchtab snap -i -c
229
-
230
- # Click an element
231
- pinchtab click e5
232
-
233
- # Extract text
234
- pinchtab text
235
- ```
236
-
237
- Or use the HTTP API directly:
238
- ```bash
239
- # Create an instance (returns instance id)
240
- INST=$(curl -s -X POST http://localhost:9867/instances/launch \
241
- -H "Content-Type: application/json" \
242
- -d '{"name":"work","mode":"headless"}' | jq -r '.id')
243
-
244
- # Open a tab in that instance
245
- TAB=$(curl -s -X POST http://localhost:9867/instances/$INST/tabs/open \
246
- -H "Content-Type: application/json" \
247
- -d '{"url":"https://pinchtab.com"}' | jq -r '.tabId')
248
-
249
- # Get snapshot
250
- curl "http://localhost:9867/tabs/$TAB/snapshot?filter=interactive"
251
-
252
- # Click element
253
- curl -X POST "http://localhost:9867/tabs/$TAB/action" \
254
- -H "Content-Type: application/json" \
255
- -d '{"kind":"click","ref":"e5"}'
256
- ```
257
-
258
- ---
259
-
260
- ## Core Concepts
261
-
262
- **Server** — The main PinchTab process. It manages profiles, instances, routing, and the dashboard.
263
-
264
- **Instance** — A running Chrome process. Each instance can have one profile.
265
-
266
- **Profile** — Browser state (cookies, history, local storage). Log in once, stay logged in across restarts.
267
-
268
- **Tab** — A single webpage. Each instance can have multiple tabs.
269
-
270
- **Bridge** — The single-instance runtime behind a managed instance. Usually spawned by the server, not started manually.
271
-
272
- Read more in the [Core Concepts](https://pinchtab.com/docs/core-concepts) guide.
273
-
274
- ---
275
-
276
- ## Why PinchTab?
277
-
278
- | Aspect | PinchTab |
279
- |--------|----------|
280
- | **Tokens performance** | ✅ |
281
- | **Headless and Headed** | ✅ |
282
- | **Profile** | ✅ |
283
- | **Advanced CDP control** | ✅ |
284
- | **Persistent sessions** | ✅ |
285
- | **Binary size** | ✅ |
286
- | **Multi-instance** | ✅ |
287
- | **External Chrome attach** | ✅ |
288
-
289
- ---
290
-
291
- ## Privacy
292
-
293
- PinchTab is a fully open-source, local-only tool. No telemetry, no analytics, no outbound connections. The binary binds to `127.0.0.1` by default. Persistent profiles store browser sessions locally on your machine — similar to how a human reuses their browser. The single Go binary (~16 MB) is fully verifiable: build from source at [github.com/pinchtab/pinchtab](https://github.com/pinchtab/pinchtab).
294
-
295
- ---
296
-
297
- ## Documentation
298
-
299
- Full docs at **[pinchtab.com/docs](https://pinchtab.com/docs)**
300
-
301
- ### MCP (SMCP) integration
302
-
303
- An **SMCP plugin** in this repo lets AI agents control PinchTab via the [Model Context Protocol](https://github.com/sanctumos/smcp) (SMCP). One plugin exposes 15 tools (e.g. `pinchtab__navigate`, `pinchtab__snapshot`, `pinchtab__action`). No extra runtime deps (stdlib only). See **[plugins/README.md](plugins/README.md)** for setup (env vars and paths).
304
-
305
- ---
306
-
307
- ## Examples
308
-
309
- ### AI Agent Automation
310
-
311
- ```bash
312
- # Your AI agent can:
313
- pinchtab nav https://pinchtab.com
314
- pinchtab snap -i # Get clickable elements
315
- pinchtab click e5 # Click by ref
316
- pinchtab fill e3 "user@pinchtab.com" # Fill input
317
- pinchtab press e7 Enter # Submit form
318
- ```
319
-
320
- ### Data Extraction
321
-
322
- ```bash
323
- # Extract text (token-efficient)
324
- pinchtab nav https://pinchtab.com/article
325
- pinchtab text # ~800 tokens instead of 10,000
326
- ```
327
-
328
- ### Multi-Instance Workflows
329
-
330
- ```bash
331
- # Run multiple instances in parallel
332
- curl -s -X POST http://localhost:9867/instances/start \
333
- -H "Content-Type: application/json" \
334
- -d '{"profileId":"alice","mode":"headless"}'
335
-
336
- curl -s -X POST http://localhost:9867/instances/start \
337
- -H "Content-Type: application/json" \
338
- -d '{"profileId":"bob","mode":"headless"}'
339
-
340
- # Each instance is isolated
341
- curl http://localhost:9867/instances
342
- ```
343
-
344
- See [chrome-files.md](chrome-files.md) for technical details on how PinchTab manages Chrome user data directories and ensures isolation between parallel instances.
345
-
346
- ---
347
-
348
- ## Development
349
-
350
- Want to contribute? Start with [CONTRIBUTING.md](CONTRIBUTING.md).
351
- The full setup and workflow guide lives at [docs/guides/contributing.md](docs/guides/contributing.md).
352
-
353
- **Quick start:**
354
- ```bash
355
- git clone https://github.com/pinchtab/pinchtab.git
356
- cd pinchtab
357
- ./dev doctor # Verifies environment, offers hooks/deps setup
358
- go build ./cmd/pinchtab # Build pinchtab binary
359
- ```
360
-
361
- ---
362
-
363
- ## License
364
-
365
- MIT — Free and open source.
366
-
367
- ---
368
-
369
- **Get started:** [pinchtab.com/docs](https://pinchtab.com/docs)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pinchtab/RELEASE.md DELETED
@@ -1,224 +0,0 @@
1
- # Release Process
2
-
3
- **⚠️ CRITICAL:** The npm package depends on Go binaries being in the GitHub release. The release pipeline will fail hard if goreleaser doesn't upload binaries — npm users won't get a working binary.
4
-
5
- Pinchtab uses an automated CI/CD pipeline triggered by Git tags. When you push a tag like `v0.7.0`, GitHub Actions:
6
-
7
- 1. **Builds Go binaries** — darwin-arm64, darwin-x64, linux-x64, linux-arm64, windows-x64
8
- 2. **Creates GitHub release** — with checksums.txt for integrity verification
9
- 3. **Publishes to npm** — TypeScript SDK with auto-download postinstall script
10
- 4. **Builds Docker images** — linux/amd64, linux/arm64
11
-
12
- ## Prerequisites
13
-
14
- ### Secrets (configure once in GitHub)
15
-
16
- Go to **Settings → Secrets and variables → Actions** and add:
17
-
18
- - **NPM_TOKEN** — npm authentication token
19
- - Create at https://npmjs.com/settings/~/tokens
20
- - Scope: `automation` (publish + read)
21
-
22
- - **DOCKERHUB_USER** — Docker Hub username (if using Docker Hub)
23
- - **DOCKERHUB_TOKEN** — Docker Hub personal access token
24
-
25
- ### Local setup
26
-
27
- ```bash
28
- # 1. Ensure main branch is up to date
29
- git checkout main && git pull origin main
30
-
31
- # 2. Merge feature branches
32
- # (all features should be on main before tagging)
33
-
34
- # 3. Verify version consistency
35
- cat package.json | jq .version # npm package
36
- cat go.mod | grep "module" # Go module
37
- git describe --tags # latest tag
38
- ```
39
-
40
- ## Pre-Release Checklist
41
-
42
- Goreleaser is already configured correctly. Verify before tagging:
43
-
44
- ```bash
45
- # 1. Check builds section (compiles for all platforms)
46
- grep -A 8 "^builds:" .goreleaser.yml
47
- # Should output: linux/darwin/windows + amd64/arm64
48
-
49
- # 2. Check archives use binary format (required for npm)
50
- grep -A 5 "^archives:" .goreleaser.yml
51
- # Should show: format: binary (not tar/zip)
52
-
53
- # 3. Verify checksums enabled
54
- grep "checksum:" .goreleaser.yml
55
- # Should output: name_template: checksums.txt
56
-
57
- # 4. Check release config
58
- grep -A 2 "^release:" .goreleaser.yml
59
- # Should output: GitHub owner/repo
60
- ```
61
-
62
- **Configuration status:**
63
- - ✅ `builds:` section compiles all platforms (darwin-arm64, darwin-amd64, linux-arm64, linux-amd64, windows-amd64, windows-arm64)
64
- - ✅ `archives:` outputs binaries directly: `pinchtab-darwin-arm64`, `pinchtab-linux-x64`, etc.
65
- - ✅ `checksum:` generates `checksums.txt` (used by npm postinstall verification)
66
- - ✅ `release:` points to GitHub (uploads everything automatically)
67
-
68
- **Ready to release.** Just tag and push.
69
-
70
- ## Releasing
71
-
72
- ### For patch/minor versions (recommended)
73
-
74
- ```bash
75
- # 1. Bump version in all places
76
- npm version patch # or minor, major
77
- git push origin main
78
-
79
- # 2. Create tag
80
- git tag v0.7.1
81
- git push origin v0.7.1
82
- ```
83
-
84
- ### For manual releases
85
-
86
- ```bash
87
- # 1. Tag directly
88
- git tag -a v0.7.1 -m "Release v0.7.1"
89
- git push origin v0.7.1
90
-
91
- # 2. Or via GitHub UI: Releases → Create from tag
92
- # (workflow will auto-create if not present)
93
- ```
94
-
95
- ### Using workflow_dispatch (manual trigger)
96
-
97
- If you need to re-release an existing tag:
98
-
99
- 1. Go to **Actions → Release**
100
- 2. **Run workflow**
101
- 3. Enter tag (e.g. `v0.7.1`)
102
-
103
- For a no-side-effects dry run from `main` or any other ref:
104
-
105
- 1. Go to **Actions → Release**
106
- 2. **Run workflow**
107
- 3. Leave `tag` empty
108
- 4. Set `ref` to the branch, tag, or commit you want to test
109
- 5. Set `dry_run` to `true`
110
-
111
- Dry-run behavior:
112
- - GoReleaser runs in snapshot mode, so artifacts are built but not published
113
- - npm runs `npm publish --dry-run`
114
- - Docker runs a multi-arch `buildx` build with `push=false`
115
- - ClawHub skill publishing is not part of this workflow and is therefore skipped
116
-
117
- ## Pipeline details
118
-
119
- ### 1. Goreleaser (Go binary) — CRITICAL for npm
120
-
121
- Triggered on `v*` tag push. Builds binaries and creates GitHub release.
122
-
123
- **What it does:**
124
- - ✅ Compiles for all platforms (darwin-arm64, darwin-x64, linux-x64, linux-arm64, windows-x64)
125
- - ✅ Generates `checksums.txt` (SHA256)
126
- - ✅ **Uploads binaries to GitHub Releases** ← Required by npm postinstall!
127
- - Also: Docker images, changelog, etc.
128
-
129
- **⚠️ CRITICAL:** npm postinstall script downloads binaries from GitHub Releases. If the release doesn't have the binaries (e.g., only Docker images), `npm install pinchtab` will fail silently and the binary won't be available.
130
-
131
- **Configured in:** `.goreleaser.yml`
132
-
133
- **Verify release has binaries:**
134
- ```bash
135
- curl -s https://api.github.com/repos/pinchtab/pinchtab/releases/v0.7.0 | jq '.assets[].name'
136
- # Should output: pinchtab-darwin-arm64, pinchtab-darwin-x64, etc.
137
- ```
138
-
139
- ### 2. npm publish
140
-
141
- Depends on: `release` job (waits for goreleaser to finish)
142
-
143
- **What it does:**
144
- - Syncs version from tag (v0.7.0 → 0.7.0)
145
- - Builds TypeScript (`npm run build`)
146
- - Publishes to npm registry
147
- - Users get postinstall script that downloads binaries from GitHub Releases
148
-
149
- **User flow on `npm install pinchtab`:**
150
- ```
151
- 1. npm downloads @pinchtab/cli package
152
- 2. Runs postinstall script
153
- 3. Script detects OS/arch (darwin-arm64, linux-x64, etc.)
154
- 4. Downloads binary from GitHub release
155
- 5. Verifies SHA256 checksum
156
- 6. Stores in ~/.pinchtab/bin/0.7.0/pinchtab-<os>-<arch>
157
- 7. Makes executable
158
- 8. If ANY STEP fails → npm install fails (exit 1)
159
- ```
160
-
161
- **⚠️ REQUIRES:** Goreleaser must have successfully uploaded binaries to GitHub release. The npm postinstall will verify this and fail hard if binaries are missing.
162
-
163
- ### 3. Docker
164
-
165
- GitHub Actions builds the release image directly from the tagged source with `docker buildx`.
166
- The workflow pushes the same multi-arch build to both GHCR and Docker Hub, so Docker no longer depends on GoReleaser's temporary build context.
167
-
168
- ### 4. ClawHub skill
169
-
170
- The main `Release` workflow publishes the Pinchtab skill to ClawHub only after both npm and Docker complete successfully.
171
- The standalone `Publish Skill` workflow is manual-only and is intended for retries or one-off recovery publishes.
172
-
173
- ## Troubleshooting
174
-
175
- ### npm publish fails (403)
176
-
177
- - Check **NPM_TOKEN** is set in secrets
178
- - Verify token has `automation` scope
179
- - Check you're not already published (can't overwrite existing version)
180
-
181
- ### Binary checksum mismatch
182
-
183
- - goreleaser must generate `checksums.txt`
184
- - Verify `.goreleaser.yml` has `checksum:` section
185
- - Check GitHub release includes `checksums.txt`
186
-
187
- ### Docker push fails
188
-
189
- - Verify DOCKERHUB_USER and DOCKERHUB_TOKEN
190
- - Check token has permission to push
191
-
192
- ## Rolling back
193
-
194
- If something goes wrong:
195
-
196
- ```bash
197
- # Delete the tag locally and on GitHub
198
- git tag -d v0.7.1
199
- git push origin :refs/tags/v0.7.1
200
-
201
- # Delete npm version (requires owner permission)
202
- npm unpublish pinchtab@0.7.1
203
-
204
- # Revert any commits
205
- git revert <commit>
206
- git push origin main
207
-
208
- # Retag when ready
209
- git tag v0.7.1
210
- git push origin v0.7.1
211
- ```
212
-
213
- ## Version strategy
214
-
215
- - Use **semantic versioning**: v0.7.0 (major.minor.patch)
216
- - Tag on main branch only
217
- - One tag = one release (all artifacts)
218
- - npm version must match Go binary tag
219
-
220
- ## See also
221
-
222
- - `.github/workflows/release.yml` — GitHub Actions workflow
223
- - `.goreleaser.yml` — Go binary release config
224
- - `npm/package.json` — npm package metadata
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pinchtab/SECURITY.md DELETED
@@ -1,27 +0,0 @@
1
- # Security Policy
2
-
3
- ## Supported Versions
4
-
5
- We currently support the following versions of Pinchtab with security updates:
6
-
7
- | Version | Supported |
8
- | ------- | ------------------ |
9
- | v0.7.x | :white_check_mark: |
10
- | v0.6.x | :white_check_mark: |
11
- | < v0.6 | :x: |
12
-
13
- ## Reporting a Vulnerability
14
-
15
- We take the security of our browser automation bridge seriously. If you believe you have found a security vulnerability, please do not report it via a public GitHub issue.
16
-
17
- Instead, please report vulnerabilities privately by:
18
-
19
- 1. Opening a **Private Vulnerability Report** on GitHub (if available for this repo).
20
- 2. Or emailing the maintainer directly at [INSERT EMAIL ADDRESS].
21
-
22
- Please include:
23
- - A description of the vulnerability.
24
- - Steps to reproduce (proof of concept).
25
- - Potential impact.
26
-
27
- We will acknowledge receipt of your report within 48 hours and provide a timeline for a fix if necessary.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pinchtab/TESTING.md DELETED
@@ -1,176 +0,0 @@
1
- # Testing
2
-
3
- ## Quick Start with dev
4
-
5
- The `dev` developer toolkit is the easiest way to run checks and tests:
6
-
7
- ```bash
8
- ./dev # Interactive picker
9
- ./dev test # All tests (unit + E2E)
10
- ./dev test unit # Unit tests only
11
- ./dev e2e # E2E tests (both curl and CLI)
12
- ./dev e2e orchestrator # Orchestrator-heavy E2E tests only
13
- ./dev e2e curl # E2E curl tests only
14
- ./dev e2e cli # E2E CLI tests only
15
- ./dev check # All checks (format, vet, build, lint)
16
- ./dev check go # Go checks only
17
- ./dev check security # Gosec security scan
18
- ./dev format dashboard # Run Prettier on dashboard sources
19
- ./dev doctor # Setup dev environment
20
- ```
21
-
22
- ## Unit Tests
23
-
24
- ```bash
25
- go test ./...
26
- # or
27
- ./dev test unit
28
- ```
29
-
30
- Unit tests are standard Go tests that validate individual packages and functions without launching a full server.
31
-
32
- ## E2E Tests
33
-
34
- End-to-end tests launch a real pinchtab server with Chrome and run e2e-level tests against it.
35
-
36
- ### Curl Tests (HTTP API)
37
-
38
- ```bash
39
- ./dev e2e curl
40
- ```
41
-
42
- Runs 183 HTTP-level tests using curl against the server. Tests the REST API, navigation, snapshots, and other HTTP endpoints.
43
-
44
- ### CLI Tests
45
-
46
- ```bash
47
- ./dev e2e cli
48
- ```
49
-
50
- Runs CLI e2e tests. Tests the command-line interface directly.
51
-
52
- ### Both E2E Test Suites
53
-
54
- ```bash
55
- ./dev e2e
56
- ```
57
-
58
- Runs all E2E tests (curl + CLI, 224 tests total).
59
-
60
- ### Orchestrator E2E Suite
61
-
62
- ```bash
63
- ./dev e2e orchestrator
64
- ```
65
-
66
- Runs the orchestration-focused curl scenarios, including multi-instance flows and remote bridge attachment against the dedicated `pinchtab-bridge` Compose service.
67
-
68
- ## Environment Variables
69
-
70
- | Variable | Default | Description |
71
- |---|---|---|
72
- | `CI` | _(unset)_ | Set to `true` for longer health check timeouts (60s vs 30s) |
73
-
74
- ### Temp Directory Layout
75
-
76
- Each E2E test run creates a single temp directory under `/tmp/pinchtab-test-*/`:
77
-
78
- ```
79
- /tmp/pinchtab-test-123456789/
80
- ├── pinchtab # Compiled test binary
81
- ├── state/ # Dashboard state (profiles, instances)
82
- └── profiles/ # Chrome user-data directories
83
- ```
84
-
85
- Everything is cleaned up automatically when tests finish.
86
-
87
- ## Test File Structure
88
-
89
- E2E tests are organized in two directories:
90
-
91
- - **`tests/e2e/scenarios/*.sh`** — HTTP curl-based tests (183 tests)
92
- - Test the REST API directly
93
- - Use Docker Compose: `tests/e2e/docker-compose.yml`
94
-
95
- - **`tests/e2e/scenarios-orchestrator/*.sh`** — orchestration-heavy curl tests
96
- - Test multi-instance flows and remote bridge attachment
97
- - Use Docker Compose: `tests/e2e/docker-compose-orchestrator.yml`
98
-
99
- - **`tests/e2e/scenarios-cli/*.sh`** — CLI e2e tests (41 tests)
100
- - Test the command-line interface
101
- - Use Docker Compose: `tests/e2e/docker-compose.cli.yml`
102
-
103
- Each test is a standalone bash script that:
104
- 1. Starts the test server (or uses existing)
105
- 2. Runs curl or CLI commands
106
- 3. Asserts expected output or exit codes
107
- 4. Cleans up
108
-
109
- ## Writing New E2E Tests
110
-
111
- Create a new bash script in `tests/e2e/scenarios/` (for curl tests) or `tests/e2e/scenarios-cli/` (for CLI tests):
112
-
113
- ### Example: Simple Curl Test
114
-
115
- ```bash
116
- #!/bin/bash
117
-
118
- # tests/e2e/scenarios/test-my-feature.sh
119
-
120
- set -e # Exit on error
121
-
122
- # Source helpers
123
- . "$(dirname "$0")/../helpers.sh"
124
-
125
- # Test setup
126
- SERVER_URL="http://localhost:9867"
127
-
128
- # Start server if needed
129
- start_test_server
130
-
131
- # Run test
132
- echo "Testing my feature..."
133
- RESPONSE=$(curl -s "$SERVER_URL/health")
134
-
135
- if [ "$(echo "$RESPONSE" | jq -r '.status')" != "ok" ]; then
136
- echo "❌ Health check failed"
137
- exit 1
138
- fi
139
-
140
- echo "✅ Test passed"
141
- ```
142
-
143
- ### Example: CLI Test
144
-
145
- ```bash
146
- #!/bin/bash
147
-
148
- # tests/e2e/scenarios-cli/test-my-cli.sh
149
-
150
- set -e
151
-
152
- # Source helpers
153
- . "$(dirname "$0")/../helpers.sh"
154
-
155
- # Test the CLI
156
- echo "Testing pinchtab CLI..."
157
- OUTPUT=$($PINCHTAB_BIN --version)
158
-
159
- if [[ ! "$OUTPUT" =~ pinchtab ]]; then
160
- echo "❌ Version output incorrect"
161
- exit 1
162
- fi
163
-
164
- echo "✅ CLI test passed"
165
- ```
166
-
167
- ## Coverage
168
-
169
- Generate coverage for unit tests:
170
-
171
- ```bash
172
- go test ./... -coverprofile=coverage.out
173
- go tool cover -html=coverage.out
174
- ```
175
-
176
- Note: E2E tests are black-box tests and don't contribute to code coverage metrics directly.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pinchtab/THIRD_PARTY_LICENSES.md DELETED
@@ -1,98 +0,0 @@
1
- # Third-Party Licenses
2
-
3
- Pinchtab depends on the following open-source packages. All are compatible with MIT licensing.
4
-
5
- ## Direct Dependencies
6
-
7
- ### chromedp/chromedp
8
- - **License:** MIT
9
- - **Copyright:** (c) 2016-2025 Kenneth Shaw
10
- - **URL:** https://github.com/chromedp/chromedp
11
- - **Purpose:** Chrome DevTools Protocol driver — launches and controls Chrome
12
-
13
- ### chromedp/cdproto
14
- - **License:** MIT
15
- - **Copyright:** (c) 2016-2025 Kenneth Shaw
16
- - **URL:** https://github.com/chromedp/cdproto
17
- - **Purpose:** Generated Go types for the Chrome DevTools Protocol
18
-
19
- ## Transitive Dependencies
20
-
21
- ### chromedp/sysutil
22
- - **License:** MIT
23
- - **Copyright:** (c) 2016-2017 Kenneth Shaw
24
- - **URL:** https://github.com/chromedp/sysutil
25
- - **Purpose:** System utilities for chromedp (finding Chrome binary)
26
-
27
- ### go-json-experiment/json
28
- - **License:** BSD 3-Clause
29
- - **Copyright:** (c) 2020 The Go Authors
30
- - **URL:** https://github.com/go-json-experiment/json
31
- - **Purpose:** Experimental JSON library used by cdproto
32
-
33
- ### gobwas/ws
34
- - **License:** MIT
35
- - **Copyright:** (c) 2017-2021 Sergey Kamardin
36
- - **URL:** https://github.com/gobwas/ws
37
- - **Purpose:** WebSocket implementation for CDP communication
38
-
39
- ### gobwas/httphead
40
- - **License:** MIT
41
- - **Copyright:** (c) 2017 Sergey Kamardin
42
- - **URL:** https://github.com/gobwas/httphead
43
- - **Purpose:** HTTP header parsing (ws dependency)
44
-
45
- ### gobwas/pool
46
- - **License:** MIT
47
- - **Copyright:** (c) 2017-2019 Sergey Kamardin
48
- - **URL:** https://github.com/gobwas/pool
49
- - **Purpose:** Pool utilities (ws dependency)
50
-
51
- ### golang.org/x/sys
52
- - **License:** BSD 3-Clause
53
- - **Copyright:** (c) 2009 The Go Authors
54
- - **URL:** https://github.com/golang/sys
55
- - **Purpose:** Go system call wrappers
56
-
57
- ### github.com/ledongthuc/pdf
58
- - **License:** BSD 3-Clause
59
- - **Copyright:** (c) 2009 The Go Authors
60
- - **URL:** https://github.com/ledongthuc/pdf
61
- - **Purpose:** Transitive dependency in module graph (test tooling chain)
62
-
63
- ### github.com/orisano/pixelmatch
64
- - **License:** MIT
65
- - **Copyright:** (c) 2022 orisano
66
- - **URL:** https://github.com/orisano/pixelmatch
67
- - **Purpose:** Transitive dependency in module graph (test tooling chain)
68
-
69
- ### gopkg.in/check.v1
70
- - **License:** BSD-style
71
- - **Copyright:** (c) 2010-2013 Gustavo Niemeyer
72
- - **URL:** https://gopkg.in/check.v1
73
- - **Purpose:** Transitive dependency in module graph (test tooling chain)
74
-
75
- ### gopkg.in/yaml.v3
76
- - **License:** Apache 2.0 / MIT
77
- - **Copyright:** (c) 2006-2011 Kirill Simonov, (c) 2011-2019 Canonical Ltd
78
- - **URL:** https://github.com/go-yaml/yaml
79
- - **Purpose:** YAML output format for snapshots
80
-
81
- ## Summary
82
-
83
- | Package | License | Compatible |
84
- |---------|---------|------------|
85
- | chromedp/chromedp | MIT | ✅ |
86
- | chromedp/cdproto | MIT | ✅ |
87
- | chromedp/sysutil | MIT | ✅ |
88
- | go-json-experiment/json | BSD 3-Clause | ✅ |
89
- | gobwas/ws | MIT | ✅ |
90
- | gobwas/httphead | MIT | ✅ |
91
- | gobwas/pool | MIT | ✅ |
92
- | golang.org/x/sys | BSD 3-Clause | ✅ |
93
- | github.com/ledongthuc/pdf | BSD 3-Clause | ✅ |
94
- | github.com/orisano/pixelmatch | MIT | ✅ |
95
- | gopkg.in/check.v1 | BSD-style | ✅ |
96
- | gopkg.in/yaml.v3 | Apache 2.0 / MIT | ✅ |
97
-
98
- All dependencies are MIT, BSD-style, or Apache 2.0 licensed, compatible with Pinchtab's MIT license.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pinchtab/assets/docs-no-background-256.png DELETED
Binary file (71.1 kB)
 
pinchtab/assets/favicon.png DELETED
Binary file (1.91 kB)
 
pinchtab/assets/pinchtab-headless.png DELETED

Git LFS Details

  • SHA256: ce6460c357b720a8febef68203ab064750a29de094b2c9b7e173d67dfef7aa61
  • Pointer size: 131 Bytes
  • Size of remote file: 563 kB
pinchtab/cmd/pinchtab/build_test.go DELETED
@@ -1,111 +0,0 @@
1
- package main
2
-
3
- import (
4
- "os"
5
- "path/filepath"
6
- "testing"
7
-
8
- "gopkg.in/yaml.v3"
9
- )
10
-
11
- // GoReleaserConfig represents the minimal goreleaser config we care about
12
- type GoReleaserConfig struct {
13
- Builds []struct {
14
- GOOS []string `yaml:"goos"`
15
- GOARCH []string `yaml:"goarch"`
16
- } `yaml:"builds"`
17
- }
18
-
19
- // TestBinaryPermutations verifies all expected binary permutations are configured in goreleaser
20
- func TestBinaryPermutations(t *testing.T) {
21
- // Find .goreleaser.yml in repo root (2 levels up from cmd/pinchtab/)
22
- repoRoot := filepath.Join("..", "..", ".goreleaser.yml")
23
- data, err := os.ReadFile(repoRoot)
24
- if err != nil {
25
- t.Fatalf("failed to read .goreleaser.yml at %s: %v", repoRoot, err)
26
- }
27
-
28
- var cfg GoReleaserConfig
29
- if err := yaml.Unmarshal(data, &cfg); err != nil {
30
- t.Fatalf("failed to parse .goreleaser.yml: %v", err)
31
- }
32
-
33
- if len(cfg.Builds) == 0 {
34
- t.Fatal("no builds configured in .goreleaser.yml")
35
- }
36
-
37
- build := cfg.Builds[0]
38
-
39
- // Expected OS/arch combinations
40
- expectedOS := map[string]bool{
41
- "linux": true,
42
- "darwin": true,
43
- "windows": true,
44
- }
45
-
46
- expectedArch := map[string]bool{
47
- "amd64": true,
48
- "arm64": true,
49
- }
50
-
51
- // Verify all expected OS are configured
52
- for os := range expectedOS {
53
- found := false
54
- for _, configOS := range build.GOOS {
55
- if configOS == os {
56
- found = true
57
- break
58
- }
59
- }
60
- if !found {
61
- t.Errorf("OS %q not found in goreleaser config", os)
62
- }
63
- }
64
-
65
- // Verify all expected architectures are configured
66
- for arch := range expectedArch {
67
- found := false
68
- for _, configArch := range build.GOARCH {
69
- if configArch == arch {
70
- found = true
71
- break
72
- }
73
- }
74
- if !found {
75
- t.Errorf("Architecture %q not found in goreleaser config", arch)
76
- }
77
- }
78
-
79
- // Calculate expected binary count
80
- totalExpected := len(expectedOS) * len(expectedArch)
81
- totalConfigured := len(build.GOOS) * len(build.GOARCH)
82
-
83
- if totalConfigured != totalExpected {
84
- t.Errorf("expected %d binaries (3 OS × 2 arch), but config produces %d",
85
- totalExpected, totalConfigured)
86
- }
87
-
88
- t.Logf("✓ Binary matrix verified: %d OS × %d arch = %d total binaries",
89
- len(build.GOOS), len(build.GOARCH), totalConfigured)
90
- }
91
-
92
- // TestExpectedBinaryNames verifies correct naming for all permutations
93
- func TestExpectedBinaryNames(t *testing.T) {
94
- expectedBinaries := []string{
95
- "pinchtab-linux-amd64",
96
- "pinchtab-linux-arm64",
97
- "pinchtab-darwin-amd64",
98
- "pinchtab-darwin-arm64",
99
- "pinchtab-windows-amd64.exe",
100
- "pinchtab-windows-arm64.exe",
101
- }
102
-
103
- if len(expectedBinaries) != 6 {
104
- t.Errorf("expected 6 binaries, got %d", len(expectedBinaries))
105
- }
106
-
107
- t.Logf("Expected binary names (%d total):", len(expectedBinaries))
108
- for _, bin := range expectedBinaries {
109
- t.Logf(" ✓ %s", bin)
110
- }
111
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pinchtab/cmd/pinchtab/cmd_bridge.go DELETED
@@ -1,47 +0,0 @@
1
- package main
2
-
3
- import (
4
- "fmt"
5
- "strings"
6
-
7
- "github.com/pinchtab/pinchtab/internal/config"
8
- "github.com/pinchtab/pinchtab/internal/server"
9
- "github.com/spf13/cobra"
10
- )
11
-
12
- var bridgeEngine string
13
-
14
- var bridgeCmd = &cobra.Command{
15
- Use: "bridge",
16
- Short: "Start single-instance bridge-only server",
17
- RunE: func(cmd *cobra.Command, args []string) error {
18
- cfg := config.Load()
19
- engineMode, err := resolveBridgeEngine(bridgeEngine, cfg.Engine)
20
- if err != nil {
21
- return err
22
- }
23
- cfg.Engine = engineMode
24
- server.RunBridgeServer(cfg)
25
- return nil
26
- },
27
- }
28
-
29
- func resolveBridgeEngine(flagValue, configValue string) (string, error) {
30
- engineMode := strings.ToLower(strings.TrimSpace(configValue))
31
- if strings.TrimSpace(flagValue) != "" {
32
- engineMode = strings.ToLower(strings.TrimSpace(flagValue))
33
- }
34
- if engineMode == "" {
35
- engineMode = "chrome"
36
- }
37
- if engineMode != "chrome" && engineMode != "lite" && engineMode != "auto" {
38
- return "", fmt.Errorf("invalid --engine %q (expected chrome, lite, or auto)", engineMode)
39
- }
40
- return engineMode, nil
41
- }
42
-
43
- func init() {
44
- bridgeCmd.GroupID = "primary"
45
- bridgeCmd.Flags().StringVar(&bridgeEngine, "engine", "", "Bridge engine: chrome, lite, or auto (overrides config)")
46
- rootCmd.AddCommand(bridgeCmd)
47
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pinchtab/cmd/pinchtab/cmd_bridge_test.go DELETED
@@ -1,36 +0,0 @@
1
- package main
2
-
3
- import "testing"
4
-
5
- func TestResolveBridgeEngine(t *testing.T) {
6
- tests := []struct {
7
- name string
8
- flagValue string
9
- cfgValue string
10
- want string
11
- wantErr bool
12
- }{
13
- {name: "config default", cfgValue: "lite", want: "lite"},
14
- {name: "flag overrides config", flagValue: "auto", cfgValue: "chrome", want: "auto"},
15
- {name: "empty falls back to chrome", want: "chrome"},
16
- {name: "invalid", flagValue: "bogus", wantErr: true},
17
- }
18
-
19
- for _, tt := range tests {
20
- t.Run(tt.name, func(t *testing.T) {
21
- got, err := resolveBridgeEngine(tt.flagValue, tt.cfgValue)
22
- if tt.wantErr {
23
- if err == nil {
24
- t.Fatal("expected error")
25
- }
26
- return
27
- }
28
- if err != nil {
29
- t.Fatalf("unexpected error: %v", err)
30
- }
31
- if got != tt.want {
32
- t.Fatalf("got %q want %q", got, tt.want)
33
- }
34
- })
35
- }
36
- }