From e6b1009b8995bbbccab692c154d151c2901b5ac9 Mon Sep 17 00:00:00 2001 From: pewdiepie-archdaemon Date: Tue, 9 Jun 2026 09:48:59 +0900 Subject: [PATCH] Remove non-merge-ready workspace and terminal agent hooks --- app.py | 7 -- routes/chat_routes.py | 12 +-- routes/workspace_routes.py | 56 ------------- static/app.js | 4 - static/index.html | 13 --- static/js/chat.js | 4 - static/js/settings.js | 2 +- static/js/slashCommands.js | 37 --------- static/js/storage.js | 3 +- static/js/workspace.js | 160 ------------------------------------- static/style.css | 43 ---------- 11 files changed, 5 insertions(+), 336 deletions(-) delete mode 100644 routes/workspace_routes.py delete mode 100644 static/js/workspace.js diff --git a/app.py b/app.py index 0af6b18ea..22b63cc82 100644 --- a/app.py +++ b/app.py @@ -529,9 +529,6 @@ upload_cleanup_task = None from routes.emoji_routes import setup_emoji_routes app.include_router(setup_emoji_routes()) -from routes.workspace_routes import setup_workspace_routes -app.include_router(setup_workspace_routes()) - # Sessions from routes.session_routes import setup_session_routes session_config = {"REQUEST_TIMEOUT": REQUEST_TIMEOUT, "OPENAI_API_KEY": OPENAI_API_KEY, "SESSIONS_FILE": SESSIONS_FILE} @@ -650,10 +647,6 @@ app.include_router(calendar_router) from routes.shell_routes import setup_shell_routes app.include_router(setup_shell_routes()) -# Terminal agents (tmux-backed Codex/Claude/shell sessions) -from routes.terminal_agent_routes import setup_terminal_agent_routes -app.include_router(setup_terminal_agent_routes()) - # Cookbook (model download/serve/cache, cookbook state sync) from routes.cookbook_routes import setup_cookbook_routes app.include_router(setup_cookbook_routes()) diff --git a/routes/chat_routes.py b/routes/chat_routes.py index 9880522c8..39c17ec6c 100644 --- a/routes/chat_routes.py +++ b/routes/chat_routes.py @@ -456,12 +456,7 @@ def setup_chat_routes( # manual form posts that still send plan_mode=true. plan_mode = False chat_mode = str(form_data.get("mode", "")).lower() # 'chat' or 'agent' - # Workspace: confine the agent's file/shell tools to this folder. Validate - # it's a real directory; ignore (no confinement) otherwise. - workspace = (form_data.get("workspace") or "").strip() - if workspace: - _ws_real = os.path.realpath(os.path.expanduser(workspace)) - workspace = _ws_real if os.path.isdir(_ws_real) else "" + workspace = "" # Plan mode is a modifier on agent mode — it only makes sense with tools. if plan_mode: chat_mode = "agent" @@ -1140,7 +1135,7 @@ def setup_chat_routes( tool_policy=tool_policy, owner=_user, fallbacks=_fallback_candidates, - workspace=workspace or None, + workspace=None, plan_mode=plan_mode, approved_plan=approved_plan or None, ): @@ -1272,8 +1267,7 @@ def setup_chat_routes( # without waiting on the next streamed chunk. # # Normal chat/agent streams keep the DETACHED behavior below: they - # survive the client closing the tab / navigating away (true - # terminal-agent semantics). The SSE response just subscribes (replay + # survive the client closing the tab / navigating away. The SSE response just subscribes (replay # buffered output + live); dropping the SSE only removes a subscriber — # the run keeps going and saves the assistant message on completion # regardless. Reconnect via /api/chat/resume. diff --git a/routes/workspace_routes.py b/routes/workspace_routes.py deleted file mode 100644 index f7b27fbdc..000000000 --- a/routes/workspace_routes.py +++ /dev/null @@ -1,56 +0,0 @@ -"""Workspace API — browse server directories to pick a tool workspace folder.""" -import os -from fastapi import APIRouter, Request, HTTPException, Query - -from src.auth_helpers import get_current_user -from src.tool_security import owner_is_admin_or_single_user - - -def setup_workspace_routes(): - router = APIRouter(prefix="/api/workspace", tags=["workspace"]) - - @router.get("/browse") - def browse(request: Request, path: str = Query(default="")): - """List subdirectories of `path` (default: home) so the UI can navigate - the server filesystem and pick a workspace folder. Directories only. - - ADMIN-ONLY: this enumerates the server filesystem, so it is gated the - same way the file/shell tools are (read_file/write_file/bash are in - NON_ADMIN_BLOCKED_TOOLS). A non-admin who can't use those tools must not - be able to map the host's directory tree either. - """ - owner = get_current_user(request) - if not owner_is_admin_or_single_user(owner): - raise HTTPException(status_code=403, detail="Workspace browsing is admin-only") - - # Resolve symlinks so the reported path is canonical and the UI navigates - # real directories (defends against symlink games in displayed paths). - target = os.path.realpath(os.path.expanduser(path.strip() or "~")) - if not os.path.isdir(target): - target = os.path.realpath(os.path.expanduser("~")) - - dirs = [] - try: - with os.scandir(target) as it: - for entry in it: - try: - # Don't follow symlinks when classifying — a symlinked - # dir is skipped rather than letting the browser wander - # off via a link. Hidden entries are omitted. - if entry.is_dir(follow_symlinks=False) and not entry.name.startswith("."): - # Build the child path server-side with os.path.join - # so it's correct on Windows (backslashes) and Linux. - dirs.append({"name": entry.name, "path": os.path.join(target, entry.name)}) - except OSError: - continue - except (PermissionError, OSError): - dirs = [] - - parent = os.path.dirname(target) - return { - "path": target, - "parent": parent if parent and parent != target else None, - "dirs": sorted(dirs, key=lambda d: d["name"].lower()), - } - - return router diff --git a/static/app.js b/static/app.js index 20ef1f705..c75070bf2 100644 --- a/static/app.js +++ b/static/app.js @@ -4,7 +4,6 @@ // ============================================ import Storage from './js/storage.js'; import uiModule from './js/ui.js'; -import workspaceModule from './js/workspace.js'; import fileHandlerModule from './js/fileHandler.js'; import modelsModule from './js/models.js'; import ragModule from './js/rag.js'; @@ -1702,9 +1701,6 @@ function initializeEventListeners() { } setupToggle('web-toggle-btn', 'web-toggle', 'web'); setupToggle('bash-toggle-btn', 'bash-toggle', 'bash'); - try { workspaceModule.initWorkspace(); } catch (_) {} - - try { workspaceModule.initWorkspace(); } catch (_) {} // Document editor toggle (special: uses module panel, not a checkbox) const overflowDocBtn = el('overflow-doc-btn'); diff --git a/static/index.html b/static/index.html index ae3092659..4ca33c072 100644 --- a/static/index.html +++ b/static/index.html @@ -1040,13 +1040,6 @@ RAG - - - - - - - `; - document.body.appendChild(_modal); - _modal.querySelector('#workspace-close').addEventListener('click', closeWorkspaceBrowser); - _modal.querySelector('#workspace-cancel').addEventListener('click', closeWorkspaceBrowser); - // Editable path bar: Enter navigates to a typed/pasted folder. - _modal.querySelector('#workspace-cur-path').addEventListener('keydown', (e) => { - if (e.key === 'Enter') { - e.preventDefault(); - const v = e.target.value.trim(); - if (v) _navigate(v); - } - }); - _modal.querySelector('#workspace-use').addEventListener('click', () => { - setWorkspace(_curPath); - if (uiModule && uiModule.showToast) uiModule.showToast(`Workspace set: ${_basename(_curPath)}`); - closeWorkspaceBrowser(); - }); - const content = _modal.querySelector('.modal-content'); - const header = _modal.querySelector('.modal-header'); - if (content && header) makeWindowDraggable(_modal, { content, header }); - return _modal; -} - -export async function openWorkspaceBrowser() { - const modal = _getModal(); - modal.style.display = 'flex'; - try { - _render(await _load(getWorkspace() || '')); - } catch (e) { - if (uiModule && uiModule.showError) uiModule.showError('Could not browse folders'); - } -} - -export function closeWorkspaceBrowser() { - if (_modal) _modal.style.display = 'none'; -} - -export function initWorkspace() { - // Restore persisted workspace into the pill on load. - syncWorkspaceIndicator(getWorkspace()); - const overflow = document.getElementById('overflow-workspace-btn'); - if (overflow) overflow.addEventListener('click', openWorkspaceBrowser); - const pill = document.getElementById('workspace-indicator-btn'); - if (pill) pill.addEventListener('click', clearWorkspace); -} - -export default { initWorkspace, openWorkspaceBrowser, getWorkspace, setWorkspace, clearWorkspace, syncWorkspaceIndicator }; diff --git a/static/style.css b/static/style.css index 55a7a0dbc..ae5b68375 100644 --- a/static/style.css +++ b/static/style.css @@ -36363,49 +36363,6 @@ body.theme-frosted .modal { line-height: 1.4; color: color-mix(in srgb, var(--fg) 45%, transparent); } -/* ── Workspace picker ───────────────────────────────────────────── */ -/* Layout (width/flex column/max-height) inherited from base .modal-content. */ -/* Editable path/address bar: reuses .styled-prompt-input for border/bg/radius/ - focus ring (set in the element's class list). Overrides only the deltas: - mono font, and full-bleed via flex stretch with no horizontal margin (the - modal-content's 10px padding is the gutter) instead of the base width:100%, - which overflowed against the overflow:auto scrollbar. */ -.workspace-cur { - align-self: stretch; - width: auto; - min-width: 0; - margin: 4px 0 8px; - font-family: var(--mono, monospace); - font-size: 12px; -} -/* flex/overflow inherited from base .modal-body; only the padding differs. */ -.workspace-body { padding: 6px 0; } -.workspace-row { - padding: 7px 18px; - cursor: pointer; - font-size: 13px; - display: flex; - align-items: center; - gap: 8px; -} -.workspace-row > span { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} -.workspace-row-icon { flex-shrink: 0; opacity: 0.75; } -.workspace-row:hover { - background: color-mix(in srgb, var(--border) 20%, transparent); -} -.workspace-up { opacity: 0.7; } -.workspace-empty { padding: 14px 18px; opacity: 0.5; font-size: 13px; } -.workspace-footer { - display: flex; - justify-content: flex-end; - gap: 8px; - padding: 10px 18px; - border-top: 1px solid var(--border); -} /* Cookbook serve panel: Launch + ^ split button pair */ .hwfit-serve-launch-group { display: inline-flex;