From efaf375e437ab7c224aff4930cdc1f8108c36da6 Mon Sep 17 00:00:00 2001 From: mountain Date: Mon, 20 Jul 2026 22:48:59 +0800 Subject: [PATCH 1/2] feat(api): default port 7566 + name the install extra/command "web" The API server also serves the Knowledge Workbench UI, so `web` reads truer than `api` for what people install and run. Make `[web]` the canonical extra and add an `openkb-web` console script; keep `[api]` (as a self-referential alias) and `openkb-api` working for backwards compatibility. README and the rest-api example now lead with the `web` names. `python -m openkb.api` (the module path) is unchanged. Also move the default API port off the crowded 8000 to 7566 ("KB" = ASCII 75,66) to cut collisions with other local dev servers. Updated: the argparse default, the default CORS origins, the Vite dev-proxy target, the connection-dialog placeholder, and the README/examples. A server already running on 8000 keeps its port; the new default only applies to freshly started servers. Claude-Session: https://claude.ai/code/session_01XMxbhmAkxxVV8CFWCZDBaG --- README.md | 10 +++++----- examples/rest-api/README.md | 20 ++++++++++---------- frontend/src/App.tsx | 2 +- frontend/vite.config.ts | 4 ++-- openkb/api.py | 2 +- openkb/api_helpers.py | 4 ++-- pyproject.toml | 8 +++++++- uv.lock | 10 +++++++++- 8 files changed, 37 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 5e265cf1..988bebda 100644 --- a/README.md +++ b/README.md @@ -123,13 +123,13 @@ Subscription-based providers that authenticate via OAuth device flow (e.g. `chat OpenKB ships a bundled web UI, served by the REST API at `/`. Install the API extra and start the server — no configuration needed: ```bash -pip install "openkb[api]" -openkb-api # serves the API + Workbench at http://127.0.0.1:8000/ +pip install "openkb[web]" +openkb-web # serves the API + Workbench at http://127.0.0.1:7566/ ``` -Open `http://127.0.0.1:8000/` for the Workbench. Auth is off by default (local-first); set `OPENKB_API_TOKEN` to require a bearer token before exposing the server. See the [full Web UI guide](examples/rest-api/README.md#knowledge-workbench-web-ui). +Open `http://127.0.0.1:7566/` for the Workbench. Auth is off by default (local-first); set `OPENKB_API_TOKEN` to require a bearer token before exposing the server. See the [full Web UI guide](examples/rest-api/README.md#knowledge-workbench-web-ui). -> Working on the UI itself? Run the Vite dev server with `cd frontend && npm install && npm run dev` (it proxies `/api` to a running `openkb-api`), or `npm run build` to regenerate the bundled `openkb/web/`. +> Working on the UI itself? Run the Vite dev server with `cd frontend && npm install && npm run dev` (it proxies `/api` to a running `openkb-web`), or `npm run build` to regenerate the bundled `openkb/web/`. # 🧩 How OpenKB Works @@ -341,7 +341,7 @@ The skill is read-only. It won't run `openkb add`, `remove`, or `lint --fix` wit # REST API -OpenKB ships a FastAPI service for HTTP clients. Install with `pip install -e ".[api]"`, then start with `python -m openkb.api`. The interactive API reference is at [`/docs`](http://127.0.0.1:8000/docs) (importable into Postman). +OpenKB ships a FastAPI service for HTTP clients. Install with `pip install -e ".[web]"`, then start with `python -m openkb.api`. The interactive API reference is at [`/docs`](http://127.0.0.1:7566/docs) (importable into Postman). See the [full REST API reference](examples/rest-api/README.md#rest-api) for endpoints, auth, and SSE streaming. diff --git a/examples/rest-api/README.md b/examples/rest-api/README.md index ff7dfc70..0aecc29a 100644 --- a/examples/rest-api/README.md +++ b/examples/rest-api/README.md @@ -2,7 +2,7 @@ This guide covers the OpenKB REST API (FastAPI) and the bundled Knowledge Workbench web UI. -> The interactive API reference is served live at [`/docs`](http://127.0.0.1:8000/docs) (OpenAPI/Swagger) once the server is running — you can import `/openapi.json` directly into Postman. +> The interactive API reference is served live at [`/docs`](http://127.0.0.1:7566/docs) (OpenAPI/Swagger) once the server is running — you can import `/openapi.json` directly into Postman. ## Knowledge Workbench (Web UI) @@ -11,10 +11,10 @@ OpenKB ships a bundled web single-page app — the **Knowledge Workbench** — s ```bash # 1. Install with the API extra (the built UI ships inside the package) -pip install "openkb[api]" +pip install "openkb[web]" # 2. Start the server — no config needed for local use -openkb-api --host 127.0.0.1 --port 8000 # serves the API + Workbench at http://127.0.0.1:8000/ +openkb-web --host 127.0.0.1 --port 7566 # serves the API + Workbench at http://127.0.0.1:7566/ ``` Optional environment variables: @@ -22,9 +22,9 @@ Optional environment variables: - `OPENKB_KB_ROOT` — where REST-created knowledge bases are stored (default `~/.config/openkb/kbs`). - `OPENKB_API_TOKEN` — set it to require bearer auth (see [Authentication](#authentication-and-common-behavior)); leave unset for open local use. -> **From a source checkout?** The built bundle (`openkb/web/`) is git-ignored, so an editable install (`pip install -e ".[api]"`) has no UI until you build it once: `cd frontend && npm install && npm run build` (outputs to `openkb/web/`). Or run the Vite dev server with `npm run dev` (it proxies `/api` to a running `openkb-api`). Without the bundle, `openkb-api` serves only the REST API under `/api/v1` and `/` returns a 404. +> **From a source checkout?** The built bundle (`openkb/web/`) is git-ignored, so an editable install (`pip install -e ".[web]"`) has no UI until you build it once: `cd frontend && npm install && npm run build` (outputs to `openkb/web/`). Or run the Vite dev server with `npm run dev` (it proxies `/api` to a running `openkb-web`). Without the bundle, `openkb-web` serves only the REST API under `/api/v1` and `/` returns a 404. -Open `http://127.0.0.1:8000/` in your browser. With no `OPENKB_API_TOKEN` set it connects to the local API immediately — no prompt. (If a token is configured, a **Connection** dialog asks for it once and caches it in the browser; you can also open it manually to point the UI at a remote API base.) The Workbench then provides: +Open `http://127.0.0.1:7566/` in your browser. With no `OPENKB_API_TOKEN` set it connects to the local API immediately — no prompt. (If a token is configured, a **Connection** dialog asks for it once and caches it in the browser; you can also open it manually to point the UI at a remote API base.) The Workbench then provides: - **Overview** — index/concept/summary/report stat cards, clickable concept chips, recent documents, and last-compile/lint activity. - **Documents** — drag-and-drop multi-file upload with per-file SSE progress, hash table, and delete with confirmation. @@ -44,7 +44,7 @@ frontends, or other HTTP clients. Install the API dependencies if needed: ```bash -pip install -e ".[api]" +pip install -e ".[web]" ``` Start the API server: @@ -52,7 +52,7 @@ Start the API server: ```powershell $env:OPENKB_API_TOKEN="test-token" $env:OPENKB_KB_ROOT="D:\project\OpenKB\kbs" -.\.venv\Scripts\python.exe -m openkb.api --host 127.0.0.1 --port 8000 +.\.venv\Scripts\python.exe -m openkb.api --host 127.0.0.1 --port 7566 ``` ### Authentication and common behavior @@ -61,7 +61,7 @@ Auth is **opt-in**, controlled by the `OPENKB_API_TOKEN` server environment variable: - **Unset (default)** — the API is unauthenticated. This is the local-first - default, so `openkb-api` and the Workbench work with no configuration. + default, so `openkb-web` and the Workbench work with no configuration. - **Set** — every request must carry the token, and the Workbench prompts for it once (cached in the browser): @@ -73,7 +73,7 @@ variable: > **Exposing the server?** Always set `OPENKB_API_TOKEN` when binding to a > non-loopback host (e.g. `--host 0.0.0.0`) — otherwise the API, and every KB -> it can reach, is world-open. `openkb-api` prints a warning in that case. +> it can reach, is world-open. `openkb-web` prints a warning in that case. `OPENKB_KB_ROOT` is optional. It controls where REST-created knowledge bases are stored. If unset, OpenKB uses `~/.config/openkb/kbs`. @@ -419,4 +419,4 @@ no watcher is active for that KB. stop after this many seconds). Stream events: `start`, the watcher's own events (e.g. `added`, `updated`, `failed`, `final`), `error`, `done`. -The full OpenAPI schema is at [`/openapi.json`](http://127.0.0.1:8000/openapi.json) — importable into Postman or any OpenAPI-compatible client. +The full OpenAPI schema is at [`/openapi.json`](http://127.0.0.1:7566/openapi.json) — importable into Postman or any OpenAPI-compatible client. diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index ff8dbbba..5e76f020 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -107,7 +107,7 @@ function ConnectionDialog({ setBase(e.target.value)} - placeholder="http://127.0.0.1:8000" + placeholder="http://127.0.0.1:7566" className={inputCls} /> diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 54b1a52e..562a4d05 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -2,7 +2,7 @@ import path from "path" import react from "@vitejs/plugin-react" import { defineConfig } from "vite" -// Dev server proxies /api to the OpenKB REST API on :8000; production +// Dev server proxies /api to the OpenKB REST API on :7566; production // serves the built bundle from the same origin via FastAPI StaticFiles. // outDir MUST stay ../openkb/web — the wheel packages it from there // (see pyproject.toml [tool.hatch.build] artifacts). @@ -22,7 +22,7 @@ export default defineConfig({ port: 5173, proxy: { "/api": { - target: "http://127.0.0.1:8000", + target: "http://127.0.0.1:7566", changeOrigin: true, }, }, diff --git a/openkb/api.py b/openkb/api.py index 1d0d4973..92d26ec9 100644 --- a/openkb/api.py +++ b/openkb/api.py @@ -776,7 +776,7 @@ async def api_not_found(path: str) -> Any: def main() -> None: parser = argparse.ArgumentParser(description="Run the OpenKB REST API.") parser.add_argument("--host", default="127.0.0.1") - parser.add_argument("--port", type=int, default=8000) + parser.add_argument("--port", type=int, default=7566) parser.add_argument("--reload", action="store_true") args = parser.parse_args() diff --git a/openkb/api_helpers.py b/openkb/api_helpers.py index f05e9dad..d42bf4a9 100644 --- a/openkb/api_helpers.py +++ b/openkb/api_helpers.py @@ -69,8 +69,8 @@ def _configure_cors(app: FastAPI) -> None: origins = [o.strip() for o in raw.split(",") if o.strip()] or [ "http://localhost:5173", "http://127.0.0.1:5173", - "http://localhost:8000", - "http://127.0.0.1:8000", + "http://localhost:7566", + "http://127.0.0.1:7566", ] # A wildcard origin with credentials is insecure: any site can issue # credentialed cross-origin requests. Reject this combination by forcing diff --git a/pyproject.toml b/pyproject.toml index cac0bf50..389a8b2b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -59,6 +59,8 @@ Issues = "https://github.com/VectifyAI/OpenKB/issues" [project.scripts] openkb = "openkb.cli:cli" +openkb-web = "openkb.api:main" +# Backwards-compatible alias for the historical name; same entry point. openkb-api = "openkb.api:main" [tool.pytest.ini_options] @@ -73,7 +75,11 @@ dev = [ "mypy==1.15.0", "types-PyYAML==6.0.12.20260518", ] -api = ["fastapi", "uvicorn", "python-multipart"] +# The Knowledge Workbench Web UI is served by this FastAPI server. `web` is the +# canonical extra; `api` is a backwards-compatible alias so an existing +# `pip install "openkb[api]"` keeps working. +web = ["fastapi", "uvicorn", "python-multipart"] +api = ["openkb[web]"] [tool.hatch.version] source = "vcs" diff --git a/uv.lock b/uv.lock index 4e36cbf1..b9c12c73 100644 --- a/uv.lock +++ b/uv.lock @@ -1989,11 +1989,17 @@ dev = [ { name = "ruff" }, { name = "types-pyyaml" }, ] +web = [ + { name = "fastapi" }, + { name = "python-multipart" }, + { name = "uvicorn" }, +] [package.metadata] requires-dist = [ { name = "click", specifier = "==8.4.0" }, { name = "fastapi", marker = "extra == 'api'" }, + { name = "fastapi", marker = "extra == 'web'" }, { name = "httpx", marker = "extra == 'dev'" }, { name = "json-repair", specifier = "==0.59.10" }, { name = "litellm", specifier = "==1.87.2" }, @@ -2008,15 +2014,17 @@ requires-dist = [ { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = "==1.3.0" }, { name = "python-dotenv", specifier = "==1.2.2" }, { name = "python-multipart", marker = "extra == 'api'" }, + { name = "python-multipart", marker = "extra == 'web'" }, { name = "pyyaml", specifier = "==6.0.3" }, { name = "rich", specifier = "==15.0.0" }, { name = "ruff", marker = "extra == 'dev'", specifier = "==0.9.7" }, { name = "trafilatura", specifier = "==2.0.0" }, { name = "types-pyyaml", marker = "extra == 'dev'", specifier = "==6.0.12.20260518" }, { name = "uvicorn", marker = "extra == 'api'" }, + { name = "uvicorn", marker = "extra == 'web'" }, { name = "watchdog", specifier = "==6.0.0" }, ] -provides-extras = ["api", "dev"] +provides-extras = ["api", "dev", "web"] [[package]] name = "openpyxl" From 379cf1bae01182ae1898c090ab00c72feb32d491 Mon Sep 17 00:00:00 2001 From: mountain Date: Mon, 20 Jul 2026 22:48:59 +0800 Subject: [PATCH 2/2] fix(workbench): keep ArtifactPanel header buttons out from under the global chrome pills MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The docked artifact panel's header action buttons (open-in-tab / download / close) sit at the panel's top-right — exactly where App.tsx's global floating chrome cluster (theme + i18n toggles, `absolute right-3 z-40`) renders. The cluster's higher stacking context covered the download and close buttons. Reserve the ~112px chrome lane on the header with `pr-28`, matching the existing convention used by KbList's header row and KbDetail's gear row, so the panel buttons always clear the pills. Claude-Session: https://claude.ai/code/session_01XMxbhmAkxxVV8CFWCZDBaG --- frontend/src/components/ArtifactPanel.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/ArtifactPanel.tsx b/frontend/src/components/ArtifactPanel.tsx index 5695088a..a24816a1 100644 --- a/frontend/src/components/ArtifactPanel.tsx +++ b/frontend/src/components/ArtifactPanel.tsx @@ -222,8 +222,11 @@ export default function ArtifactPanel({ className="absolute left-0 top-0 z-10 h-full w-1.5 -translate-x-1/2 cursor-col-resize hover:bg-accent-brand/30" /> - {/* header */} -
+ {/* header — pr-28 reserves the global top-right chrome lane (theme + i18n + pills in App.tsx, absolute right-3 z-40) so the panel's action buttons + (open / download / close) never sit UNDER it. Same reserve convention + as KbList's header row and KbDetail's gear row. */} +
{artifactLabel(active, t)}