feat(webui): add project workspaces and access controls (#4007)

* feat(webui): add project workspaces and access controls

* feat(webui): add project workspaces and access controls

* refactor(tools): centralize workspace access resolution

* refactor(webui): remove unused workspace host state

* fix(webui): hide estimated file edit label

* fix(webui): clarify file edit deletion feedback

* fix(webui): label deleted file activity

* fix(webui): flatten file edit activity rows

* fix(core): remove path-only patch deletion

* fix(core): keep apply patch non-destructive

* refactor(webui): trim workspace host plumbing

* fix(tools): register exec with tools config
This commit is contained in:
Xubin Ren
2026-05-29 03:42:53 +08:00
committed by GitHub
parent 84428136e6
commit 3a420136bb
111 changed files with 9972 additions and 1822 deletions
+5 -18
View File
@@ -5,7 +5,7 @@ import path from "node:path";
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), "");
const target = env.NANOBOT_API_URL ?? "http://127.0.0.1:8765";
const wsTarget = target.replace(/^http/, "ws");
const hmrPath = "/__nanobot_vite_hmr";
return {
plugins: [react()],
@@ -60,30 +60,17 @@ export default defineConfig(({ mode }) => {
host: "127.0.0.1",
port: 5173,
strictPort: true,
// Move Vite's HMR socket to a dedicated port so it doesn't collide with
// the ``/`` proxy below (Vite HMR and the nanobot ws upgrade both sit on
// the root path, which triggers spurious write-after-end errors as each
// side tries to close the other's socket).
// Keep Vite's HMR socket on a dedicated path. Nanobot's app WebSocket is
// opened directly from the browser to the gateway, so the dev server
// should never proxy WebSocket upgrades.
hmr: {
host: "127.0.0.1",
port: 5174,
path: hmrPath,
},
proxy: {
"/webui": { target, changeOrigin: true },
"/api": { target, changeOrigin: true },
"/auth": { target, changeOrigin: true },
// Forward only WebSocket upgrades on ``/`` to the nanobot gateway;
// plain HTTP GETs on ``/`` must stay with Vite so it can serve the SPA.
// ``bypass`` returning the original URL skips the proxy for that
// request; returning undefined lets the proxy (and ws upgrade handler)
// take it.
"/": {
target: wsTarget,
ws: true,
changeOrigin: true,
bypass: (req) =>
req.headers.upgrade === "websocket" ? undefined : req.url,
},
},
},
test: {