feat(serve): add embedded Scalar API reference UI with OpenAPI 3.1 spec - #90
Merged
Conversation
There was a problem hiding this comment.
Copilot wasn't able to review any files in this pull request.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Add swaggo/swag v2 annotations to serve.go handlers (handleCreateTask, handleGetTask, handleListTasks, handleHealthz) - Extract handleHealthz from inline lambda to named function - Add doc.go as swag general-info anchor (avoids swag v2 @description bug) - Refactor startServer into newMux + startServer for testability - Create cmd/serve/webui/ with index.html, swagger.yaml, scalar.standalone.js - Add go:embed webui in webui.go; register /docs/, /swagger.yaml, /{$} routes - Add route tests: /docs/ (200 HTML), /swagger.yaml (OpenAPI), / (redirect), /healthz - Add openapi Makefile target and swag v2 installer in install-deps-dev - Update serve command long help to mention /docs/ and /swagger.yaml - Update README.md with browser UI and make openapi instructions
Copilot
AI
changed the title
[WIP] Add embedded browser GUI to Go CLI serve command
feat(serve): add embedded Scalar API reference UI with OpenAPI 3.1 spec
Jul 6, 2026
ks6088ts
marked this pull request as ready for review
July 6, 2026 01:56
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.
Adds a fully-offline browser GUI to the
servecommand by embedding a Scalar API reference UI and a swag-generated OpenAPI 3.1 spec directly into the binary. No network access required at runtime.New routes
GET /docs/GET /swagger.yamlGET //docs/Code changes
cmd/serve/doc.go— Dedicated swag general-info anchor (@title,@version,@description,@BasePath /v1). Isolated from handler files to work around a swag v2 bug where handler@Descriptionannotations overwriteinfo.description.cmd/serve/serve.go— Added swag operation annotations to all four handlers; extractedhandleHealthzfrom an inline lambda; refactored mux setup intonewMux(store, cliURL, yolo)(makes routes unit-testable without starting a real listener).cmd/serve/webui.go—//go:embed webui+registerWebUI()mounts the file server at/docs/, exposes/swagger.yaml, and redirects/.cmd/serve/webui/— Committed assets:index.html(Scalar loader, same-origin server URL),swagger.yaml(generated),scalar.standalone.js(vendored@scalar/api-reference@1.62.4, 3.6 MB).Tooling
make openapiregeneratesswagger.yamlviaswag init --v3.1 -g cmd/serve/doc.go.make install-deps-devnow installsswag(dev-only; no new entry ingo.mod).Tests
Four new route-level tests in
serve_test.goexercisenewMux()directly:/docs/returnstext/html,/swagger.yamlcontainsopenapi:andpaths:,/returns 302 to/docs/,/healthzreturns{"status":"ok"}.Original prompt
Add an embedded browser GUI (Scalar API reference) to the Go CLI
servecommandGoal
The Go CLI in this repository has a
servecommand that starts an HTTP serverexposing a REST API for running GitHub Copilot tasks. Today it is API-only.
Add a static web GUI, embedded into the single CLI binary, so users can open
a browser and send requests to the API interactively. Requirements:
standalone). All web assets must be embedded via
go:embed.grows.
Approved design decisions (do not deviate)
"Send"/try console). Vendor the Scalar standalone browser bundle as a
local file and commit it to the repo so the build and runtime work
offline. Do NOT load Scalar from a CDN.
github.com/swaggo/swag/v2),OpenAPI 3.1 output.
swagis a dev/build-time tool only — install it viathe Makefile
install-deps-devtarget. The generated spec file iscommitted and embedded. Do NOT add
swag(or any swagger runtime lib) tothe module's runtime dependencies in
go.mod, and do NOT import swag/docspackages from application code.
task_id; the client polls GET). Do NOT build a custom auto-poll progresspage. Rely on Scalar's built-in request console. The user submits via Scalar,
copies the
task_id, and issues the GET request in the same console./docs/and redirect the root path/to/docs/.Repository layout (important)
This is a multi-project repo. The Go CLI lives under
src/go/. All filepaths below are relative to
src/go/unless stated otherwise. Run all Go andMake commands from within
src/go/.Key facts (already verified):
github.com/ks6088ts/template-github-copilot/src/go. Go 1.26.spf13/cobra. HTTP server: Go standard librarynet/httpwithhttp.ServeMuxusing Go 1.22+ method+path route patterns(e.g.
mux.HandleFunc("POST /v1/tasks", ...)). No third-party HTTP framework.go:embedis already used elsewhere in the codebase (incmd/tutorial/), sothe pattern is established — follow it.
src/go/Makefile. Formatting isgofmt -e -l -s,linting is
golangci-lint. Thebuildtarget runsgo mod tidythen builds asingle static binary (CGO disabled,
-trimpath).src/go/.goreleaser.yamlhas ago generate ./...pre-hook. Do NOT add a//go:generate swag ...directive anywhere, so that releases do not depend onswag being installed. The OpenAPI spec is regenerated only via an explicit
make openapitarget.The
servecommand (grounding for annotations & routing)File:
src/go/cmd/serve/serve.go. Relevant existing symbols:func startServer(ctx context.Context, addr, cliURL string, defaultYolo bool) error— builds
mux := http.NewServeMux()inline, registers all routes, then startsand gracefully shuts down an
*http.Server./v1/...and legacy unversioned):POST /tasksandPOST /v1/tasks->handleCreateTaskGET /tasks/{id}andGET /v1/tasks/{id}->handleGetTaskGET /tasksandGET /v1/tasks->handleListTasksGET /healthz-> an inline lambda returning{"status":"ok"}.startServer):func handleCreateTask(w http.ResponseWriter, r *http.Request, store *taskStore, cliURL string, defaultYolo bool)func handleGetTask(w http.ResponseWriter, r *http.Request, store *taskStore)func handleListTasks(w http.ResponseWriter, r *http.Request, store *taskStore)taskRequest— JSON body for create. Public JSON fields:prompt(string,required),
model(string),system_message(string),image_data(base64 string),
image_mime_type(string),yolo(*bool). Other fieldsare internal and already tagged
json:"-".taskCreatedResponse—{ "task_id": string }returned by create (HTTP 202).Task— full task state returned by get/list:id,status(pending|running|done|failed),
prompt,model,progress(array),result,error,created_at,completed_at.func writeJSON(w http.ResponseWriter, status int, v any)setsContent-Type: application/json. Reuse it.Endpoint POST accepts BOTH
application/jsonandmultipart/form-data. For theOpenAPI spec, documenting the JSON body is sufficient; you may optionally note...