[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6GAN1T6F6GD61KCB379D9RY":3,"skill-same:01M6GAN1T6F6GD61KCB379D9RY":77},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":37,"linkOnly":38,"markdownContent":39,"markdownBody":40,"frontMatter":41,"refName":55,"commitSha":56,"skillDirectory":57,"forksCount":58,"repositoryHtmlUrl":59,"repositoryHomepageUrl":6,"ownerHtmlUrl":60,"ownerBlogUrl":6,"ownerDescription":61,"updatedAt":62,"install":63,"resources":76},"01M6GAN1T6F6GD61KCB379D9RY","pinggy-tunnel",null,"Zero-install localhost tunnels over SSH via Pinggy.","zapabob\u002FSkills","zapabob","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F130830376?v=4","01M6DBMYZAGRTFQDSQYW3W764P","峯岸 亮","01M6DBMZ3GKDSJ01NZ9XTZD7JD",2,"MIT","https:\u002F\u002Fgithub.com\u002Fzapabob\u002FSkills\u002Fblob\u002Fmain\u002Farchive\u002Fskill-md-reviewed\u002Fsha256\u002F8c\u002F8c490b70421a4610aae3e402f55f5d82765e8e1d7efaa3e7cbb0673b4c3f97c7\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[24,25,26,27,28,29,30,31,32,33,34,35,36],"tunnel","pinggy tunnel","macos","linux","windows","networking","ZeRO","ssh","hermes","hermes agent","install","webhook","via","2026-11-10 10:12:30",false,"---\nname: pinggy-tunnel\ndescription: Zero-install localhost tunnels over SSH via Pinggy.\nversion: 0.1.0\nauthor: Teknium (teknium1), Hermes Agent\nlicense: MIT\nplatforms: [linux, macos, windows]\nmetadata:\n  hermes:\n    tags: [Pinggy, Tunnel, Networking, SSH, Webhook, Localhost]\n    related_skills: []\n---\n\n# Pinggy Tunnel Skill\n\nExpose a local service (dev server, webhook receiver, MCP endpoint, demo) to the public internet using a Pinggy SSH reverse tunnel. No daemon to install — the user's stock SSH client connects to `a.pinggy.io:443` and Pinggy hands back a public HTTP\u002FHTTPS URL.\n\nFree tier: 60-minute tunnels, random subdomain, no signup. Pro tier ($3\u002Fmo) is an opt-in with a token.\n\n## When to Use\n\n- User asks to \"expose this locally\", \"share my dev server\", \"make this URL public\", \"tunnel port N\", \"get a public URL for a webhook\"\n- Need to receive a webhook callback during a local task (Stripe, GitHub, Discord, AgentMail)\n- Sharing a one-off HTTP demo (MCP server, Ollama\u002FvLLM endpoint, dashboard) with a remote party\n- The host has SSH but no `cloudflared` \u002F `ngrok` binary, and installing one would be overkill\n\nIf the host already has `cloudflared` configured, prefer the `cloudflared-quick-tunnel` skill — Cloudflare quick tunnels don't expire after 60 minutes.\n\n## Prerequisites\n\n- `ssh` on PATH (`ssh -V`). Default on Linux, macOS, and Windows 10+. No other install.\n- A local service listening on `127.0.0.1:\u003Cport>` before the tunnel starts. Pinggy will return URLs but they'll 502 until the local origin is up.\n\nOptional:\n\n- `PINGGY_TOKEN` env var for paid Pro features (persistent subdomain, custom domain, multiple tunnels, no 60-minute cap). Free tier needs no credentials.\n\n## Quick Reference\n\n```bash\n# Plain HTTP\u002FHTTPS tunnel for port 8000 (free tier)\nssh -p 443 -o StrictHostKeyChecking=no -o ServerAliveInterval=30 \\\n    -R0:localhost:8000 [REDACTED_EMAIL]\n\n# TCP tunnel (databases, raw SSH, etc.)\nssh -p 443 -o StrictHostKeyChecking=no -R0:localhost:5432 [REDACTED_EMAIL]\n\n# TLS tunnel (Pinggy can't decrypt — bring your own certs at origin)\nssh -p 443 -o StrictHostKeyChecking=no -R0:localhost:443 [REDACTED_EMAIL]\n\n# Basic auth gate (b:user:pass)\nssh -p 443 -o StrictHostKeyChecking=no -R0:localhost:8000 \\\n    \"b:admin:[REDACTED_EMAIL]\"\n\n# Bearer token gate (k:token)\nssh -p 443 -o StrictHostKeyChecking=no -R0:localhost:8000 \\\n    \"k:[REDACTED_EMAIL]\"\n\n# IP whitelist (w:CIDR)\nssh -p 443 -o StrictHostKeyChecking=no -R0:localhost:8000 \\\n    \"w:203.0.113.0\u002F[REDACTED_EMAIL]\"\n\n# Enable CORS + force HTTPS redirect\nssh -p 443 -o StrictHostKeyChecking=no -R0:localhost:8000 \\\n    \"co+x:[REDACTED_EMAIL]\"\n\n# Pro tier (persistent URL, no 60-min cap)\nssh -p 443 -o StrictHostKeyChecking=no -R0:localhost:8000 \"$PINGGY_TOKEN+a.pinggy.io\"\n```\n\n## Procedure — Start a Tunnel and Get the URL\n\nThe model SHOULD use the `terminal` tool. The tunnel must stay alive for the duration of the share, so run it as a background process and parse the public URL from stdout.\n\n### 1. Confirm a local origin is up\n\n```bash\ncurl -sI http:\u002F\u002F127.0.0.1:8000\u002F | head -1\n# expect HTTP\u002F1.x 200 (or any non-connection-refused response)\n```\n\nIf nothing is listening yet, start it first (e.g. `python -m http.server 8000 --bind 127.0.0.1`). Pinggy will happily return a URL pointed at nothing — the user will see 502 until the origin comes up.\n\n### 2. Launch the tunnel as a background process\n\nUse `terminal(background=True)` and capture output to a logfile (Pinggy prints the URLs on stdout, then keeps the connection open):\n\n```bash\nLOG=~\u002F.hermes\u002Fcache\u002Fscratch\u002Fpinggy-8000.log\nnohup ssh -p 443 \\\n    -o StrictHostKeyChecking=no \\\n    -o UserKnownHostsFile=\u002Fdev\u002Fnull \\\n    -o ServerAliveInterval=30 \\\n    -o ServerAliveCountMax=3 \\\n    -R0:localhost:8000 [REDACTED_EMAIL] \\\n    > \"$LOG\" 2>&1 &\necho $! > ~\u002F.hermes\u002Fcache\u002Fscratch\u002Fpinggy-8000.pid\n```\n\n`StrictHostKeyChecking=no` + `UserKnownHostsFile=\u002Fdev\u002Fnull` skips the first-run host-key prompt. `ServerAliveInterval=30` keeps the SSH session from getting torn down by an idle NAT.\n\n### 3. Parse the URL out of the log\n\n```bash\nsleep 4\ngrep -oE 'https:\u002F\u002F[a-z0-9-]+\\.[a-z]+\\.pinggy\\.link' ~\u002F.hermes\u002Fcache\u002Fscratch\u002Fpinggy-8000.log | head -1\n```\n\nExpected output looks like:\n\n```\nYou are not authenticated.\nYour tunnel will expire in 60 minutes.\nhttp:\u002F\u002Fyqycl-98-162-69-48.a.free.pinggy.link\nhttps:\u002F\u002Fyqycl-98-162-69-48.a.free.pinggy.link\n```\n\nHand the `https:\u002F\u002F...pinggy.link` URL to the user.\n\n### 4. Verify\n\n```bash\ncurl -sI https:\u002F\u002F\u003Cthe-url>\u002F | head -3\n# expect 200\u002F302\u002Fwhatever the local origin actually returns\n```\n\nIf you get `502 Bad Gateway`, the SSH session is up but the local origin isn't listening — fix step 1 first.\n\n### 5. Teardown\n\n```bash\nkill \"$(cat ~\u002F.hermes\u002Fcache\u002Fscratch\u002Fpinggy-8000.pid)\"\n# or, if the pid file got lost:\npkill -f 'ssh -p 443 .* free@a\\.pinggy\\.io'\n```\n\nIf you have a session_id from `terminal(background=True)`, prefer `process(action='kill', session_id=...)`.\n\n## Access Control via Username Keywords\n\nPinggy stacks control flags into the SSH username separated by `+`. Always quote the whole `user@host` argument when it contains a `+`:\n\n| Keyword | Effect |\n|---------|--------|\n| `b:user:pass` | HTTP Basic auth gate |\n| `k:token` | Bearer-token header gate (`Authorization: Bearer \u003Ctoken>`) |\n| `w:CIDR` | IP whitelist (single IP or CIDR, repeatable) |\n| `co` | Add `Access-Control-Allow-Origin: *` (CORS) |\n| `x:https` | Force HTTPS — auto-redirect HTTP to HTTPS |\n| `a:Name:Value` | Add request header |\n| `u:Name:Value` | Update request header |\n| `r:Name` | Remove request header |\n| `qr` | Print a QR code of the URL to stdout (handy for mobile sharing) |\n\nCombine freely: `\"b:admin:secret+co+x:[REDACTED_EMAIL]\"`.\n\n## Web Debugger (optional)\n\nPinggy can mirror the inbound traffic to `localhost:4300` for inspection. Add a local forward to the SSH command:\n\n```bash\nssh -p 443 -L4300:localhost:4300 -R0:localhost:8000 [REDACTED_EMAIL]\n```\n\nThen open `http:\u002F\u002Flocalhost:4300` in a browser to see live request\u002Fresponse pairs.\n\n## Pitfalls\n\n- **60-minute hard cap on the free tier.** The SSH session terminates at the 60-minute mark; the URL goes dead. For longer shares, either use `PINGGY_TOKEN` (Pro) or auto-restart with a shell loop (note that the URL changes on every restart for free-tier).\n- **Free-tier URL is random and changes on restart.** Don't bookmark it, don't paste it into a config file. Re-parse from the log each time.\n- **Concurrent free tunnels are limited to one per source IP.** Starting a second tunnel from the same machine usually kills the first. Pro tier lifts this.\n- **`+` in usernames must be quoted.** Bare `ssh ... b:admin:[REDACTED_EMAIL]` works in bash but breaks under shells that treat `+` specially or when assembled programmatically. Always wrap in double quotes.\n- **Don't tunnel anything sensitive without an access-control flag.** A bare HTTP tunnel is reachable by anyone with the URL. Use `b:`, `k:`, or `w:` for non-public services.\n- **`process(action='log')` may miss SSH banner output.** Pinggy prints the URLs and then the SSH session goes interactive. Always redirect to a logfile and `grep` the file directly — same pattern as `cloudflared-quick-tunnel`.\n- **Host-key prompt on first run.** Default OpenSSH config asks the user to accept Pinggy's host key. Always pass `-o StrictHostKeyChecking=no -o UserKnownHostsFile=\u002Fdev\u002Fnull` for unattended runs.\n- **TCP and TLS tunnels return a `\u003Csubdomain>.a.pinggy.online:\u003Cport>` pair, not an https URL.** Parse with a different regex (`tcp:\u002F\u002F` and a port). Don't assume every Pinggy tunnel is HTTP.\n- **Pro mode requires the token as the username, not a flag.** Use `\"$PINGGY_TOKEN+a.pinggy.io\"` (no `free@`). With a token you can also add `:persistent` for a stable subdomain — see `pinggy.io\u002Fdocs\u002F`.\n\n## Recipes\n\nComposite patterns combining a local origin with a Pinggy tunnel. Each recipe is self-contained — start the origin, start the tunnel, parse the URL, hand it back to the user.\n\n### Recipe 1 — Receive a webhook callback\n\nUse this when an external service (Stripe, GitHub, Discord, AgentMail, etc.) needs to POST to a publicly reachable URL during a local task.\n\n```bash\n# 1. Tiny capturing server: every request gets appended to ~\u002F.hermes\u002Fcache\u002Fscratch\u002Fwebhook-hits.log\ncat >~\u002F.hermes\u002Fcache\u002Fscratch\u002Fwebhook-server.py \u003C\u003C'PY'\nimport http.server, json, datetime, pathlib\nLOG = pathlib.Path(\"~\u002F.hermes\u002Fcache\u002Fscratch\u002Fwebhook-hits.log\").expanduser()\nclass H(http.server.BaseHTTPRequestHandler):\n    def _capture(self):\n        n = int(self.headers.get(\"content-length\") or 0)\n        body = self.rfile.read(n).decode(\"utf-8\", \"replace\") if n else \"\"\n        rec = {\"t\": datetime.datetime.utcnow().isoformat(), \"path\": self.path,\n               \"method\": self.command, \"headers\": dict(self.headers), \"body\": body}\n        with LOG.open(\"a\") as f: f.write(json.dumps(rec) + \"\\n\")\n        self.send_response(200); self.send_header(\"content-type\",\"application\u002Fjson\")\n        self.end_headers(); self.wfile.write(b'{\"ok\":true}\\n')\n    def do_GET(self): self._capture()\n    def do_POST(self): self._capture()\n    def log_message(self,*a,**k): pass\nhttp.server.HTTPServer((\"127.0.0.1\", 18080), H).serve_forever()\nPY\nnohup python ~\u002F.hermes\u002Fcache\u002Fscratch\u002Fwebhook-server.py >~\u002F.hermes\u002Fcache\u002Fscratch\u002Fwebhook-server.log 2>&1 &\necho $! >~\u002F.hermes\u002Fcache\u002Fscratch\u002Fwebhook-server.pid\n\n# 2. Tunnel — bearer-token-gate so randos can't pollute the capture log\nnohup ssh -p 443 -o StrictHostKeyChecking=no -o UserKnownHostsFile=\u002Fdev\u002Fnull \\\n    -o ServerAliveInterval=30 \\\n    -R0:localhost:18080 \"k:$(openssl rand -hex 12)[REDACTED_EMAIL]\" \\\n    >~\u002F.hermes\u002Fcache\u002Fscratch\u002Fwebhook-pinggy.log 2>&1 &\necho $! >~\u002F.hermes\u002Fcache\u002Fscratch\u002Fwebhook-pinggy.pid\nsleep 5\nURL=$(grep -oE 'https:\u002F\u002F[a-z0-9-]+\\.[a-z]+\\.pinggy\\.link' ~\u002F.hermes\u002Fcache\u002Fscratch\u002Fwebhook-pinggy.log | head -1)\necho \"Webhook URL: $URL\"\n\n# 3. While the agent works, watch hits land\ntail -f ~\u002F.hermes\u002Fcache\u002Fscratch\u002Fwebhook-hits.log\n```\n\nHand `$URL` to the service that needs to call you. Teardown: `kill $(cat ~\u002F.hermes\u002Fcache\u002Fscratch\u002Fwebhook-server.pid) $(cat ~\u002F.hermes\u002Fcache\u002Fscratch\u002Fwebhook-pinggy.pid)`.\n\n### Recipe 2 — Expose an MCP server over HTTP\u002FSSE\n\nUse when a remote MCP client (Claude Desktop on another machine, a teammate's editor, etc.) needs to reach an MCP server running on the local box. Only works for MCP servers that speak HTTP transport — stdio-mode servers can't be tunneled.\n\n```bash\n# 1. Start the MCP server in HTTP mode (example: a FastMCP server on port 8765)\nnohup python my_mcp_server.py --transport http --port 8765 \\\n    >~\u002F.hermes\u002Fcache\u002Fscratch\u002Fmcp-server.log 2>&1 &\necho $! >~\u002F.hermes\u002Fcache\u002Fscratch\u002Fmcp-server.pid\n\n# 2. Tunnel with a bearer token — MCP traffic should not be open to the internet\nTOKEN=$(openssl rand -hex 16)\nnohup ssh -p 443 -o StrictHostKeyChecking=no -o UserKnownHostsFile=\u002Fdev\u002Fnull \\\n    -o ServerAliveInterval=30 \\\n    -R0:localhost:8765 \"k:$[REDACTED_EMAIL]\" \\\n    >~\u002F.hermes\u002Fcache\u002Fscratch\u002Fmcp-pinggy.log 2>&1 &\necho $! >~\u002F.hermes\u002Fcache\u002Fscratch\u002Fmcp-pinggy.pid\nsleep 5\nURL=$(grep -oE 'https:\u002F\u002F[a-z0-9-]+\\.[a-z]+\\.pinggy\\.link' ~\u002F.hermes\u002Fcache\u002Fscratch\u002Fmcp-pinggy.log | head -1)\necho \"MCP URL: $URL\"\necho \"Bearer token: $TOKEN\"\n```\n\nThe remote client connects to `$URL` with `Authorization: Bearer $TOKEN`. Hermes' own native MCP client config: `{\"transport\": \"http\", \"url\": \"\u003CURL>\", \"headers\": {\"Authorization\": \"Bearer \u003CTOKEN>\"}}`.\n\n### Recipe 3 — Expose a local LLM endpoint (Ollama \u002F vLLM \u002F llama.cpp)\n\nShare a local model with a remote caller (another agent, a phone, a teammate). Ollama listens on `:11434`, vLLM and llama.cpp typically on `:8000`.\n\n```bash\n# Pre-req: the model server is already running on 127.0.0.1:11434 (Ollama default)\nTOKEN=$(openssl rand -hex 16)\nnohup ssh -p 443 -o StrictHostKeyChecking=no -o UserKnownHostsFile=\u002Fdev\u002Fnull \\\n    -o ServerAliveInterval=30 \\\n    -R0:localhost:11434 \"k:$[REDACTED_EMAIL]\" \\\n    >~\u002F.hermes\u002Fcache\u002Fscratch\u002Fllm-pinggy.log 2>&1 &\necho $! >~\u002F.hermes\u002Fcache\u002Fscratch\u002Fllm-pinggy.pid\nsleep 5\nURL=$(grep -oE 'https:\u002F\u002F[a-z0-9-]+\\.[a-z]+\\.pinggy\\.link' ~\u002F.hermes\u002Fcache\u002Fscratch\u002Fllm-pinggy.log | head -1)\necho \"Endpoint: $URL\"\necho \"Token:    $TOKEN\"\n\n# Verify\ncurl -s \"$URL\u002Fapi\u002Ftags\" -H \"Authorization: Bearer $TOKEN\" | head\n```\n\n`co` enables CORS so a browser caller can hit the endpoint. Drop `co` for backend-only callers. For an OpenAI-compatible vLLM\u002Fllama.cpp endpoint, callers use base URL `$URL\u002Fv1` with `Authorization: Bearer $TOKEN` — but note Pinggy strips\u002Freplaces nothing in the body, so the model server itself sees Pinggy's token; the local server should be configured to ignore auth (it's already on `127.0.0.1`) and let Pinggy do the gating.\n\n### Recipe 4 — Share a dev server with a one-shot password\n\nThe fastest \"let a teammate poke at my running app\" pattern. Random password, prints once, dies when you Ctrl-C.\n\n```bash\nPASS=$(openssl rand -base64 12 | tr -d '+\u002F=' | head -c 12)\necho \"Dev server password: $PASS\"\nssh -p 443 -o StrictHostKeyChecking=no -o UserKnownHostsFile=\u002Fdev\u002Fnull \\\n    -o ServerAliveInterval=30 \\\n    -R0:localhost:3000 \"b:dev:$PASS+co+x:[REDACTED_EMAIL]\"\n# URL prints to the terminal. Share URL + password. Ctrl-C to tear down.\n```\n\n`b:dev:$PASS` gates the URL with HTTP Basic auth. `x:https` forces TLS. `co` adds CORS for SPA frontends.\n\n## Verification\n\n```bash\n# End-to-end: spin up a trivial origin, tunnel it, hit it, tear down\npython -m http.server 18000 --bind 127.0.0.1 >~\u002F.hermes\u002Fcache\u002Fscratch\u002Forigin.log 2>&1 &\nORIGIN_PID=$!\n\nnohup ssh -p 443 \\\n    -o StrictHostKeyChecking=no \\\n    -o UserKnownHostsFile=\u002Fdev\u002Fnull \\\n    -R0:localhost:18000 [REDACTED_EMAIL] >~\u002F.hermes\u002Fcache\u002Fscratch\u002Fpinggy-verify.log 2>&1 &\nSSH_PID=$!\n\nsleep 5\nURL=$(grep -oE 'https:\u002F\u002F[a-z0-9-]+\\.[a-z]+\\.pinggy\\.link' ~\u002F.hermes\u002Fcache\u002Fscratch\u002Fpinggy-verify.log | head -1)\necho \"URL: $URL\"\ncurl -sI \"$URL\u002F\" | head -1\n\nkill \"$SSH_PID\" \"$ORIGIN_PID\"\n```\n\nExpected: a `pinggy.link` URL and `HTTP\u002F2 200` on the curl head.\n","# Pinggy Tunnel Skill\n\nExpose a local service (dev server, webhook receiver, MCP endpoint, demo) to the public internet using a Pinggy SSH reverse tunnel. No daemon to install — the user's stock SSH client connects to `a.pinggy.io:443` and Pinggy hands back a public HTTP\u002FHTTPS URL.\n\nFree tier: 60-minute tunnels, random subdomain, no signup. Pro tier ($3\u002Fmo) is an opt-in with a token.\n\n## When to Use\n\n- User asks to \"expose this locally\", \"share my dev server\", \"make this URL public\", \"tunnel port N\", \"get a public URL for a webhook\"\n- Need to receive a webhook callback during a local task (Stripe, GitHub, Discord, AgentMail)\n- Sharing a one-off HTTP demo (MCP server, Ollama\u002FvLLM endpoint, dashboard) with a remote party\n- The host has SSH but no `cloudflared` \u002F `ngrok` binary, and installing one would be overkill\n\nIf the host already has `cloudflared` configured, prefer the `cloudflared-quick-tunnel` skill — Cloudflare quick tunnels don't expire after 60 minutes.\n\n## Prerequisites\n\n- `ssh` on PATH (`ssh -V`). Default on Linux, macOS, and Windows 10+. No other install.\n- A local service listening on `127.0.0.1:\u003Cport>` before the tunnel starts. Pinggy will return URLs but they'll 502 until the local origin is up.\n\nOptional:\n\n- `PINGGY_TOKEN` env var for paid Pro features (persistent subdomain, custom domain, multiple tunnels, no 60-minute cap). Free tier needs no credentials.\n\n## Quick Reference\n\n```bash\n# Plain HTTP\u002FHTTPS tunnel for port 8000 (free tier)\nssh -p 443 -o StrictHostKeyChecking=no -o ServerAliveInterval=30 \\\n    -R0:localhost:8000 [REDACTED_EMAIL]\n\n# TCP tunnel (databases, raw SSH, etc.)\nssh -p 443 -o StrictHostKeyChecking=no -R0:localhost:5432 [REDACTED_EMAIL]\n\n# TLS tunnel (Pinggy can't decrypt — bring your own certs at origin)\nssh -p 443 -o StrictHostKeyChecking=no -R0:localhost:443 [REDACTED_EMAIL]\n\n# Basic auth gate (b:user:pass)\nssh -p 443 -o StrictHostKeyChecking=no -R0:localhost:8000 \\\n    \"b:admin:[REDACTED_EMAIL]\"\n\n# Bearer token gate (k:token)\nssh -p 443 -o StrictHostKeyChecking=no -R0:localhost:8000 \\\n    \"k:[REDACTED_EMAIL]\"\n\n# IP whitelist (w:CIDR)\nssh -p 443 -o StrictHostKeyChecking=no -R0:localhost:8000 \\\n    \"w:203.0.113.0\u002F[REDACTED_EMAIL]\"\n\n# Enable CORS + force HTTPS redirect\nssh -p 443 -o StrictHostKeyChecking=no -R0:localhost:8000 \\\n    \"co+x:[REDACTED_EMAIL]\"\n\n# Pro tier (persistent URL, no 60-min cap)\nssh -p 443 -o StrictHostKeyChecking=no -R0:localhost:8000 \"$PINGGY_TOKEN+a.pinggy.io\"\n```\n\n## Procedure — Start a Tunnel and Get the URL\n\nThe model SHOULD use the `terminal` tool. The tunnel must stay alive for the duration of the share, so run it as a background process and parse the public URL from stdout.\n\n### 1. Confirm a local origin is up\n\n```bash\ncurl -sI http:\u002F\u002F127.0.0.1:8000\u002F | head -1\n# expect HTTP\u002F1.x 200 (or any non-connection-refused response)\n```\n\nIf nothing is listening yet, start it first (e.g. `python -m http.server 8000 --bind 127.0.0.1`). Pinggy will happily return a URL pointed at nothing — the user will see 502 until the origin comes up.\n\n### 2. Launch the tunnel as a background process\n\nUse `terminal(background=True)` and capture output to a logfile (Pinggy prints the URLs on stdout, then keeps the connection open):\n\n```bash\nLOG=~\u002F.hermes\u002Fcache\u002Fscratch\u002Fpinggy-8000.log\nnohup ssh -p 443 \\\n    -o StrictHostKeyChecking=no \\\n    -o UserKnownHostsFile=\u002Fdev\u002Fnull \\\n    -o ServerAliveInterval=30 \\\n    -o ServerAliveCountMax=3 \\\n    -R0:localhost:8000 [REDACTED_EMAIL] \\\n    > \"$LOG\" 2>&1 &\necho $! > ~\u002F.hermes\u002Fcache\u002Fscratch\u002Fpinggy-8000.pid\n```\n\n`StrictHostKeyChecking=no` + `UserKnownHostsFile=\u002Fdev\u002Fnull` skips the first-run host-key prompt. `ServerAliveInterval=30` keeps the SSH session from getting torn down by an idle NAT.\n\n### 3. Parse the URL out of the log\n\n```bash\nsleep 4\ngrep -oE 'https:\u002F\u002F[a-z0-9-]+\\.[a-z]+\\.pinggy\\.link' ~\u002F.hermes\u002Fcache\u002Fscratch\u002Fpinggy-8000.log | head -1\n```\n\nExpected output looks like:\n\n```\nYou are not authenticated.\nYour tunnel will expire in 60 minutes.\nhttp:\u002F\u002Fyqycl-98-162-69-48.a.free.pinggy.link\nhttps:\u002F\u002Fyqycl-98-162-69-48.a.free.pinggy.link\n```\n\nHand the `https:\u002F\u002F...pinggy.link` URL to the user.\n\n### 4. Verify\n\n```bash\ncurl -sI https:\u002F\u002F\u003Cthe-url>\u002F | head -3\n# expect 200\u002F302\u002Fwhatever the local origin actually returns\n```\n\nIf you get `502 Bad Gateway`, the SSH session is up but the local origin isn't listening — fix step 1 first.\n\n### 5. Teardown\n\n```bash\nkill \"$(cat ~\u002F.hermes\u002Fcache\u002Fscratch\u002Fpinggy-8000.pid)\"\n# or, if the pid file got lost:\npkill -f 'ssh -p 443 .* free@a\\.pinggy\\.io'\n```\n\nIf you have a session_id from `terminal(background=True)`, prefer `process(action='kill', session_id=...)`.\n\n## Access Control via Username Keywords\n\nPinggy stacks control flags into the SSH username separated by `+`. Always quote the whole `user@host` argument when it contains a `+`:\n\n| Keyword | Effect |\n|---------|--------|\n| `b:user:pass` | HTTP Basic auth gate |\n| `k:token` | Bearer-token header gate (`Authorization: Bearer \u003Ctoken>`) |\n| `w:CIDR` | IP whitelist (single IP or CIDR, repeatable) |\n| `co` | Add `Access-Control-Allow-Origin: *` (CORS) |\n| `x:https` | Force HTTPS — auto-redirect HTTP to HTTPS |\n| `a:Name:Value` | Add request header |\n| `u:Name:Value` | Update request header |\n| `r:Name` | Remove request header |\n| `qr` | Print a QR code of the URL to stdout (handy for mobile sharing) |\n\nCombine freely: `\"b:admin:secret+co+x:[REDACTED_EMAIL]\"`.\n\n## Web Debugger (optional)\n\nPinggy can mirror the inbound traffic to `localhost:4300` for inspection. Add a local forward to the SSH command:\n\n```bash\nssh -p 443 -L4300:localhost:4300 -R0:localhost:8000 [REDACTED_EMAIL]\n```\n\nThen open `http:\u002F\u002Flocalhost:4300` in a browser to see live request\u002Fresponse pairs.\n\n## Pitfalls\n\n- **60-minute hard cap on the free tier.** The SSH session terminates at the 60-minute mark; the URL goes dead. For longer shares, either use `PINGGY_TOKEN` (Pro) or auto-restart with a shell loop (note that the URL changes on every restart for free-tier).\n- **Free-tier URL is random and changes on restart.** Don't bookmark it, don't paste it into a config file. Re-parse from the log each time.\n- **Concurrent free tunnels are limited to one per source IP.** Starting a second tunnel from the same machine usually kills the first. Pro tier lifts this.\n- **`+` in usernames must be quoted.** Bare `ssh ... b:admin:[REDACTED_EMAIL]` works in bash but breaks under shells that treat `+` specially or when assembled programmatically. Always wrap in double quotes.\n- **Don't tunnel anything sensitive without an access-control flag.** A bare HTTP tunnel is reachable by anyone with the URL. Use `b:`, `k:`, or `w:` for non-public services.\n- **`process(action='log')` may miss SSH banner output.** Pinggy prints the URLs and then the SSH session goes interactive. Always redirect to a logfile and `grep` the file directly — same pattern as `cloudflared-quick-tunnel`.\n- **Host-key prompt on first run.** Default OpenSSH config asks the user to accept Pinggy's host key. Always pass `-o StrictHostKeyChecking=no -o UserKnownHostsFile=\u002Fdev\u002Fnull` for unattended runs.\n- **TCP and TLS tunnels return a `\u003Csubdomain>.a.pinggy.online:\u003Cport>` pair, not an https URL.** Parse with a different regex (`tcp:\u002F\u002F` and a port). Don't assume every Pinggy tunnel is HTTP.\n- **Pro mode requires the token as the username, not a flag.** Use `\"$PINGGY_TOKEN+a.pinggy.io\"` (no `free@`). With a token you can also add `:persistent` for a stable subdomain — see `pinggy.io\u002Fdocs\u002F`.\n\n## Recipes\n\nComposite patterns combining a local origin with a Pinggy tunnel. Each recipe is self-contained — start the origin, start the tunnel, parse the URL, hand it back to the user.\n\n### Recipe 1 — Receive a webhook callback\n\nUse this when an external service (Stripe, GitHub, Discord, AgentMail, etc.) needs to POST to a publicly reachable URL during a local task.\n\n```bash\n# 1. Tiny capturing server: every request gets appended to ~\u002F.hermes\u002Fcache\u002Fscratch\u002Fwebhook-hits.log\ncat >~\u002F.hermes\u002Fcache\u002Fscratch\u002Fwebhook-server.py \u003C\u003C'PY'\nimport http.server, json, datetime, pathlib\nLOG = pathlib.Path(\"~\u002F.hermes\u002Fcache\u002Fscratch\u002Fwebhook-hits.log\").expanduser()\nclass H(http.server.BaseHTTPRequestHandler):\n    def _capture(self):\n        n = int(self.headers.get(\"content-length\") or 0)\n        body = self.rfile.read(n).decode(\"utf-8\", \"replace\") if n else \"\"\n        rec = {\"t\": datetime.datetime.utcnow().isoformat(), \"path\": self.path,\n               \"method\": self.command, \"headers\": dict(self.headers), \"body\": body}\n        with LOG.open(\"a\") as f: f.write(json.dumps(rec) + \"\\n\")\n        self.send_response(200); self.send_header(\"content-type\",\"application\u002Fjson\")\n        self.end_headers(); self.wfile.write(b'{\"ok\":true}\\n')\n    def do_GET(self): self._capture()\n    def do_POST(self): self._capture()\n    def log_message(self,*a,**k): pass\nhttp.server.HTTPServer((\"127.0.0.1\", 18080), H).serve_forever()\nPY\nnohup python ~\u002F.hermes\u002Fcache\u002Fscratch\u002Fwebhook-server.py >~\u002F.hermes\u002Fcache\u002Fscratch\u002Fwebhook-server.log 2>&1 &\necho $! >~\u002F.hermes\u002Fcache\u002Fscratch\u002Fwebhook-server.pid\n\n# 2. Tunnel — bearer-token-gate so randos can't pollute the capture log\nnohup ssh -p 443 -o StrictHostKeyChecking=no -o UserKnownHostsFile=\u002Fdev\u002Fnull \\\n    -o ServerAliveInterval=30 \\\n    -R0:localhost:18080 \"k:$(openssl rand -hex 12)[REDACTED_EMAIL]\" \\\n    >~\u002F.hermes\u002Fcache\u002Fscratch\u002Fwebhook-pinggy.log 2>&1 &\necho $! >~\u002F.hermes\u002Fcache\u002Fscratch\u002Fwebhook-pinggy.pid\nsleep 5\nURL=$(grep -oE 'https:\u002F\u002F[a-z0-9-]+\\.[a-z]+\\.pinggy\\.link' ~\u002F.hermes\u002Fcache\u002Fscratch\u002Fwebhook-pinggy.log | head -1)\necho \"Webhook URL: $URL\"\n\n# 3. While the agent works, watch hits land\ntail -f ~\u002F.hermes\u002Fcache\u002Fscratch\u002Fwebhook-hits.log\n```\n\nHand `$URL` to the service that needs to call you. Teardown: `kill $(cat ~\u002F.hermes\u002Fcache\u002Fscratch\u002Fwebhook-server.pid) $(cat ~\u002F.hermes\u002Fcache\u002Fscratch\u002Fwebhook-pinggy.pid)`.\n\n### Recipe 2 — Expose an MCP server over HTTP\u002FSSE\n\nUse when a remote MCP client (Claude Desktop on another machine, a teammate's editor, etc.) needs to reach an MCP server running on the local box. Only works for MCP servers that speak HTTP transport — stdio-mode servers can't be tunneled.\n\n```bash\n# 1. Start the MCP server in HTTP mode (example: a FastMCP server on port 8765)\nnohup python my_mcp_server.py --transport http --port 8765 \\\n    >~\u002F.hermes\u002Fcache\u002Fscratch\u002Fmcp-server.log 2>&1 &\necho $! >~\u002F.hermes\u002Fcache\u002Fscratch\u002Fmcp-server.pid\n\n# 2. Tunnel with a bearer token — MCP traffic should not be open to the internet\nTOKEN=$(openssl rand -hex 16)\nnohup ssh -p 443 -o StrictHostKeyChecking=no -o UserKnownHostsFile=\u002Fdev\u002Fnull \\\n    -o ServerAliveInterval=30 \\\n    -R0:localhost:8765 \"k:$[REDACTED_EMAIL]\" \\\n    >~\u002F.hermes\u002Fcache\u002Fscratch\u002Fmcp-pinggy.log 2>&1 &\necho $! >~\u002F.hermes\u002Fcache\u002Fscratch\u002Fmcp-pinggy.pid\nsleep 5\nURL=$(grep -oE 'https:\u002F\u002F[a-z0-9-]+\\.[a-z]+\\.pinggy\\.link' ~\u002F.hermes\u002Fcache\u002Fscratch\u002Fmcp-pinggy.log | head -1)\necho \"MCP URL: $URL\"\necho \"Bearer token: $TOKEN\"\n```\n\nThe remote client connects to `$URL` with `Authorization: Bearer $TOKEN`. Hermes' own native MCP client config: `{\"transport\": \"http\", \"url\": \"\u003CURL>\", \"headers\": {\"Authorization\": \"Bearer \u003CTOKEN>\"}}`.\n\n### Recipe 3 — Expose a local LLM endpoint (Ollama \u002F vLLM \u002F llama.cpp)\n\nShare a local model with a remote caller (another agent, a phone, a teammate). Ollama listens on `:11434`, vLLM and llama.cpp typically on `:8000`.\n\n```bash\n# Pre-req: the model server is already running on 127.0.0.1:11434 (Ollama default)\nTOKEN=$(openssl rand -hex 16)\nnohup ssh -p 443 -o StrictHostKeyChecking=no -o UserKnownHostsFile=\u002Fdev\u002Fnull \\\n    -o ServerAliveInterval=30 \\\n    -R0:localhost:11434 \"k:$[REDACTED_EMAIL]\" \\\n    >~\u002F.hermes\u002Fcache\u002Fscratch\u002Fllm-pinggy.log 2>&1 &\necho $! >~\u002F.hermes\u002Fcache\u002Fscratch\u002Fllm-pinggy.pid\nsleep 5\nURL=$(grep -oE 'https:\u002F\u002F[a-z0-9-]+\\.[a-z]+\\.pinggy\\.link' ~\u002F.hermes\u002Fcache\u002Fscratch\u002Fllm-pinggy.log | head -1)\necho \"Endpoint: $URL\"\necho \"Token:    $TOKEN\"\n\n# Verify\ncurl -s \"$URL\u002Fapi\u002Ftags\" -H \"Authorization: Bearer $TOKEN\" | head\n```\n\n`co` enables CORS so a browser caller can hit the endpoint. Drop `co` for backend-only callers. For an OpenAI-compatible vLLM\u002Fllama.cpp endpoint, callers use base URL `$URL\u002Fv1` with `Authorization: Bearer $TOKEN` — but note Pinggy strips\u002Freplaces nothing in the body, so the model server itself sees Pinggy's token; the local server should be configured to ignore auth (it's already on `127.0.0.1`) and let Pinggy do the gating.\n\n### Recipe 4 — Share a dev server with a one-shot password\n\nThe fastest \"let a teammate poke at my running app\" pattern. Random password, prints once, dies when you Ctrl-C.\n\n```bash\nPASS=$(openssl rand -base64 12 | tr -d '+\u002F=' | head -c 12)\necho \"Dev server password: $PASS\"\nssh -p 443 -o StrictHostKeyChecking=no -o UserKnownHostsFile=\u002Fdev\u002Fnull \\\n    -o ServerAliveInterval=30 \\\n    -R0:localhost:3000 \"b:dev:$PASS+co+x:[REDACTED_EMAIL]\"\n# URL prints to the terminal. Share URL + password. Ctrl-C to tear down.\n```\n\n`b:dev:$PASS` gates the URL with HTTP Basic auth. `x:https` forces TLS. `co` adds CORS for SPA frontends.\n\n## Verification\n\n```bash\n# End-to-end: spin up a trivial origin, tunnel it, hit it, tear down\npython -m http.server 18000 --bind 127.0.0.1 >~\u002F.hermes\u002Fcache\u002Fscratch\u002Forigin.log 2>&1 &\nORIGIN_PID=$!\n\nnohup ssh -p 443 \\\n    -o StrictHostKeyChecking=no \\\n    -o UserKnownHostsFile=\u002Fdev\u002Fnull \\\n    -R0:localhost:18000 [REDACTED_EMAIL] >~\u002F.hermes\u002Fcache\u002Fscratch\u002Fpinggy-verify.log 2>&1 &\nSSH_PID=$!\n\nsleep 5\nURL=$(grep -oE 'https:\u002F\u002F[a-z0-9-]+\\.[a-z]+\\.pinggy\\.link' ~\u002F.hermes\u002Fcache\u002Fscratch\u002Fpinggy-verify.log | head -1)\necho \"URL: $URL\"\ncurl -sI \"$URL\u002F\" | head -1\n\nkill \"$SSH_PID\" \"$ORIGIN_PID\"\n```\n\nExpected: a `pinggy.link` URL and `HTTP\u002F2 200` on the curl head.\n",{"name":5,"author":42,"license":15,"version":43,"metadata":44,"platforms":54,"description":7},"Teknium (teknium1), Hermes Agent","0.1.0",{"hermes":45},{"tags":46,"related_skills":53},[47,48,49,50,51,52],"Pinggy","Tunnel","Networking","SSH","Webhook","Localhost",[],[27,26,28],"main","0d1434f89f4bc6b07e9d272accd8c1013768820f","archive\u002Fskill-md-reviewed\u002Fsha256\u002F8c\u002F8c490b70421a4610aae3e402f55f5d82765e8e1d7efaa3e7cbb0673b4c3f97c7",0,"https:\u002F\u002Fgithub.com\u002Fzapabob\u002FSkills","https:\u002F\u002Fgithub.com\u002Fzapabob","The Open University of JAPAN　2019-now\r\n\r\nTokyo University of Science　2007-2010","2026-10-26 16:29:01",{"source":8,"skillName":5,"commands":64,"prompt":74,"zipAvailable":75},[65,68,71],{"tool":66,"command":67},"npx","npx skills add zapabob\u002FSkills --skill pinggy-tunnel",{"tool":69,"command":70},"bunx","bunx skills add zapabob\u002FSkills --skill pinggy-tunnel",{"tool":72,"command":73},"pnpm","pnpm dlx skills add zapabob\u002FSkills --skill pinggy-tunnel","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fzapabob\u002FSkills\u002Fblob\u002Fmain\u002Farchive\u002Fskill-md-reviewed\u002Fsha256\u002F8c\u002F8c490b70421a4610aae3e402f55f5d82765e8e1d7efaa3e7cbb0673b4c3f97c7\u002FSKILL.md",true,[],[78,106,141,168,195,212],{"skillNo":79,"name":80,"nameZh":6,"description":81,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":82,"categories":83,"keywords":93,"publishedAt":37},"01M6GJTE1GESGGF9K028Y7753E","xurl","X\u002FTwitter via xurl CLI: post, search, DM, media, v2 API.","https:\u002F\u002Fgithub.com\u002Fzapabob\u002FSkills\u002Fblob\u002Fmain\u002Fregistry\u002Fskills\u002Fxurl\u002Fvariants\u002Fhermes-agent\u002FSKILL.md",[84,87,88],{"categoryNo":85,"categoryName":86,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000010","后端与接口",{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":89,"categoryName":90,"taxonomyType":91,"taxonomyTypeDesc":92},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[80,94,26,95,96,27,28,97,98,99,100,101,102,32,33,103,55,104,36,105],"openclaw","cli","github","official","search","https","social-media","api","Media","twitter","social","post",{"skillNo":107,"name":108,"nameZh":6,"description":109,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":110,"categories":111,"keywords":123,"publishedAt":37},"01M6GJT8S1PXQ8WPXT1HSNBWH2","writing-plans","Write implementation plans: bite-sized tasks, paths, code.","https:\u002F\u002Fgithub.com\u002Fzapabob\u002FSkills\u002Fblob\u002Fmain\u002Fregistry\u002Fskills\u002Fwriting-plans\u002Fvariants\u002Fhermes-agent\u002FSKILL.md",[112,115,118,119,120],{"categoryNo":113,"categoryName":114,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000015","视觉设计",{"categoryNo":116,"categoryName":117,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000018","写作与文案",{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":89,"categoryName":90,"taxonomyType":91,"taxonomyTypeDesc":92},{"categoryNo":121,"categoryName":122,"taxonomyType":91,"taxonomyTypeDesc":92},"01JBSEEDCAT000000000000036","内容创作者",[124,125,126,127,26,128,27,129,28,130,131,132,133,134,135,136,137,138,139,140],"writing","writing plans","plans","design","review","workflow","documentation","tasks","development","code","planning","code-review","subagent","write","superpowers","paths","test-driven-development",{"skillNo":142,"name":143,"nameZh":6,"description":144,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":145,"categories":146,"keywords":150,"publishedAt":37},"01M6GJST0H5X7WHBJ3BZK0DVKH","whisper","OpenAI's general-purpose speech recognition model. Supports 99 languages, transcription, translation to English, and language identification. Six model sizes from tiny (39M params) to large (1550M params). Use for speech-to-text, podcast transcription, or multilingual audio processing. Best for robust, multilingual ASR.","https:\u002F\u002Fgithub.com\u002Fzapabob\u002FSkills\u002Fblob\u002Fmain\u002Fregistry\u002Fskills\u002Fwhisper\u002Fvariants\u002Fhermes-agent-optional\u002FSKILL.md",[147],{"categoryNo":148,"categoryName":149,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000016","图像与多媒体",[143,26,27,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167],"openai","speech","multimodal","Transformers","ASR","Audio","text","model","speech recognition","use","general","multilingual","translation","english","identification","processing","Audio Processing",{"skillNo":169,"name":170,"nameZh":6,"description":171,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":172,"categories":173,"keywords":177,"publishedAt":37},"01M6GJSMRQ8EETZTYMG72P70EA","weights-and-biases","W&B: log ML experiments, sweeps, model registry, dashboards.","https:\u002F\u002Fgithub.com\u002Fzapabob\u002FSkills\u002Fblob\u002Fmain\u002Fregistry\u002Fskills\u002Fweights-and-biases\u002Fvariants\u002Fhermes-agent\u002FSKILL.md",[174],{"categoryNo":175,"categoryName":176,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000013","分析与可视化",[178,26,179,180,28,181,182,183,184,185,186,158,187,188,189,190,191,192,193,194],"weights and biases","tensorflow","experiments","pytorch","visualization","registry","real-time","tuning","dashboards","experiment","log","time","Experiment Tracking","Model Registry","Hyperparameter Tuning","WandB","Real-Time Visualization",{"skillNo":196,"name":197,"nameZh":6,"description":198,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":199,"categories":200,"keywords":202,"publishedAt":37},"01M6GJS19KDPRY112JTR326S06","watchers","Poll RSS, JSON APIs, and GitHub with watermark dedup.","https:\u002F\u002Fgithub.com\u002Fzapabob\u002FSkills\u002Fblob\u002Fmain\u002Fregistry\u002Fskills\u002Fwatchers\u002Fvariants\u002Fhermes-agent-optional\u002FSKILL.md",[201],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[197,26,203,96,27,204,205,206,207,208,209,32,33,210,211],"terminal","automation","http","monitoring","cron","json","rss","devops","with",{"skillNo":213,"name":214,"nameZh":6,"description":215,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":216,"categories":217,"keywords":220,"publishedAt":37},"01M6GJRHJD2A6Q830SWF9W48BC","serving-llms-vllm","vLLM: high-throughput LLM serving, OpenAI API, quantization.","https:\u002F\u002Fgithub.com\u002Fzapabob\u002FSkills\u002Fblob\u002Fmain\u002Fregistry\u002Fskills\u002Fvllm\u002Fvariants\u002Fhermes-agent\u002FSKILL.md",[218,219],{"categoryNo":85,"categoryName":86,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":89,"categoryName":90,"taxonomyType":91,"taxonomyTypeDesc":92},[221,222,223,27,224,151,225,226,227,154,228,101,229,230,231,232,233,234,235,236],"llms","vLLM","serving llms vllm","research","openai api","tensor","Inference Serving","Production","llm serving","High Throughput","PagedAttention","Continuous Batching","Quantization","Tensor Parallelism","high-throughput","throughput"]