-
Notifications
You must be signed in to change notification settings - Fork 1
feat(frontend): phase 0 foundation — pnpm workspace, tooling reconcile, Go embed handler #162
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
AgentWrapper
wants to merge
1
commit into
main
Choose a base branch
from
session/ao-16
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
Empty file.
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| <!doctype html> | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charset="UTF-8" /> | ||
| <title>Agent Orchestrator</title> | ||
| </head> | ||
| <body> | ||
| frontend not built — run pnpm web:build | ||
| </body> | ||
| </html> |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| // Package webui serves the built single-page web UI from the daemon. The | ||
| // compiled Vite assets are embedded at build time so the daemon ships as a | ||
| // single binary with no runtime dependency on the frontend toolchain. | ||
| // | ||
| // The bundle is produced by `npm run build:frontend`, which runs the ao-web | ||
| // Vite build and copies frontend/apps/web/dist/* into dist/ here. When the | ||
| // frontend has not been built, dist/ holds only a placeholder index.html (plus | ||
| // .gitkeep) so the //go:embed directive still compiles. | ||
| package webui | ||
|
|
||
| import ( | ||
| "embed" | ||
| "io/fs" | ||
| "net/http" | ||
| "strings" | ||
| ) | ||
|
|
||
| //go:embed dist | ||
| var embedded embed.FS | ||
|
|
||
| // Handler returns an http.Handler that serves the embedded SPA. | ||
| // | ||
| // Static files resolve directly. Any path that does not map to an embedded file | ||
| // falls back to index.html so client-side (TanStack Router) deep links survive | ||
| // a hard refresh. The one exception is /assets/*: a miss there is a genuinely | ||
| // absent build artifact, so it returns 404 rather than masking the error by | ||
| // serving the HTML shell with a 200. | ||
| func Handler() http.Handler { | ||
| dist, err := fs.Sub(embedded, "dist") | ||
| if err != nil { | ||
| // dist is a compile-time embed; a failure here is a programming error, | ||
| // not a runtime condition, so failing loudly is correct. | ||
| panic(err) | ||
| } | ||
| fileServer := http.FileServer(http.FS(dist)) | ||
|
|
||
| return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
| name := strings.TrimPrefix(r.URL.Path, "/") | ||
| if name == "" { | ||
| name = "index.html" | ||
| } | ||
|
|
||
| if _, statErr := fs.Stat(dist, name); statErr != nil { | ||
| // Missing asset references are real 404s, not SPA routes. | ||
| if strings.HasPrefix(r.URL.Path, "/assets/") { | ||
| http.NotFound(w, r) | ||
| return | ||
| } | ||
| // Unknown non-asset path: serve the SPA shell so the client router | ||
| // can resolve the deep link. Rewrite onto a clone to avoid mutating | ||
| // the caller's request. | ||
| r = r.Clone(r.Context()) | ||
| r.URL.Path = "/" | ||
| } | ||
| fileServer.ServeHTTP(w, r) | ||
| }) | ||
| } | ||
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| package webui | ||
|
|
||
| import ( | ||
| "net/http" | ||
| "net/http/httptest" | ||
| "strings" | ||
| "testing" | ||
| ) | ||
|
|
||
| // An unknown, non-asset path must fall back to the embedded index.html (the SPA | ||
| // shell) so client-side router deep links survive a hard refresh. | ||
| func TestHandler_ServesIndexForUnknownPath(t *testing.T) { | ||
| rec := httptest.NewRecorder() | ||
| req := httptest.NewRequest(http.MethodGet, "/sessions/abc/deep/link", nil) | ||
|
|
||
| Handler().ServeHTTP(rec, req) | ||
|
|
||
| if rec.Code != http.StatusOK { | ||
| t.Fatalf("unknown path: got status %d, want %d", rec.Code, http.StatusOK) | ||
| } | ||
| if body := rec.Body.String(); !strings.Contains(body, "frontend not built") { | ||
| t.Fatalf("unknown path: body did not serve the index shell, got %q", body) | ||
| } | ||
| } | ||
|
|
||
| // A missing asset is a genuinely absent build artifact, not a router route, so | ||
| // it must 404 rather than be masked by the HTML shell. | ||
| func TestHandler_NotFoundForMissingAsset(t *testing.T) { | ||
| rec := httptest.NewRecorder() | ||
| req := httptest.NewRequest(http.MethodGet, "/assets/missing.js", nil) | ||
|
|
||
| Handler().ServeHTTP(rec, req) | ||
|
|
||
| if rec.Code != http.StatusNotFound { | ||
| t.Fatalf("missing asset: got status %d, want %d", rec.Code, http.StatusNotFound) | ||
| } | ||
| } | ||
|
|
||
| // The root path serves the embedded index.html directly. | ||
| func TestHandler_ServesIndexForRoot(t *testing.T) { | ||
| rec := httptest.NewRecorder() | ||
| req := httptest.NewRequest(http.MethodGet, "/", nil) | ||
|
|
||
| Handler().ServeHTTP(rec, req) | ||
|
|
||
| if rec.Code != http.StatusOK { | ||
| t.Fatalf("root: got status %d, want %d", rec.Code, http.StatusOK) | ||
| } | ||
| if body := rec.Body.String(); !strings.Contains(body, "frontend not built") { | ||
| t.Fatalf("root: body did not serve the index shell, got %q", body) | ||
| } | ||
| } |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| **/dist/ | ||
| **/node_modules/ | ||
| **/.tanstack/ | ||
| pnpm-lock.yaml | ||
| # Legacy reference UI — tooled separately (see eslint.config.js). | ||
| src/ |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| { | ||
| "semi": true, | ||
| "singleQuote": false, | ||
| "trailingComma": "all", | ||
| "printWidth": 100 | ||
| } |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| <!doctype html> | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charset="UTF-8" /> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
| <title>Agent Orchestrator — Landing</title> | ||
| </head> | ||
| <body> | ||
| <div id="app"></div> | ||
| <script type="module" src="/src/main.ts"></script> | ||
| </body> | ||
| </html> |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| { | ||
| "name": "@aoagents/ao-landing", | ||
| "version": "0.0.0", | ||
| "private": true, | ||
| "type": "module", | ||
| "scripts": { | ||
| "dev": "vite", | ||
| "build": "vite build", | ||
| "preview": "vite preview", | ||
| "typecheck": "tsc --noEmit" | ||
| }, | ||
| "devDependencies": { | ||
| "typescript": "6.0.3", | ||
| "vite": "8.0.16" | ||
| } | ||
| } |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| // Empty Vite landing scaffold (phase 0). Real content is ported from the | ||
| // existing Next.js landing in a later session. | ||
| const app = document.querySelector<HTMLDivElement>("#app"); | ||
| if (app) { | ||
| app.textContent = "Agent Orchestrator landing — scaffold placeholder"; | ||
| } |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| /// <reference types="vite/client" /> |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| { | ||
| "extends": "../../tsconfig.base.json", | ||
| "compilerOptions": { | ||
| "lib": ["ES2022", "DOM", "DOM.Iterable"], | ||
| "types": ["vite/client"] | ||
| }, | ||
| "include": ["src", "vite.config.ts"] | ||
| } |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| import { defineConfig } from "vite"; | ||
|
|
||
| // Empty Vite scaffold for the landing app. The existing Next.js landing under | ||
| // frontend/src/landing is intentionally NOT moved here this session (it is | ||
| // tied to the react-doctor CI job); its port to this app lands later. | ||
| export default defineConfig({ | ||
| server: { | ||
| port: 5174, | ||
| }, | ||
| }); |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| <!doctype html> | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charset="UTF-8" /> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
| <title>Agent Orchestrator</title> | ||
| </head> | ||
| <body> | ||
| <div id="root"></div> | ||
| <script type="module" src="/src/main.tsx"></script> | ||
| </body> | ||
| </html> |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| { | ||
| "name": "@aoagents/ao-web", | ||
| "version": "0.0.0", | ||
| "private": true, | ||
| "type": "module", | ||
| "scripts": { | ||
| "dev": "vite", | ||
| "build": "vite build", | ||
| "preview": "vite preview", | ||
| "typecheck": "tsc --noEmit", | ||
| "test": "vitest run" | ||
| }, | ||
| "dependencies": { | ||
| "@aoagents/core": "workspace:*", | ||
| "@aoagents/runtime": "workspace:*", | ||
| "@tanstack/react-query": "5.101.0", | ||
| "@tanstack/react-router": "1.170.15", | ||
| "@xterm/xterm": "6.0.0", | ||
| "react": "19.2.7", | ||
| "react-dom": "19.2.7" | ||
| }, | ||
| "devDependencies": { | ||
| "@tailwindcss/vite": "4.3.0", | ||
| "@types/react": "19.2.17", | ||
| "@types/react-dom": "19.2.3", | ||
| "@vitejs/plugin-react": "6.0.2", | ||
| "tailwindcss": "4.3.0", | ||
| "typescript": "6.0.3", | ||
| "vite": "8.0.16", | ||
| "vitest": "4.1.8" | ||
| } | ||
| } |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| // Placeholder route component for phase 0. Real UI (layout, panes, terminal | ||
| // mux) lands in later phases — this only proves the Vite + React + Router + | ||
| // Query + Tailwind toolchain boots and renders. | ||
| export function ScaffoldingLive() { | ||
| return ( | ||
| <main className="flex min-h-screen flex-col items-center justify-center gap-2 font-sans"> | ||
| <h1 className="text-2xl font-semibold">scaffolding live</h1> | ||
| <p className="text-sm opacity-70"> | ||
| Agent Orchestrator web UI — phase 0 foundation. No components yet. | ||
| </p> | ||
| </main> | ||
| ); | ||
| } |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| import { describe, expect, it } from "vitest"; | ||
|
|
||
| import { router } from "../router"; | ||
|
|
||
| // Phase 0 smoke test: the toolchain (Vite + Vitest + TanStack Router) is wired | ||
| // and the code-based route tree constructs without error. Behavioral UI tests | ||
| // arrive with the first real components. | ||
| describe("ao-web scaffolding", () => { | ||
| it("constructs the TanStack Router instance", () => { | ||
| expect(router).toBeDefined(); | ||
| expect(router.routeTree).toBeDefined(); | ||
| }); | ||
|
|
||
| it("registers the index route on the tree", () => { | ||
| expect(router.routeTree.children).toBeDefined(); | ||
| }); | ||
| }); |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| @import "tailwindcss"; |
Oops, something went wrong.
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"/",r.URL.RawPathis not cleared. If the original request carried a percent-encoded path (e.g./foo%2Fbar),RawPathretains that value whilePathis"/".http.URL.EscapedPath()— which some middleware and redirect logic calls — would then return the stale encoded value instead of"/". SettingRawPath = ""restores consistency soEscapedPath()falls back toPath.