Compare commits

..
Author SHA1 Message Date
CloakHQ c35afec09a feat: add GitHub Models issue knowledge responder
Auto-responds to new issues with related docs and past issues.
Uses actions/ai-inference with GPT-4.1 (free GitHub Models).
5 search strategies: title, keywords, body terms, labels, error msgs.
No external API keys needed.
2026-04-10 23:22:48 +02:00
CloakHQ c6d3469e4c release: v0.3.24 — SOCKS5 proxy support, arm64 146 upgrade, ElementHandle humanize 2026-04-10 22:42:29 +02:00
8 changed files with 230 additions and 11 deletions
@@ -0,0 +1,62 @@
name: issue-knowledge-responder
description: First-responder bot that finds related docs and past issues for new GitHub issues
model: openai/gpt-4.1
modelParameters:
maxCompletionTokens: 1500
temperature: 0.3
messages:
- role: system
content: |
You are a first-responder bot for CloakBrowser, an open-source stealth Chromium wrapper.
Your ONLY job: find related resources from the provided context and point the user to them.
You are NOT a maintainer. You do NOT answer questions yourself.
Rules:
- NEVER answer the question yourself. Only point to existing resources.
- NEVER reveal internal patch details, CDP stealth methods, or bypass techniques.
- Keep it short. 5-10 lines max.
- Only include resources that are ACTUALLY relevant, not vaguely similar.
- If the issue is about financial, government, or healthcare targets, output RESPOND: NO
- Maximum 5 related issues. Prefer closed+resolved over open.
- role: user
content: |
## The new issue
{{issue_body}}
## Past issues found by search
{{past_issues}}
## Past issue details (body + comments from top matches)
{{issue_details}}
## README.md (excerpt)
{{readme}}
## js/README.md (excerpt)
{{js_readme}}
## CHANGELOG.md (recent)
{{changelog}}
## Your task
Based on the context above, decide:
**If you found related resources** (doc sections, past issues, or both), output EXACTLY this format:
RESPOND: YES
---
While you wait for the team to respond, I found some resources that might be related:
**From the docs:**
- [description of relevant section] (in `README.md` / `js/README.md`)
**Related issues:**
- #123 — [title] (closed/open) — [one-line summary of resolution or discussion]
Hope this helps! The team will follow up soon.
---
**If you found NOTHING relevant**, output EXACTLY:
RESPOND: NO
@@ -0,0 +1,148 @@
name: Issue Knowledge Responder
on:
issues:
types: [opened]
jobs:
respond:
if: github.event.issue.user.type != 'Bot'
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: read
issues: write
models: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Gather context
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
REPO: ${{ github.repository }}
run: |
mkdir -p /tmp/ctx
# 1. Read the full issue
gh issue view "$ISSUE_NUMBER" --repo "$REPO" --json title,body,labels --jq '
"Title: " + .title + "\n\nBody:\n" + (.body // "No body") + "\n\nLabels: " + ([.labels[].name] | join(", "))
' > /tmp/ctx/issue_body.txt
# 2. Extract keywords from title + body
TITLE=$(gh issue view "$ISSUE_NUMBER" --repo "$REPO" --json title --jq '.title')
BODY_TEXT=$(gh issue view "$ISSUE_NUMBER" --repo "$REPO" --json body --jq '.body // ""' | head -20)
STOPWORDS='^(the|a|an|is|in|on|at|to|for|of|with|and|or|not|bug|issue|error|problem|help|please|how|why|what|does|can|i|my|me|it|this|that|when|from|have|has|been|are|was|were|be|do|did|just|but|if|so|no|yes|all|any|some|like|get|got|use|using|used|try|tried|also|want|need|would|could|should|about|into|after|before|only|very|too|more|most|much|own|same|other|than|then|there|here|each|every|both|few|many|such|these|those|its|new|old|first|last|long|great|little|right|big|high|different|small|large|next|early|young|important|public|bad|same|able)$'
TITLE_KW=$(echo "$TITLE" | tr -cs 'a-zA-Z0-9' ' ' | tr ' ' '\n' | \
grep -v -i -E "$STOPWORDS" | head -5 | tr '\n' ' ')
BODY_KW=$(echo "$BODY_TEXT" | tr -cs 'a-zA-Z0-9' ' ' | tr ' ' '\n' | \
grep -v -i -E "$STOPWORDS" | sort | uniq -c | sort -rn | awk '{print $2}' | head -5 | tr '\n' ' ')
# 3. Search past issues — 5 strategies, deduplicated
# A: full title
gh issue list --repo "$REPO" --state all --search "$TITLE" --limit 10 \
--json number,title,state --jq '.[] | "#\(.number) — \(.title) (\(.state))"' \
2>/dev/null > /tmp/ctx/search_results.txt || true
# B: title keywords
gh issue list --repo "$REPO" --state all --search "$TITLE_KW" --limit 10 \
--json number,title,state --jq '.[] | "#\(.number) — \(.title) (\(.state))"' \
2>/dev/null >> /tmp/ctx/search_results.txt || true
# C: body keywords (most frequent terms from body)
if [ -n "$BODY_KW" ]; then
gh issue list --repo "$REPO" --state all --search "$BODY_KW" --limit 10 \
--json number,title,state --jq '.[] | "#\(.number) — \(.title) (\(.state))"' \
2>/dev/null >> /tmp/ctx/search_results.txt || true
fi
# D: search by labels if the new issue has any
LABELS=$(gh issue view "$ISSUE_NUMBER" --repo "$REPO" --json labels --jq '[.labels[].name] | join(",")')
if [ -n "$LABELS" ]; then
IFS=',' read -ra LABEL_ARR <<< "$LABELS"
for LBL in "${LABEL_ARR[@]}"; do
gh issue list --repo "$REPO" --state all --label "$LBL" --limit 10 \
--json number,title,state --jq '.[] | "#\(.number) — \(.title) (\(.state))"' \
2>/dev/null >> /tmp/ctx/search_results.txt || true
done
fi
# E: search for error messages / code snippets in body (first backtick block or quoted line)
ERROR_MSG=$(echo "$BODY_TEXT" | grep -oP '`[^`]{5,80}`' | head -1 | tr -d '`')
if [ -n "$ERROR_MSG" ]; then
gh issue list --repo "$REPO" --state all --search "$ERROR_MSG" --limit 5 \
--json number,title,state --jq '.[] | "#\(.number) — \(.title) (\(.state))"' \
2>/dev/null >> /tmp/ctx/search_results.txt || true
fi
# Deduplicate, exclude self, keep top 20
sort -u /tmp/ctx/search_results.txt | \
grep -v "^#${ISSUE_NUMBER} " | head -20 > /tmp/ctx/past_issues.txt
# 4. Read top 7 issue details (body + comments)
echo "" > /tmp/ctx/issue_details.txt
for NUM in $(grep -oP '#\K[0-9]+' /tmp/ctx/past_issues.txt | head -7); do
gh issue view "$NUM" --repo "$REPO" --json title,state,body,comments --jq '
"### #'"$NUM"' — " + .title + " (" + .state + ")\n" +
(.body // "")[:500] + "\n\nComments:\n" +
([.comments[:3][] | .body[:300]] | join("\n---\n"))
' >> /tmp/ctx/issue_details.txt 2>/dev/null || true
echo -e "\n---\n" >> /tmp/ctx/issue_details.txt
done
# 5. Read docs
head -200 README.md > /tmp/ctx/readme.txt 2>/dev/null || echo "No README" > /tmp/ctx/readme.txt
head -150 js/README.md > /tmp/ctx/js_readme.txt 2>/dev/null || echo "No JS README" > /tmp/ctx/js_readme.txt
head -100 CHANGELOG.md > /tmp/ctx/changelog.txt 2>/dev/null || echo "No CHANGELOG" > /tmp/ctx/changelog.txt
# Debug: show what we gathered
echo "=== Issue ===" && head -5 /tmp/ctx/issue_body.txt
echo "=== Past issues ===" && cat /tmp/ctx/past_issues.txt
echo "=== Keywords: $KEYWORDS ==="
- name: AI analysis
id: ai
uses: actions/ai-inference@v1
with:
prompt-file: .github/prompts/issue-responder.prompt.yml
file_input: |
issue_body: /tmp/ctx/issue_body.txt
past_issues: /tmp/ctx/past_issues.txt
issue_details: /tmp/ctx/issue_details.txt
readme: /tmp/ctx/readme.txt
js_readme: /tmp/ctx/js_readme.txt
changelog: /tmp/ctx/changelog.txt
- name: Parse and post
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
RESPONSE=$(cat "${{ steps.ai.outputs.response-file }}")
echo "=== AI Response ==="
echo "$RESPONSE"
if echo "$RESPONSE" | grep -q "RESPOND: YES"; then
# Extract content between --- markers
COMMENT=$(echo "$RESPONSE" | sed -n '/^---$/,/^---$/p' | sed '1d;$d')
if [ -z "$COMMENT" ]; then
# Fallback: everything after "RESPOND: YES", strip --- lines
COMMENT=$(echo "$RESPONSE" | sed '1,/RESPOND: YES/d' | sed '/^---$/d')
fi
if [ -n "$COMMENT" ]; then
echo "$COMMENT" > /tmp/comment.txt
gh issue comment "${{ github.event.issue.number }}" \
--repo "${{ github.repository }}" \
--body-file /tmp/comment.txt
echo "✅ Comment posted"
else
echo "⚠️ RESPOND: YES but empty comment body, skipping"
fi
else
echo "️ No relevant resources found, staying silent"
fi
+8
View File
@@ -6,6 +6,14 @@ Changes are tagged: **[wrapper]** for Python/JS wrapper, **[binary]** for Chromi
---
## [0.3.24] — 2026-04-10
- **[wrapper]** Native SOCKS5 proxy support — pass `proxy="socks5://user:pass@host:port"` directly. Credentials handled natively by Chrome. Works across all launch functions, Python + JS.
- **[wrapper]** Add Playwright ElementHandle humanize support — `element_handle.click()`, `.fill()`, `.type()` now use human-like behavior when `humanize=True` (thanks [@lilos](https://github.com/lilos), #133)
- **[binary]** Upgrade Linux arm64 to Chromium 146.0.7680.177.2 (49 patches) — now matches Linux x64
- **[binary]** New build 146.0.7680.177.2 for both Linux platforms: native SOCKS5 proxy with UDP ASSOCIATE (QUIC/HTTP3 over SOCKS5)
- **[docs]** Clarify humanize requires wrapper import over CDP (#126)
## [0.3.23] — 2026-04-09
- **[wrapper]** Add full Puppeteer humanize support — human-like mouse, keyboard, and scroll behavior for `puppeteer-core` users (thanks [@evelaa123](https://github.com/evelaa123), #129)
+6 -5
View File
@@ -128,10 +128,11 @@ Open [http://localhost:8080](http://localhost:8080). Create a profile. Click **L
---
## Latest: v0.3.22 (Chromium 146.0.7680.177.1)
## Latest: v0.3.24 (Chromium 146.0.7680.177.2)
- **Native SOCKS5 proxy** — `proxy="socks5://user:pass@host:port"` works directly in all launch functions, Python + JS. QUIC/HTTP3 tunnels through SOCKS5 via UDP ASSOCIATE.
- **Chromium 146 upgrade** — rebased all patches from 145.0.7632.x to 146.0.7680.177
- **49 fingerprint patches** (Linux x64) — 1 new patch, all existing patches carried forward
- **49 fingerprint patches** Linux arm64 now matches Linux x64 on Chromium 146
- **WebRTC IP spoofing** — `--fingerprint-webrtc-ip=auto` resolves your proxy's exit IP and spoofs WebRTC ICE candidates. Auto-injected when using `geoip=True` (no extra network call)
- **Proxy signal removal** — DNS/connect/SSL timing zeroed, proxy cache headers stripped, Proxy-Connection header leak removed
- **`cloakserve` CDP multiplexer** — rewritten as a multi-connection CDP proxy with per-connection fingerprint seeds
@@ -371,7 +372,7 @@ from cloakbrowser import binary_info, clear_cache, ensure_binary
# Check binary installation status
print(binary_info())
# {'version': '146.0.7680.177.1', 'platform': 'linux-x64', 'installed': True, ...}
# {'version': '146.0.7680.177.2', 'platform': 'linux-x64', 'installed': True, ...}
# Force re-download
clear_cache()
@@ -673,7 +674,7 @@ browser = await launch_async(args=["--remote-debugging-port=9242"])
| Platform | Chromium | Patches | Status |
|---|---|---|---|
| Linux x86_64 | 146 | 49 | ✅ Latest |
| Linux arm64 (RPi, Graviton) | 145 | 48 | ✅ |
| Linux arm64 (RPi, Graviton) | 146 | 49 | ✅ Latest |
| macOS arm64 (Apple Silicon) | 145 | 26 | ✅ |
| macOS x86_64 (Intel) | 145 | 26 | ✅ |
| Windows x86_64 | 145 | 48 | ✅ |
@@ -1059,7 +1060,7 @@ All releases are signed for supply chain verification.
```bash
# Verify GPG signature (binary release tag)
gpg --keyserver keyserver.ubuntu.com --recv-keys C60C0DDC9D0DE2DD
git verify-tag chromium-v146.0.7680.177.1
git verify-tag chromium-v146.0.7680.177.2
# Verify GitHub binary attestation (Sigstore)
gh attestation verify cloakbrowser-linux-x64.tar.gz --repo CloakHQ/cloakbrowser
+1 -1
View File
@@ -1 +1 @@
__version__ = "0.3.23"
__version__ = "0.3.24"
+2 -2
View File
@@ -18,8 +18,8 @@ from ._version import __version__
CHROMIUM_VERSION = "146.0.7680.177.1"
PLATFORM_CHROMIUM_VERSIONS: dict[str, str] = {
"linux-x64": "146.0.7680.177.1",
"linux-arm64": "145.0.7632.159.7",
"linux-x64": "146.0.7680.177.2",
"linux-arm64": "146.0.7680.177.2",
"darwin-arm64": "145.0.7632.109.2",
"darwin-x64": "145.0.7632.109.2",
"windows-x64": "145.0.7632.159.7",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "cloakbrowser",
"version": "0.3.23",
"version": "0.3.24",
"description": "Stealth Chromium that passes every bot detection test. Drop-in Playwright/Puppeteer replacement with source-level fingerprint patches.",
"type": "module",
"main": "dist/index.js",
+2 -2
View File
@@ -30,8 +30,8 @@ export { WRAPPER_VERSION };
export const CHROMIUM_VERSION = "146.0.7680.177.1";
export const PLATFORM_CHROMIUM_VERSIONS: Record<string, string> = {
"linux-x64": "146.0.7680.177.1",
"linux-arm64": "145.0.7632.159.7",
"linux-x64": "146.0.7680.177.2",
"linux-arm64": "146.0.7680.177.2",
"darwin-arm64": "145.0.7632.109.2",
"darwin-x64": "145.0.7632.109.2",
"windows-x64": "145.0.7632.159.7",