feat: add crust doctor, ACP stdio proxy, and security hardening#30
Merged
chen-zichen merged 6 commits intomainfrom Feb 26, 2026
Merged
feat: add crust doctor, ACP stdio proxy, and security hardening#30chen-zichen merged 6 commits intomainfrom
chen-zichen merged 6 commits intomainfrom
Conversation
Providers like Zhipu AI (GLM) use versioned base URLs such as /api/paas/v4. When the client sends /v1/chat/completions, the naive path join produced /api/paas/v4/v1/chat/completions (404). Add pathHasVersion/stripLeadingVersion helpers that detect version segments in provider URLs and strip the client's redundant /vN prefix. Also add GLM as a builtin provider.
pathHasVersion now matches any segment starting with "v" + digit (e.g. v1beta, v2alpha1), not just pure vN. This fixes Gemini's /v1beta/openai path being treated as unversioned, which caused client /v1 to be prepended and produce 404s. Updates the Gemini builtin URL to the correct OpenAI-compatible endpoint at /v1beta/openai, removes unused isDigits helper, and uses maps.Copy in BuiltinProviders accessor.
Fix HTTP client connection leak (defer CloseIdleConnections), send minimal JSON body for Anthropic POST probes instead of nil, and move markdown heading outside code fence in --report output.
…lignment - Add defer client.CloseIdleConnections() to prevent transport leak - Send minimal JSON body for Anthropic POST probes instead of nil - Move markdown heading outside code fence in --report output - Pad raw provider name before ANSI styling for correct column alignment
- Run provider checks concurrently with sync.WaitGroup (~2s vs ~20s) - Add http.ProxyFromEnvironment to Transport (fixes CONN timeouts behind proxies) - Broaden HTTP 400 handling to all providers (Gemini returns 400 for unauthenticated GET /models) - Respect --retries 0 instead of forcing minimum 1 - Use tagged switch for staticcheck QF1002
TotalToolCalls only counts events reaching RecordEvent — safe calls with text-only LLM responses skip evaluation entirely, making "N total, N blocked (100%)" misleading. Show only blocked count instead. Also add doctor command documentation to cli.md and README.
0639553 to
a19f06a
Compare
chen-zichen
approved these changes
Feb 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
crust doctor— new diagnostic command that probes all provider endpoints (builtin + user-configured)using the same URL-joining logic as the proxy, catching path bugs (404), auth issues (401/403), and
connectivity errors before they hit production. Supports
--timeout,--retries,--report(privacy-safemarkdown for GitHub issues).
crust wrap) — wraps IDE agent subprocesses (e.g. VS Code Copilot) over stdin/stdout,applying the same security rules as the HTTP proxy. Includes mock agent for testing.
pathHasVersionnow handlesv1beta-style segments, preventing/v1beta/openai/v1/chat/completionsdouble-pathing for Gemini and similar providers.TotalToolCallsonly counts events reaching
RecordEvent, so safe text-only responses were excluded, making "N total, Nblocked (100%)" incorrect. Now shows blocked count only.
buffering fixes; engine edge-case handling.
Test plan
go build ./...passesgo test -race ./...passesSecurity checklist