mirror of
https://github.com/anomalyco/opencode.git
synced 2026-04-26 11:40:49 +00:00
docs(effect): add generated http route inventory
This commit is contained in:
parent
5cd178ba70
commit
2b028287e2
3 changed files with 286 additions and 23 deletions
138
packages/opencode/script/http-route-inventory.ts
Normal file
138
packages/opencode/script/http-route-inventory.ts
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
#!/usr/bin/env bun
|
||||
|
||||
import type { Hono } from "hono"
|
||||
import type { UpgradeWebSocket } from "hono/ws"
|
||||
import { WorkspacePaths } from "../src/server/routes/instance/httpapi/workspace"
|
||||
import { FilePaths } from "../src/server/routes/instance/httpapi/file"
|
||||
import { McpPaths } from "../src/server/routes/instance/httpapi/mcp"
|
||||
import { Flag } from "../src/flag/flag"
|
||||
import { ControlPlaneRoutes } from "../src/server/routes/control"
|
||||
import { WorkspaceRoutes } from "../src/server/routes/control/workspace"
|
||||
import { InstanceRoutes } from "../src/server/routes/instance"
|
||||
import { UIRoutes } from "../src/server/routes/ui"
|
||||
|
||||
type Method = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "ALL"
|
||||
|
||||
interface Route {
|
||||
surface: string
|
||||
method: Method
|
||||
path: string
|
||||
status: string
|
||||
}
|
||||
|
||||
const methodOrder = new Map<Method, number>([
|
||||
["GET", 0],
|
||||
["POST", 1],
|
||||
["PUT", 2],
|
||||
["PATCH", 3],
|
||||
["DELETE", 4],
|
||||
["ALL", 5],
|
||||
])
|
||||
|
||||
const bridged = new Set([
|
||||
key("GET", "/question"),
|
||||
key("POST", "/question/:requestID/reply"),
|
||||
key("POST", "/question/:requestID/reject"),
|
||||
key("GET", "/permission"),
|
||||
key("POST", "/permission/:requestID/reply"),
|
||||
key("GET", "/config"),
|
||||
key("GET", "/config/providers"),
|
||||
key("GET", "/provider"),
|
||||
key("GET", "/provider/auth"),
|
||||
key("POST", "/provider/:providerID/oauth/authorize"),
|
||||
key("POST", "/provider/:providerID/oauth/callback"),
|
||||
key("GET", "/project"),
|
||||
key("GET", "/project/current"),
|
||||
key("GET", FilePaths.list),
|
||||
key("GET", FilePaths.content),
|
||||
key("GET", FilePaths.status),
|
||||
key("GET", McpPaths.status),
|
||||
...Object.values(WorkspacePaths).map((path) => key("GET", path)),
|
||||
])
|
||||
|
||||
const topLevelNext = new Set([
|
||||
key("GET", "/path"),
|
||||
key("GET", "/vcs"),
|
||||
key("GET", "/vcs/diff"),
|
||||
key("GET", "/command"),
|
||||
key("GET", "/agent"),
|
||||
key("GET", "/skill"),
|
||||
key("GET", "/lsp"),
|
||||
key("GET", "/formatter"),
|
||||
])
|
||||
|
||||
function key(method: string, path: string) {
|
||||
return `${method} ${path}`
|
||||
}
|
||||
|
||||
function normalize(prefix: string, route: string) {
|
||||
if (!prefix) return route
|
||||
if (route === "/") return prefix
|
||||
return `${prefix}${route}`.replaceAll(/\/+/g, "/")
|
||||
}
|
||||
|
||||
function routes(surface: string, app: Hono, prefix = "") {
|
||||
const seen = new Map<string, Route>()
|
||||
for (const route of app.routes as Array<{ method: Method; path: string }>) {
|
||||
if (surface !== "ui" && route.method === "ALL" && route.path === "/*") continue
|
||||
const path = normalize(prefix, route.path)
|
||||
seen.set(key(route.method, path), {
|
||||
surface,
|
||||
method: route.method,
|
||||
path,
|
||||
status: classify(route.method, path, surface),
|
||||
})
|
||||
}
|
||||
return [...seen.values()].toSorted(compare)
|
||||
}
|
||||
|
||||
function compare(a: Route, b: Route) {
|
||||
return (
|
||||
a.surface.localeCompare(b.surface) ||
|
||||
a.path.localeCompare(b.path) ||
|
||||
(methodOrder.get(a.method) ?? 99) - (methodOrder.get(b.method) ?? 99)
|
||||
)
|
||||
}
|
||||
|
||||
function classify(method: Method, path: string, surface: string) {
|
||||
if (bridged.has(key(method, path))) return "bridged"
|
||||
if (topLevelNext.has(key(method, path))) return "next"
|
||||
if (surface === "ui") return "special"
|
||||
if (path === "/event") return "special"
|
||||
if (path.startsWith("/pty") || path.startsWith("/tui")) return "special"
|
||||
if (path.startsWith("/session") || path.startsWith("/sync")) return "later"
|
||||
if (path.startsWith("/experimental")) return method === "GET" ? "next" : "later"
|
||||
if (path.startsWith("/mcp")) return "later"
|
||||
if (path === "/instance/dispose") return "next"
|
||||
return "later"
|
||||
}
|
||||
|
||||
function table(items: Route[]) {
|
||||
return [
|
||||
"| Surface | Method | Path | Status |",
|
||||
"| --- | --- | --- | --- |",
|
||||
...items.map((item) => `| ${item.surface} | \`${item.method}\` | \`${item.path}\` | \`${item.status}\` |`),
|
||||
].join("\n")
|
||||
}
|
||||
|
||||
Flag.OPENCODE_EXPERIMENTAL_HTTPAPI = false
|
||||
|
||||
const websocket = (() => () => new Response(null, { status: 501 })) as unknown as UpgradeWebSocket
|
||||
const inventory = [
|
||||
...routes("control", ControlPlaneRoutes()),
|
||||
...routes("workspace", WorkspaceRoutes(), "/experimental/workspace"),
|
||||
...routes("instance", InstanceRoutes(websocket)),
|
||||
...routes("ui", UIRoutes()),
|
||||
].toSorted(compare)
|
||||
|
||||
await Bun.write(
|
||||
new URL("../specs/effect/http-route-inventory.md", import.meta.url),
|
||||
`# Http Route Inventory
|
||||
|
||||
Generated from Hono route registrations by \`packages/opencode/script/http-route-inventory.ts\`.
|
||||
|
||||
Status meanings are defined in \`specs/effect/http-api.md\`.
|
||||
|
||||
${table(inventory)}
|
||||
`,
|
||||
)
|
||||
|
|
@ -53,6 +53,12 @@ Before porting more routes, cover the bridge behavior that every route depends o
|
|||
|
||||
Create a route inventory from the actual Hono registrations and classify each route.
|
||||
|
||||
The generated inventory lives in `specs/effect/http-route-inventory.md` and is produced by:
|
||||
|
||||
```bash
|
||||
bun run script/http-route-inventory.ts
|
||||
```
|
||||
|
||||
Statuses:
|
||||
|
||||
- `bridged`: served through the `HttpApi` bridge when the flag is on.
|
||||
|
|
@ -130,31 +136,31 @@ Use raw Effect HTTP routes where `HttpApi` does not fit. The goal is deleting Ho
|
|||
|
||||
## Current Route Status
|
||||
|
||||
| Area | Status | Notes |
|
||||
| ------------------------ | ----------------- | -------------------------------------------------------------- |
|
||||
| `question` | `bridged` | `GET /question`, reply, reject |
|
||||
| `permission` | `bridged` | list and reply |
|
||||
| `provider` | `bridged` | list, auth, OAuth authorize/callback |
|
||||
| `config` | `bridged` partial | reads only; mutation remains Hono |
|
||||
| `project` | `bridged` partial | reads only; git-init remains Hono |
|
||||
| `file` | `bridged` partial | list/content/status only |
|
||||
| `mcp` | `bridged` partial | status only |
|
||||
| `workspace` | `implemented` | `HttpApi` group exists, but bridge mounting needs verification |
|
||||
| top-level instance reads | `next` | path, vcs, command, agent, skill, lsp, formatter |
|
||||
| experimental JSON routes | `next/later` | console, tool, worktree, resource, global session list |
|
||||
| `session` | `later/special` | large stateful surface plus streaming |
|
||||
| `sync` | `later` | process/control side effects |
|
||||
| `event` | `special` | SSE |
|
||||
| `pty` | `special` | websocket |
|
||||
| `tui` | `special` | UI bridge |
|
||||
| Area | Status | Notes |
|
||||
| ------------------------ | ----------------- | --------------------------------------------------------------------- |
|
||||
| `question` | `bridged` | `GET /question`, reply, reject |
|
||||
| `permission` | `bridged` | list and reply |
|
||||
| `provider` | `bridged` | list, auth, OAuth authorize/callback |
|
||||
| `config` | `bridged` partial | reads only; `PATCH /config` remains Hono |
|
||||
| `project` | `bridged` partial | reads only; update and git-init remain Hono |
|
||||
| `file` | `bridged` partial | `GET /file`, `GET /file/content`, `GET /file/status` |
|
||||
| `mcp` | `bridged` partial | `GET /mcp` only |
|
||||
| `workspace` | `bridged` partial | reads only; create/remove/session-restore remain Hono |
|
||||
| top-level instance reads | `next` | `GET /path`, `/vcs`, `/vcs/diff`, `/command`, `/agent`, `/skill`, etc. |
|
||||
| experimental JSON routes | `next/later` | console, tool, worktree, resource, global session list |
|
||||
| `session` | `later/special` | large stateful surface plus streaming |
|
||||
| `sync` | `later` | process/control side effects |
|
||||
| `event` | `special` | SSE |
|
||||
| `pty` | `special` | websocket |
|
||||
| `tui` | `special` | UI bridge |
|
||||
|
||||
See `specs/effect/http-route-inventory.md` for exact paths and per-route status.
|
||||
|
||||
## Next PRs
|
||||
|
||||
1. Add bridge-level auth and instance-context tests for the current `HttpApi` bridge.
|
||||
2. Produce a generated route inventory from Hono registrations and update `Current Route Status` with exact paths.
|
||||
3. Fix the `workspace` status: mount it if it should be reachable, or remove it from the composed `HttpApi` layer.
|
||||
4. Port the top-level JSON reads.
|
||||
5. Start the Effect OpenAPI/SDK generation path for already-bridged routes.
|
||||
2. Port the top-level JSON reads.
|
||||
3. Start the Effect OpenAPI/SDK generation path for already-bridged routes.
|
||||
|
||||
## Checklist
|
||||
|
||||
|
|
@ -165,8 +171,8 @@ Use raw Effect HTTP routes where `HttpApi` does not fit. The goal is deleting Ho
|
|||
- [x] Attach auth middleware in route modules.
|
||||
- [x] Support `auth_token` as a query security scheme.
|
||||
- [ ] Add bridge-level auth and instance tests.
|
||||
- [ ] Complete exact Hono route inventory.
|
||||
- [ ] Resolve implemented-but-unmounted route groups.
|
||||
- [x] Complete exact Hono route inventory.
|
||||
- [x] Resolve implemented-but-unmounted route groups.
|
||||
- [ ] Port remaining JSON routes.
|
||||
- [ ] Generate SDK/OpenAPI from Effect routes.
|
||||
- [ ] Flip ported JSON routes to default-on with fallback.
|
||||
|
|
|
|||
119
packages/opencode/specs/effect/http-route-inventory.md
Normal file
119
packages/opencode/specs/effect/http-route-inventory.md
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
# Http Route Inventory
|
||||
|
||||
Generated from Hono route registrations by `packages/opencode/script/http-route-inventory.ts`.
|
||||
|
||||
Status meanings are defined in `specs/effect/http-api.md`.
|
||||
|
||||
| Surface | Method | Path | Status |
|
||||
| --- | --- | --- | --- |
|
||||
| control | `PUT` | `/auth/:providerID` | `later` |
|
||||
| control | `DELETE` | `/auth/:providerID` | `later` |
|
||||
| control | `GET` | `/doc` | `later` |
|
||||
| control | `POST` | `/log` | `later` |
|
||||
| instance | `GET` | `/agent` | `next` |
|
||||
| instance | `GET` | `/command` | `next` |
|
||||
| instance | `GET` | `/config` | `bridged` |
|
||||
| instance | `PATCH` | `/config` | `later` |
|
||||
| instance | `GET` | `/config/providers` | `bridged` |
|
||||
| instance | `GET` | `/event` | `special` |
|
||||
| instance | `GET` | `/experimental/console` | `next` |
|
||||
| instance | `GET` | `/experimental/console/orgs` | `next` |
|
||||
| instance | `POST` | `/experimental/console/switch` | `later` |
|
||||
| instance | `GET` | `/experimental/resource` | `next` |
|
||||
| instance | `GET` | `/experimental/session` | `next` |
|
||||
| instance | `GET` | `/experimental/tool` | `next` |
|
||||
| instance | `GET` | `/experimental/tool/ids` | `next` |
|
||||
| instance | `GET` | `/experimental/worktree` | `next` |
|
||||
| instance | `POST` | `/experimental/worktree` | `later` |
|
||||
| instance | `DELETE` | `/experimental/worktree` | `later` |
|
||||
| instance | `POST` | `/experimental/worktree/reset` | `later` |
|
||||
| instance | `GET` | `/file` | `bridged` |
|
||||
| instance | `GET` | `/file/content` | `bridged` |
|
||||
| instance | `GET` | `/file/status` | `bridged` |
|
||||
| instance | `GET` | `/find` | `later` |
|
||||
| instance | `GET` | `/find/file` | `later` |
|
||||
| instance | `GET` | `/find/symbol` | `later` |
|
||||
| instance | `GET` | `/formatter` | `next` |
|
||||
| instance | `POST` | `/instance/dispose` | `next` |
|
||||
| instance | `GET` | `/lsp` | `next` |
|
||||
| instance | `GET` | `/mcp` | `bridged` |
|
||||
| instance | `POST` | `/mcp` | `later` |
|
||||
| instance | `POST` | `/mcp/:name/auth` | `later` |
|
||||
| instance | `DELETE` | `/mcp/:name/auth` | `later` |
|
||||
| instance | `POST` | `/mcp/:name/auth/authenticate` | `later` |
|
||||
| instance | `POST` | `/mcp/:name/auth/callback` | `later` |
|
||||
| instance | `POST` | `/mcp/:name/connect` | `later` |
|
||||
| instance | `POST` | `/mcp/:name/disconnect` | `later` |
|
||||
| instance | `GET` | `/path` | `next` |
|
||||
| instance | `GET` | `/permission` | `bridged` |
|
||||
| instance | `POST` | `/permission/:requestID/reply` | `bridged` |
|
||||
| instance | `GET` | `/project` | `bridged` |
|
||||
| instance | `PATCH` | `/project/:projectID` | `later` |
|
||||
| instance | `GET` | `/project/current` | `bridged` |
|
||||
| instance | `POST` | `/project/git/init` | `later` |
|
||||
| instance | `GET` | `/provider` | `bridged` |
|
||||
| instance | `POST` | `/provider/:providerID/oauth/authorize` | `bridged` |
|
||||
| instance | `POST` | `/provider/:providerID/oauth/callback` | `bridged` |
|
||||
| instance | `GET` | `/provider/auth` | `bridged` |
|
||||
| instance | `GET` | `/pty` | `special` |
|
||||
| instance | `POST` | `/pty` | `special` |
|
||||
| instance | `GET` | `/pty/:ptyID` | `special` |
|
||||
| instance | `PUT` | `/pty/:ptyID` | `special` |
|
||||
| instance | `DELETE` | `/pty/:ptyID` | `special` |
|
||||
| instance | `GET` | `/pty/:ptyID/connect` | `special` |
|
||||
| instance | `GET` | `/question` | `bridged` |
|
||||
| instance | `POST` | `/question/:requestID/reject` | `bridged` |
|
||||
| instance | `POST` | `/question/:requestID/reply` | `bridged` |
|
||||
| instance | `GET` | `/session` | `later` |
|
||||
| instance | `POST` | `/session` | `later` |
|
||||
| instance | `GET` | `/session/:sessionID` | `later` |
|
||||
| instance | `PATCH` | `/session/:sessionID` | `later` |
|
||||
| instance | `DELETE` | `/session/:sessionID` | `later` |
|
||||
| instance | `POST` | `/session/:sessionID/abort` | `later` |
|
||||
| instance | `GET` | `/session/:sessionID/children` | `later` |
|
||||
| instance | `POST` | `/session/:sessionID/command` | `later` |
|
||||
| instance | `GET` | `/session/:sessionID/diff` | `later` |
|
||||
| instance | `POST` | `/session/:sessionID/fork` | `later` |
|
||||
| instance | `POST` | `/session/:sessionID/init` | `later` |
|
||||
| instance | `GET` | `/session/:sessionID/message` | `later` |
|
||||
| instance | `POST` | `/session/:sessionID/message` | `later` |
|
||||
| instance | `GET` | `/session/:sessionID/message/:messageID` | `later` |
|
||||
| instance | `DELETE` | `/session/:sessionID/message/:messageID` | `later` |
|
||||
| instance | `PATCH` | `/session/:sessionID/message/:messageID/part/:partID` | `later` |
|
||||
| instance | `DELETE` | `/session/:sessionID/message/:messageID/part/:partID` | `later` |
|
||||
| instance | `POST` | `/session/:sessionID/permissions/:permissionID` | `later` |
|
||||
| instance | `POST` | `/session/:sessionID/prompt_async` | `later` |
|
||||
| instance | `POST` | `/session/:sessionID/revert` | `later` |
|
||||
| instance | `POST` | `/session/:sessionID/share` | `later` |
|
||||
| instance | `DELETE` | `/session/:sessionID/share` | `later` |
|
||||
| instance | `POST` | `/session/:sessionID/shell` | `later` |
|
||||
| instance | `POST` | `/session/:sessionID/summarize` | `later` |
|
||||
| instance | `GET` | `/session/:sessionID/todo` | `later` |
|
||||
| instance | `POST` | `/session/:sessionID/unrevert` | `later` |
|
||||
| instance | `GET` | `/session/status` | `later` |
|
||||
| instance | `GET` | `/skill` | `next` |
|
||||
| instance | `POST` | `/sync/history` | `later` |
|
||||
| instance | `POST` | `/sync/replay` | `later` |
|
||||
| instance | `POST` | `/sync/start` | `later` |
|
||||
| instance | `POST` | `/tui/append-prompt` | `special` |
|
||||
| instance | `POST` | `/tui/clear-prompt` | `special` |
|
||||
| instance | `GET` | `/tui/control/next` | `special` |
|
||||
| instance | `POST` | `/tui/control/response` | `special` |
|
||||
| instance | `POST` | `/tui/execute-command` | `special` |
|
||||
| instance | `POST` | `/tui/open-help` | `special` |
|
||||
| instance | `POST` | `/tui/open-models` | `special` |
|
||||
| instance | `POST` | `/tui/open-sessions` | `special` |
|
||||
| instance | `POST` | `/tui/open-themes` | `special` |
|
||||
| instance | `POST` | `/tui/publish` | `special` |
|
||||
| instance | `POST` | `/tui/select-session` | `special` |
|
||||
| instance | `POST` | `/tui/show-toast` | `special` |
|
||||
| instance | `POST` | `/tui/submit-prompt` | `special` |
|
||||
| instance | `GET` | `/vcs` | `next` |
|
||||
| instance | `GET` | `/vcs/diff` | `next` |
|
||||
| ui | `ALL` | `/*` | `special` |
|
||||
| workspace | `GET` | `/experimental/workspace` | `bridged` |
|
||||
| workspace | `POST` | `/experimental/workspace` | `later` |
|
||||
| workspace | `DELETE` | `/experimental/workspace/:id` | `later` |
|
||||
| workspace | `POST` | `/experimental/workspace/:id/session-restore` | `later` |
|
||||
| workspace | `GET` | `/experimental/workspace/adaptor` | `bridged` |
|
||||
| workspace | `GET` | `/experimental/workspace/status` | `bridged` |
|
||||
Loading…
Add table
Add a link
Reference in a new issue