Conversation
Adds the PermissionConsent dialog for userspace app installs: lists each
requested capability with a plain-English description mirrored from
tinyagentos/userspace/capabilities.py, flags the gated ones (app.net,
app.agent, app.llm, app.memory), and Allow/Deny grants or skips them via
POST /api/userspace-apps/{app_id}/permissions.
Wires it into the Store's Apps tab via a new ImportAppButton that installs
a .taosapp package and shows the dialog whenever the install response sets
needs_consent.
Docs-Reviewed: frontend-only Store components (install-consent dialog + .taosapp import button); no README app-list or route surface change, userspace install/consent tracked under App Runtime epic #196
* feat(userspace): container app tier - deploy, proxy, lifecycle (App Runtime M4) Lifts the 501 gate on container-type .taosapp packages and wires up the full lifecycle: install registers the app without deploying, enable spins up its backend as a Docker container (512m/1cpu cap, port published to 127.0.0.1 only) and records the runtime location, disable/uninstall tear the container back down. A new proxy route forwards the desktop iframe's requests to the app's recorded 127.0.0.1:<port> location only, stripping hop-by-hop headers and the session cookie, with the same sandbox CSP serve_bundle applies. The web-app static security analyzer is unchanged for web packages and is skipped only for container packages, which ship an opaque image instead of analyzable source. * fix(userspace): proxy cookie scrub must never leak taos_session on parse failure * test(userspace): update stale 501 container-gate assertions for the lifted web-only gate
…1585) createNote() called crypto.randomUUID() unguarded, but that API only exists in secure contexts (https, or localhost) and taOS is normally reached over plain http (e.g. http://taos.local:6969). There it throws inside the click handler, so every "New note" / "Create your first note" button silently does nothing. Fall back to a non-crypto id generator when randomUUID isn't available, matching the guard already used in officestudio/slides/deck.ts for the same reason. Docs-Reviewed: test-only addition under desktop/src/apps/__tests__/, no app added/removed and no user-facing behavior to document beyond the bug fix itself
…1586) * feat(logs): system-logs API for the Logs app Add tinyagentos/routes/system_logs.py exposing the operator log viewer backend (#1548 part 1): GET /api/system-logs/sources, /{source} paged read, /{source}/stream SSE tail, and /bundle bug-report payload. Every line is passed through log_redaction.redact before it leaves the box. journald sources go through a fixed unit allowlist and run via asyncio subprocess (argv-list, hard timeouts, output caps, kill on disconnect), never shell=True. Unknown source ids are rejected 400. Routes are admin-session only and registered in routes/__init__. * feat(logs): wire Settings Logs pane to the system-logs API The Settings Logs pane only ever read GET /api/client-logs (browser-side JS errors captured via window.onerror), so a failed backend operation -- an agent deploy, a model install, a provider probe -- never showed up there: those failures are never uncaught JS exceptions, so the pane stayed empty even after real, repeated failures (#1583). Add a source-tabbed layout: the existing client-errors view stays as the default "Client" tab, and every available system-logs source (controller, rkllama, qmd, LLM proxy) that GET /api/system-logs/sources reports gets its own tab backed by GET /api/system-logs/{source} for the last 200 lines plus a Live toggle that opens the SSE tail at /api/system-logs/{source}/stream. Sources are discovered at runtime so an install without journald (or before a service is even installed) just shows the Client tab, unchanged. Docs-Reviewed: tinyagentos/routes/system_logs.py (added in the prior commit) is an internal operator-only API surface with no public contract to document beyond its own module docstring; the Settings pane it feeds is a frontend wiring change, not a new app or route.
…cross assistant/push/webstudio (#1587)
…r download (#1581, #1548) (#1588) Delete was optimistic UI-only: handleDelete just filtered the row out of local state and never called the backend, so the file reappeared on reopen. DELETE /api/models/{model_id} already existed but nothing wired to it — the gap was that downloaded_files entries only carried a filename, not the catalog model id the endpoint needs, so there was no way for the frontend to know what to call. Backend now attaches model_id to each downloaded_files entry when its filename matches a manifest variant's naming convention; handleDelete calls DELETE with it and only drops the row after the backend confirms, surfacing an error and keeping the row on failure. The download auto-refresh remainder (#1548) was a race, not a missing refetch: fetchModels() already re-fetched /api/models on download complete, but with no sequence guard a slower, earlier in-flight call (e.g. the initial mount fetch) could resolve after a newer one and clobber the just-installed state back to "not downloaded" — exactly what stuck until a manual reopen (a fresh mount only issues one fetch, so there's nothing to race). fetchModels now tags each call with a sequence number and discards any response that isn't from the latest call, mirroring the same stale-response guard DownloadProgress's poll() already used. Docs-Reviewed: ModelsApp.test.tsx is a test-only addition alongside the existing ModelsApp.download.test.tsx, no app added/removed and no user-facing behavior to document beyond the bug fixes themselves
…image + notifies (#1582) (#1590) Installing an agent-framework manifest (hermes/openclaw, method: script) fell through _legacy_install's default case, which marked it "installed" without running anything -- their install.sh is written to run once inside a fresh per-agent LXC container at deploy time (see deployer.py), not on the controller host at Store-install time. Running it here would pollute the host instead of a container that doesn't exist yet. "Installing" a framework now means: enable it for deploy (mark installed in the registry/store), kick off a background prefetch of its dedicated base image via ensure_image_present when one exists (trackable through the existing GET /api/agent-image/status), and post an actionable notification that opens the Agents app. Prefetch failure is non-fatal -- the framework stays enabled and a first deploy falls back to a cold image build. Also fixed the same silent-success bug for non-framework method: script manifests (ollama, tailscale, ...) -- they now actually run through ScriptInstaller and return an explicit error instead of a false "installed" when the script is missing or fails. Frontend: the Store's agent-framework cards poll real base-image prefetch progress instead of showing instant completion, and the installed-state action button now opens the Agents app instead of being a no-op. Docs-Reviewed: no new app or route was added -- only a test file landed under desktop/src/apps/StoreApp/, so README.md's app inventory is unaffected.
…ework install, model delete, providers, logs, text editor) (#1591)
Qodo reviews are paused for this user.Troubleshooting steps vary by plan Learn more → On a Teams plan? Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center? |
|
👋 Thanks for the PR! This one targets See CONTRIBUTING.md for the branch model. |
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (2)
📒 Files selected for processing (42)
📝 WalkthroughWalkthroughThis PR adds a container app tier (per-app localhost-bound Docker containers with a reverse proxy), install-time permission consent for userspace apps with a local package importer, backend-driven agent-framework installs with base-image prefetch tracking, real backend-driven model deletion and stale-refresh fixes, a system logs viewer with live tail, a shared secure-context-safe ID generator adopted across several files, and a version bump. ChangesContainer App Tier
Install-time Permission Consent
Agent-Framework Install
Model Deletion and Refresh Fixes
System Logs Viewer
ID Generation Hardening
Version Bump
Estimated code review effort: 4 (Complex) | ~75 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant UserspaceAppsRoute
participant ContainerBackend
Client->>UserspaceAppsRoute: GET /api/userspace-apps/{app_id}/proxy/{path}
UserspaceAppsRoute->>UserspaceAppsRoute: scrub taos_session cookie, filter headers
UserspaceAppsRoute->>ContainerBackend: forward request to recorded host:port
ContainerBackend-->>UserspaceAppsRoute: response
UserspaceAppsRoute-->>Client: streamed response with CSP header
sequenceDiagram
participant User
participant ImportAppButton
participant Backend
participant PermissionConsent
User->>ImportAppButton: select .taosapp/.zip
ImportAppButton->>Backend: installUserspaceApp(file)
Backend-->>ImportAppButton: install result + required permissions
alt permissions required
ImportAppButton->>PermissionConsent: show consent dialog
User->>PermissionConsent: Allow or Deny
PermissionConsent->>Backend: POST /permissions (granted)
end
ImportAppButton->>ImportAppButton: emit USERSPACE_APPS_CHANGED
sequenceDiagram
participant StoreAppUI
participant InstallV2Route
participant AgentImagePrefetch
StoreAppUI->>InstallV2Route: POST /api/store/install-v2
InstallV2Route->>AgentImagePrefetch: start supervised prefetch (if dedicated base)
InstallV2Route-->>StoreAppUI: { installed: true, prefetch }
loop while prefetch active
StoreAppUI->>AgentImagePrefetch: GET /api/agent-image/status
AgentImagePrefetch-->>StoreAppUI: status
end
StoreAppUI->>StoreAppUI: dispatch taos:open-app { app: "agents" }
Possibly related PRs
Suggested reviewers: ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
| except httpx.TimeoutException: | ||
| return JSONResponse({"error": f"app '{app_id}' timed out"}, status_code=504) | ||
|
|
||
| resp_headers = _filter_proxy_headers(dict(upstream_resp.headers)) |
There was a problem hiding this comment.
CRITICAL: Set-Cookie from the upstream container backend is forwarded to the user agent. _filter_proxy_headers only drops hop-by-hop, authorization, and scrubs Cookie headers from the request direction -- it has no rule for response Set-Cookie. An untrusted (potentially compromised or simply buggy) container backend can therefore set arbitrary cookies on the user's browser, including one named taos_session, overwriting the controller's session credential with one the backend chose. Every subsequent request to the controller would then authenticate as that token. Strip set-cookie here (and ideally any response header the backend should not be able to influence), or document why the existing cookie scrub is sufficient in the reverse direction. The whole point of routing container traffic through this proxy is to confine the backend's blast radius, and this undermines that boundary.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
| return JSONResponse( | ||
| {"error": "container app manifest missing or invalid"}, status_code=500 | ||
| ) | ||
| result = await deploy_app_container(app_id, container_spec) |
There was a problem hiding this comment.
WARNING: enable_app has no try/except around await deploy_app_container(...). The function's success branch rolls back set_enabled(False) only on result.get("success") is False. If deploy_app_container raises (DNS/timeout/permission error in the Docker call, anything uncaught in the deploy path), the app is left half-enabled in the store (enabled=True was already persisted on line 331) with container_host/container_port still null, so subsequent calls to /proxy/... 404 until something else clears the flag. Wrap the deploy call in try/except and route any exception through the same 502 + rollback path, or persist the deploy state atomically with the enable state.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
| name = entry.get("name") | ||
| if not name: | ||
| continue | ||
| full = await store.get(name) |
There was a problem hiding this comment.
SUGGESTION: _known_secret_values loops over each entry and awaits store.get(name) sequentially. With up to _MAX_KNOWN_SECRETS (200) entries and one round-trip per entry, this is O(N) sequential awaits on every /sources, /{source}, /bundle, and /stream request. Use asyncio.gather (or a bounded asyncio.Semaphore to avoid hammering the secrets store) to fan out the per-entry lookups in parallel. The early-return guard above already protects against giant stores; this is just about being friendly within the cap.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
| return max(1, min(lines, 2000)) | ||
|
|
||
|
|
||
| async def _list_sources(request: Request) -> "list[dict]": |
There was a problem hiding this comment.
SUGGESTION: _list_sources invokes _probe_unit_available (a fresh journalctl subprocess) for each of three units (controller, rkllama, qmd) sequentially inside one /sources request. With journalctl taking a few hundred ms each, the endpoint pays 3x the latency it needs to. Probe them with asyncio.gather (or a small bounded concurrency) and short-circuit when _journalctl_missing() already returned True (the current loop still calls await _probe_unit_available for each unit even after it would be guaranteed to fail).
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
|
|
||
| # Module-level HTTP client for the container-app proxy -- avoids per-request | ||
| # connection churn, mirrors service_proxy.py's pattern. | ||
| _container_proxy_client = httpx.AsyncClient(timeout=60.0) |
There was a problem hiding this comment.
SUGGESTION: _container_proxy_client is a module-level httpx.AsyncClient with no shutdown wiring. Process exit will eventually close it, but FastAPI startup/shutdown events should explicitly await client.aclose() to flush in-flight streams and release sockets cleanly during reloads/restarts. Otherwise an in-flight proxy stream on shutdown can leak a half-closed upstream connection.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
Code Review SummaryStatus: 5 Issues Found | Recommendation: Address before merge Overview
Issue Details (click to expand)CRITICAL
WARNING
SUGGESTION
Files Reviewed (20 files)
Fix these issues in Kilo Cloud Reviewed by minimax-m3 · Input: 109.3K · Output: 9.4K · Cached: 2.4M |
Promotes dev to master for 1.0.0-beta.21.
Summary by Gitar
ReserveHandleCardto the account panel to promote username reservations./api/userspace-apps/{app_id}for direct retrieval of individual app metadata.This will update automatically on new commits.
Summary by CodeRabbit