Merge branch 'main' into fix/issue-69-confirmation-gates

This commit is contained in:
Magnus Hedemark
2026-07-11 17:15:04 -04:00
6 changed files with 137 additions and 0 deletions
+1
View File
@@ -109,6 +109,7 @@ When the user mentions these keywords, load the corresponding skill:
| "langgraph", "multi-agent", "state machine", "graph-based workflow", "LangGraph", "supervisor pattern", "swarm pattern", "agent orchestration", "graph state", "subgraph", "agent routing", "tool-calling loop", "agent loop", "stateful agent", "durable execution", "human in the loop langgraph", "checkpointer", "langgraph persistence" | [langgraph](langgraph/SKILL.md) |
| "debate", "council", "multi-perspective", "structured debate", "get multiple perspectives", "expert panel", "decision landscape", "what would experts say", "what are we missing", "convergence", "false consensus", "agent-council", "pre-mortem" | [agent-council](agent-council/SKILL.md) |
| "skill format", "how do I make a skill", "agentskills.io" | [agent-skills](agent-skills/SKILL.md) |
| "FlareSolverr", "Cloudflare challenge", "DDoS-GUARD", "browser-backed request" | [flaresolverr](flaresolverr/SKILL.md) |
| "last.fm", "scrobble", "music discovery", "listening history", "similar artists", "lastfm", "weekly top artists", "genre charts" | [lastfm](lastfm/SKILL.md) |
| "nous", "theia", "hermes brand", "brand identity", "style guide", "mascot", "anime style", "cyber-classical", "color palette reference" | [nous-branding](nous-branding/SKILL.md) |
| "okf", "open knowledge format", "knowledge bundle", "LLM wiki", "agent knowledge", "Google knowledge format", "markdown knowledge", "vendor-neutral knowledge", "create an OKF bundle", "validate OKF", "concept document", "knowledge format" | [open-knowledge-format](open-knowledge-format/SKILL.md) |
+4
View File
@@ -72,6 +72,10 @@ fixed-layout, accessibility, and media overlays. Portable across any AgentSkills
Safe Forgejo API v1 CLI for issues, pull requests, repositories, file contents, metadata, webhooks, and user settings. Includes a guarded generic `/api/v1/` route for version-specific endpoints such as Actions and admin APIs.
### [flaresolverr](flaresolverr/SKILL.md)
Use a private FlareSolverr service through a dependency-free JSON CLI when ordinary HTTP retrieval is blocked by a browser challenge.
### [ghost-cli](ghost-cli/SKILL.md)
Ghost CMS from the terminal. Manage posts and pages, list tags, and check site info. Admin API key from Ghost Integrations. JWT authentication handled automatically.
+48
View File
@@ -0,0 +1,48 @@
# FlareSolverr
## Why Install This Skill
Use a browser-backed proxy when ordinary HTTP clients encounter a browser challenge. The JSON CLI gives an agent a bounded, inspectable interface without requiring a Python package.
Use it only with a private FlareSolverr service and only for sites you are authorized to access.
Use a browser-backed proxy from the terminal when a site rejects ordinary HTTP clients with a Cloudflare or DDoS-GUARD challenge.
## What you get
| Path | Purpose |
|---|---|
| `SKILL.md` | Agent routing and safe usage |
| `scripts/flaresolverr` | Dependency-free JSON CLI |
| `scripts/test-flaresolverr.sh` | Deterministic dry-run smoke checks |
## What You Get
- `SKILL.md`: agent routing and safe usage
- `scripts/flaresolverr`: dependency-free JSON CLI
- `scripts/test-flaresolverr.sh`: deterministic smoke checks
## Quick Start
```sh
python3 scripts/flaresolverr health
```
## Quick start
```sh
python3 scripts/flaresolverr health
python3 scripts/flaresolverr get https://example.com
```
Set `FLARESOLVERR_SERVER` or pass `--server`. The default is `http://localhost:8191`. Keep the service private.
## Triggers
- Cloudflare or DDoS-GUARD browser challenges
- A site that requires cookie-preserving browser requests
- FlareSolverr session management from an agent workflow
## Requirements
Python 3.9+ and a running FlareSolverr service. No Python packages are required.
+41
View File
@@ -0,0 +1,41 @@
---
name: flaresolverr
description: Use FlareSolverr through a small CLI when a site requires a browser-backed request to pass Cloudflare or DDoS-GUARD challenges.
---
# FlareSolverr
## Quick Start
```sh
python3 scripts/flaresolverr --server http://localhost:8191 health
```
Use this skill when ordinary HTTP retrieval is blocked by a browser challenge. FlareSolverr must already be running; this skill does not bypass authentication or authorize access to restricted content.
## CLI
```text
python3 flaresolverr/scripts/flaresolverr --server http://localhost:8191 health
python3 flaresolverr/scripts/flaresolverr --server http://localhost:8191 get https://example.com
python3 flaresolverr/scripts/flaresolverr session create
python3 flaresolverr/scripts/flaresolverr session list
python3 flaresolverr/scripts/flaresolverr session destroy SESSION_ID
```
Every command emits JSON. `get` and `post` use FlareSolverr's `/v1` API and preserve the returned status, URL, headers, and response body. Use `--timeout` to bound a request and `--session` when a site needs cookie continuity.
## Setup
Run FlareSolverr separately, commonly with Docker:
```yaml
services:
flaresolverr:
image: ghcr.io/flaresolverr/flaresolverr:latest
ports: ["8191:8191"]
environment:
LOG_LEVEL: info
```
Do not expose the service publicly. Prefer a pinned image tag in production and use the vendor's documentation for browser and platform compatibility.
+37
View File
@@ -0,0 +1,37 @@
#!/usr/bin/env python3
"""Small JSON CLI for the FlareSolverr v1 API."""
import argparse, json, os, sys, urllib.request
def call(server, payload, timeout):
req=urllib.request.Request(server.rstrip('/') + '/v1', data=json.dumps(payload).encode(), headers={'Content-Type':'application/json'})
try:
with urllib.request.urlopen(req, timeout=timeout) as response:
return json.load(response)
except Exception as exc:
print(json.dumps({'error': type(exc).__name__, 'message': str(exc)}), file=sys.stderr)
return 1
def main():
p=argparse.ArgumentParser(description='FlareSolverr JSON CLI')
p.add_argument('--server', default=os.getenv('FLARESOLVERR_SERVER','http://localhost:8191'))
p.add_argument('--timeout', type=float, default=60)
s=p.add_subparsers(dest='command', required=True)
s.add_parser('health')
for method in ('get','post'):
x=s.add_parser(method); x.add_argument('url'); x.add_argument('--session'); x.add_argument('--data', default='')
x=s.add_parser('session'); x.add_argument('action', choices=('create','list','destroy')); x.add_argument('session_id', nargs='?')
a=p.parse_args()
if a.command=='health': payload={'cmd':'sessions.list'}
elif a.command=='session':
payload={'cmd': 'sessions.' + a.action}
if a.action=='destroy': payload['session']='' if a.session_id is None else a.session_id
else:
payload={'cmd':'request.'+a.command, 'url':a.url, 'maxTimeout':int(a.timeout*1000)}
if a.session: payload['session']=a.session
if a.command=='post' and a.data: payload['postData']=a.data
result=call(a.server, payload, a.timeout)
if result==1: return 1
print(json.dumps(result, sort_keys=True))
return 0
if __name__=='__main__': raise SystemExit(main())
+6
View File
@@ -0,0 +1,6 @@
#!/bin/sh
set -eu
cd "$(dirname "$0")/.."
python3 scripts/flaresolverr --help >/dev/null
python3 -m py_compile scripts/flaresolverr
printf '%s\n' 'FlareSolverr CLI smoke checks passed.'