Skip to content

feat(serve): add embedded Scalar API reference UI with OpenAPI 3.1 spec - #90

Merged
ks6088ts merged 2 commits into
mainfrom
copilot/add-embedded-browser-gui
Jul 6, 2026
Merged

feat(serve): add embedded Scalar API reference UI with OpenAPI 3.1 spec#90
ks6088ts merged 2 commits into
mainfrom
copilot/add-embedded-browser-gui

Conversation

Copilot AI commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Adds a fully-offline browser GUI to the serve command 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

Route Behaviour
GET /docs/ Scalar API reference UI (embedded, offline)
GET /swagger.yaml OpenAPI 3.1 spec
GET / 302 redirect → /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 @Description annotations overwrite info.description.
  • cmd/serve/serve.go — Added swag operation annotations to all four handlers; extracted handleHealthz from an inline lambda; refactored mux setup into newMux(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 openapi regenerates swagger.yaml via swag init --v3.1 -g cmd/serve/doc.go.
  • make install-deps-dev now installs swag (dev-only; no new entry in go.mod).

Tests

Four new route-level tests in serve_test.go exercise newMux() directly: /docs/ returns text/html, /swagger.yaml contains openapi: and paths:, / returns 302 to /docs/, /healthz returns {"status":"ok"}.

Original prompt

Add an embedded browser GUI (Scalar API reference) to the Go CLI serve command

Goal

The Go CLI in this repository has a serve command that starts an HTTP server
exposing 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:

  • Simple, modern, minimal-dependency static web page.
  • Works from the single CLI binary with no network access (fully offline,
    standalone). All web assets must be embedded via go:embed.
  • Future-proof: driven by an OpenAPI spec so the UI stays in sync as the API
    grows.

Approved design decisions (do not deviate)

  1. UI viewer: Scalar (modern OpenAPI API reference with a built-in request
    "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.
  2. OpenAPI spec: code-generated with swaggo/swag v2 (github.com/swaggo/swag/v2),
    OpenAPI 3.1 output. swag is a dev/build-time tool only — install it via
    the Makefile install-deps-dev target. The generated spec file is
    committed and embedded. Do NOT add swag (or any swagger runtime lib) to
    the module's runtime dependencies in go.mod, and do NOT import swag/docs
    packages from application code.
  3. Async UX: try-console only. The API is asynchronous (POST returns a
    task_id; the client polls GET). Do NOT build a custom auto-poll progress
    page. 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.
  4. Serve the UI at /docs/ and redirect the root path / to /docs/.
  5. Same-origin only — no CORS, no auth. Keep it minimal.

Repository layout (important)

This is a multi-project repo. The Go CLI lives under src/go/. All file
paths below are relative to src/go/ unless stated otherwise. Run all Go and
Make commands from within src/go/.

Key facts (already verified):

  • Module path: github.com/ks6088ts/template-github-copilot/src/go. Go 1.26.
  • CLI framework: spf13/cobra. HTTP server: Go standard library
    net/http with http.ServeMux using Go 1.22+ method+path route patterns
    (e.g. mux.HandleFunc("POST /v1/tasks", ...)). No third-party HTTP framework.
  • go:embed is already used elsewhere in the codebase (in cmd/tutorial/), so
    the pattern is established — follow it.
  • Build/lint/test is driven by src/go/Makefile. Formatting is gofmt -e -l -s,
    linting is golangci-lint. The build target runs go mod tidy then builds a
    single static binary (CGO disabled, -trimpath).
  • src/go/.goreleaser.yaml has a go generate ./... pre-hook. Do NOT add a
    //go:generate swag ... directive anywhere, so that releases do not depend on
    swag being installed. The OpenAPI spec is regenerated only via an explicit
    make openapi target.

The serve command (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 starts
    and gracefully shuts down an *http.Server.
  • Route registration currently maps (both /v1/... and legacy unversioned):
    • POST /tasks and POST /v1/tasks -> handleCreateTask
    • GET /tasks/{id} and GET /v1/tasks/{id} -> handleGetTask
    • GET /tasks and GET /v1/tasks -> handleListTasks
    • GET /healthz -> an inline lambda returning {"status":"ok"}.
  • Handler signatures (they take extra args, wrapped in closures inside
    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)
  • Request/response types (already defined in this file):
    • 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 fields
      are 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.
  • Helper: func writeJSON(w http.ResponseWriter, status int, v any) sets
    Content-Type: application/json. Reuse it.

Endpoint POST accepts BOTH application/json and multipart/form-data. For the
OpenAPI spec, documenting the JSON body is sufficient; you may optionally note...

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copilot AI requested a review from ks6088ts July 6, 2026 01:14
@ks6088ts
ks6088ts marked this pull request as ready for review July 6, 2026 01:56
@ks6088ts
ks6088ts merged commit 3fc0ab6 into main Jul 6, 2026
5 checks passed
@ks6088ts
ks6088ts deleted the copilot/add-embedded-browser-gui branch July 6, 2026 01:58
@ks6088ts
ks6088ts restored the copilot/add-embedded-browser-gui branch July 6, 2026 01:59
@ks6088ts
ks6088ts deleted the copilot/add-embedded-browser-gui branch July 6, 2026 01:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants