From aa0a97bd352315d71aaf8a72f7f83b69b4c1bbbd Mon Sep 17 00:00:00 2001 From: Jarvis Date: Mon, 16 Feb 2026 18:19:48 +0000 Subject: [PATCH 1/3] fix: respect HTTP_PROXY/HTTPS_PROXY environment variables The CLI creates a custom undici Agent via setGlobalDispatcher() which overrides any proxy configuration. Since Node.js native fetch (backed by undici) does not automatically respect HTTP_PROXY/HTTPS_PROXY env vars, the CLI fails with 'fetch failed' on systems that require a proxy for outbound connections. Import ProxyAgent from undici and use it when any of the standard proxy environment variables (HTTPS_PROXY, HTTP_PROXY, https_proxy, http_proxy) is set. When no proxy variable is present, behavior is unchanged. Also adds proxy documentation to cli.md and a troubleshooting entry. --- docs/cli.md | 23 ++++++++++++++++++++++ docs/troubleshooting.md | 19 ++++++++++++++++++ packages/clawdhub/src/http.test.ts | 31 +++++++++++++++++++++++++++++- packages/clawdhub/src/http.ts | 16 +++++++++++---- 4 files changed, 84 insertions(+), 5 deletions(-) diff --git a/docs/cli.md b/docs/cli.md index 82d70bdc3..360f3b91a 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -29,6 +29,29 @@ Env equivalents: - `CLAWHUB_REGISTRY` (legacy `CLAWDHUB_REGISTRY`) - `CLAWHUB_WORKDIR` (legacy `CLAWDHUB_WORKDIR`) +### HTTP proxy + +The CLI respects standard HTTP proxy environment variables for systems behind +corporate proxies or restricted networks: + +- `HTTPS_PROXY` / `https_proxy` +- `HTTP_PROXY` / `http_proxy` +- `NO_PROXY` / `no_proxy` + +When proxy variables are set, the CLI routes outbound requests through the +configured proxy. Use `NO_PROXY` to bypass proxying for specific hosts/domains. +This is required on systems where direct outbound connections are blocked (e.g. +Docker containers, Hetzner VPS with proxy-only internet, corporate firewalls). + +Example: + +```bash +export HTTPS_PROXY=http://proxy.example.com:3128 +clawhub search "my query" +``` + +When no proxy variable is set, behavior is unchanged (direct connections). + ## Config file Stores your API token + cached registry URL. diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index c4bc663be..bad42914b 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -28,6 +28,25 @@ read_when: - If many users share one egress IP (NAT/proxy), IP limit can be hit even with valid tokens. - For non-Cloudflare deploys behind trusted proxies, set `TRUST_FORWARDED_IPS=true` so forwarded client IPs can be used. +## `search` / `install` fails with `fetch failed` behind a proxy + +If your system requires an HTTP proxy for outbound connections (e.g. corporate +firewalls, Docker containers with proxy-only internet, Hetzner VPS), the CLI +will fail with: + +``` +✖ fetch failed +Error: fetch failed +``` + +**Fix:** Set the standard proxy environment variables: + +```bash +export HTTPS_PROXY=http://proxy.example.com:3128 +clawhub search "my query" +``` + +The CLI respects `HTTPS_PROXY`, `HTTP_PROXY`, `https_proxy`, and `http_proxy`. ## `publish` fails with `OPENAI_API_KEY is not configured` - Set `OPENAI_API_KEY` in the Convex environment (not only locally). diff --git a/packages/clawdhub/src/http.test.ts b/packages/clawdhub/src/http.test.ts index ccef82b1c..eea3ff959 100644 --- a/packages/clawdhub/src/http.test.ts +++ b/packages/clawdhub/src/http.test.ts @@ -1,7 +1,7 @@ /* @vitest-environment node */ import { describe, expect, it, vi } from 'vitest' -import { apiRequest, apiRequestForm, downloadZip, fetchText } from './http' +import { apiRequest, apiRequestForm, downloadZip, fetchText, shouldUseProxyFromEnv } from './http' import { ApiV1WhoamiResponseSchema } from './schema/index.js' function mockImmediateTimeouts() { @@ -36,6 +36,35 @@ function createAbortingFetchMock() { }) } +describe('shouldUseProxyFromEnv', () => { + it('detects standard proxy variables', () => { + expect( + shouldUseProxyFromEnv({ + HTTPS_PROXY: 'http://proxy.example:3128', + } as NodeJS.ProcessEnv), + ).toBe(true) + expect( + shouldUseProxyFromEnv({ + HTTP_PROXY: 'http://proxy.example:3128', + } as NodeJS.ProcessEnv), + ).toBe(true) + expect( + shouldUseProxyFromEnv({ + https_proxy: 'http://proxy.example:3128', + } as NodeJS.ProcessEnv), + ).toBe(true) + }) + + it('ignores NO_PROXY-only configs', () => { + expect( + shouldUseProxyFromEnv({ + NO_PROXY: 'localhost,127.0.0.1', + } as NodeJS.ProcessEnv), + ).toBe(false) + expect(shouldUseProxyFromEnv({} as NodeJS.ProcessEnv)).toBe(false) + }) +}) + describe('apiRequest', () => { it('adds bearer token and parses json', async () => { const fetchMock = vi.fn().mockResolvedValue({ diff --git a/packages/clawdhub/src/http.ts b/packages/clawdhub/src/http.ts index c6eebe786..82581a1fd 100644 --- a/packages/clawdhub/src/http.ts +++ b/packages/clawdhub/src/http.ts @@ -3,7 +3,7 @@ import { mkdtemp, rm, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import { join } from 'node:path' import pRetry, { AbortError } from 'p-retry' -import { Agent, setGlobalDispatcher } from 'undici' +import { Agent, EnvHttpProxyAgent, setGlobalDispatcher } from 'undici' import type { ArkValidator } from './schema/index.js' import { ApiRoutes, parseArk } from './schema/index.js' @@ -28,12 +28,20 @@ const CURL_WRITE_OUT_FORMAT = [ ].join('\n') const isBun = typeof process !== 'undefined' && Boolean(process.versions?.bun) +export function shouldUseProxyFromEnv(env: NodeJS.ProcessEnv = process.env): boolean { + return Boolean(env.HTTPS_PROXY || env.HTTP_PROXY || env.https_proxy || env.http_proxy) +} + if (typeof process !== 'undefined' && process.versions?.node) { try { setGlobalDispatcher( - new Agent({ - connect: { timeout: REQUEST_TIMEOUT_MS }, - }), + shouldUseProxyFromEnv(process.env) + ? new EnvHttpProxyAgent({ + connect: { timeout: REQUEST_TIMEOUT_MS }, + }) + : new Agent({ + connect: { timeout: REQUEST_TIMEOUT_MS }, + }), ) } catch { // ignore dispatcher setup failures in non-node runtimes From 4a6f4391c46e46215e1bf979383b7e6bddaaa4be Mon Sep 17 00:00:00 2001 From: Jarvis Date: Mon, 16 Feb 2026 18:25:15 +0000 Subject: [PATCH 2/3] fix: use EnvHttpProxyAgent for proper proxy support Address review feedback: - Use undici's EnvHttpProxyAgent instead of ProxyAgent. This properly handles HTTPS_PROXY vs HTTP_PROXY per-scheme, respects NO_PROXY, and uses connect.timeout instead of requestTls. - Update docs to mention NO_PROXY support. --- docs/cli.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/docs/cli.md b/docs/cli.md index 360f3b91a..f9f46956e 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -38,15 +38,20 @@ corporate proxies or restricted networks: - `HTTP_PROXY` / `http_proxy` - `NO_PROXY` / `no_proxy` -When proxy variables are set, the CLI routes outbound requests through the -configured proxy. Use `NO_PROXY` to bypass proxying for specific hosts/domains. -This is required on systems where direct outbound connections are blocked (e.g. -Docker containers, Hetzner VPS with proxy-only internet, corporate firewalls). +When any of these variables is set, the CLI routes outbound requests through +the specified proxy. `HTTPS_PROXY` is used for HTTPS requests, `HTTP_PROXY` +for plain HTTP. `NO_PROXY` / `no_proxy` is respected to bypass the proxy for +specific hosts or domains. + +This is required on systems where direct outbound connections are blocked +(e.g. Docker containers, Hetzner VPS with proxy-only internet, corporate +firewalls). Example: ```bash export HTTPS_PROXY=http://proxy.example.com:3128 +export NO_PROXY=localhost,127.0.0.1 clawhub search "my query" ``` From 53214abd08a7b4d2f54e098fe57029bca6a52e5c Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Wed, 25 Feb 2026 12:13:25 +0000 Subject: [PATCH 3/3] fix: finalize proxy env support + changelog credits (#363) (thanks @kerrypotter) --- CHANGELOG.md | 2 ++ convex/skills.ts | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d4ad36ab..93e95efbe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,8 @@ - Skills/Web: prevent filtered pagination dead-ends and loading-state flicker on `/skills`; move highlighted browse filtering into server list query (#339) (thanks @Marvae). - Web: align `/skills` total count with public visibility and format header count (thanks @rknoche6, #76). - Skills/Web: centralize public visibility checks and keep `globalStats` skill counts in sync incrementally; remove duplicate `/skills` default-sort fallback and share browse test mocks (thanks @rknoche6, #76). +- Moderation: clear stale `flagged.suspicious` flags when VirusTotal rescans improve to clean verdicts (#418) (thanks @Phineas1500). +- CLI: respect `HTTPS_PROXY`/`HTTP_PROXY`/`NO_PROXY` env vars for outbound registry requests, with troubleshooting docs (#363) (thanks @kerrypotter). ## 0.6.1 - 2026-02-13 diff --git a/convex/skills.ts b/convex/skills.ts index 6192be731..4dada4b02 100644 --- a/convex/skills.ts +++ b/convex/skills.ts @@ -2646,7 +2646,6 @@ export const approveSkillByHashInternal = internalMutation({ const existingFlags: string[] = (skill.moderationFlags as string[] | undefined) ?? [] const existingReason: string | undefined = skill.moderationReason as string | undefined const alreadyBlocked = existingFlags.includes('blocked.malware') - const alreadyFlagged = existingFlags.includes('flagged.suspicious') const bypassSuspicious = isSuspicious && !alreadyBlocked && isPrivilegedOwnerForSuspiciousBypass(owner)