Skip to content

Framework update — Phase 1 (detect + install, no handoff)#233

Merged
jaylfc merged 31 commits intomasterfrom
feat/framework-update-phase-1
Apr 19, 2026
Merged

Framework update — Phase 1 (detect + install, no handoff)#233
jaylfc merged 31 commits intomasterfrom
feat/framework-update-phase-1

Conversation

@jaylfc
Copy link
Copy Markdown
Owner

@jaylfc jaylfc commented Apr 19, 2026

Summary

Phase 1 of the framework-update feature per docs/superpowers/specs/2026-04-18-framework-update-phase-1-design.md. Adds per-agent manual updates for installed frameworks (openclaw today, generic contract for hermes / smolagents / etc.). Includes a pre-update LXC snapshot with 3-deep retention and a 120s bootstrap-ping deadline. Failure surfaces a red banner with the snapshot name for manual rollback.

Scope

In: new Framework tab in Agent Settings (between Memory and Skills), 4 manifest metadata fields (release_source / release_asset_pattern / install_script / service_name), 7 per-agent state fields, hourly GitHub Releases poll, container-side install script baked into the base image, sidebar dot, Store pill.

Out (separate specs): Phase 2 graceful handoff (pause-and-resume note via taosmd), Phase 3 batch updates + auto-rollback, framework swap UI.

What's new

  • Backend

    • tinyagentos/frameworks.py — manifest registry + validator
    • tinyagentos/github_releases.py — release fetch + pure parser
    • tinyagentos/framework_update.py — orchestrator (snapshot → install → wait-for-bootstrap → verify)
    • tinyagentos/routes/framework.py — GET /api/agents/{slug}/framework, POST /api/agents/{slug}/framework/update, GET /api/frameworks/latest
    • tinyagentos/scripts/taos-framework-update.sh — container-side installer, baked into base image
    • Two-line bump on the existing /api/openclaw/bootstrap handler so the runner can detect when the agent is back
    • auto_update.py poll block runs hourly alongside the existing taOS self-update tick
  • Frontend

    • desktop/src/components/agent-settings/FrameworkTab.tsx with installed/latest rows, Update button, confirmation dialog, live-elapsed Updating banner, failure banner with snapshot name
    • desktop/src/lib/framework-api.ts — small fetch wrappers
    • Sidebar dot on out-of-date agents
    • Store pill on framework cards counting affected agents
    • Desktop bundle rebuilt

Test plan

  • Backend unit + integration tests: 1847 passed (full suite, excl. hardware/e2e)
  • Manifest validation: 5 tests
  • Release parser: 3 tests
  • Snapshot helpers: 4 tests
  • Update runner state machine: 9 tests (snapshot prune, bootstrap-ping wait, start_update happy/error/snapshot-fail/timeout)
  • API: 8 tests (GET / POST / latest, 200/400/404/409 paths)
  • Bootstrap-ping bump: 1 test
  • Polling service: 3 tests
  • Frontend typecheck clean
  • Playwright skeletons in tests/e2e/test_framework_tab.py + test_framework_store_pill.py — selectors carry TODOs to be tuned against live DOM on first real run
  • Manual smoke on Pi: deploy an out-of-date agent, run update, verify bridge reconnects and /opt/taos/framework.version updates (queued post-merge)

Notes

  • Spec lives at docs/superpowers/specs/2026-04-18-framework-update-phase-1-design.md (gitignored per project policy — local only).
  • The container-side image-bake step (Task 9.1) launches a temporary container after first import to inject the install script, then republishes the image. ~5s overhead on first boot, fully non-fatal.
  • Closes the architectural gap that previously had no per-agent update path. Phase 2 (graceful handoff) and Phase 3 (batch + auto-rollback) build on this foundation.

Summary by CodeRabbit

Release Notes

  • New Features

    • Framework update management: Detect available updates for agent frameworks with visual indicators and one-click update capability.
    • File import tool for uploading and embedding documents into agents.
    • Enhanced Store app with improved app browsing and installation workflow.
  • Chores

    • Updated application bundles and dependencies to improve performance and stability.

jaylfc added 23 commits April 19, 2026 01:08
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 19, 2026

Warning

Rate limit exceeded

@jaylfc has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 17 minutes and 7 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 17 minutes and 7 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 069a18ab-6372-4538-9f16-e4bf76700c8c

📥 Commits

Reviewing files that changed from the base of the PR and between a36837f and ad6c13d.

📒 Files selected for processing (8)
  • tinyagentos/bridge_session.py
  • tinyagentos/frameworks.py
  • tinyagentos/scripts/install_hermes.sh
  • tinyagentos/scripts/install_langroid.sh
  • tinyagentos/scripts/install_openai-agents-sdk.sh
  • tinyagentos/scripts/install_openai_agents_sdk.sh
  • tinyagentos/scripts/install_pocketflow.sh
  • tinyagentos/scripts/install_smolagents.sh
📝 Walkthrough

Walkthrough

Introduces a framework update feature for agents with API helpers, a new FrameworkTab component for managing updates with status polling, integration of update-availability indicators in the AgentsApp and StoreApp, and rebuilding of bundled static assets with updated module hashes.

Changes

Cohort / File(s) Summary
Framework Update Feature
desktop/src/lib/framework-api.ts, desktop/src/components/agent-settings/FrameworkTab.tsx
New API module exporting fetchFrameworkState, startFrameworkUpdate, fetchLatestFrameworks; new FrameworkTab component with polling logic to track update status, elapsed time, and error handling; displays update availability, confirmation modal, and progress/failure UI.
Agent & Store Integration
desktop/src/apps/AgentsApp.tsx, desktop/src/apps/StoreApp.tsx
Extended Agent interface with framework_version_sha; AgentsApp fetches latest frameworks, passes to AgentRow, renders yellow update indicator; added "Framework" tab linking to FrameworkTab; StoreApp computes per-app affected count and renders "Update available" badge in card UI.
Build Configuration
desktop/tsconfig.tsbuildinfo
Updated TypeScript build tracking file list to include new framework-api and FrameworkTab source files.
Bundled Assets Rebuild
static/desktop/assets/*
Rebuilt static assets with updated import hashes for icons (vendor-icons-CiM_hUpN.jsvendor-icons-DcMSPw1y.js), MobileSplitView, tokens, and codemirror modules; removed old StoreApp bundle, added new StoreApp and framework-api bundles; minor internal refactoring in ActivityApp, MemoryApp, TasksApp, GitHubApp, TextEditorApp, and others.
Documentation
docs/superpowers/notes/2026-04-19-multi-framework-chat-routing.md
New architectural note documenting multi-framework chat routing gap, current OpenClaw-only SSE delivery, adapter framework status, concrete implementation on Pi (Hermes installer, framework-specific install scripts, deployer updates), and next steps for wiring adapter startup.

Sequence Diagram

sequenceDiagram
    participant User
    participant FrameworkTab
    participant framework-api
    participant Backend
    participant UI

    User->>FrameworkTab: Load agent framework state
    FrameworkTab->>framework-api: fetchFrameworkState(agent.name)
    framework-api->>Backend: GET /api/agents/{name}/framework
    Backend-->>framework-api: FrameworkState
    framework-api-->>FrameworkTab: FrameworkState
    FrameworkTab->>UI: Render (update available/idle)

    User->>FrameworkTab: Click "Update Framework"
    FrameworkTab->>FrameworkTab: Show confirmation modal
    User->>FrameworkTab: Confirm update
    FrameworkTab->>framework-api: startFrameworkUpdate(agent.name, version)
    framework-api->>Backend: POST /api/agents/{name}/framework/update
    Backend-->>framework-api: Response
    FrameworkTab->>FrameworkTab: Set state to "updating"

    rect rgba(100, 150, 200, 0.5)
    Note over FrameworkTab,UI: Polling every 2 seconds
    loop While updating
        FrameworkTab->>framework-api: fetchFrameworkState(agent.name)
        framework-api->>Backend: GET /api/agents/{name}/framework
        Backend-->>framework-api: FrameworkState (status)
        framework-api-->>FrameworkTab: FrameworkState
        FrameworkTab->>UI: Update elapsed timer & status
    end
    end

    Backend->>Backend: Update completes
    FrameworkTab->>framework-api: Final fetchFrameworkState
    framework-api-->>FrameworkTab: status="idle"
    FrameworkTab->>UI: Render success or failure
    FrameworkTab->>User: Invoke onUpdated callback
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A framework update hops into view,
With polling and progress, forever true,
Agents now wearing their latest attire,
While stores display badges of updates to acquire!
The Pi learns Hermes, adapters take flight,
Multi-framework routing shines ever so bright. 🌟

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 1.06% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Framework update — Phase 1 (detect + install, no handoff)' directly and clearly summarizes the main change: implementing Phase 1 of a framework update feature with detection and installation capabilities, explicitly noting handoff is out of scope.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/framework-update-phase-1

Comment @coderabbitai help to get the list of available commands and usage tips.

<div className="fixed inset-0 bg-black/60 flex items-center justify-center z-50">
<div className="bg-shell-bg border border-white/10 rounded p-4 max-w-sm">
<p className="text-sm mb-3">
Update {agent.name}'s {state.framework} to <code>{state.latest?.tag}</code>?
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: Handle case where state.latest?.tag is null to avoid displaying 'undefined' in the confirmation dialog.

Suggested change
Update {agent.name}'s {state.framework} to <code>{state.latest?.tag}</code>?
Update {agent.name}'s {state.framework} to <code>{state.latest?.tag ?? "latest"}</code>?

@kilo-code-bot
Copy link
Copy Markdown

kilo-code-bot bot commented Apr 19, 2026

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 0
Issue Details (click to expand)

No issues found.

Files Reviewed (13 files)
  • desktop/src/apps/AgentsApp.tsx - 0 issues
  • desktop/src/apps/StoreApp.tsx - 0 issues
  • desktop/src/components/agent-settings/FrameworkTab.tsx - 0 issues
  • desktop/src/lib/framework-api.ts - 0 issues
  • desktop/tsconfig.tsbuildinfo - 0 issues
  • tinyagentos/bridge_session.py - 0 issues
  • tinyagentos/frameworks.py - 0 issues
  • tinyagentos/scripts/install_hermes.sh - 0 issues
  • tinyagentos/scripts/install_langroid.sh - 0 issues
  • tinyagentos/scripts/install_openai-agents-sdk.sh - 0 issues
  • tinyagentos/scripts/install_openai_agents_sdk.sh - 0 issues
  • tinyagentos/scripts/install_pocketflow.sh - 0 issues
  • tinyagentos/scripts/install_smolagents.sh - 0 issues

Fix these issues in Kilo Cloud


Reviewed by grok-code-fast-1:optimized:free · 362,851 tokens

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Nitpick comments (2)
static/desktop/assets/BrowserApp-Bb5zltov.js (1)

1-1: This is minified build output — source files should be reviewed instead.

This file is a bundled/minified JavaScript artifact generated by the build toolchain. Meaningful code review should target the original source files (likely .tsx or .ts in a src/ directory) rather than transpiled output.

If the source files are part of this PR but not included in the review, please add them. If this bundle is committed solely to track build output, consider whether it should be in .gitignore and served from a build pipeline instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@static/desktop/assets/BrowserApp-Bb5zltov.js` at line 1, The reviewed file is
a minified build artifact (BrowserApp exported as we) and should not be reviewed
directly; update the PR to include the original source (e.g., the React/TSX
component that compiles to BrowserApp/we) or remove this bundle from the commit
and add it to .gitignore; if the intent is to ship built artifacts, move them to
a dedicated build/output directory and document the generation step in the CI
pipeline so reviewers can inspect the source (look for the component that
defines useState/useCallback handlers like O, X, te and the exported
BrowserApp/we).
static/desktop/assets/ImportApp-AV3jmR5U.js (1)

1-1: Silent error handling hides upload failures from users.

The upload loop catches errors silently:

try{await fetch("/api/import/upload",{method:"POST",body:n})}catch{}

If an individual file upload fails (network error, server error), the user sees a success message like "Uploaded 3 files" even though some may have failed. Consider tracking failures and reporting them in the status message.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@static/desktop/assets/ImportApp-AV3jmR5U.js` at line 1, The upload loop in
function T silently swallows errors and doesn't check response.ok, causing false
"Uploaded X files" messages; modify T (the async upload function) to track
per-file failures by checking the fetch response.ok and catching exceptions to
increment a failure counter and collect failed file names (use the existing i
array items like a.file/a.name), update progress v as before, and at the end
call o(...) with a final status that includes succeeded vs failed counts and
names (e.g., "Uploaded 2 of 3 files; failed: fileA.txt") and preserve toggling b
(isUploading) and other state updates as currently done.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@desktop/src/components/agent-settings/FrameworkTab.tsx`:
- Around line 32-36: The update call must pass the confirmed tag and mark the UI
as updating immediately: in doUpdate(), before awaiting startFrameworkUpdate,
call the local updater state setter (e.g., setUpdating(true)) so the polling
loop arms, and call startFrameworkUpdate with the confirmed tag (e.g.,
startFrameworkUpdate(agent.name, { targetVersion: state.latest.tag }) or the
equivalent API shape) instead of the current single-arg call; keep existing
error handling (setErr) and ensure setSubmitting(false) and setConfirming(false)
are still run in finally.
- Around line 11-22: The polling useEffect that starts an interval when
state?.update_status === "updating" can keep polling the previous agent because
its dependency array only includes state?.update_status; update the effect to
also depend on agent.name so the interval is torn down when the agent changes
(i.e., modify the dependency array of the useEffect that uses setInterval and
clearInterval to include agent.name), ensuring the existing cleanup runs and
load() will reflect the current agent.name instead of allowing stale responses
to overwrite state.

In `@static/desktop/assets/framework-api-DrEi_Szr.js`:
- Line 1: The helper function s (exported as f) is ignoring the second parameter
(targetVersion) and always POSTs an empty body to /framework/update; update the
implementation of function s(r, t) (the /framework/update caller) to include the
targetVersion in the JSON body (e.g., JSON.stringify({ targetVersion: t })) when
present and handle missing/undefined values appropriately, then rebuild the
desktop bundle so the generated asset reflects the TypeScript source changes.

In `@static/desktop/assets/GitHubApp-CJvVZ0RH.js`:
- Line 1: The release detail component (ze) builds a fallback URL r="#" when
repo is missing and still calls window.open(r, ...); update the Open on GitHub
button in ze to guard against the placeholder by either disabling the button
when r === "#" (like the Save button does) or wrapping the onClick so it only
calls window.open when r !== "#", ensuring no navigation is attempted for the
placeholder URL.
- Line 1: Replace the no-op click handlers on the two Connect CTAs (the button
with aria-label "Connect GitHub account" and the banner "Connect" button inside
ne) so they call a real auth starter instead of ()=>{}; add or call a
startGitHubAuth helper (implement it to open your auth entry point, e.g.
window.open('/api/github/auth/start','_blank') or invoke the Secrets app URI)
and use that function in the onClick handlers; ensure the helper is
imported/defined in this module and keep existing R.authenticated logic intact.
- Line 1: The watched tab never gets data because Z is declared without a setter
(const [Z] = i.useState([])) and the tab-switch logic calls the starred fetch
(I()) for "watched"; update the nt component to use a proper state pair (const
[Z, setZ] = i.useState([])), implement/populate watched repos via a fetch (reuse
or add a fetch function similar to We that calls the watched API) and call that
setter when loading watched repos, and change the tab-switch effect (where n
changes) to call the watched fetch when n==="watched" instead of calling I();
ensure the memo g continues to reference Z for watched filtering.

In `@static/desktop/assets/ImportApp-AV3jmR5U.js`:
- Line 1: The embed POST is sending {agent: r} but the backend expects
agent_name; in function P (the embed handler that does
fetch("/api/import/embed", ... , body:JSON.stringify({agent:r}))), change the
payload key to agent_name (i.e. JSON.stringify({agent_name: r})) so the request
body matches the backend's body.get("agent_name") check and stops returning 400.
- Line 1: The embed request (function P in component Y) currently sends only
{agent: r} but the backend requires a files array; modify the upload flow
(function T and the queued-file state managed by i) to capture successful upload
responses (the returned filename) for each file, store those uploaded filenames
(e.g., add uploadedName or a filesUploaded array in state), and then change P to
include body: JSON.stringify({agent: r, files: [/* list of uploaded filenames
*/]}) so the embed endpoint receives the required files list; ensure T updates
state only after a successful upload and collects those names to be sent by P.

In `@static/desktop/assets/SettingsApp-Dxe4E4Jg.js`:
- Line 1: The default YAML template in function Ce contains embedded literal
line numbers (e.g. "2", "3", …) making the config invalid; replace the initial
useState string in Ce with a clean, valid YAML template (no line-number markers)
that reflects expected keys (e.g. top-level comments and example fields) so
backend saves/loads valid YAML when defaults are used.

---

Nitpick comments:
In `@static/desktop/assets/BrowserApp-Bb5zltov.js`:
- Line 1: The reviewed file is a minified build artifact (BrowserApp exported as
we) and should not be reviewed directly; update the PR to include the original
source (e.g., the React/TSX component that compiles to BrowserApp/we) or remove
this bundle from the commit and add it to .gitignore; if the intent is to ship
built artifacts, move them to a dedicated build/output directory and document
the generation step in the CI pipeline so reviewers can inspect the source (look
for the component that defines useState/useCallback handlers like O, X, te and
the exported BrowserApp/we).

In `@static/desktop/assets/ImportApp-AV3jmR5U.js`:
- Line 1: The upload loop in function T silently swallows errors and doesn't
check response.ok, causing false "Uploaded X files" messages; modify T (the
async upload function) to track per-file failures by checking the fetch
response.ok and catching exceptions to increment a failure counter and collect
failed file names (use the existing i array items like a.file/a.name), update
progress v as before, and at the end call o(...) with a final status that
includes succeeded vs failed counts and names (e.g., "Uploaded 2 of 3 files;
failed: fileA.txt") and preserve toggling b (isUploading) and other state
updates as currently done.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: acbe85b5-d92c-434b-be0a-ea4cf7f5def4

📥 Commits

Reviewing files that changed from the base of the PR and between f979a04 and 3ccd512.

📒 Files selected for processing (84)
  • desktop/src/apps/AgentsApp.tsx
  • desktop/src/apps/StoreApp.tsx
  • desktop/src/components/agent-settings/FrameworkTab.tsx
  • desktop/src/lib/framework-api.ts
  • desktop/tsconfig.tsbuildinfo
  • static/desktop/assets/ActivityApp-DLf5-AAB.js
  • static/desktop/assets/AgentBrowsersApp-wWjBRYht.js
  • static/desktop/assets/AgentsApp-_Qg7-zrP.js
  • static/desktop/assets/BrowserApp-Bb5zltov.js
  • static/desktop/assets/CalendarApp-BJnvuKGY.js
  • static/desktop/assets/ChannelsApp-q8uhDhwu.js
  • static/desktop/assets/ClusterApp-ioljQelH.js
  • static/desktop/assets/ContactsApp-DxPrMLAp.js
  • static/desktop/assets/FilesApp-BTNsw5-U.js
  • static/desktop/assets/GitHubApp-CJvVZ0RH.js
  • static/desktop/assets/ImageViewerApp-DFBYGYf6.js
  • static/desktop/assets/ImagesApp-BE4XWuXd.js
  • static/desktop/assets/ImportApp-AV3jmR5U.js
  • static/desktop/assets/ImportApp-BhVzWoYf.js
  • static/desktop/assets/LibraryApp-NzJAyw3P.js
  • static/desktop/assets/MCPApp-BNKCQ0Yu.js
  • static/desktop/assets/MemoryApp-Bv8OQwsD.js
  • static/desktop/assets/MessagesApp-CpdV4x56.js
  • static/desktop/assets/MobileSplitView-qc4KfHBU.js
  • static/desktop/assets/ModelsApp-DBNNEeby.js
  • static/desktop/assets/ProvidersApp-xjy6jve3.js
  • static/desktop/assets/RedditApp-CkwARPpU.js
  • static/desktop/assets/SecretsApp-DQmL3uER.js
  • static/desktop/assets/SettingsApp-Dxe4E4Jg.js
  • static/desktop/assets/StoreApp-B0Yyd-Nx.js
  • static/desktop/assets/StoreApp-ByYq7iFv.js
  • static/desktop/assets/TasksApp-Cue8cE8j.js
  • static/desktop/assets/TextEditorApp-BY7tHNNz.js
  • static/desktop/assets/XApp-DSy-xMWn.js
  • static/desktop/assets/YouTubeApp-Bv-vMHrm.js
  • static/desktop/assets/chat-CXuhmImJ.js
  • static/desktop/assets/framework-api-DrEi_Szr.js
  • static/desktop/assets/index-AdP8dET7.js
  • static/desktop/assets/index-B1RaCrsP.js
  • static/desktop/assets/index-B4nMaGyz.js
  • static/desktop/assets/index-BWQn2E_V.js
  • static/desktop/assets/index-BihecqOj.js
  • static/desktop/assets/index-BmxogU3l.js
  • static/desktop/assets/index-CEB840_S.js
  • static/desktop/assets/index-CLD8ZaMh.js
  • static/desktop/assets/index-CMdPCfyQ.js
  • static/desktop/assets/index-CU-BnpOm.js
  • static/desktop/assets/index-CxwKt0z8.js
  • static/desktop/assets/index-DRCGGbc1.js
  • static/desktop/assets/index-DRlwiMyJ.js
  • static/desktop/assets/index-DSXyGPzk.js
  • static/desktop/assets/index-E-g1VTjI.js
  • static/desktop/assets/index-gywVfqgD.js
  • static/desktop/assets/index-hX7Cdp4T.js
  • static/desktop/assets/main-DolX-lHu.js
  • static/desktop/assets/main-RoS4E9ey.js
  • static/desktop/assets/tokens-BDvF0_Hi.js
  • static/desktop/assets/tokens-Cgwnj38Z.css
  • static/desktop/assets/tokens-f9GIns3r.css
  • static/desktop/assets/vendor-codemirror-Byxbuxf1.js
  • static/desktop/assets/vendor-icons-DcMSPw1y.js
  • static/desktop/chat.html
  • static/desktop/index.html
  • tests/e2e/test_framework_store_pill.py
  • tests/e2e/test_framework_tab.py
  • tests/test_auto_update_framework.py
  • tests/test_config_normalize.py
  • tests/test_containers_snapshots.py
  • tests/test_framework_api.py
  • tests/test_framework_manifest.py
  • tests/test_framework_update_runner.py
  • tests/test_github_releases.py
  • tests/test_openclaw_bootstrap_ping.py
  • tinyagentos/agent_image.py
  • tinyagentos/app.py
  • tinyagentos/auto_update.py
  • tinyagentos/config.py
  • tinyagentos/containers/__init__.py
  • tinyagentos/framework_update.py
  • tinyagentos/frameworks.py
  • tinyagentos/github_releases.py
  • tinyagentos/routes/framework.py
  • tinyagentos/routes/openclaw.py
  • tinyagentos/scripts/taos-framework-update.sh
💤 Files with no reviewable changes (2)
  • static/desktop/assets/StoreApp-ByYq7iFv.js
  • static/desktop/assets/ImportApp-BhVzWoYf.js

Comment on lines +11 to +22
async function load() {
try { setState(await fetchFrameworkState(agent.name)); setErr(null); }
catch (e: any) { setErr(String(e)); }
}

useEffect(() => { load(); }, [agent.name]);

useEffect(() => {
if (state?.update_status !== "updating") return;
const id = setInterval(() => { load(); }, 2000);
return () => clearInterval(id);
}, [state?.update_status]);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cd desktop/src/components/agent-settings && wc -l FrameworkTab.tsx

Repository: jaylfc/tinyagentos

Length of output: 82


🏁 Script executed:

cd desktop/src/components/agent-settings && head -50 FrameworkTab.tsx

Repository: jaylfc/tinyagentos

Length of output: 2184


🏁 Script executed:

cd desktop/src/components/agent-settings && tail -60 FrameworkTab.tsx

Repository: jaylfc/tinyagentos

Length of output: 2734


🏁 Script executed:

cd desktop/src/components/agent-settings && sed -n '40,106p' FrameworkTab.tsx

Repository: jaylfc/tinyagentos

Length of output: 3063


Add agent.name to the polling effect dependency array to prevent stale requests from previous agents.

The polling effect (lines 18-22) depends only on state?.update_status and will continue running even when the agent changes. If the user switches agents while update_status === "updating", the old agent's polling interval persists and competes with the new agent's load request. Whichever response finishes last will overwrite the current state, potentially showing stale data from the previous agent.

Add agent.name to the dependency array so the interval cleans up when the agent changes:

}, [state?.update_status, agent.name]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@desktop/src/components/agent-settings/FrameworkTab.tsx` around lines 11 - 22,
The polling useEffect that starts an interval when state?.update_status ===
"updating" can keep polling the previous agent because its dependency array only
includes state?.update_status; update the effect to also depend on agent.name so
the interval is torn down when the agent changes (i.e., modify the dependency
array of the useEffect that uses setInterval and clearInterval to include
agent.name), ensuring the existing cleanup runs and load() will reflect the
current agent.name instead of allowing stale responses to overwrite state.

Comment thread desktop/src/components/agent-settings/FrameworkTab.tsx
@@ -0,0 +1 @@
async function o(r){const t=await fetch(`/api/agents/${encodeURIComponent(r)}/framework`);if(!t.ok)throw new Error(`framework fetch ${t.status}`);return t.json()}async function s(r,t){const e=await fetch(`/api/agents/${encodeURIComponent(r)}/framework/update`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({})});if(!e.ok){const a=await e.json().catch(()=>({}));throw new Error(a.error||`update start ${e.status}`)}}async function n(r=!1){const t=await fetch(`/api/frameworks/latest${r?"?refresh=true":""}`);if(!t.ok)throw new Error(`latest frameworks ${t.status}`);return t.json()}export{n as a,o as f,s};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Rebuild the desktop bundle — this helper still discards targetVersion.

The generated asset accepts a second parameter but always POSTs {} to /framework/update. That means the shipped desktop bundle will ignore the version pinning supported by desktop/src/lib/framework-api.ts, so runtime behavior will diverge from the TypeScript source until this bundle is regenerated.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@static/desktop/assets/framework-api-DrEi_Szr.js` at line 1, The helper
function s (exported as f) is ignoring the second parameter (targetVersion) and
always POSTs an empty body to /framework/update; update the implementation of
function s(r, t) (the /framework/update caller) to include the targetVersion in
the JSON body (e.g., JSON.stringify({ targetVersion: t })) when present and
handle missing/undefined values appropriately, then rebuild the desktop bundle
so the generated asset reflects the TypeScript source changes.

@@ -1 +1 @@
import{r as i,j as e}from"./vendor-react-l6srOxy7.js";import{B as x,I as Be,C as H,a as O,c as E,S as Te,d as Ae,e as Ge,f as M,g as U}from"./toolbar-UW6q5pkx.js";import{M as He}from"./MobileSplitView-D5Xh9lUE.js";import{u as Oe}from"./use-is-mobile-v5lglusa.js";import{aX as y,aY as w,B as ie,aR as oe,aZ as v,a_ as N,am as ce,r as Ee,S as de,D as q,aL as W,a2 as Me,a0 as P,ay as K,a$ as F,aF as xe,ac as Ue}from"./vendor-icons-CiM_hUpN.js";import"./vendor-radix-BhM7AEEG.js";import"./vendor-layout-B-pp9n1f.js";async function k(l,c,s){try{const d=await fetch(l,{...s,headers:{Accept:"application/json",...s==null?void 0:s.headers}});return!d.ok||!(d.headers.get("content-type")??"").includes("application/json")?c:await d.json()}catch{return c}}async function qe(l,c,s){return k(l,s,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(c)})}async function We(l){const s=new URLSearchParams().toString(),d=`/api/github/starred${s?`?${s}`:""}`,n=await k(d,{repos:[],total:0});return{repos:Array.isArray(n.repos)?n.repos:[],total:n.total??0}}async function Pe(){const l=await k("/api/github/notifications",{notifications:[],unread_count:0});return{notifications:Array.isArray(l.notifications)?l.notifications:[],unread_count:l.unread_count??0}}async function Ke(l,c){try{const s=await fetch(`/api/github/repo/${encodeURIComponent(l)}/${encodeURIComponent(c)}`,{headers:{Accept:"application/json"}});return!s.ok||!(s.headers.get("content-type")??"").includes("application/json")?null:await s.json()}catch{return null}}async function Fe(l,c,s){try{const d=await fetch(`/api/github/repo/${encodeURIComponent(l)}/${encodeURIComponent(c)}/issues/${s}`,{headers:{Accept:"application/json"}});return!d.ok||!(d.headers.get("content-type")??"").includes("application/json")?null:await d.json()}catch{return null}}async function Ve(l,c){const s=await k(`/api/github/repo/${encodeURIComponent(l)}/${encodeURIComponent(c)}/releases`,{releases:[]});return Array.isArray(s.releases)?s.releases:[]}async function Je(){return k("/api/github/auth/status",{authenticated:!1})}async function Ye(l){return qe("/api/knowledge/ingest",{url:l,title:"",text:"",categories:[],source:"github-browser"},null)}const j=l=>{if(!l)return"";const c=new Date(l),s=(Date.now()-c.getTime())/1e3;return s<60?"just now":s<3600?`${Math.floor(s/60)}m ago`:s<86400?`${Math.floor(s/3600)}h ago`:s<604800?`${Math.floor(s/86400)}d ago`:c.toLocaleDateString()},Xe=l=>l<1024?`${l} B`:l<1048576?`${(l/1024).toFixed(1)} KB`:`${(l/1048576).toFixed(1)} MB`,he=l=>l==="open"?"bg-green-500/15 text-green-400 border-green-500/30":l==="closed"?"bg-red-500/15 text-red-400 border-red-500/30":l==="merged"?"bg-slate-500/15 text-slate-400 border-slate-500/30":"bg-white/10 text-shell-text-tertiary border-white/10";function Ze({comment:l,depth:c=0}){const[s,d]=i.useState(c>=3);return e.jsxs("div",{className:`border-l-2 ${c===0?"border-white/10":"border-white/5"} pl-3 py-1`,style:{marginLeft:c>0?`${c*12}px`:0},children:[e.jsxs("div",{className:"flex items-center gap-2 mb-1",children:[e.jsx("span",{className:"text-xs font-medium text-shell-text-secondary",children:l.author}),e.jsx("span",{className:"text-[10px] text-shell-text-tertiary",children:j(l.created_at)}),c>=3&&e.jsx("button",{className:"text-[10px] text-accent hover:underline ml-1",onClick:()=>d(n=>!n),"aria-expanded":!s,"aria-label":s?"Expand comment":"Collapse comment",children:s?"expand":"collapse"})]}),!s&&e.jsxs(e.Fragment,{children:[e.jsx("p",{className:"text-xs text-shell-text-secondary whitespace-pre-wrap leading-relaxed mb-1",children:l.body}),Object.keys(l.reactions??{}).length>0&&e.jsx("div",{className:"flex gap-1.5 flex-wrap mb-1",children:Object.entries(l.reactions).map(([n,u])=>u>0?e.jsxs("span",{className:"px-1.5 py-0.5 rounded bg-white/5 border border-white/10 text-[10px] text-shell-text-secondary","aria-label":`${n}: ${u}`,children:[n," ",u]},n):null)})]})]})}function nt({windowId:l}){const[,c]=i.useState("list"),[s,d]=i.useState(null),[n,u]=i.useState("starred"),[S,V]=i.useState("repos"),[pe,ue]=i.useState(null),[J,be]=i.useState([]),[Y,me]=i.useState([]),[C,fe]=i.useState(0),[X]=i.useState([]),[Z,$]=i.useState(!0),[p,D]=i.useState(""),[Q,z]=i.useState(!1),[ge,ee]=i.useState([]),[je,te]=i.useState(!1),[m,se]=i.useState(!1),[h,L]=i.useState(!1),[R,ye]=i.useState({authenticated:!1}),f=Oe(),ae=i.useCallback(async()=>{const t=await Je();ye(t)},[]),I=i.useCallback(async()=>{$(!0);const t=await We();be(t.repos),$(!1)},[]),_=i.useCallback(async()=>{$(!0);const t=await Pe();me(t.notifications),fe(t.unread_count),$(!1)},[]);i.useEffect(()=>{ae(),I(),_()},[ae,I,_]),i.useEffect(()=>{c("list"),d(null),D(""),n==="starred"||n==="watched"?I():n==="notifications"&&_()},[n,I,_]);const B=i.useCallback(async t=>{c("detail"),d({type:"repo",repo:t}),L(!1),te(!1),z(!0);const[a,r]=await Promise.all([Ve(t.owner,t.name),Ke(t.owner,t.name)]);ee(a),r&&d({type:"repo",repo:r}),z(!1)},[]),T=i.useCallback(async t=>{c("detail"),d({type:"issue",issue:t}),L(!1),z(!0);const[a,r]=t.repo.split("/");if(a&&r){const o=await Fe(a,r,t.number);o&&d({type:"issue",issue:o})}z(!1)},[]),re=i.useCallback((t,a)=>{c("detail"),d({type:"release",release:{...t,repo:a}}),L(!1)},[]),b=i.useCallback(()=>{c("list"),d(null),ee([])},[]),le=i.useMemo(()=>s?s.type==="repo"&&s.repo?`repo:${s.repo.owner}/${s.repo.name}`:s.type==="issue"&&s.issue?`issue:${s.issue.repo}#${s.issue.number}`:s.type==="release"&&s.release?`release:${s.release.tag}`:null:null,[s]),A=i.useCallback(async t=>{se(!0);const a=await Ye(t);se(!1),a&&L(!0)},[]),g=i.useMemo(()=>n==="starred"||n==="watched"?(n==="watched"?X:J).filter(a=>{var o;if(!p)return!0;const r=p.toLowerCase();return a.name.toLowerCase().includes(r)||a.owner.toLowerCase().includes(r)||((o=a.description)==null?void 0:o.toLowerCase().includes(r))}):n==="notifications"?Y.filter(t=>{if(!p)return!0;const a=p.toLowerCase();return t.title.toLowerCase().includes(a)||t.repo.toLowerCase().includes(a)}):[],[n,J,X,Y,p]),we=e.jsxs("nav",{className:"w-52 shrink-0 border-r border-white/5 bg-shell-surface/30 flex flex-col overflow-hidden","aria-label":"GitHub Browser navigation",children:[e.jsxs("div",{className:"flex items-center gap-2 px-3 py-3 border-b border-white/5 shrink-0",children:[e.jsx(y,{size:15,className:"text-accent","aria-hidden":"true"}),e.jsx("h1",{className:"text-sm font-semibold",children:"GitHub"})]}),e.jsxs("div",{className:"flex-1 overflow-y-auto p-2 space-y-4",children:[e.jsx("section",{"aria-label":"Sections",children:e.jsxs("div",{className:"space-y-0.5",children:[e.jsxs(x,{variant:n==="starred"?"secondary":"ghost",size:"sm","aria-pressed":n==="starred",onClick:()=>u("starred"),className:"w-full justify-start text-xs h-7 px-2 gap-1.5",children:[e.jsx(w,{size:11,"aria-hidden":"true"}),"Starred Repos"]}),e.jsxs(x,{variant:n==="notifications"?"secondary":"ghost",size:"sm","aria-pressed":n==="notifications",onClick:()=>u("notifications"),className:"w-full justify-between text-xs h-7 px-2",children:[e.jsxs("span",{className:"flex items-center gap-1.5",children:[e.jsx(ie,{size:11,"aria-hidden":"true"}),"Notifications"]}),C>0&&e.jsx("span",{className:"px-1.5 py-0.5 rounded-full bg-accent text-white text-[10px] tabular-nums","aria-label":`${C} unread`,children:C})]}),e.jsxs(x,{variant:n==="watched"?"secondary":"ghost",size:"sm","aria-pressed":n==="watched",onClick:()=>u("watched"),className:"w-full justify-start text-xs h-7 px-2 gap-1.5",children:[e.jsx(oe,{size:11,"aria-hidden":"true"}),"Watched"]})]})}),e.jsxs("section",{"aria-label":"Content type",children:[e.jsx("p",{className:"text-[10px] uppercase tracking-wider text-shell-text-tertiary px-2 mb-1.5",children:"Content"}),e.jsx("div",{className:"space-y-0.5",children:[{id:"repos",label:"Repos",icon:y},{id:"issues",label:"Issues",icon:v},{id:"prs",label:"Pull Requests",icon:N},{id:"releases",label:"Releases",icon:ce}].map(({id:t,label:a,icon:r})=>e.jsxs(x,{variant:S===t?"secondary":"ghost",size:"sm","aria-pressed":S===t,onClick:()=>V(t),className:"w-full justify-start text-xs h-7 px-2 gap-1.5",children:[e.jsx(r,{size:11,"aria-hidden":"true"}),a]},t))})]}),e.jsxs("section",{"aria-label":"Status filter",children:[e.jsx("p",{className:"text-[10px] uppercase tracking-wider text-shell-text-tertiary px-2 mb-1.5",children:"Status"}),e.jsx("div",{className:"space-y-0.5",children:["open","closed","merged"].map(t=>{const a=pe===t;return e.jsx(x,{variant:a?"secondary":"ghost",size:"sm","aria-pressed":a,onClick:()=>ue(r=>r===t?null:t),className:"w-full justify-start text-xs h-7 px-2 capitalize",children:t},t)})})]})]}),e.jsx("div",{className:"shrink-0 border-t border-white/5 px-3 py-2",children:R.authenticated?e.jsxs("div",{className:"space-y-0.5",children:[e.jsx("p",{className:"text-[10px] text-shell-text-tertiary capitalize",children:R.method??"connected"}),e.jsxs("p",{className:"text-xs text-shell-text-secondary truncate",children:["@",R.username]})]}):e.jsx("button",{className:"text-xs text-accent hover:underline",onClick:()=>{},"aria-label":"Connect GitHub account",children:"Connect GitHub"})})]}),ne=R.authenticated?null:e.jsxs("div",{className:"flex items-center gap-3 px-4 py-2 bg-amber-500/10 border-b border-amber-500/20 text-xs text-amber-300 shrink-0",role:"banner","aria-label":"GitHub authentication notice",children:[e.jsx(Ee,{size:13,"aria-hidden":"true"}),e.jsx("span",{children:"Connect GitHub for starred repos and notifications."}),e.jsx("button",{className:"ml-auto underline hover:text-amber-200","aria-label":"Open Secrets app to connect GitHub",children:"Connect"})]}),ve=t=>e.jsxs(H,{className:"cursor-pointer hover:border-white/15 transition-colors",onClick:()=>B(t),onKeyDown:a=>{(a.key==="Enter"||a.key===" ")&&(a.preventDefault(),B(t))},tabIndex:0,role:"button","aria-label":`Open ${t.owner}/${t.name}`,children:[e.jsxs(O,{className:"pb-1 p-3",children:[e.jsxs("div",{className:"flex items-start justify-between gap-2",children:[e.jsxs("h3",{className:"text-sm font-medium leading-snug",children:[e.jsxs("span",{className:"text-shell-text-tertiary",children:[t.owner,"/"]}),t.name]}),t.language&&e.jsx("span",{className:"shrink-0 text-[10px] px-1.5 py-0.5 rounded bg-accent/10 text-accent border border-accent/20",children:t.language})]}),t.description&&e.jsx("p",{className:"text-[11px] text-shell-text-secondary line-clamp-1 leading-relaxed mt-0.5",children:t.description})]}),e.jsx(E,{className:"pt-0 px-3 pb-3",children:e.jsxs("div",{className:"flex items-center gap-3 text-[10px] text-shell-text-tertiary",children:[e.jsxs("span",{className:"flex items-center gap-1","aria-label":`${t.stars} stars`,children:[e.jsx(w,{size:10,"aria-hidden":"true"}),t.stars.toLocaleString()]}),e.jsxs("span",{className:"flex items-center gap-1","aria-label":`${t.forks} forks`,children:[e.jsx(W,{size:10,"aria-hidden":"true"}),t.forks.toLocaleString()]}),e.jsx("span",{className:"ml-auto",children:j(t.updated_at)})]})})]},`${t.owner}/${t.name}`),Ne=t=>e.jsxs(H,{className:"cursor-pointer hover:border-white/15 transition-colors",onClick:()=>T(t),onKeyDown:a=>{(a.key==="Enter"||a.key===" ")&&(a.preventDefault(),T(t))},tabIndex:0,role:"button","aria-label":`Open ${t.is_pull_request?"PR":"issue"}: ${t.title}`,children:[e.jsx(O,{className:"pb-1 p-3",children:e.jsxs("div",{className:"flex items-start gap-2",children:[t.is_pull_request?e.jsx(N,{size:13,className:"mt-0.5 shrink-0 text-accent","aria-hidden":"true"}):e.jsx(v,{size:13,className:"mt-0.5 shrink-0 text-green-400","aria-hidden":"true"}),e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsx("h3",{className:"text-sm font-medium leading-snug line-clamp-1",children:t.title}),e.jsx("p",{className:"text-[11px] text-shell-text-tertiary mt-0.5",children:t.repo})]}),e.jsx("span",{className:`shrink-0 text-[10px] px-1.5 py-0.5 rounded border ${he(t.state)}`,"aria-label":`Status: ${t.state}`,children:t.state})]})}),e.jsxs(E,{className:"pt-0 px-3 pb-3 space-y-1.5",children:[t.labels.length>0&&e.jsx("div",{className:"flex flex-wrap gap-1","aria-label":"Labels",children:t.labels.map(a=>e.jsx("span",{className:"px-1.5 py-0.5 rounded bg-white/5 border border-white/10 text-[10px] text-shell-text-secondary",children:a},a))}),e.jsxs("div",{className:"flex items-center gap-3 text-[10px] text-shell-text-tertiary",children:[e.jsxs("span",{className:"flex items-center gap-1",children:[e.jsx(Me,{size:10,"aria-hidden":"true"}),t.comments.length]}),e.jsx("span",{children:t.author}),e.jsx("span",{className:"ml-auto",children:j(t.created_at)})]})]})]},`${t.repo}#${t.number}`),ke=(t,a="")=>e.jsxs(H,{className:"cursor-pointer hover:border-white/15 transition-colors",onClick:()=>re(t,a),onKeyDown:r=>{(r.key==="Enter"||r.key===" ")&&(r.preventDefault(),re(t,a))},tabIndex:0,role:"button","aria-label":`Open release ${t.tag}`,children:[e.jsx(O,{className:"pb-1 p-3",children:e.jsxs("div",{className:"flex items-start justify-between gap-2",children:[e.jsxs("div",{children:[e.jsxs("h3",{className:"text-sm font-medium leading-snug flex items-center gap-1.5",children:[e.jsx(xe,{size:11,"aria-hidden":"true",className:"text-accent"}),t.tag]}),a&&e.jsx("p",{className:"text-[11px] text-shell-text-tertiary mt-0.5",children:a})]}),t.prerelease&&e.jsx("span",{className:"shrink-0 text-[10px] px-1.5 py-0.5 rounded bg-amber-500/15 text-amber-400 border border-amber-500/30",children:"pre-release"})]})}),e.jsx(E,{className:"pt-0 px-3 pb-3",children:e.jsx("p",{className:"text-[10px] text-shell-text-tertiary",children:j(t.published_at)})})]},t.tag),Se=e.jsxs("main",{className:"flex-1 flex flex-col overflow-hidden","aria-label":"GitHub content list",children:[e.jsx("div",{className:"flex items-center gap-2 px-4 py-3 border-b border-white/5 shrink-0",children:e.jsxs("div",{className:"relative flex-1",children:[e.jsx(de,{size:14,className:"absolute left-3 top-1/2 -translate-y-1/2 text-shell-text-tertiary pointer-events-none z-10","aria-hidden":"true"}),e.jsx(Be,{type:"search",value:p,onChange:t=>D(t.target.value),placeholder:"Search…",className:"pl-8 h-8","aria-label":"Search GitHub content"})]})}),e.jsx("div",{className:"flex-1 overflow-y-auto p-3 space-y-2",role:"list","aria-label":"GitHub items",children:Z?e.jsx("div",{className:"flex items-center justify-center h-full text-shell-text-tertiary text-sm",role:"status","aria-live":"polite",children:"Loading…"}):g.length===0?e.jsxs("div",{className:"flex flex-col items-center justify-center h-full gap-3 text-shell-text-tertiary",children:[e.jsx(y,{size:36,className:"opacity-20","aria-hidden":"true"}),e.jsx("p",{className:"text-sm",children:p?"No results for your search":"Nothing here yet"})]}):n==="notifications"?g.map(t=>e.jsx("div",{role:"listitem",children:Ne(t)},`${t.repo}#${t.number}`)):g.map(t=>e.jsx("div",{role:"listitem",children:ve(t)},`${t.owner}/${t.name}`))})]}),Ce=t=>{const a=`https://github.com/${t.owner}/${t.name}`,r=ge[0]??null;return e.jsx("main",{className:"flex-1 flex flex-col overflow-hidden","aria-label":`${t.owner}/${t.name} detail`,children:e.jsxs("div",{className:"flex-1 overflow-y-auto",children:[e.jsxs("div",{className:"px-5 pt-4 pb-3 border-b border-white/5",children:[!f&&e.jsxs(x,{variant:"ghost",size:"sm",onClick:b,className:"text-xs mb-3 -ml-1 text-shell-text-secondary","aria-label":"Back to list",onKeyDown:o=>o.key==="Escape"&&b(),children:[e.jsx(P,{size:14,"aria-hidden":"true"}),"Back"]}),e.jsxs("h2",{className:"text-lg font-semibold leading-snug mb-1",children:[e.jsxs("span",{className:"text-shell-text-tertiary",children:[t.owner,"/"]}),t.name]}),t.description&&e.jsx("p",{className:"text-sm text-shell-text-secondary mb-3",children:t.description}),e.jsxs("div",{className:"flex flex-wrap gap-2 mb-3",children:[e.jsxs("span",{className:"flex items-center gap-1 text-[11px] px-2 py-0.5 rounded bg-white/5 border border-white/10 text-shell-text-secondary","aria-label":`${t.stars} stars`,children:[e.jsx(w,{size:10,"aria-hidden":"true"}),t.stars.toLocaleString()," stars"]}),e.jsxs("span",{className:"flex items-center gap-1 text-[11px] px-2 py-0.5 rounded bg-white/5 border border-white/10 text-shell-text-secondary","aria-label":`${t.forks} forks`,children:[e.jsx(W,{size:10,"aria-hidden":"true"}),t.forks.toLocaleString()," forks"]}),t.language&&e.jsx("span",{className:"text-[11px] px-2 py-0.5 rounded bg-accent/10 text-accent border border-accent/20",children:t.language}),t.license&&e.jsx("span",{className:"text-[11px] px-2 py-0.5 rounded bg-white/5 border border-white/10 text-shell-text-secondary",children:t.license})]}),t.topics.length>0&&e.jsx("div",{className:"flex flex-wrap gap-1 mb-2","aria-label":"Topics",children:t.topics.map(o=>e.jsx("span",{className:"px-1.5 py-0.5 rounded-full bg-blue-500/10 text-blue-400 text-[10px] border border-blue-500/20",children:o},o))})]}),t.readme_content&&e.jsxs("div",{className:"px-5 py-4 border-b border-white/5",children:[e.jsx("h3",{className:"text-xs font-semibold text-shell-text-tertiary uppercase tracking-wider mb-2",children:"README"}),e.jsx("div",{className:"rounded-lg bg-white/[0.02] border border-white/5 p-3 max-h-64 overflow-y-auto",children:e.jsx("pre",{className:"text-xs text-shell-text-secondary whitespace-pre-wrap leading-relaxed font-sans",children:Q?"Loading…":t.readme_content})})]}),r&&e.jsxs("div",{className:"px-5 py-4 border-b border-white/5",children:[e.jsx("h3",{className:"text-xs font-semibold text-shell-text-tertiary uppercase tracking-wider mb-2",children:"Latest Release"}),ke(r,`${t.owner}/${t.name}`)]}),e.jsxs("div",{className:"px-5 py-3 border-b border-white/5 flex items-center justify-between",children:[e.jsx("label",{htmlFor:`monitor-${t.name}`,className:"text-xs text-shell-text-secondary cursor-pointer",children:"Monitor releases"}),e.jsx(Te,{id:`monitor-${t.name}`,checked:je,onCheckedChange:te,"aria-label":"Monitor releases for this repository"})]}),e.jsxs("div",{className:"px-5 py-3 flex flex-wrap gap-2",children:[e.jsxs(x,{size:"sm",variant:"ghost",className:"text-xs gap-1.5",onClick:()=>window.open(a,"_blank","noopener,noreferrer"),"aria-label":"Open on GitHub",children:[e.jsx(K,{size:13,"aria-hidden":"true"}),"Open on GitHub"]}),e.jsxs(x,{size:"sm",variant:h?"secondary":"outline",className:"text-xs gap-1.5",onClick:()=>A(a),disabled:m||h,"aria-label":h?"Saved to library":"Save to Library",children:[e.jsx(F,{size:13,"aria-hidden":"true"}),h?"Saved":m?"Saving…":"Save to Library"]})]})]})})},$e=t=>{const a=`https://github.com/${t.repo}/${t.is_pull_request?"pull":"issues"}/${t.number}`;return e.jsx("main",{className:"flex-1 flex flex-col overflow-hidden","aria-label":`Issue ${t.number} detail`,children:e.jsxs("div",{className:"flex-1 overflow-y-auto",children:[e.jsxs("div",{className:"px-5 pt-4 pb-3 border-b border-white/5",children:[!f&&e.jsxs(x,{variant:"ghost",size:"sm",onClick:b,className:"text-xs mb-3 -ml-1 text-shell-text-secondary","aria-label":"Back to list",onKeyDown:r=>r.key==="Escape"&&b(),children:[e.jsx(P,{size:14,"aria-hidden":"true"}),"Back"]}),e.jsxs("div",{className:"flex items-start gap-2 mb-2",children:[t.is_pull_request?e.jsx(N,{size:16,className:"mt-0.5 shrink-0 text-accent","aria-hidden":"true"}):e.jsx(v,{size:16,className:"mt-0.5 shrink-0 text-green-400","aria-hidden":"true"}),e.jsx("h2",{className:"text-base font-semibold leading-snug flex-1",children:t.title}),e.jsx("span",{className:`shrink-0 text-[10px] px-1.5 py-0.5 rounded border ${he(t.state)}`,"aria-label":`Status: ${t.state}`,children:t.state})]}),e.jsxs("p",{className:"text-xs text-shell-text-tertiary mb-2",children:[t.repo," · ",t.author," · ",j(t.created_at)]}),t.labels.length>0&&e.jsx("div",{className:"flex flex-wrap gap-1 mb-2","aria-label":"Labels",children:t.labels.map(r=>e.jsx("span",{className:"px-1.5 py-0.5 rounded bg-white/5 border border-white/10 text-[10px] text-shell-text-secondary",children:r},r))})]}),e.jsx("div",{className:"px-5 py-3 flex-1",children:e.jsxs(Ae,{defaultValue:"discussion",children:[e.jsxs(Ge,{children:[e.jsx(M,{value:"discussion",children:"Discussion"}),e.jsx(M,{value:"history",children:"History"}),e.jsx(M,{value:"metadata",children:"Metadata"})]}),e.jsxs(U,{value:"discussion",children:[t.body&&e.jsx("div",{className:"rounded-lg bg-white/[0.02] border border-white/5 p-3 mb-3 mt-3",children:e.jsx("p",{className:"text-xs text-shell-text-secondary whitespace-pre-wrap leading-relaxed",children:Q?"Loading…":t.body})}),t.comments.length>0&&e.jsxs("div",{className:"space-y-2 mt-2","aria-label":"Comments",children:[e.jsxs("p",{className:"text-[10px] uppercase tracking-wider text-shell-text-tertiary mb-1",children:[t.comments.length," comment",t.comments.length!==1?"s":""]}),t.comments.map((r,o)=>e.jsx(Ze,{comment:r,depth:0},o))]})]}),e.jsx(U,{value:"history",children:e.jsx("div",{className:"mt-3 text-xs text-shell-text-tertiary italic",children:"Issue history not available in this view."})}),e.jsx(U,{value:"metadata",children:e.jsx("div",{className:"mt-3 space-y-2",children:[{label:"Number",value:`#${t.number}`},{label:"State",value:t.state},{label:"Author",value:t.author},{label:"Repo",value:t.repo},{label:"Type",value:t.is_pull_request?"Pull Request":"Issue"},{label:"Created",value:t.created_at}].map(({label:r,value:o})=>e.jsxs("div",{className:"flex justify-between text-xs",children:[e.jsx("span",{className:"text-shell-text-tertiary",children:r}),e.jsx("span",{className:"text-shell-text-secondary",children:o})]},r))})})]})}),e.jsxs("div",{className:"px-5 py-3 flex flex-wrap gap-2 border-t border-white/5",children:[e.jsxs(x,{size:"sm",variant:"ghost",className:"text-xs gap-1.5",onClick:()=>window.open(a,"_blank","noopener,noreferrer"),"aria-label":"Open on GitHub",children:[e.jsx(K,{size:13,"aria-hidden":"true"}),"Open on GitHub"]}),e.jsxs(x,{size:"sm",variant:h?"secondary":"outline",className:"text-xs gap-1.5",onClick:()=>A(a),disabled:m||h,"aria-label":h?"Saved to library":"Save to Library",children:[e.jsx(F,{size:13,"aria-hidden":"true"}),h?"Saved":m?"Saving…":"Save to Library"]})]})]})})},ze=t=>{const a=t.repo??"",r=a?`https://github.com/${a}/releases/tag/${encodeURIComponent(t.tag)}`:"#";return e.jsx("main",{className:"flex-1 flex flex-col overflow-hidden","aria-label":`Release ${t.tag} detail`,children:e.jsxs("div",{className:"flex-1 overflow-y-auto",children:[e.jsxs("div",{className:"px-5 pt-4 pb-3 border-b border-white/5",children:[!f&&e.jsxs(x,{variant:"ghost",size:"sm",onClick:b,className:"text-xs mb-3 -ml-1 text-shell-text-secondary","aria-label":"Back to list",onKeyDown:o=>o.key==="Escape"&&b(),children:[e.jsx(P,{size:14,"aria-hidden":"true"}),"Back"]}),e.jsxs("div",{className:"flex items-start gap-2 mb-1",children:[e.jsx(xe,{size:16,className:"mt-0.5 shrink-0 text-accent","aria-hidden":"true"}),e.jsx("h2",{className:"text-lg font-semibold leading-snug",children:t.tag}),t.prerelease&&e.jsx("span",{className:"text-[10px] px-1.5 py-0.5 rounded bg-amber-500/15 text-amber-400 border border-amber-500/30",children:"pre-release"})]}),a&&e.jsx("p",{className:"text-xs text-shell-text-tertiary mb-1",children:a}),e.jsxs("p",{className:"text-xs text-shell-text-tertiary",children:[t.author," · ",j(t.published_at)]})]}),t.body&&e.jsxs("div",{className:"px-5 py-4 border-b border-white/5",children:[e.jsx("h3",{className:"text-xs font-semibold text-shell-text-tertiary uppercase tracking-wider mb-2",children:"Release Notes"}),e.jsx("pre",{className:"text-xs text-shell-text-secondary whitespace-pre-wrap leading-relaxed font-sans",children:t.body})]}),t.assets.length>0&&e.jsxs("div",{className:"px-5 py-4 border-b border-white/5",children:[e.jsxs("h3",{className:"text-xs font-semibold text-shell-text-tertiary uppercase tracking-wider mb-2",children:["Assets (",t.assets.length,")"]}),e.jsx("div",{className:"space-y-1.5",role:"list","aria-label":"Release assets",children:t.assets.map(o=>e.jsxs("div",{className:"flex items-center gap-3 px-3 py-2 rounded-lg bg-white/[0.02] border border-white/5 text-xs",role:"listitem",children:[e.jsx(Ue,{size:11,"aria-hidden":"true",className:"text-shell-text-tertiary shrink-0"}),e.jsx("span",{className:"flex-1 truncate text-shell-text-secondary font-mono",children:o.name}),e.jsx("span",{className:"text-shell-text-tertiary shrink-0",children:Xe(o.size)}),e.jsxs("span",{className:"text-shell-text-tertiary shrink-0","aria-label":`${o.download_count} downloads`,children:[o.download_count.toLocaleString()," dl"]})]},o.name))})]}),e.jsxs("div",{className:"px-5 py-3 flex flex-wrap gap-2",children:[e.jsxs(x,{size:"sm",variant:"ghost",className:"text-xs gap-1.5",onClick:()=>window.open(r,"_blank","noopener,noreferrer"),"aria-label":"Open on GitHub",children:[e.jsx(K,{size:13,"aria-hidden":"true"}),"Open on GitHub"]}),e.jsxs(x,{size:"sm",variant:h?"secondary":"outline",className:"text-xs gap-1.5",onClick:()=>A(r),disabled:m||h||r==="#","aria-label":h?"Saved to library":"Save to Library",children:[e.jsx(F,{size:13,"aria-hidden":"true"}),h?"Saved":m?"Saving…":"Save to Library"]})]})]})})},Le=s?s.type==="repo"&&s.repo?Ce(s.repo):s.type==="issue"&&s.issue?$e(s.issue):s.type==="release"&&s.release?ze(s.release):null:null,Re=i.useMemo(()=>s?s.type==="repo"&&s.repo?`${s.repo.owner}/${s.repo.name}`:s.type==="issue"&&s.issue?s.issue.title:s.type==="release"&&s.release?s.release.tag:"":"",[s]),Ie=!f||le===null,_e=e.jsxs("div",{style:{display:"flex",flexDirection:"column",height:"100%"},children:[ne,e.jsx("div",{style:{padding:"8px 0 4px",borderBottom:"1px solid rgba(255,255,255,0.05)",flexShrink:0},children:e.jsx("div",{style:{margin:"0 12px",borderRadius:16,background:"rgba(255,255,255,0.05)",border:"1px solid rgba(255,255,255,0.08)",overflow:"hidden"},children:[{id:"starred",label:"Starred Repos",icon:w,badge:null},{id:"notifications",label:"Notifications",icon:ie,badge:C},{id:"watched",label:"Watched",icon:oe,badge:null}].map(({id:t,label:a,icon:r,badge:o},G,De)=>e.jsxs("button",{type:"button",onClick:()=>u(t),"aria-pressed":n===t,"aria-label":a,style:{display:"flex",alignItems:"center",gap:10,width:"100%",padding:"14px 16px",background:n===t?"rgba(255,255,255,0.08)":"none",border:"none",borderBottom:G===De.length-1?"none":"1px solid rgba(255,255,255,0.06)",cursor:"pointer",color:"inherit",textAlign:"left"},children:[e.jsx(r,{size:15,style:{color:"rgba(255,255,255,0.6)",flexShrink:0},"aria-hidden":"true"}),e.jsx("span",{style:{flex:1,fontSize:15,fontWeight:500,color:"rgba(255,255,255,0.9)"},children:a}),o!=null&&o>0&&e.jsx("span",{style:{fontSize:11,padding:"1px 7px",borderRadius:20,background:"var(--accent, #7c6be8)",color:"#fff",fontWeight:600},"aria-label":`${o} unread`,children:o}),e.jsx(q,{size:14,style:{color:"rgba(255,255,255,0.3)",flexShrink:0},"aria-hidden":"true"})]},t))})}),e.jsxs("div",{style:{padding:"8px 0 4px",borderBottom:"1px solid rgba(255,255,255,0.05)",flexShrink:0},children:[e.jsx("div",{style:{fontSize:12,textTransform:"uppercase",letterSpacing:.5,color:"rgba(255,255,255,0.45)",padding:"0 20px 6px",fontWeight:600},children:"Content"}),e.jsx("div",{style:{margin:"0 12px",borderRadius:16,background:"rgba(255,255,255,0.05)",border:"1px solid rgba(255,255,255,0.08)",overflow:"hidden"},children:[{id:"repos",label:"Repos",icon:y},{id:"issues",label:"Issues",icon:v},{id:"prs",label:"Pull Requests",icon:N},{id:"releases",label:"Releases",icon:ce}].map(({id:t,label:a,icon:r},o,G)=>e.jsxs("button",{type:"button",onClick:()=>V(t),"aria-pressed":S===t,"aria-label":a,style:{display:"flex",alignItems:"center",gap:10,width:"100%",padding:"12px 16px",background:S===t?"rgba(255,255,255,0.08)":"none",border:"none",borderBottom:o===G.length-1?"none":"1px solid rgba(255,255,255,0.06)",cursor:"pointer",color:"inherit",textAlign:"left"},children:[e.jsx(r,{size:14,style:{color:"rgba(255,255,255,0.6)",flexShrink:0},"aria-hidden":"true"}),e.jsx("span",{style:{flex:1,fontSize:14,color:"rgba(255,255,255,0.85)"},children:a})]},t))})]}),e.jsxs("div",{style:{flex:1,overflowY:"auto",padding:"8px 0 16px"},children:[e.jsx("div",{style:{fontSize:12,textTransform:"uppercase",letterSpacing:.5,color:"rgba(255,255,255,0.45)",padding:"4px 20px 8px",fontWeight:600},children:n==="notifications"?"Notifications":n==="watched"?"Watched":"Starred"}),e.jsx("div",{style:{padding:"0 12px 8px"},children:e.jsxs("div",{style:{position:"relative"},children:[e.jsx(de,{size:13,style:{position:"absolute",left:10,top:"50%",transform:"translateY(-50%)",color:"rgba(255,255,255,0.4)",pointerEvents:"none"},"aria-hidden":"true"}),e.jsx("input",{type:"search",value:p,onChange:t=>D(t.target.value),placeholder:"Search…","aria-label":"Search GitHub content",style:{width:"100%",padding:"8px 12px 8px 30px",borderRadius:10,background:"rgba(255,255,255,0.06)",border:"1px solid rgba(255,255,255,0.1)",color:"inherit",fontSize:13,outline:"none",boxSizing:"border-box"}})]})}),Z?e.jsx("div",{style:{padding:"24px 20px",textAlign:"center",fontSize:13,color:"rgba(255,255,255,0.4)"},role:"status","aria-live":"polite",children:"Loading…"}):g.length===0?e.jsx("div",{style:{padding:"32px 20px",textAlign:"center",fontSize:13,color:"rgba(255,255,255,0.4)"},children:p?"No results for your search":"Nothing here yet"}):e.jsx("div",{style:{margin:"0 12px",borderRadius:16,background:"rgba(255,255,255,0.05)",border:"1px solid rgba(255,255,255,0.08)",overflow:"hidden"},role:"list","aria-label":"GitHub items",children:n==="notifications"?g.map((t,a,r)=>e.jsxs("button",{type:"button",role:"listitem",onClick:()=>T(t),"aria-label":`Open ${t.is_pull_request?"PR":"issue"}: ${t.title}`,style:{display:"flex",alignItems:"center",gap:10,width:"100%",padding:"14px 16px",background:"none",border:"none",borderBottom:a===r.length-1?"none":"1px solid rgba(255,255,255,0.06)",cursor:"pointer",color:"inherit",textAlign:"left"},children:[t.is_pull_request?e.jsx(N,{size:13,style:{flexShrink:0,color:"rgba(130,140,255,0.9)"},"aria-hidden":"true"}):e.jsx(v,{size:13,style:{flexShrink:0,color:"rgba(80,200,120,0.9)"},"aria-hidden":"true"}),e.jsxs("div",{style:{flex:1,minWidth:0},children:[e.jsx("div",{style:{fontSize:14,fontWeight:500,color:"rgba(255,255,255,0.9)",overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap",marginBottom:2},children:t.title}),e.jsx("div",{style:{fontSize:12,color:"rgba(255,255,255,0.45)"},children:t.repo})]}),e.jsx(q,{size:14,style:{color:"rgba(255,255,255,0.3)",flexShrink:0},"aria-hidden":"true"})]},`${t.repo}#${t.number}`)):g.map((t,a,r)=>e.jsxs("button",{type:"button",role:"listitem",onClick:()=>B(t),"aria-label":`Open ${t.owner}/${t.name}`,style:{display:"flex",alignItems:"center",gap:10,width:"100%",padding:"14px 16px",background:"none",border:"none",borderBottom:a===r.length-1?"none":"1px solid rgba(255,255,255,0.06)",cursor:"pointer",color:"inherit",textAlign:"left"},children:[e.jsxs("div",{style:{flex:1,minWidth:0},children:[e.jsxs("div",{style:{fontSize:14,fontWeight:600,color:"rgba(255,255,255,0.95)",overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap",marginBottom:2},children:[e.jsxs("span",{style:{color:"rgba(255,255,255,0.5)"},children:[t.owner,"/"]}),t.name]}),t.description&&e.jsx("div",{style:{fontSize:12,color:"rgba(255,255,255,0.45)",overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:t.description}),e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:10,marginTop:4,fontSize:11,color:"rgba(255,255,255,0.35)"},children:[e.jsxs("span",{style:{display:"flex",alignItems:"center",gap:3},"aria-label":`${t.stars} stars`,children:[e.jsx(w,{size:9,"aria-hidden":"true"})," ",t.stars.toLocaleString()]}),e.jsxs("span",{style:{display:"flex",alignItems:"center",gap:3},"aria-label":`${t.forks} forks`,children:[e.jsx(W,{size:9,"aria-hidden":"true"})," ",t.forks.toLocaleString()]}),t.language&&e.jsx("span",{children:t.language})]})]}),e.jsx(q,{size:14,style:{color:"rgba(255,255,255,0.3)",flexShrink:0},"aria-hidden":"true"})]},`${t.owner}/${t.name}`))})]})]});return e.jsxs("div",{className:"flex flex-col h-full min-h-0 overflow-hidden bg-shell-surface text-shell-text select-none relative",children:[Ie&&e.jsx("div",{className:"flex items-center justify-between px-4 py-3 border-b border-white/5 shrink-0",children:e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(y,{size:15,className:"text-accent shrink-0","aria-hidden":"true"}),e.jsx("h1",{className:"text-sm font-semibold",children:"GitHub"})]})}),e.jsx(He,{selectedId:le,onBack:b,listTitle:"GitHub",detailTitle:Re,listWidth:208,list:f?_e:e.jsxs("div",{className:"flex h-full overflow-hidden",children:[we,e.jsxs("div",{className:"flex-1 flex flex-col overflow-hidden",children:[ne,Se]})]}),detail:Le??(f?null:e.jsx("div",{className:"flex items-center justify-center h-full text-shell-text-tertiary text-sm",children:"Select an item to view details"}))})]})}export{nt as GitHubApp};
import{r as i,j as e}from"./vendor-react-l6srOxy7.js";import{B as x,I as Be,C as H,a as M,c as O,S as Ge,d as Te,e as Ae,f as E,g as U}from"./toolbar-UW6q5pkx.js";import{M as He}from"./MobileSplitView-qc4KfHBU.js";import{u as Me}from"./use-is-mobile-v5lglusa.js";import{aY as y,aZ as w,B as ie,aS as oe,a_ as v,a$ as N,an as ce,r as Oe,S as de,D as q,aM as W,a2 as Ee,a0 as P,az as K,b0 as F,aG as xe,ad as Ue}from"./vendor-icons-DcMSPw1y.js";import"./vendor-radix-BhM7AEEG.js";import"./vendor-layout-B-pp9n1f.js";async function k(l,c,s){try{const d=await fetch(l,{...s,headers:{Accept:"application/json",...s==null?void 0:s.headers}});return!d.ok||!(d.headers.get("content-type")??"").includes("application/json")?c:await d.json()}catch{return c}}async function qe(l,c,s){return k(l,s,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(c)})}async function We(l){const s=new URLSearchParams().toString(),d=`/api/github/starred${s?`?${s}`:""}`,n=await k(d,{repos:[],total:0});return{repos:Array.isArray(n.repos)?n.repos:[],total:n.total??0}}async function Pe(){const l=await k("/api/github/notifications",{notifications:[],unread_count:0});return{notifications:Array.isArray(l.notifications)?l.notifications:[],unread_count:l.unread_count??0}}async function Ke(l,c){try{const s=await fetch(`/api/github/repo/${encodeURIComponent(l)}/${encodeURIComponent(c)}`,{headers:{Accept:"application/json"}});return!s.ok||!(s.headers.get("content-type")??"").includes("application/json")?null:await s.json()}catch{return null}}async function Fe(l,c,s){try{const d=await fetch(`/api/github/repo/${encodeURIComponent(l)}/${encodeURIComponent(c)}/issues/${s}`,{headers:{Accept:"application/json"}});return!d.ok||!(d.headers.get("content-type")??"").includes("application/json")?null:await d.json()}catch{return null}}async function Ve(l,c){const s=await k(`/api/github/repo/${encodeURIComponent(l)}/${encodeURIComponent(c)}/releases`,{releases:[]});return Array.isArray(s.releases)?s.releases:[]}async function Je(){return k("/api/github/auth/status",{authenticated:!1})}async function Ye(l){return qe("/api/knowledge/ingest",{url:l,title:"",text:"",categories:[],source:"github-browser"},null)}const j=l=>{if(!l)return"";const c=new Date(l),s=(Date.now()-c.getTime())/1e3;return s<60?"just now":s<3600?`${Math.floor(s/60)}m ago`:s<86400?`${Math.floor(s/3600)}h ago`:s<604800?`${Math.floor(s/86400)}d ago`:c.toLocaleDateString()},Ze=l=>l<1024?`${l} B`:l<1048576?`${(l/1024).toFixed(1)} KB`:`${(l/1048576).toFixed(1)} MB`,he=l=>l==="open"?"bg-green-500/15 text-green-400 border-green-500/30":l==="closed"?"bg-red-500/15 text-red-400 border-red-500/30":l==="merged"?"bg-slate-500/15 text-slate-400 border-slate-500/30":"bg-white/10 text-shell-text-tertiary border-white/10";function Qe({comment:l,depth:c=0}){const[s,d]=i.useState(c>=3);return e.jsxs("div",{className:`border-l-2 ${c===0?"border-white/10":"border-white/5"} pl-3 py-1`,style:{marginLeft:c>0?`${c*12}px`:0},children:[e.jsxs("div",{className:"flex items-center gap-2 mb-1",children:[e.jsx("span",{className:"text-xs font-medium text-shell-text-secondary",children:l.author}),e.jsx("span",{className:"text-[10px] text-shell-text-tertiary",children:j(l.created_at)}),c>=3&&e.jsx("button",{className:"text-[10px] text-accent hover:underline ml-1",onClick:()=>d(n=>!n),"aria-expanded":!s,"aria-label":s?"Expand comment":"Collapse comment",children:s?"expand":"collapse"})]}),!s&&e.jsxs(e.Fragment,{children:[e.jsx("p",{className:"text-xs text-shell-text-secondary whitespace-pre-wrap leading-relaxed mb-1",children:l.body}),Object.keys(l.reactions??{}).length>0&&e.jsx("div",{className:"flex gap-1.5 flex-wrap mb-1",children:Object.entries(l.reactions).map(([n,u])=>u>0?e.jsxs("span",{className:"px-1.5 py-0.5 rounded bg-white/5 border border-white/10 text-[10px] text-shell-text-secondary","aria-label":`${n}: ${u}`,children:[n," ",u]},n):null)})]})]})}function nt({windowId:l}){const[,c]=i.useState("list"),[s,d]=i.useState(null),[n,u]=i.useState("starred"),[S,V]=i.useState("repos"),[pe,ue]=i.useState(null),[J,be]=i.useState([]),[Y,me]=i.useState([]),[C,fe]=i.useState(0),[Z]=i.useState([]),[Q,$]=i.useState(!0),[p,D]=i.useState(""),[X,z]=i.useState(!1),[ge,ee]=i.useState([]),[je,te]=i.useState(!1),[m,se]=i.useState(!1),[h,L]=i.useState(!1),[R,ye]=i.useState({authenticated:!1}),f=Me(),ae=i.useCallback(async()=>{const t=await Je();ye(t)},[]),I=i.useCallback(async()=>{$(!0);const t=await We();be(t.repos),$(!1)},[]),_=i.useCallback(async()=>{$(!0);const t=await Pe();me(t.notifications),fe(t.unread_count),$(!1)},[]);i.useEffect(()=>{ae(),I(),_()},[ae,I,_]),i.useEffect(()=>{c("list"),d(null),D(""),n==="starred"||n==="watched"?I():n==="notifications"&&_()},[n,I,_]);const B=i.useCallback(async t=>{c("detail"),d({type:"repo",repo:t}),L(!1),te(!1),z(!0);const[a,r]=await Promise.all([Ve(t.owner,t.name),Ke(t.owner,t.name)]);ee(a),r&&d({type:"repo",repo:r}),z(!1)},[]),G=i.useCallback(async t=>{c("detail"),d({type:"issue",issue:t}),L(!1),z(!0);const[a,r]=t.repo.split("/");if(a&&r){const o=await Fe(a,r,t.number);o&&d({type:"issue",issue:o})}z(!1)},[]),re=i.useCallback((t,a)=>{c("detail"),d({type:"release",release:{...t,repo:a}}),L(!1)},[]),b=i.useCallback(()=>{c("list"),d(null),ee([])},[]),le=i.useMemo(()=>s?s.type==="repo"&&s.repo?`repo:${s.repo.owner}/${s.repo.name}`:s.type==="issue"&&s.issue?`issue:${s.issue.repo}#${s.issue.number}`:s.type==="release"&&s.release?`release:${s.release.tag}`:null:null,[s]),T=i.useCallback(async t=>{se(!0);const a=await Ye(t);se(!1),a&&L(!0)},[]),g=i.useMemo(()=>n==="starred"||n==="watched"?(n==="watched"?Z:J).filter(a=>{var o;if(!p)return!0;const r=p.toLowerCase();return a.name.toLowerCase().includes(r)||a.owner.toLowerCase().includes(r)||((o=a.description)==null?void 0:o.toLowerCase().includes(r))}):n==="notifications"?Y.filter(t=>{if(!p)return!0;const a=p.toLowerCase();return t.title.toLowerCase().includes(a)||t.repo.toLowerCase().includes(a)}):[],[n,J,Z,Y,p]),we=e.jsxs("nav",{className:"w-52 shrink-0 border-r border-white/5 bg-shell-surface/30 flex flex-col overflow-hidden","aria-label":"GitHub Browser navigation",children:[e.jsxs("div",{className:"flex items-center gap-2 px-3 py-3 border-b border-white/5 shrink-0",children:[e.jsx(y,{size:15,className:"text-accent","aria-hidden":"true"}),e.jsx("h1",{className:"text-sm font-semibold",children:"GitHub"})]}),e.jsxs("div",{className:"flex-1 overflow-y-auto p-2 space-y-4",children:[e.jsx("section",{"aria-label":"Sections",children:e.jsxs("div",{className:"space-y-0.5",children:[e.jsxs(x,{variant:n==="starred"?"secondary":"ghost",size:"sm","aria-pressed":n==="starred",onClick:()=>u("starred"),className:"w-full justify-start text-xs h-7 px-2 gap-1.5",children:[e.jsx(w,{size:11,"aria-hidden":"true"}),"Starred Repos"]}),e.jsxs(x,{variant:n==="notifications"?"secondary":"ghost",size:"sm","aria-pressed":n==="notifications",onClick:()=>u("notifications"),className:"w-full justify-between text-xs h-7 px-2",children:[e.jsxs("span",{className:"flex items-center gap-1.5",children:[e.jsx(ie,{size:11,"aria-hidden":"true"}),"Notifications"]}),C>0&&e.jsx("span",{className:"px-1.5 py-0.5 rounded-full bg-accent text-white text-[10px] tabular-nums","aria-label":`${C} unread`,children:C})]}),e.jsxs(x,{variant:n==="watched"?"secondary":"ghost",size:"sm","aria-pressed":n==="watched",onClick:()=>u("watched"),className:"w-full justify-start text-xs h-7 px-2 gap-1.5",children:[e.jsx(oe,{size:11,"aria-hidden":"true"}),"Watched"]})]})}),e.jsxs("section",{"aria-label":"Content type",children:[e.jsx("p",{className:"text-[10px] uppercase tracking-wider text-shell-text-tertiary px-2 mb-1.5",children:"Content"}),e.jsx("div",{className:"space-y-0.5",children:[{id:"repos",label:"Repos",icon:y},{id:"issues",label:"Issues",icon:v},{id:"prs",label:"Pull Requests",icon:N},{id:"releases",label:"Releases",icon:ce}].map(({id:t,label:a,icon:r})=>e.jsxs(x,{variant:S===t?"secondary":"ghost",size:"sm","aria-pressed":S===t,onClick:()=>V(t),className:"w-full justify-start text-xs h-7 px-2 gap-1.5",children:[e.jsx(r,{size:11,"aria-hidden":"true"}),a]},t))})]}),e.jsxs("section",{"aria-label":"Status filter",children:[e.jsx("p",{className:"text-[10px] uppercase tracking-wider text-shell-text-tertiary px-2 mb-1.5",children:"Status"}),e.jsx("div",{className:"space-y-0.5",children:["open","closed","merged"].map(t=>{const a=pe===t;return e.jsx(x,{variant:a?"secondary":"ghost",size:"sm","aria-pressed":a,onClick:()=>ue(r=>r===t?null:t),className:"w-full justify-start text-xs h-7 px-2 capitalize",children:t},t)})})]})]}),e.jsx("div",{className:"shrink-0 border-t border-white/5 px-3 py-2",children:R.authenticated?e.jsxs("div",{className:"space-y-0.5",children:[e.jsx("p",{className:"text-[10px] text-shell-text-tertiary capitalize",children:R.method??"connected"}),e.jsxs("p",{className:"text-xs text-shell-text-secondary truncate",children:["@",R.username]})]}):e.jsx("button",{className:"text-xs text-accent hover:underline",onClick:()=>{},"aria-label":"Connect GitHub account",children:"Connect GitHub"})})]}),ne=R.authenticated?null:e.jsxs("div",{className:"flex items-center gap-3 px-4 py-2 bg-amber-500/10 border-b border-amber-500/20 text-xs text-amber-300 shrink-0",role:"banner","aria-label":"GitHub authentication notice",children:[e.jsx(Oe,{size:13,"aria-hidden":"true"}),e.jsx("span",{children:"Connect GitHub for starred repos and notifications."}),e.jsx("button",{className:"ml-auto underline hover:text-amber-200","aria-label":"Open Secrets app to connect GitHub",children:"Connect"})]}),ve=t=>e.jsxs(H,{className:"cursor-pointer hover:border-white/15 transition-colors",onClick:()=>B(t),onKeyDown:a=>{(a.key==="Enter"||a.key===" ")&&(a.preventDefault(),B(t))},tabIndex:0,role:"button","aria-label":`Open ${t.owner}/${t.name}`,children:[e.jsxs(M,{className:"pb-1 p-3",children:[e.jsxs("div",{className:"flex items-start justify-between gap-2",children:[e.jsxs("h3",{className:"text-sm font-medium leading-snug",children:[e.jsxs("span",{className:"text-shell-text-tertiary",children:[t.owner,"/"]}),t.name]}),t.language&&e.jsx("span",{className:"shrink-0 text-[10px] px-1.5 py-0.5 rounded bg-accent/10 text-accent border border-accent/20",children:t.language})]}),t.description&&e.jsx("p",{className:"text-[11px] text-shell-text-secondary line-clamp-1 leading-relaxed mt-0.5",children:t.description})]}),e.jsx(O,{className:"pt-0 px-3 pb-3",children:e.jsxs("div",{className:"flex items-center gap-3 text-[10px] text-shell-text-tertiary",children:[e.jsxs("span",{className:"flex items-center gap-1","aria-label":`${t.stars} stars`,children:[e.jsx(w,{size:10,"aria-hidden":"true"}),t.stars.toLocaleString()]}),e.jsxs("span",{className:"flex items-center gap-1","aria-label":`${t.forks} forks`,children:[e.jsx(W,{size:10,"aria-hidden":"true"}),t.forks.toLocaleString()]}),e.jsx("span",{className:"ml-auto",children:j(t.updated_at)})]})})]},`${t.owner}/${t.name}`),Ne=t=>e.jsxs(H,{className:"cursor-pointer hover:border-white/15 transition-colors",onClick:()=>G(t),onKeyDown:a=>{(a.key==="Enter"||a.key===" ")&&(a.preventDefault(),G(t))},tabIndex:0,role:"button","aria-label":`Open ${t.is_pull_request?"PR":"issue"}: ${t.title}`,children:[e.jsx(M,{className:"pb-1 p-3",children:e.jsxs("div",{className:"flex items-start gap-2",children:[t.is_pull_request?e.jsx(N,{size:13,className:"mt-0.5 shrink-0 text-accent","aria-hidden":"true"}):e.jsx(v,{size:13,className:"mt-0.5 shrink-0 text-green-400","aria-hidden":"true"}),e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsx("h3",{className:"text-sm font-medium leading-snug line-clamp-1",children:t.title}),e.jsx("p",{className:"text-[11px] text-shell-text-tertiary mt-0.5",children:t.repo})]}),e.jsx("span",{className:`shrink-0 text-[10px] px-1.5 py-0.5 rounded border ${he(t.state)}`,"aria-label":`Status: ${t.state}`,children:t.state})]})}),e.jsxs(O,{className:"pt-0 px-3 pb-3 space-y-1.5",children:[t.labels.length>0&&e.jsx("div",{className:"flex flex-wrap gap-1","aria-label":"Labels",children:t.labels.map(a=>e.jsx("span",{className:"px-1.5 py-0.5 rounded bg-white/5 border border-white/10 text-[10px] text-shell-text-secondary",children:a},a))}),e.jsxs("div",{className:"flex items-center gap-3 text-[10px] text-shell-text-tertiary",children:[e.jsxs("span",{className:"flex items-center gap-1",children:[e.jsx(Ee,{size:10,"aria-hidden":"true"}),t.comments.length]}),e.jsx("span",{children:t.author}),e.jsx("span",{className:"ml-auto",children:j(t.created_at)})]})]})]},`${t.repo}#${t.number}`),ke=(t,a="")=>e.jsxs(H,{className:"cursor-pointer hover:border-white/15 transition-colors",onClick:()=>re(t,a),onKeyDown:r=>{(r.key==="Enter"||r.key===" ")&&(r.preventDefault(),re(t,a))},tabIndex:0,role:"button","aria-label":`Open release ${t.tag}`,children:[e.jsx(M,{className:"pb-1 p-3",children:e.jsxs("div",{className:"flex items-start justify-between gap-2",children:[e.jsxs("div",{children:[e.jsxs("h3",{className:"text-sm font-medium leading-snug flex items-center gap-1.5",children:[e.jsx(xe,{size:11,"aria-hidden":"true",className:"text-accent"}),t.tag]}),a&&e.jsx("p",{className:"text-[11px] text-shell-text-tertiary mt-0.5",children:a})]}),t.prerelease&&e.jsx("span",{className:"shrink-0 text-[10px] px-1.5 py-0.5 rounded bg-amber-500/15 text-amber-400 border border-amber-500/30",children:"pre-release"})]})}),e.jsx(O,{className:"pt-0 px-3 pb-3",children:e.jsx("p",{className:"text-[10px] text-shell-text-tertiary",children:j(t.published_at)})})]},t.tag),Se=e.jsxs("main",{className:"flex-1 flex flex-col overflow-hidden","aria-label":"GitHub content list",children:[e.jsx("div",{className:"flex items-center gap-2 px-4 py-3 border-b border-white/5 shrink-0",children:e.jsxs("div",{className:"relative flex-1",children:[e.jsx(de,{size:14,className:"absolute left-3 top-1/2 -translate-y-1/2 text-shell-text-tertiary pointer-events-none z-10","aria-hidden":"true"}),e.jsx(Be,{type:"search",value:p,onChange:t=>D(t.target.value),placeholder:"Search…",className:"pl-8 h-8","aria-label":"Search GitHub content"})]})}),e.jsx("div",{className:"flex-1 overflow-y-auto p-3 space-y-2",role:"list","aria-label":"GitHub items",children:Q?e.jsx("div",{className:"flex items-center justify-center h-full text-shell-text-tertiary text-sm",role:"status","aria-live":"polite",children:"Loading…"}):g.length===0?e.jsxs("div",{className:"flex flex-col items-center justify-center h-full gap-3 text-shell-text-tertiary",children:[e.jsx(y,{size:36,className:"opacity-20","aria-hidden":"true"}),e.jsx("p",{className:"text-sm",children:p?"No results for your search":"Nothing here yet"})]}):n==="notifications"?g.map(t=>e.jsx("div",{role:"listitem",children:Ne(t)},`${t.repo}#${t.number}`)):g.map(t=>e.jsx("div",{role:"listitem",children:ve(t)},`${t.owner}/${t.name}`))})]}),Ce=t=>{const a=`https://github.com/${t.owner}/${t.name}`,r=ge[0]??null;return e.jsx("main",{className:"flex-1 flex flex-col overflow-hidden","aria-label":`${t.owner}/${t.name} detail`,children:e.jsxs("div",{className:"flex-1 overflow-y-auto",children:[e.jsxs("div",{className:"px-5 pt-4 pb-3 border-b border-white/5",children:[!f&&e.jsxs(x,{variant:"ghost",size:"sm",onClick:b,className:"text-xs mb-3 -ml-1 text-shell-text-secondary","aria-label":"Back to list",onKeyDown:o=>o.key==="Escape"&&b(),children:[e.jsx(P,{size:14,"aria-hidden":"true"}),"Back"]}),e.jsxs("h2",{className:"text-lg font-semibold leading-snug mb-1",children:[e.jsxs("span",{className:"text-shell-text-tertiary",children:[t.owner,"/"]}),t.name]}),t.description&&e.jsx("p",{className:"text-sm text-shell-text-secondary mb-3",children:t.description}),e.jsxs("div",{className:"flex flex-wrap gap-2 mb-3",children:[e.jsxs("span",{className:"flex items-center gap-1 text-[11px] px-2 py-0.5 rounded bg-white/5 border border-white/10 text-shell-text-secondary","aria-label":`${t.stars} stars`,children:[e.jsx(w,{size:10,"aria-hidden":"true"}),t.stars.toLocaleString()," stars"]}),e.jsxs("span",{className:"flex items-center gap-1 text-[11px] px-2 py-0.5 rounded bg-white/5 border border-white/10 text-shell-text-secondary","aria-label":`${t.forks} forks`,children:[e.jsx(W,{size:10,"aria-hidden":"true"}),t.forks.toLocaleString()," forks"]}),t.language&&e.jsx("span",{className:"text-[11px] px-2 py-0.5 rounded bg-accent/10 text-accent border border-accent/20",children:t.language}),t.license&&e.jsx("span",{className:"text-[11px] px-2 py-0.5 rounded bg-white/5 border border-white/10 text-shell-text-secondary",children:t.license})]}),t.topics.length>0&&e.jsx("div",{className:"flex flex-wrap gap-1 mb-2","aria-label":"Topics",children:t.topics.map(o=>e.jsx("span",{className:"px-1.5 py-0.5 rounded-full bg-blue-500/10 text-blue-400 text-[10px] border border-blue-500/20",children:o},o))})]}),t.readme_content&&e.jsxs("div",{className:"px-5 py-4 border-b border-white/5",children:[e.jsx("h3",{className:"text-xs font-semibold text-shell-text-tertiary uppercase tracking-wider mb-2",children:"README"}),e.jsx("div",{className:"rounded-lg bg-white/[0.02] border border-white/5 p-3 max-h-64 overflow-y-auto",children:e.jsx("pre",{className:"text-xs text-shell-text-secondary whitespace-pre-wrap leading-relaxed font-sans",children:X?"Loading…":t.readme_content})})]}),r&&e.jsxs("div",{className:"px-5 py-4 border-b border-white/5",children:[e.jsx("h3",{className:"text-xs font-semibold text-shell-text-tertiary uppercase tracking-wider mb-2",children:"Latest Release"}),ke(r,`${t.owner}/${t.name}`)]}),e.jsxs("div",{className:"px-5 py-3 border-b border-white/5 flex items-center justify-between",children:[e.jsx("label",{htmlFor:`monitor-${t.name}`,className:"text-xs text-shell-text-secondary cursor-pointer",children:"Monitor releases"}),e.jsx(Ge,{id:`monitor-${t.name}`,checked:je,onCheckedChange:te,"aria-label":"Monitor releases for this repository"})]}),e.jsxs("div",{className:"px-5 py-3 flex flex-wrap gap-2",children:[e.jsxs(x,{size:"sm",variant:"ghost",className:"text-xs gap-1.5",onClick:()=>window.open(a,"_blank","noopener,noreferrer"),"aria-label":"Open on GitHub",children:[e.jsx(K,{size:13,"aria-hidden":"true"}),"Open on GitHub"]}),e.jsxs(x,{size:"sm",variant:h?"secondary":"outline",className:"text-xs gap-1.5",onClick:()=>T(a),disabled:m||h,"aria-label":h?"Saved to library":"Save to Library",children:[e.jsx(F,{size:13,"aria-hidden":"true"}),h?"Saved":m?"Saving…":"Save to Library"]})]})]})})},$e=t=>{const a=`https://github.com/${t.repo}/${t.is_pull_request?"pull":"issues"}/${t.number}`;return e.jsx("main",{className:"flex-1 flex flex-col overflow-hidden","aria-label":`Issue ${t.number} detail`,children:e.jsxs("div",{className:"flex-1 overflow-y-auto",children:[e.jsxs("div",{className:"px-5 pt-4 pb-3 border-b border-white/5",children:[!f&&e.jsxs(x,{variant:"ghost",size:"sm",onClick:b,className:"text-xs mb-3 -ml-1 text-shell-text-secondary","aria-label":"Back to list",onKeyDown:r=>r.key==="Escape"&&b(),children:[e.jsx(P,{size:14,"aria-hidden":"true"}),"Back"]}),e.jsxs("div",{className:"flex items-start gap-2 mb-2",children:[t.is_pull_request?e.jsx(N,{size:16,className:"mt-0.5 shrink-0 text-accent","aria-hidden":"true"}):e.jsx(v,{size:16,className:"mt-0.5 shrink-0 text-green-400","aria-hidden":"true"}),e.jsx("h2",{className:"text-base font-semibold leading-snug flex-1",children:t.title}),e.jsx("span",{className:`shrink-0 text-[10px] px-1.5 py-0.5 rounded border ${he(t.state)}`,"aria-label":`Status: ${t.state}`,children:t.state})]}),e.jsxs("p",{className:"text-xs text-shell-text-tertiary mb-2",children:[t.repo," · ",t.author," · ",j(t.created_at)]}),t.labels.length>0&&e.jsx("div",{className:"flex flex-wrap gap-1 mb-2","aria-label":"Labels",children:t.labels.map(r=>e.jsx("span",{className:"px-1.5 py-0.5 rounded bg-white/5 border border-white/10 text-[10px] text-shell-text-secondary",children:r},r))})]}),e.jsx("div",{className:"px-5 py-3 flex-1",children:e.jsxs(Te,{defaultValue:"discussion",children:[e.jsxs(Ae,{children:[e.jsx(E,{value:"discussion",children:"Discussion"}),e.jsx(E,{value:"history",children:"History"}),e.jsx(E,{value:"metadata",children:"Metadata"})]}),e.jsxs(U,{value:"discussion",children:[t.body&&e.jsx("div",{className:"rounded-lg bg-white/[0.02] border border-white/5 p-3 mb-3 mt-3",children:e.jsx("p",{className:"text-xs text-shell-text-secondary whitespace-pre-wrap leading-relaxed",children:X?"Loading…":t.body})}),t.comments.length>0&&e.jsxs("div",{className:"space-y-2 mt-2","aria-label":"Comments",children:[e.jsxs("p",{className:"text-[10px] uppercase tracking-wider text-shell-text-tertiary mb-1",children:[t.comments.length," comment",t.comments.length!==1?"s":""]}),t.comments.map((r,o)=>e.jsx(Qe,{comment:r,depth:0},o))]})]}),e.jsx(U,{value:"history",children:e.jsx("div",{className:"mt-3 text-xs text-shell-text-tertiary italic",children:"Issue history not available in this view."})}),e.jsx(U,{value:"metadata",children:e.jsx("div",{className:"mt-3 space-y-2",children:[{label:"Number",value:`#${t.number}`},{label:"State",value:t.state},{label:"Author",value:t.author},{label:"Repo",value:t.repo},{label:"Type",value:t.is_pull_request?"Pull Request":"Issue"},{label:"Created",value:t.created_at}].map(({label:r,value:o})=>e.jsxs("div",{className:"flex justify-between text-xs",children:[e.jsx("span",{className:"text-shell-text-tertiary",children:r}),e.jsx("span",{className:"text-shell-text-secondary",children:o})]},r))})})]})}),e.jsxs("div",{className:"px-5 py-3 flex flex-wrap gap-2 border-t border-white/5",children:[e.jsxs(x,{size:"sm",variant:"ghost",className:"text-xs gap-1.5",onClick:()=>window.open(a,"_blank","noopener,noreferrer"),"aria-label":"Open on GitHub",children:[e.jsx(K,{size:13,"aria-hidden":"true"}),"Open on GitHub"]}),e.jsxs(x,{size:"sm",variant:h?"secondary":"outline",className:"text-xs gap-1.5",onClick:()=>T(a),disabled:m||h,"aria-label":h?"Saved to library":"Save to Library",children:[e.jsx(F,{size:13,"aria-hidden":"true"}),h?"Saved":m?"Saving…":"Save to Library"]})]})]})})},ze=t=>{const a=t.repo??"",r=a?`https://github.com/${a}/releases/tag/${encodeURIComponent(t.tag)}`:"#";return e.jsx("main",{className:"flex-1 flex flex-col overflow-hidden","aria-label":`Release ${t.tag} detail`,children:e.jsxs("div",{className:"flex-1 overflow-y-auto",children:[e.jsxs("div",{className:"px-5 pt-4 pb-3 border-b border-white/5",children:[!f&&e.jsxs(x,{variant:"ghost",size:"sm",onClick:b,className:"text-xs mb-3 -ml-1 text-shell-text-secondary","aria-label":"Back to list",onKeyDown:o=>o.key==="Escape"&&b(),children:[e.jsx(P,{size:14,"aria-hidden":"true"}),"Back"]}),e.jsxs("div",{className:"flex items-start gap-2 mb-1",children:[e.jsx(xe,{size:16,className:"mt-0.5 shrink-0 text-accent","aria-hidden":"true"}),e.jsx("h2",{className:"text-lg font-semibold leading-snug",children:t.tag}),t.prerelease&&e.jsx("span",{className:"text-[10px] px-1.5 py-0.5 rounded bg-amber-500/15 text-amber-400 border border-amber-500/30",children:"pre-release"})]}),a&&e.jsx("p",{className:"text-xs text-shell-text-tertiary mb-1",children:a}),e.jsxs("p",{className:"text-xs text-shell-text-tertiary",children:[t.author," · ",j(t.published_at)]})]}),t.body&&e.jsxs("div",{className:"px-5 py-4 border-b border-white/5",children:[e.jsx("h3",{className:"text-xs font-semibold text-shell-text-tertiary uppercase tracking-wider mb-2",children:"Release Notes"}),e.jsx("pre",{className:"text-xs text-shell-text-secondary whitespace-pre-wrap leading-relaxed font-sans",children:t.body})]}),t.assets.length>0&&e.jsxs("div",{className:"px-5 py-4 border-b border-white/5",children:[e.jsxs("h3",{className:"text-xs font-semibold text-shell-text-tertiary uppercase tracking-wider mb-2",children:["Assets (",t.assets.length,")"]}),e.jsx("div",{className:"space-y-1.5",role:"list","aria-label":"Release assets",children:t.assets.map(o=>e.jsxs("div",{className:"flex items-center gap-3 px-3 py-2 rounded-lg bg-white/[0.02] border border-white/5 text-xs",role:"listitem",children:[e.jsx(Ue,{size:11,"aria-hidden":"true",className:"text-shell-text-tertiary shrink-0"}),e.jsx("span",{className:"flex-1 truncate text-shell-text-secondary font-mono",children:o.name}),e.jsx("span",{className:"text-shell-text-tertiary shrink-0",children:Ze(o.size)}),e.jsxs("span",{className:"text-shell-text-tertiary shrink-0","aria-label":`${o.download_count} downloads`,children:[o.download_count.toLocaleString()," dl"]})]},o.name))})]}),e.jsxs("div",{className:"px-5 py-3 flex flex-wrap gap-2",children:[e.jsxs(x,{size:"sm",variant:"ghost",className:"text-xs gap-1.5",onClick:()=>window.open(r,"_blank","noopener,noreferrer"),"aria-label":"Open on GitHub",children:[e.jsx(K,{size:13,"aria-hidden":"true"}),"Open on GitHub"]}),e.jsxs(x,{size:"sm",variant:h?"secondary":"outline",className:"text-xs gap-1.5",onClick:()=>T(r),disabled:m||h||r==="#","aria-label":h?"Saved to library":"Save to Library",children:[e.jsx(F,{size:13,"aria-hidden":"true"}),h?"Saved":m?"Saving…":"Save to Library"]})]})]})})},Le=s?s.type==="repo"&&s.repo?Ce(s.repo):s.type==="issue"&&s.issue?$e(s.issue):s.type==="release"&&s.release?ze(s.release):null:null,Re=i.useMemo(()=>s?s.type==="repo"&&s.repo?`${s.repo.owner}/${s.repo.name}`:s.type==="issue"&&s.issue?s.issue.title:s.type==="release"&&s.release?s.release.tag:"":"",[s]),Ie=!f||le===null,_e=e.jsxs("div",{style:{display:"flex",flexDirection:"column",height:"100%"},children:[ne,e.jsx("div",{style:{padding:"8px 0 4px",borderBottom:"1px solid rgba(255,255,255,0.05)",flexShrink:0},children:e.jsx("div",{style:{margin:"0 12px",borderRadius:16,background:"rgba(255,255,255,0.05)",border:"1px solid rgba(255,255,255,0.08)",overflow:"hidden"},children:[{id:"starred",label:"Starred Repos",icon:w,badge:null},{id:"notifications",label:"Notifications",icon:ie,badge:C},{id:"watched",label:"Watched",icon:oe,badge:null}].map(({id:t,label:a,icon:r,badge:o},A,De)=>e.jsxs("button",{type:"button",onClick:()=>u(t),"aria-pressed":n===t,"aria-label":a,style:{display:"flex",alignItems:"center",gap:10,width:"100%",padding:"14px 16px",background:n===t?"rgba(255,255,255,0.08)":"none",border:"none",borderBottom:A===De.length-1?"none":"1px solid rgba(255,255,255,0.06)",cursor:"pointer",color:"inherit",textAlign:"left"},children:[e.jsx(r,{size:15,style:{color:"rgba(255,255,255,0.6)",flexShrink:0},"aria-hidden":"true"}),e.jsx("span",{style:{flex:1,fontSize:15,fontWeight:500,color:"rgba(255,255,255,0.9)"},children:a}),o!=null&&o>0&&e.jsx("span",{style:{fontSize:11,padding:"1px 7px",borderRadius:20,background:"var(--accent, #7c6be8)",color:"#fff",fontWeight:600},"aria-label":`${o} unread`,children:o}),e.jsx(q,{size:14,style:{color:"rgba(255,255,255,0.3)",flexShrink:0},"aria-hidden":"true"})]},t))})}),e.jsxs("div",{style:{padding:"8px 0 4px",borderBottom:"1px solid rgba(255,255,255,0.05)",flexShrink:0},children:[e.jsx("div",{style:{fontSize:12,textTransform:"uppercase",letterSpacing:.5,color:"rgba(255,255,255,0.45)",padding:"0 20px 6px",fontWeight:600},children:"Content"}),e.jsx("div",{style:{margin:"0 12px",borderRadius:16,background:"rgba(255,255,255,0.05)",border:"1px solid rgba(255,255,255,0.08)",overflow:"hidden"},children:[{id:"repos",label:"Repos",icon:y},{id:"issues",label:"Issues",icon:v},{id:"prs",label:"Pull Requests",icon:N},{id:"releases",label:"Releases",icon:ce}].map(({id:t,label:a,icon:r},o,A)=>e.jsxs("button",{type:"button",onClick:()=>V(t),"aria-pressed":S===t,"aria-label":a,style:{display:"flex",alignItems:"center",gap:10,width:"100%",padding:"12px 16px",background:S===t?"rgba(255,255,255,0.08)":"none",border:"none",borderBottom:o===A.length-1?"none":"1px solid rgba(255,255,255,0.06)",cursor:"pointer",color:"inherit",textAlign:"left"},children:[e.jsx(r,{size:14,style:{color:"rgba(255,255,255,0.6)",flexShrink:0},"aria-hidden":"true"}),e.jsx("span",{style:{flex:1,fontSize:14,color:"rgba(255,255,255,0.85)"},children:a})]},t))})]}),e.jsxs("div",{style:{flex:1,overflowY:"auto",padding:"8px 0 16px"},children:[e.jsx("div",{style:{fontSize:12,textTransform:"uppercase",letterSpacing:.5,color:"rgba(255,255,255,0.45)",padding:"4px 20px 8px",fontWeight:600},children:n==="notifications"?"Notifications":n==="watched"?"Watched":"Starred"}),e.jsx("div",{style:{padding:"0 12px 8px"},children:e.jsxs("div",{style:{position:"relative"},children:[e.jsx(de,{size:13,style:{position:"absolute",left:10,top:"50%",transform:"translateY(-50%)",color:"rgba(255,255,255,0.4)",pointerEvents:"none"},"aria-hidden":"true"}),e.jsx("input",{type:"search",value:p,onChange:t=>D(t.target.value),placeholder:"Search…","aria-label":"Search GitHub content",style:{width:"100%",padding:"8px 12px 8px 30px",borderRadius:10,background:"rgba(255,255,255,0.06)",border:"1px solid rgba(255,255,255,0.1)",color:"inherit",fontSize:13,outline:"none",boxSizing:"border-box"}})]})}),Q?e.jsx("div",{style:{padding:"24px 20px",textAlign:"center",fontSize:13,color:"rgba(255,255,255,0.4)"},role:"status","aria-live":"polite",children:"Loading…"}):g.length===0?e.jsx("div",{style:{padding:"32px 20px",textAlign:"center",fontSize:13,color:"rgba(255,255,255,0.4)"},children:p?"No results for your search":"Nothing here yet"}):e.jsx("div",{style:{margin:"0 12px",borderRadius:16,background:"rgba(255,255,255,0.05)",border:"1px solid rgba(255,255,255,0.08)",overflow:"hidden"},role:"list","aria-label":"GitHub items",children:n==="notifications"?g.map((t,a,r)=>e.jsxs("button",{type:"button",role:"listitem",onClick:()=>G(t),"aria-label":`Open ${t.is_pull_request?"PR":"issue"}: ${t.title}`,style:{display:"flex",alignItems:"center",gap:10,width:"100%",padding:"14px 16px",background:"none",border:"none",borderBottom:a===r.length-1?"none":"1px solid rgba(255,255,255,0.06)",cursor:"pointer",color:"inherit",textAlign:"left"},children:[t.is_pull_request?e.jsx(N,{size:13,style:{flexShrink:0,color:"rgba(130,140,255,0.9)"},"aria-hidden":"true"}):e.jsx(v,{size:13,style:{flexShrink:0,color:"rgba(80,200,120,0.9)"},"aria-hidden":"true"}),e.jsxs("div",{style:{flex:1,minWidth:0},children:[e.jsx("div",{style:{fontSize:14,fontWeight:500,color:"rgba(255,255,255,0.9)",overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap",marginBottom:2},children:t.title}),e.jsx("div",{style:{fontSize:12,color:"rgba(255,255,255,0.45)"},children:t.repo})]}),e.jsx(q,{size:14,style:{color:"rgba(255,255,255,0.3)",flexShrink:0},"aria-hidden":"true"})]},`${t.repo}#${t.number}`)):g.map((t,a,r)=>e.jsxs("button",{type:"button",role:"listitem",onClick:()=>B(t),"aria-label":`Open ${t.owner}/${t.name}`,style:{display:"flex",alignItems:"center",gap:10,width:"100%",padding:"14px 16px",background:"none",border:"none",borderBottom:a===r.length-1?"none":"1px solid rgba(255,255,255,0.06)",cursor:"pointer",color:"inherit",textAlign:"left"},children:[e.jsxs("div",{style:{flex:1,minWidth:0},children:[e.jsxs("div",{style:{fontSize:14,fontWeight:600,color:"rgba(255,255,255,0.95)",overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap",marginBottom:2},children:[e.jsxs("span",{style:{color:"rgba(255,255,255,0.5)"},children:[t.owner,"/"]}),t.name]}),t.description&&e.jsx("div",{style:{fontSize:12,color:"rgba(255,255,255,0.45)",overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:t.description}),e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:10,marginTop:4,fontSize:11,color:"rgba(255,255,255,0.35)"},children:[e.jsxs("span",{style:{display:"flex",alignItems:"center",gap:3},"aria-label":`${t.stars} stars`,children:[e.jsx(w,{size:9,"aria-hidden":"true"})," ",t.stars.toLocaleString()]}),e.jsxs("span",{style:{display:"flex",alignItems:"center",gap:3},"aria-label":`${t.forks} forks`,children:[e.jsx(W,{size:9,"aria-hidden":"true"})," ",t.forks.toLocaleString()]}),t.language&&e.jsx("span",{children:t.language})]})]}),e.jsx(q,{size:14,style:{color:"rgba(255,255,255,0.3)",flexShrink:0},"aria-hidden":"true"})]},`${t.owner}/${t.name}`))})]})]});return e.jsxs("div",{className:"flex flex-col h-full min-h-0 overflow-hidden bg-shell-surface text-shell-text select-none relative",children:[Ie&&e.jsx("div",{className:"flex items-center justify-between px-4 py-3 border-b border-white/5 shrink-0",children:e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(y,{size:15,className:"text-accent shrink-0","aria-hidden":"true"}),e.jsx("h1",{className:"text-sm font-semibold",children:"GitHub"})]})}),e.jsx(He,{selectedId:le,onBack:b,listTitle:"GitHub",detailTitle:Re,listWidth:208,list:f?_e:e.jsxs("div",{className:"flex h-full overflow-hidden",children:[we,e.jsxs("div",{className:"flex-1 flex flex-col overflow-hidden",children:[ne,Se]})]}),detail:Le??(f?null:e.jsx("div",{className:"flex items-center justify-center h-full text-shell-text-tertiary text-sm",children:"Select an item to view details"}))})]})}export{nt as GitHubApp};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Guard “Open on GitHub” when release URL is placeholder.

On Line 1 in release detail, r can be "#" when repo is missing. “Save to Library” correctly disables for this case, but “Open on GitHub” still attempts window.open("#", ...).

Suggested fix
- onClick:()=>window.open(r,"_blank","noopener,noreferrer")
+ onClick:()=>{ if (r !== "#") window.open(r,"_blank","noopener,noreferrer"); }
+ disabled={r==="#"}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@static/desktop/assets/GitHubApp-CJvVZ0RH.js` at line 1, The release detail
component (ze) builds a fallback URL r="#" when repo is missing and still calls
window.open(r, ...); update the Open on GitHub button in ze to guard against the
placeholder by either disabling the button when r === "#" (like the Save button
does) or wrapping the onClick so it only calls window.open when r !== "#",
ensuring no navigation is attempted for the placeholder URL.

⚠️ Potential issue | 🟠 Major

Connect buttons are wired as no-ops.

On Line 1, both unauthenticated “Connect GitHub”/“Connect” CTAs use onClick:()=>{}. This blocks the auth entry point from this UI.

Suggested fix
- onClick:()=>{}
+ onClick:openGitHubConnectFlow
+ const openGitHubConnectFlow = () => {
+   // e.g. route to Secrets app / OAuth setup screen
+   window.dispatchEvent(new CustomEvent("open-secrets-app", { detail: { provider: "github" } }));
+ };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@static/desktop/assets/GitHubApp-CJvVZ0RH.js` at line 1, Replace the no-op
click handlers on the two Connect CTAs (the button with aria-label "Connect
GitHub account" and the banner "Connect" button inside ne) so they call a real
auth starter instead of ()=>{}; add or call a startGitHubAuth helper (implement
it to open your auth entry point, e.g.
window.open('/api/github/auth/start','_blank') or invoke the Secrets app URI)
and use that function in the onClick handlers; ensure the helper is
imported/defined in this module and keep existing R.authenticated logic intact.

⚠️ Potential issue | 🟠 Major

Watched tab state is never populated.

On Line 1, watched filtering reads Z, but Z is declared via const [Z] = i.useState([]) (no setter) and never updated. The tab switch effect also calls starred fetch (I()) for watched, so watched view will remain empty.

Suggested fix
- const [Z] = i.useState([]);
+ const [Z, setWatchedRepos] = i.useState([]);

+ const fetchWatched = i.useCallback(async () => {
+   $(!0);
+   const t = await k("/api/github/watched", { repos: [] });
+   setWatchedRepos(Array.isArray(t.repos) ? t.repos : []);
+   $(!1);
+ }, []);

- n==="starred"||n==="watched"?I():n==="notifications"&&_()
+ n==="starred" ? I() : n==="watched" ? fetchWatched() : n==="notifications" && _()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@static/desktop/assets/GitHubApp-CJvVZ0RH.js` at line 1, The watched tab never
gets data because Z is declared without a setter (const [Z] = i.useState([]))
and the tab-switch logic calls the starred fetch (I()) for "watched"; update the
nt component to use a proper state pair (const [Z, setZ] = i.useState([])),
implement/populate watched repos via a fetch (reuse or add a fetch function
similar to We that calls the watched API) and call that setter when loading
watched repos, and change the tab-switch effect (where n changes) to call the
watched fetch when n==="watched" instead of calling I(); ensure the memo g
continues to reference Z for watched filtering.

@@ -0,0 +1 @@
import{r as l,j as t}from"./vendor-react-l6srOxy7.js";import{L as U,C,c as k,B as p}from"./toolbar-UW6q5pkx.js";import{ac as f,al as B,y as M,ao as O}from"./vendor-icons-DcMSPw1y.js";import"./vendor-radix-BhM7AEEG.js";import"./vendor-layout-B-pp9n1f.js";const g=[".txt",".md",".pdf",".html",".json",".csv"],L=["text/plain","text/markdown","application/pdf","text/html","application/json","text/csv"];function R(c){return c<1024?`${c} B`:c<1024*1024?`${(c/1024).toFixed(1)} KB`:`${(c/(1024*1024)).toFixed(1)} MB`}function Y({windowId:c}){const[S,D]=l.useState([]),[r,E]=l.useState(""),[i,j]=l.useState([]),[A,x]=l.useState(!1),[h,b]=l.useState(!1),[u,v]=l.useState(0),[y,w]=l.useState(!1),[d,o]=l.useState(null),m=l.useRef(null);l.useEffect(()=>{(async()=>{try{const e=await fetch("/api/agents",{headers:{Accept:"application/json"}});if(e.ok&&(e.headers.get("content-type")??"").includes("application/json")){const a=await e.json();Array.isArray(a)&&a.length>0&&D(a.map(n=>String(n.name??"unknown")))}}catch{}})()},[]);const $=l.useCallback(e=>{var a;const s="."+((a=e.name.split(".").pop())==null?void 0:a.toLowerCase());return g.includes(s)||L.includes(e.type)},[]);function N(e){const a=e.filter($).map(n=>({id:`${n.name}-${Date.now()}-${Math.random().toString(36).slice(2,6)}`,file:n,name:n.name,size:n.size}));j(n=>[...n,...a]),o(null)}function z(e){e.preventDefault(),x(!1);const s=Array.from(e.dataTransfer.files);N(s)}function F(e){e.target.files&&N(Array.from(e.target.files)),e.target.value=""}function I(e){j(s=>s.filter(a=>a.id!==e))}async function T(){if(!r||i.length===0)return;b(!0),v(0),o(null);const e=i.length;let s=0;for(const a of i){const n=new FormData;n.append("file",a.file),n.append("agent",r);try{await fetch("/api/import/upload",{method:"POST",body:n})}catch{}s++,v(Math.round(s/e*100))}b(!1),o(`Uploaded ${e} file${e!==1?"s":""} for ${r}`)}async function P(){if(r){w(!0),o(null);try{(await fetch("/api/import/embed",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({agent:r})})).ok?o("Embedding complete. Memory updated."):o("Embedding request sent. Check agent memory.")}catch{o("Could not reach embed endpoint. API may not be available.")}w(!1)}}return t.jsxs("div",{className:"flex flex-col h-full bg-shell-bg text-shell-text select-none",children:[t.jsxs("div",{className:"flex items-center gap-2 px-4 py-3 border-b border-white/5",children:[t.jsx(f,{size:18,className:"text-accent"}),t.jsx("h1",{className:"text-sm font-semibold",children:"Import"})]}),t.jsxs("div",{className:"flex-1 overflow-auto p-4 space-y-4",children:[t.jsxs("div",{className:"space-y-1.5",children:[t.jsx(U,{htmlFor:"import-agent",children:"Target Agent"}),t.jsxs("select",{id:"import-agent",value:r,onChange:e=>E(e.target.value),className:"flex h-9 w-full max-w-sm rounded-lg border border-white/10 bg-shell-bg-deep px-3 py-1 text-sm text-shell-text focus-visible:outline-none focus-visible:border-accent/40 focus-visible:ring-2 focus-visible:ring-accent/20",children:[t.jsx("option",{value:"",children:"Select an agent..."}),S.map(e=>t.jsx("option",{value:e,children:e},e))]})]}),t.jsx(C,{onDragOver:e=>{e.preventDefault(),x(!0)},onDragLeave:()=>x(!1),onDrop:z,className:`border-2 border-dashed transition-colors cursor-pointer ${A?"border-accent bg-accent/5":"border-white/10 hover:border-white/20"}`,onClick:()=>{var e;return(e=m.current)==null?void 0:e.click()},role:"button","aria-label":"Drop files here or click to browse",tabIndex:0,onKeyDown:e=>{var s;(e.key==="Enter"||e.key===" ")&&(e.preventDefault(),(s=m.current)==null||s.click())},children:t.jsxs(k,{className:"flex flex-col items-center justify-center gap-3 p-8",children:[t.jsx(f,{size:32,className:"text-shell-text-tertiary"}),t.jsxs("div",{className:"text-center",children:[t.jsx("p",{className:"text-sm text-shell-text-secondary",children:"Drag and drop files here"}),t.jsx("p",{className:"text-xs text-shell-text-tertiary mt-1",children:g.join(", ")})]}),t.jsx(p,{variant:"secondary",size:"sm",onClick:e=>{var s;e.stopPropagation(),(s=m.current)==null||s.click()},children:"Browse"}),t.jsx("input",{ref:m,type:"file",multiple:!0,accept:g.join(","),onChange:F,className:"hidden","aria-label":"Select files to import"})]})}),i.length>0&&t.jsxs("div",{className:"space-y-1.5",children:[t.jsxs("h2",{className:"text-xs text-shell-text-secondary font-medium",children:["Queued Files (",i.length,")"]}),i.map(e=>t.jsx(C,{children:t.jsxs(k,{className:"flex items-center gap-3 px-3.5 py-2.5",children:[t.jsx(B,{size:14,className:"text-shell-text-tertiary shrink-0"}),t.jsx("span",{className:"text-sm flex-1 truncate",children:e.name}),t.jsx("span",{className:"text-xs text-shell-text-tertiary tabular-nums shrink-0",children:R(e.size)}),t.jsx(p,{variant:"ghost",size:"icon",onClick:()=>I(e.id),className:"h-7 w-7 hover:text-red-400 hover:bg-red-500/15","aria-label":`Remove ${e.name}`,children:t.jsx(M,{size:14})})]})},e.id))]}),h&&t.jsxs("div",{className:"space-y-1.5",children:[t.jsxs("div",{className:"flex items-center justify-between text-xs text-shell-text-secondary",children:[t.jsx("span",{children:"Uploading..."}),t.jsxs("span",{className:"tabular-nums",children:[u,"%"]})]}),t.jsx("div",{className:"h-2 w-full rounded-full bg-white/5",role:"progressbar","aria-valuenow":u,"aria-valuemin":0,"aria-valuemax":100,children:t.jsx("div",{className:"h-full rounded-full bg-accent transition-all",style:{width:`${u}%`}})})]}),d&&t.jsx("p",{className:`text-xs ${d.includes("complete")||d.includes("Uploaded")?"text-emerald-400":"text-amber-400"}`,children:d}),t.jsxs("div",{className:"flex gap-2",children:[t.jsxs(p,{onClick:T,disabled:!r||i.length===0||h,children:[t.jsx(f,{size:14}),h?"Uploading...":"Upload"]}),t.jsxs(p,{variant:"secondary",onClick:P,disabled:!r||y,className:"bg-violet-600 text-white hover:bg-violet-500",children:[t.jsx(O,{size:14}),y?"Embedding...":"Embed"]})]})]})]})}export{Y as ImportApp};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Embed endpoint field name mismatch — embed functionality is broken.

The embed function sends {agent: r} but the backend /api/import/embed endpoint expects agent_name:

# From tinyagentos/routes/import_data.py:47-50
agent_name = body.get("agent_name")
if not agent_name:
    return JSONResponse({"error": "agent_name is required"}, status_code=400)

This will always return a 400 error. The minified source contains:

body:JSON.stringify({agent:r})

The source (pre-minification) should send agent_name instead of agent.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@static/desktop/assets/ImportApp-AV3jmR5U.js` at line 1, The embed POST is
sending {agent: r} but the backend expects agent_name; in function P (the embed
handler that does fetch("/api/import/embed", ... ,
body:JSON.stringify({agent:r}))), change the payload key to agent_name (i.e.
JSON.stringify({agent_name: r})) so the request body matches the backend's
body.get("agent_name") check and stops returning 400.

⚠️ Potential issue | 🔴 Critical

Embed request missing required files array.

In addition to the field name issue, the embed endpoint requires a files array listing the uploaded filenames:

# From tinyagentos/routes/import_data.py:48-50
filenames = body.get("files", [])
if not filenames:
    return JSONResponse({"error": "No files specified"}, status_code=400)

The frontend sends only {agent: ...} without tracking or sending the uploaded filenames. The embed request should include the list of successfully uploaded file names from the upload responses.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@static/desktop/assets/ImportApp-AV3jmR5U.js` at line 1, The embed request
(function P in component Y) currently sends only {agent: r} but the backend
requires a files array; modify the upload flow (function T and the queued-file
state managed by i) to capture successful upload responses (the returned
filename) for each file, store those uploaded filenames (e.g., add uploadedName
or a filesUploaded array in state), and then change P to include body:
JSON.stringify({agent: r, files: [/* list of uploaded filenames */]}) so the
embed endpoint receives the required files list; ensure T updates state only
after a successful upload and collects those names to be sent by P.

@@ -1,4 +1,4 @@
import{r as s,j as e}from"./vendor-react-l6srOxy7.js";import{B as b,C as w,L as N,I as E,S as T,T as Z}from"./toolbar-UW6q5pkx.js";import{u as ee}from"./main-DolX-lHu.js";import{t as te,a1 as se,an as ae,ac as K,a9 as P,as as V,at as le,au as ne,l as re,U as ce,a0 as ie,r as F,f as D,av as Y,g as oe,y as de,c as xe,ab as me,aw as ue,X as he}from"./vendor-icons-CiM_hUpN.js";import"./vendor-radix-BhM7AEEG.js";import"./vendor-layout-B-pp9n1f.js";import"./tokens-BsnKgzFE.js";import"./vendor-codemirror-BzcrrKz1.js";function pe(r,l,t=a=>a,x=a=>({value:a})){const a=`taos-pref:${r}`,[d,i]=s.useState(()=>{try{const p=localStorage.getItem(a);if(p!==null)return JSON.parse(p)}catch{}return l}),[n,c]=s.useState(!1),m=s.useRef(null);s.useEffect(()=>{let p=!1;return(async()=>{try{const o=await fetch(`/api/preferences/${encodeURIComponent(r)}`);if(!o.ok){c(!0);return}const j=await o.json();if(p)return;if(j&&typeof j=="object"&&Object.keys(j).length>0){const v=t(j);i(v);try{localStorage.setItem(a,JSON.stringify(v))}catch{}}c(!0)}catch{c(!0)}})(),()=>{p=!0}},[r]);const h=s.useCallback(p=>{i(o=>{const j=typeof p=="function"?p(o):p;try{localStorage.setItem(a,JSON.stringify(j))}catch{}return m.current!==null&&clearTimeout(m.current),m.current=setTimeout(()=>{m.current=null,fetch(`/api/preferences/${encodeURIComponent(r)}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(x(j))}).catch(()=>{})},500),j})},[a,x,r]);return[d,h,{loaded:n}]}const fe=[{id:"system",label:"System Info",icon:te},{id:"storage",label:"Storage",icon:se},{id:"memory",label:"Memory",icon:ae},{id:"backup",label:"Backup & Restore",icon:K},{id:"updates",label:"Updates",icon:P},{id:"advanced",label:"Advanced",icon:V},{id:"shortcuts",label:"Keyboard Shortcuts",icon:le},{id:"accessibility",label:"Accessibility",icon:ne},{id:"desktop",label:"Desktop & Dock",icon:re},{id:"users",label:"Users",icon:ce}],je={cpu:"Detecting...",ram:"Detecting...",npu:"Detecting...",gpu:"Detecting...",disk:"Detecting...",os:"Detecting..."},ye=[{label:"Models",size:"--",bytes:0,maxBytes:1},{label:"Data",size:"--",bytes:0,maxBytes:1},{label:"App Catalog",size:"--",bytes:0,maxBytes:1}];async function G(r,l){try{const t=await fetch(r,{headers:{Accept:"application/json"}});return!t.ok||!(t.headers.get("content-type")??"").includes("application/json")?l:await t.json()}catch{return l}}function be({value:r,max:l}){const t=l>0?Math.min(100,r/l*100):0;return e.jsx("div",{className:"h-2 w-full rounded-full bg-white/5",role:"progressbar","aria-valuenow":t,"aria-valuemin":0,"aria-valuemax":100,children:e.jsx("div",{className:"h-full rounded-full bg-sky-500 transition-all",style:{width:`${t}%`}})})}function ge(){const[r,l]=s.useState(je),[t,x]=s.useState(!1),[a,d]=s.useState(!1),i=s.useCallback(async()=>{var m,h,p,o,j,v,k,S,g,R,_,I,y;x(!0);const c=await G("/api/system",null);if(c!=null&&c.hardware||c!=null&&c.resources){const u=c.hardware??{},U=c.resources??{},C=U.ram_total_mb??u.ram_mb??0,$=U.disk_total_gb??((m=u.disk)==null?void 0:m.total_gb)??0,A=((h=u.cpu)==null?void 0:h.model)??((p=u.cpu)==null?void 0:p.soc)??"Unknown",B=(o=u.cpu)!=null&&o.cores?` × ${u.cpu.cores}`:"",L=(j=u.cpu)!=null&&j.arch?` (${u.cpu.arch})`:"",f=((v=u.gpu)==null?void 0:v.model)||((k=u.gpu)==null?void 0:k.type)||"None",z=(S=u.gpu)!=null&&S.vram_mb&&u.gpu.vram_mb>0?` (${(u.gpu.vram_mb/1024).toFixed(1)} GB)`:"",M=(g=u.npu)!=null&&g.type&&u.npu.type!=="none"?u.npu.type:"None",q=(R=u.npu)!=null&&R.tops&&u.npu.tops>0?` · ${u.npu.tops} TOPS`:"",X=(_=u.disk)!=null&&_.type?` ${u.disk.type}`:"",J=[(I=u.os)==null?void 0:I.distro,(y=u.os)==null?void 0:y.version].filter(Boolean),Q=J.length>0?J.join(" "):"—";l({cpu:`${A}${B}${L}`,ram:C>=1024?`${(C/1024).toFixed(1)} GB`:C>0?`${C} MB`:"—",npu:`${M}${q}`,gpu:`${f}${z}`,disk:$>0?`${$} GB${X}`:"—",os:Q})}else l({cpu:"Unavailable",ram:"Unavailable",npu:"Unavailable",gpu:"Unavailable",disk:"Unavailable",os:"Unavailable"});x(!1)},[]);s.useEffect(()=>{i()},[i]);const n=[["CPU",r.cpu],["RAM",r.ram],["NPU",r.npu],["GPU",r.gpu],["Disk",r.disk],["OS",r.os]];return e.jsxs("section",{"aria-label":"System information",children:[e.jsx("h2",{className:"text-lg font-semibold mb-5",children:"System Information"}),e.jsx("div",{className:"rounded-2xl bg-white/[0.04] border border-white/[0.06] overflow-x-auto backdrop-blur-sm",children:e.jsx("table",{className:"w-full text-sm min-w-[360px]",children:e.jsx("tbody",{children:n.map(([c,m])=>e.jsxs("tr",{className:"border-b border-white/5 last:border-0",children:[e.jsx("td",{className:"px-5 py-3 text-shell-text-secondary font-medium w-32",children:c}),e.jsx("td",{className:"px-5 py-3",children:m})]},c))})})}),e.jsxs("div",{className:"mt-3 flex items-center gap-2 flex-wrap",children:[e.jsxs(b,{variant:"outline",size:"sm",onClick:i,disabled:t,children:[e.jsx(P,{size:14,className:t?"animate-spin":""}),"Re-detect Hardware"]}),e.jsxs(b,{variant:"outline",size:"sm",onClick:async()=>{d(!0);try{await fetch("/api/system/restart/prepare",{method:"POST"})}catch{}},"aria-label":"Restart taOS server",children:[e.jsx(P,{size:14}),"Restart Server"]})]}),e.jsx("p",{className:"mt-2 text-xs text-shell-text-tertiary",children:"Restart the server to apply settings changes that require a reload."}),a&&e.jsx(W,{onClose:()=>d(!1)})]})}function Ne(){const[r,l]=s.useState(ye);return s.useEffect(()=>{G("/api/settings/storage",null).then(t=>{t&&Array.isArray(t)?l(t):l([{label:"Models",size:"4.2 GB",bytes:4200,maxBytes:32e3},{label:"Data",size:"1.8 GB",bytes:1800,maxBytes:32e3},{label:"App Catalog",size:"320 MB",bytes:320,maxBytes:32e3}])})},[]),e.jsxs("section",{"aria-label":"Storage usage",children:[e.jsx("h2",{className:"text-lg font-semibold mb-5",children:"Storage Usage"}),e.jsx("div",{className:"space-y-3",children:r.map(t=>e.jsxs(w,{className:"p-4",children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsx("span",{className:"text-sm font-medium",children:t.label}),e.jsx("span",{className:"text-sm text-shell-text-secondary tabular-nums",children:t.size})]}),e.jsx(be,{value:t.bytes,max:t.maxBytes})]},t.label))})]})}const ve=[{key:"capture_conversations",label:"Conversations",desc:"Messages you send to agents in the Message Hub"},{key:"capture_notes",label:"Notes",desc:"Notes from the Text Editor app"},{key:"capture_files",label:"File activity",desc:"Files you upload or open"},{key:"capture_searches",label:"Search queries",desc:"What you search for in global search"}];function we(){const[r,l]=s.useState(null),[t,x]=s.useState(null),[a,d]=s.useState(null);s.useEffect(()=>{fetch("/api/user-memory/settings").then(n=>n.ok?n.json():null).then(n=>{l(n||{})}).catch(()=>{l({}),d("Could not load memory settings.")}),fetch("/api/user-memory/stats").then(n=>n.ok?n.json():null).then(n=>{n&&x(n)}).catch(()=>{})},[]);const i=(n,c)=>{const m={...r||{},[n]:c};l(m),fetch("/api/user-memory/settings",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({[n]:c})}).then(h=>{h.ok?d(null):d(`Failed to save setting (${h.status})`)}).catch(()=>d("Could not reach backend."))};return r?e.jsxs("section",{"aria-label":"Memory capture settings",children:[e.jsx("h2",{className:"text-lg font-semibold mb-2",children:"Memory Capture"}),e.jsx("p",{className:"text-sm text-shell-text-tertiary mb-5",children:"Choose what activity gets saved to your personal memory index. All data stays on this device."}),a&&e.jsxs("p",{className:"mb-3 text-xs text-amber-400 flex items-center gap-1.5",children:[e.jsx(F,{size:12})," ",a]}),e.jsx("div",{className:"space-y-2",children:ve.map(n=>{const c=!!r[n.key],m=`capture-${String(n.key)}`;return e.jsxs(w,{className:"p-4 flex items-center justify-between gap-3",children:[e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsx(N,{htmlFor:m,className:"text-sm font-medium text-shell-text",children:n.label}),e.jsx("p",{className:"text-xs text-shell-text-tertiary mt-0.5",children:n.desc})]}),e.jsx(T,{id:m,checked:c,onCheckedChange:h=>i(n.key,h),"aria-label":`Capture ${n.label}`})]},String(n.key))})}),t&&e.jsxs(w,{className:"mt-6 p-4",children:[e.jsx("h3",{className:"text-sm font-medium mb-3",children:"Stored chunks"}),e.jsxs("div",{className:"text-xs text-shell-text-secondary mb-2 tabular-nums",children:["Total: ",t.total]}),Object.keys(t.collections||{}).length>0?e.jsx("ul",{className:"space-y-1 text-xs text-shell-text-tertiary",children:Object.entries(t.collections).map(([n,c])=>e.jsxs("li",{className:"flex justify-between tabular-nums",children:[e.jsx("span",{children:n}),e.jsx("span",{children:c})]},n))}):e.jsx("p",{className:"text-xs text-shell-text-tertiary",children:"No memories captured yet."})]})]}):e.jsxs("section",{"aria-label":"Memory capture settings",children:[e.jsx("h2",{className:"text-lg font-semibold mb-5",children:"Memory Capture"}),e.jsx("p",{className:"text-sm text-shell-text-tertiary",children:"Loading..."})]})}function ke(){const[r,l]=s.useState(null),[t,x]=s.useState(!1),a=async()=>{x(!0),l(null);try{const d=await fetch("/api/backup",{method:"POST"});d.ok?l("Backup created successfully."):l(`Backup failed (${d.status}). API may not be available yet.`)}catch{l("Could not reach backup endpoint. API not available yet.")}x(!1)};return e.jsxs("section",{"aria-label":"Backup and restore",children:[e.jsx("h2",{className:"text-lg font-semibold mb-5",children:"Backup & Restore"}),e.jsxs(w,{className:"p-4 space-y-4",children:[e.jsxs("div",{children:[e.jsx("h3",{className:"text-sm font-medium mb-2",children:"Create Backup"}),e.jsx("p",{className:"text-xs text-shell-text-tertiary mb-3",children:"Export all agents, memory, and configuration as a backup archive."}),e.jsxs(b,{size:"sm",onClick:a,disabled:t,children:[e.jsx(K,{size:14,className:t?"animate-bounce":""}),t?"Creating...":"Create Backup"]}),r&&e.jsx("p",{className:`mt-2 text-xs ${r.includes("success")?"text-emerald-400":"text-amber-400"}`,children:r})]}),e.jsx("hr",{className:"border-white/5"}),e.jsxs("div",{children:[e.jsx("h3",{className:"text-sm font-medium mb-2",children:"Restore from Backup"}),e.jsx("p",{className:"text-xs text-shell-text-tertiary mb-3",children:"Upload a previously created backup archive to restore."}),e.jsxs("label",{className:"flex flex-col items-center gap-2 p-6 rounded-lg border-2 border-dashed border-white/10 hover:border-white/20 transition-colors cursor-pointer",children:[e.jsx(me,{size:24,className:"text-shell-text-tertiary"}),e.jsx("span",{className:"text-xs text-shell-text-tertiary",children:"Click to select a backup file"}),e.jsx("input",{type:"file",accept:".tar.gz,.zip,.bak",className:"hidden","aria-label":"Upload backup file"})]})]})]})]})}function W({onClose:r}){const[l,t]=s.useState(null),[x,a]=s.useState(!1);s.useEffect(()=>{let n=!1,c=null,m=null,h=!1;const p=()=>{h||n||(h=!0,c&&clearInterval(c),m=setInterval(async()=>{if(!n)try{(await fetch("/api/settings/update-status")).ok&&(a(!0),m&&clearInterval(m),setTimeout(()=>{n||window.location.reload()},500))}catch{}},2e3))};return c=setInterval(async()=>{if(!n)try{const o=await fetch("/api/system/restart/status");if(o.ok){const j=await o.json();t(j),j.phase==="restarting"&&p()}}catch{p()}},1e3),()=>{n=!0,c&&clearInterval(c),m&&clearInterval(m)}},[]);const d=l?Object.entries(l.agents):[];function i(n){return n==="ready"?e.jsx("span",{className:"text-[10px] px-1.5 py-0.5 rounded bg-emerald-500/20 text-emerald-300",children:"ready"}):n==="timeout"?e.jsx("span",{className:"text-[10px] px-1.5 py-0.5 rounded bg-amber-500/20 text-amber-300",children:"timeout"}):n==="error"?e.jsx("span",{className:"text-[10px] px-1.5 py-0.5 rounded bg-red-500/20 text-red-300",children:"error"}):e.jsxs("span",{className:"text-[10px] px-1.5 py-0.5 rounded bg-sky-500/20 text-sky-300 flex items-center gap-1",children:[e.jsx(P,{size:10,className:"animate-spin"}),n]})}return e.jsx("div",{role:"dialog","aria-modal":"true","aria-label":"Restart progress",className:"fixed inset-0 z-50 flex items-center justify-center bg-black/60",children:e.jsxs("div",{className:"bg-shell-surface border border-white/10 rounded-xl p-6 w-full max-w-md shadow-xl space-y-4",children:[e.jsx("h3",{className:"text-base font-semibold",children:x?"Restarted — reloading…":d.length>0?"Preparing agents for restart":"Restarting server…"}),d.length>0&&e.jsx("ul",{className:"space-y-1","aria-label":"Agent preparation status",children:d.map(([n,c])=>e.jsxs("li",{className:"flex items-center justify-between text-sm",children:[e.jsx("span",{className:"text-shell-text-secondary",children:n}),i(c.status)]},n))}),(l==null?void 0:l.phase)==="restarting"&&!x&&e.jsx("p",{className:"text-xs text-shell-text-tertiary",children:"Waiting for server to come back…"}),!l&&e.jsxs("p",{className:"text-xs text-shell-text-tertiary flex items-center gap-1",children:[e.jsx(P,{size:12,className:"animate-spin"})," Connecting…"]}),e.jsx("div",{className:"flex justify-end",children:e.jsx(b,{variant:"outline",size:"sm",onClick:r,"aria-label":"Cancel restart progress dialog",children:"Cancel"})})]})})}function Se(){const[r,l]=s.useState(!1),[t,x]=s.useState(!1),[a,d]=s.useState(null),[i,n]=s.useState(null),[c,m]=s.useState({check_enabled:!0,auto_apply:!1,auto_restart:!1}),[h,p]=s.useState(null),[o,j]=s.useState(!1),[v,k]=s.useState(!1);s.useEffect(()=>{(async()=>{try{const y=await fetch("/api/preferences/auto-update");if(y.ok){const u=await y.json();u&&typeof u=="object"&&m({check_enabled:u.check_enabled??!0,auto_apply:u.auto_apply??!1,auto_restart:u.auto_restart??!1})}}catch{}try{const y=await fetch("/api/settings/update-check");y.ok&&d(await y.json())}catch{}try{const y=await fetch("/api/settings/update-status");y.ok&&p(await y.json())}catch{}})()},[]);const S=s.useCallback(async y=>{m(y);try{await fetch("/api/preferences/auto-update",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(y)})}catch{}},[]),g=async()=>{l(!0),n(null);try{const y=await fetch("/api/settings/update-check");if(y.ok){const u=await y.json();d(u),n(u.has_updates?"A new version is available.":"You are up to date.")}else n("Update check not available.")}catch{n("Could not reach update server.")}l(!1)},R=async()=>{x(!0),n(null);try{const y=await fetch("/api/settings/update",{method:"POST"});if(y.ok){const u=await y.json().catch(()=>({}));if(u.status==="restarting")j(!0);else{n(u.message??"Update applied. Restart the server to finish."),k(!0);const U=await fetch("/api/settings/update-check");U.ok&&d(await U.json());const C=await fetch("/api/settings/update-status");C.ok&&p(await C.json())}}else{const u=await y.json().catch(()=>({}));n(u.error??"Update failed.")}}catch{n("Could not apply update.")}x(!1)},_=async()=>{j(!0);try{await fetch("/api/system/restart/prepare",{method:"POST"})}catch{}},I=!!(h!=null&&h.pending_restart_sha);return e.jsxs("section",{"aria-label":"System updates",children:[e.jsx("h2",{className:"text-lg font-semibold mb-5",children:"Updates"}),I&&e.jsxs("div",{className:"mb-4 flex items-center justify-between gap-3 rounded-lg border border-amber-500/30 bg-amber-500/10 px-4 py-3",children:[e.jsxs("div",{className:"flex items-center gap-2 text-sm text-amber-200",children:[e.jsx(F,{size:16,className:"shrink-0"}),e.jsxs("span",{children:["Update pulled — restart to finish applying (",h.pending_restart_sha.slice(0,7),")"]})]}),e.jsx(b,{size:"sm",onClick:_,"aria-label":"Restart server to apply update",children:"Restart now"})]}),e.jsxs(w,{className:"p-4 space-y-4",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("div",{className:"p-2 rounded-lg bg-white/5 text-sky-400",children:e.jsx(xe,{size:20})}),e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsx("p",{className:"text-sm font-medium",children:"taOS"}),a!=null&&a.has_updates&&a.new_commit?e.jsxs("div",{className:"flex flex-col gap-0.5",children:[e.jsxs("p",{className:"text-xs text-shell-text-tertiary tabular-nums",children:[e.jsx("span",{className:"text-white/40",children:"installed "}),a.current_commit]}),e.jsxs("p",{className:"text-xs text-amber-300/90 tabular-nums",children:[e.jsx("span",{className:"text-amber-300/50",children:"available "}),a.new_commit]})]}):e.jsx("p",{className:"text-xs text-shell-text-tertiary tabular-nums",children:(a==null?void 0:a.current_commit)??"v0.1.0-dev"})]}),(a==null?void 0:a.has_updates)&&e.jsx("span",{className:"text-[10px] px-2 py-1 rounded-full font-semibold bg-amber-500/20 text-amber-300",children:"Update available"})]}),e.jsxs("div",{className:"flex gap-2 flex-wrap",children:[e.jsxs(b,{variant:"outline",size:"sm",onClick:g,disabled:r,children:[e.jsx(P,{size:14,className:r?"animate-spin":""}),r?"Checking...":"Check Now"]}),v?e.jsx(b,{size:"sm",onClick:_,"aria-label":"Restart server to apply update",children:"Restart Now"}):a!=null&&a.has_updates?e.jsx(b,{size:"sm",onClick:R,disabled:t,children:t?"Installing...":"Install Update"}):null]}),i&&e.jsxs("div",{className:"flex items-start gap-2 text-xs",children:[i.includes("up to date")||i.includes("applied")?e.jsx(D,{size:14,className:"text-emerald-400 shrink-0 mt-0.5"}):e.jsx(F,{size:14,className:"text-amber-400 shrink-0 mt-0.5"}),e.jsx("span",{className:"text-shell-text-secondary",children:i})]}),e.jsxs("div",{className:"border-t border-white/5 pt-4 space-y-3",children:[e.jsxs("div",{className:"flex items-center justify-between gap-3",children:[e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsx(N,{className:"text-sm",children:"Check for updates automatically"}),e.jsx("p",{className:"text-[11px] text-shell-text-tertiary mt-0.5",children:"Polls GitHub hourly and notifies when a new version is available."})]}),e.jsx(T,{checked:c.check_enabled??!0,onCheckedChange:y=>S({...c,check_enabled:y})})]}),e.jsxs("div",{className:"flex items-center justify-between gap-3",children:[e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsx(N,{className:"text-sm",children:"Install updates automatically"}),e.jsx("p",{className:"text-[11px] text-shell-text-tertiary mt-0.5",children:"Pulls + installs new versions as soon as they're detected. You'll still need to restart the server manually."})]}),e.jsx(T,{checked:c.auto_apply??!1,onCheckedChange:y=>S({...c,auto_apply:y}),disabled:!(c.check_enabled??!0)})]}),e.jsxs("div",{className:"flex items-center justify-between gap-3",children:[e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsx(N,{className:"text-sm",children:"Automatically restart after update"}),e.jsx("p",{className:"text-[11px] text-shell-text-tertiary mt-0.5",children:c.auto_restart?"Server will restart automatically once an update is pulled.":"We'll remind you every 6 hours when a restart is pending."})]}),e.jsx(T,{checked:c.auto_restart??!1,onCheckedChange:y=>S({...c,auto_restart:y}),"aria-label":"Automatically restart after update"})]})]})]}),o&&e.jsx(W,{onClose:()=>j(!1)})]})}function Ce(){const[r,l]=s.useState(`# taOS Configuration
import{r as s,j as e}from"./vendor-react-l6srOxy7.js";import{B as b,C as w,L as N,I as E,S as T,T as Z}from"./toolbar-UW6q5pkx.js";import{u as ee}from"./main-RoS4E9ey.js";import{t as te,a1 as se,ao as ae,ad as K,aa as P,at as V,au as le,av as ne,l as re,U as ce,a0 as ie,r as F,f as D,aw as Y,g as oe,y as de,c as xe,ac as me,ax as ue,X as he}from"./vendor-icons-DcMSPw1y.js";import"./vendor-radix-BhM7AEEG.js";import"./vendor-layout-B-pp9n1f.js";import"./tokens-BDvF0_Hi.js";import"./vendor-codemirror-Byxbuxf1.js";function pe(r,l,t=a=>a,x=a=>({value:a})){const a=`taos-pref:${r}`,[d,i]=s.useState(()=>{try{const p=localStorage.getItem(a);if(p!==null)return JSON.parse(p)}catch{}return l}),[n,c]=s.useState(!1),m=s.useRef(null);s.useEffect(()=>{let p=!1;return(async()=>{try{const o=await fetch(`/api/preferences/${encodeURIComponent(r)}`);if(!o.ok){c(!0);return}const j=await o.json();if(p)return;if(j&&typeof j=="object"&&Object.keys(j).length>0){const v=t(j);i(v);try{localStorage.setItem(a,JSON.stringify(v))}catch{}}c(!0)}catch{c(!0)}})(),()=>{p=!0}},[r]);const h=s.useCallback(p=>{i(o=>{const j=typeof p=="function"?p(o):p;try{localStorage.setItem(a,JSON.stringify(j))}catch{}return m.current!==null&&clearTimeout(m.current),m.current=setTimeout(()=>{m.current=null,fetch(`/api/preferences/${encodeURIComponent(r)}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(x(j))}).catch(()=>{})},500),j})},[a,x,r]);return[d,h,{loaded:n}]}const fe=[{id:"system",label:"System Info",icon:te},{id:"storage",label:"Storage",icon:se},{id:"memory",label:"Memory",icon:ae},{id:"backup",label:"Backup & Restore",icon:K},{id:"updates",label:"Updates",icon:P},{id:"advanced",label:"Advanced",icon:V},{id:"shortcuts",label:"Keyboard Shortcuts",icon:le},{id:"accessibility",label:"Accessibility",icon:ne},{id:"desktop",label:"Desktop & Dock",icon:re},{id:"users",label:"Users",icon:ce}],je={cpu:"Detecting...",ram:"Detecting...",npu:"Detecting...",gpu:"Detecting...",disk:"Detecting...",os:"Detecting..."},ye=[{label:"Models",size:"--",bytes:0,maxBytes:1},{label:"Data",size:"--",bytes:0,maxBytes:1},{label:"App Catalog",size:"--",bytes:0,maxBytes:1}];async function G(r,l){try{const t=await fetch(r,{headers:{Accept:"application/json"}});return!t.ok||!(t.headers.get("content-type")??"").includes("application/json")?l:await t.json()}catch{return l}}function be({value:r,max:l}){const t=l>0?Math.min(100,r/l*100):0;return e.jsx("div",{className:"h-2 w-full rounded-full bg-white/5",role:"progressbar","aria-valuenow":t,"aria-valuemin":0,"aria-valuemax":100,children:e.jsx("div",{className:"h-full rounded-full bg-sky-500 transition-all",style:{width:`${t}%`}})})}function ge(){const[r,l]=s.useState(je),[t,x]=s.useState(!1),[a,d]=s.useState(!1),i=s.useCallback(async()=>{var m,h,p,o,j,v,k,S,g,R,_,I,y;x(!0);const c=await G("/api/system",null);if(c!=null&&c.hardware||c!=null&&c.resources){const u=c.hardware??{},U=c.resources??{},C=U.ram_total_mb??u.ram_mb??0,$=U.disk_total_gb??((m=u.disk)==null?void 0:m.total_gb)??0,A=((h=u.cpu)==null?void 0:h.model)??((p=u.cpu)==null?void 0:p.soc)??"Unknown",B=(o=u.cpu)!=null&&o.cores?` × ${u.cpu.cores}`:"",L=(j=u.cpu)!=null&&j.arch?` (${u.cpu.arch})`:"",f=((v=u.gpu)==null?void 0:v.model)||((k=u.gpu)==null?void 0:k.type)||"None",z=(S=u.gpu)!=null&&S.vram_mb&&u.gpu.vram_mb>0?` (${(u.gpu.vram_mb/1024).toFixed(1)} GB)`:"",M=(g=u.npu)!=null&&g.type&&u.npu.type!=="none"?u.npu.type:"None",q=(R=u.npu)!=null&&R.tops&&u.npu.tops>0?` · ${u.npu.tops} TOPS`:"",X=(_=u.disk)!=null&&_.type?` ${u.disk.type}`:"",J=[(I=u.os)==null?void 0:I.distro,(y=u.os)==null?void 0:y.version].filter(Boolean),Q=J.length>0?J.join(" "):"—";l({cpu:`${A}${B}${L}`,ram:C>=1024?`${(C/1024).toFixed(1)} GB`:C>0?`${C} MB`:"—",npu:`${M}${q}`,gpu:`${f}${z}`,disk:$>0?`${$} GB${X}`:"—",os:Q})}else l({cpu:"Unavailable",ram:"Unavailable",npu:"Unavailable",gpu:"Unavailable",disk:"Unavailable",os:"Unavailable"});x(!1)},[]);s.useEffect(()=>{i()},[i]);const n=[["CPU",r.cpu],["RAM",r.ram],["NPU",r.npu],["GPU",r.gpu],["Disk",r.disk],["OS",r.os]];return e.jsxs("section",{"aria-label":"System information",children:[e.jsx("h2",{className:"text-lg font-semibold mb-5",children:"System Information"}),e.jsx("div",{className:"rounded-2xl bg-white/[0.04] border border-white/[0.06] overflow-x-auto backdrop-blur-sm",children:e.jsx("table",{className:"w-full text-sm min-w-[360px]",children:e.jsx("tbody",{children:n.map(([c,m])=>e.jsxs("tr",{className:"border-b border-white/5 last:border-0",children:[e.jsx("td",{className:"px-5 py-3 text-shell-text-secondary font-medium w-32",children:c}),e.jsx("td",{className:"px-5 py-3",children:m})]},c))})})}),e.jsxs("div",{className:"mt-3 flex items-center gap-2 flex-wrap",children:[e.jsxs(b,{variant:"outline",size:"sm",onClick:i,disabled:t,children:[e.jsx(P,{size:14,className:t?"animate-spin":""}),"Re-detect Hardware"]}),e.jsxs(b,{variant:"outline",size:"sm",onClick:async()=>{d(!0);try{await fetch("/api/system/restart/prepare",{method:"POST"})}catch{}},"aria-label":"Restart taOS server",children:[e.jsx(P,{size:14}),"Restart Server"]})]}),e.jsx("p",{className:"mt-2 text-xs text-shell-text-tertiary",children:"Restart the server to apply settings changes that require a reload."}),a&&e.jsx(W,{onClose:()=>d(!1)})]})}function Ne(){const[r,l]=s.useState(ye);return s.useEffect(()=>{G("/api/settings/storage",null).then(t=>{t&&Array.isArray(t)?l(t):l([{label:"Models",size:"4.2 GB",bytes:4200,maxBytes:32e3},{label:"Data",size:"1.8 GB",bytes:1800,maxBytes:32e3},{label:"App Catalog",size:"320 MB",bytes:320,maxBytes:32e3}])})},[]),e.jsxs("section",{"aria-label":"Storage usage",children:[e.jsx("h2",{className:"text-lg font-semibold mb-5",children:"Storage Usage"}),e.jsx("div",{className:"space-y-3",children:r.map(t=>e.jsxs(w,{className:"p-4",children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsx("span",{className:"text-sm font-medium",children:t.label}),e.jsx("span",{className:"text-sm text-shell-text-secondary tabular-nums",children:t.size})]}),e.jsx(be,{value:t.bytes,max:t.maxBytes})]},t.label))})]})}const ve=[{key:"capture_conversations",label:"Conversations",desc:"Messages you send to agents in the Message Hub"},{key:"capture_notes",label:"Notes",desc:"Notes from the Text Editor app"},{key:"capture_files",label:"File activity",desc:"Files you upload or open"},{key:"capture_searches",label:"Search queries",desc:"What you search for in global search"}];function we(){const[r,l]=s.useState(null),[t,x]=s.useState(null),[a,d]=s.useState(null);s.useEffect(()=>{fetch("/api/user-memory/settings").then(n=>n.ok?n.json():null).then(n=>{l(n||{})}).catch(()=>{l({}),d("Could not load memory settings.")}),fetch("/api/user-memory/stats").then(n=>n.ok?n.json():null).then(n=>{n&&x(n)}).catch(()=>{})},[]);const i=(n,c)=>{const m={...r||{},[n]:c};l(m),fetch("/api/user-memory/settings",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({[n]:c})}).then(h=>{h.ok?d(null):d(`Failed to save setting (${h.status})`)}).catch(()=>d("Could not reach backend."))};return r?e.jsxs("section",{"aria-label":"Memory capture settings",children:[e.jsx("h2",{className:"text-lg font-semibold mb-2",children:"Memory Capture"}),e.jsx("p",{className:"text-sm text-shell-text-tertiary mb-5",children:"Choose what activity gets saved to your personal memory index. All data stays on this device."}),a&&e.jsxs("p",{className:"mb-3 text-xs text-amber-400 flex items-center gap-1.5",children:[e.jsx(F,{size:12})," ",a]}),e.jsx("div",{className:"space-y-2",children:ve.map(n=>{const c=!!r[n.key],m=`capture-${String(n.key)}`;return e.jsxs(w,{className:"p-4 flex items-center justify-between gap-3",children:[e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsx(N,{htmlFor:m,className:"text-sm font-medium text-shell-text",children:n.label}),e.jsx("p",{className:"text-xs text-shell-text-tertiary mt-0.5",children:n.desc})]}),e.jsx(T,{id:m,checked:c,onCheckedChange:h=>i(n.key,h),"aria-label":`Capture ${n.label}`})]},String(n.key))})}),t&&e.jsxs(w,{className:"mt-6 p-4",children:[e.jsx("h3",{className:"text-sm font-medium mb-3",children:"Stored chunks"}),e.jsxs("div",{className:"text-xs text-shell-text-secondary mb-2 tabular-nums",children:["Total: ",t.total]}),Object.keys(t.collections||{}).length>0?e.jsx("ul",{className:"space-y-1 text-xs text-shell-text-tertiary",children:Object.entries(t.collections).map(([n,c])=>e.jsxs("li",{className:"flex justify-between tabular-nums",children:[e.jsx("span",{children:n}),e.jsx("span",{children:c})]},n))}):e.jsx("p",{className:"text-xs text-shell-text-tertiary",children:"No memories captured yet."})]})]}):e.jsxs("section",{"aria-label":"Memory capture settings",children:[e.jsx("h2",{className:"text-lg font-semibold mb-5",children:"Memory Capture"}),e.jsx("p",{className:"text-sm text-shell-text-tertiary",children:"Loading..."})]})}function ke(){const[r,l]=s.useState(null),[t,x]=s.useState(!1),a=async()=>{x(!0),l(null);try{const d=await fetch("/api/backup",{method:"POST"});d.ok?l("Backup created successfully."):l(`Backup failed (${d.status}). API may not be available yet.`)}catch{l("Could not reach backup endpoint. API not available yet.")}x(!1)};return e.jsxs("section",{"aria-label":"Backup and restore",children:[e.jsx("h2",{className:"text-lg font-semibold mb-5",children:"Backup & Restore"}),e.jsxs(w,{className:"p-4 space-y-4",children:[e.jsxs("div",{children:[e.jsx("h3",{className:"text-sm font-medium mb-2",children:"Create Backup"}),e.jsx("p",{className:"text-xs text-shell-text-tertiary mb-3",children:"Export all agents, memory, and configuration as a backup archive."}),e.jsxs(b,{size:"sm",onClick:a,disabled:t,children:[e.jsx(K,{size:14,className:t?"animate-bounce":""}),t?"Creating...":"Create Backup"]}),r&&e.jsx("p",{className:`mt-2 text-xs ${r.includes("success")?"text-emerald-400":"text-amber-400"}`,children:r})]}),e.jsx("hr",{className:"border-white/5"}),e.jsxs("div",{children:[e.jsx("h3",{className:"text-sm font-medium mb-2",children:"Restore from Backup"}),e.jsx("p",{className:"text-xs text-shell-text-tertiary mb-3",children:"Upload a previously created backup archive to restore."}),e.jsxs("label",{className:"flex flex-col items-center gap-2 p-6 rounded-lg border-2 border-dashed border-white/10 hover:border-white/20 transition-colors cursor-pointer",children:[e.jsx(me,{size:24,className:"text-shell-text-tertiary"}),e.jsx("span",{className:"text-xs text-shell-text-tertiary",children:"Click to select a backup file"}),e.jsx("input",{type:"file",accept:".tar.gz,.zip,.bak",className:"hidden","aria-label":"Upload backup file"})]})]})]})]})}function W({onClose:r}){const[l,t]=s.useState(null),[x,a]=s.useState(!1);s.useEffect(()=>{let n=!1,c=null,m=null,h=!1;const p=()=>{h||n||(h=!0,c&&clearInterval(c),m=setInterval(async()=>{if(!n)try{(await fetch("/api/settings/update-status")).ok&&(a(!0),m&&clearInterval(m),setTimeout(()=>{n||window.location.reload()},500))}catch{}},2e3))};return c=setInterval(async()=>{if(!n)try{const o=await fetch("/api/system/restart/status");if(o.ok){const j=await o.json();t(j),j.phase==="restarting"&&p()}}catch{p()}},1e3),()=>{n=!0,c&&clearInterval(c),m&&clearInterval(m)}},[]);const d=l?Object.entries(l.agents):[];function i(n){return n==="ready"?e.jsx("span",{className:"text-[10px] px-1.5 py-0.5 rounded bg-emerald-500/20 text-emerald-300",children:"ready"}):n==="timeout"?e.jsx("span",{className:"text-[10px] px-1.5 py-0.5 rounded bg-amber-500/20 text-amber-300",children:"timeout"}):n==="error"?e.jsx("span",{className:"text-[10px] px-1.5 py-0.5 rounded bg-red-500/20 text-red-300",children:"error"}):e.jsxs("span",{className:"text-[10px] px-1.5 py-0.5 rounded bg-sky-500/20 text-sky-300 flex items-center gap-1",children:[e.jsx(P,{size:10,className:"animate-spin"}),n]})}return e.jsx("div",{role:"dialog","aria-modal":"true","aria-label":"Restart progress",className:"fixed inset-0 z-50 flex items-center justify-center bg-black/60",children:e.jsxs("div",{className:"bg-shell-surface border border-white/10 rounded-xl p-6 w-full max-w-md shadow-xl space-y-4",children:[e.jsx("h3",{className:"text-base font-semibold",children:x?"Restarted — reloading…":d.length>0?"Preparing agents for restart":"Restarting server…"}),d.length>0&&e.jsx("ul",{className:"space-y-1","aria-label":"Agent preparation status",children:d.map(([n,c])=>e.jsxs("li",{className:"flex items-center justify-between text-sm",children:[e.jsx("span",{className:"text-shell-text-secondary",children:n}),i(c.status)]},n))}),(l==null?void 0:l.phase)==="restarting"&&!x&&e.jsx("p",{className:"text-xs text-shell-text-tertiary",children:"Waiting for server to come back…"}),!l&&e.jsxs("p",{className:"text-xs text-shell-text-tertiary flex items-center gap-1",children:[e.jsx(P,{size:12,className:"animate-spin"})," Connecting…"]}),e.jsx("div",{className:"flex justify-end",children:e.jsx(b,{variant:"outline",size:"sm",onClick:r,"aria-label":"Cancel restart progress dialog",children:"Cancel"})})]})})}function Se(){const[r,l]=s.useState(!1),[t,x]=s.useState(!1),[a,d]=s.useState(null),[i,n]=s.useState(null),[c,m]=s.useState({check_enabled:!0,auto_apply:!1,auto_restart:!1}),[h,p]=s.useState(null),[o,j]=s.useState(!1),[v,k]=s.useState(!1);s.useEffect(()=>{(async()=>{try{const y=await fetch("/api/preferences/auto-update");if(y.ok){const u=await y.json();u&&typeof u=="object"&&m({check_enabled:u.check_enabled??!0,auto_apply:u.auto_apply??!1,auto_restart:u.auto_restart??!1})}}catch{}try{const y=await fetch("/api/settings/update-check");y.ok&&d(await y.json())}catch{}try{const y=await fetch("/api/settings/update-status");y.ok&&p(await y.json())}catch{}})()},[]);const S=s.useCallback(async y=>{m(y);try{await fetch("/api/preferences/auto-update",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(y)})}catch{}},[]),g=async()=>{l(!0),n(null);try{const y=await fetch("/api/settings/update-check");if(y.ok){const u=await y.json();d(u),n(u.has_updates?"A new version is available.":"You are up to date.")}else n("Update check not available.")}catch{n("Could not reach update server.")}l(!1)},R=async()=>{x(!0),n(null);try{const y=await fetch("/api/settings/update",{method:"POST"});if(y.ok){const u=await y.json().catch(()=>({}));if(u.status==="restarting")j(!0);else{n(u.message??"Update applied. Restart the server to finish."),k(!0);const U=await fetch("/api/settings/update-check");U.ok&&d(await U.json());const C=await fetch("/api/settings/update-status");C.ok&&p(await C.json())}}else{const u=await y.json().catch(()=>({}));n(u.error??"Update failed.")}}catch{n("Could not apply update.")}x(!1)},_=async()=>{j(!0);try{await fetch("/api/system/restart/prepare",{method:"POST"})}catch{}},I=!!(h!=null&&h.pending_restart_sha);return e.jsxs("section",{"aria-label":"System updates",children:[e.jsx("h2",{className:"text-lg font-semibold mb-5",children:"Updates"}),I&&e.jsxs("div",{className:"mb-4 flex items-center justify-between gap-3 rounded-lg border border-amber-500/30 bg-amber-500/10 px-4 py-3",children:[e.jsxs("div",{className:"flex items-center gap-2 text-sm text-amber-200",children:[e.jsx(F,{size:16,className:"shrink-0"}),e.jsxs("span",{children:["Update pulled — restart to finish applying (",h.pending_restart_sha.slice(0,7),")"]})]}),e.jsx(b,{size:"sm",onClick:_,"aria-label":"Restart server to apply update",children:"Restart now"})]}),e.jsxs(w,{className:"p-4 space-y-4",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("div",{className:"p-2 rounded-lg bg-white/5 text-sky-400",children:e.jsx(xe,{size:20})}),e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsx("p",{className:"text-sm font-medium",children:"taOS"}),a!=null&&a.has_updates&&a.new_commit?e.jsxs("div",{className:"flex flex-col gap-0.5",children:[e.jsxs("p",{className:"text-xs text-shell-text-tertiary tabular-nums",children:[e.jsx("span",{className:"text-white/40",children:"installed "}),a.current_commit]}),e.jsxs("p",{className:"text-xs text-amber-300/90 tabular-nums",children:[e.jsx("span",{className:"text-amber-300/50",children:"available "}),a.new_commit]})]}):e.jsx("p",{className:"text-xs text-shell-text-tertiary tabular-nums",children:(a==null?void 0:a.current_commit)??"v0.1.0-dev"})]}),(a==null?void 0:a.has_updates)&&e.jsx("span",{className:"text-[10px] px-2 py-1 rounded-full font-semibold bg-amber-500/20 text-amber-300",children:"Update available"})]}),e.jsxs("div",{className:"flex gap-2 flex-wrap",children:[e.jsxs(b,{variant:"outline",size:"sm",onClick:g,disabled:r,children:[e.jsx(P,{size:14,className:r?"animate-spin":""}),r?"Checking...":"Check Now"]}),v?e.jsx(b,{size:"sm",onClick:_,"aria-label":"Restart server to apply update",children:"Restart Now"}):a!=null&&a.has_updates?e.jsx(b,{size:"sm",onClick:R,disabled:t,children:t?"Installing...":"Install Update"}):null]}),i&&e.jsxs("div",{className:"flex items-start gap-2 text-xs",children:[i.includes("up to date")||i.includes("applied")?e.jsx(D,{size:14,className:"text-emerald-400 shrink-0 mt-0.5"}):e.jsx(F,{size:14,className:"text-amber-400 shrink-0 mt-0.5"}),e.jsx("span",{className:"text-shell-text-secondary",children:i})]}),e.jsxs("div",{className:"border-t border-white/5 pt-4 space-y-3",children:[e.jsxs("div",{className:"flex items-center justify-between gap-3",children:[e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsx(N,{className:"text-sm",children:"Check for updates automatically"}),e.jsx("p",{className:"text-[11px] text-shell-text-tertiary mt-0.5",children:"Polls GitHub hourly and notifies when a new version is available."})]}),e.jsx(T,{checked:c.check_enabled??!0,onCheckedChange:y=>S({...c,check_enabled:y})})]}),e.jsxs("div",{className:"flex items-center justify-between gap-3",children:[e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsx(N,{className:"text-sm",children:"Install updates automatically"}),e.jsx("p",{className:"text-[11px] text-shell-text-tertiary mt-0.5",children:"Pulls + installs new versions as soon as they're detected. You'll still need to restart the server manually."})]}),e.jsx(T,{checked:c.auto_apply??!1,onCheckedChange:y=>S({...c,auto_apply:y}),disabled:!(c.check_enabled??!0)})]}),e.jsxs("div",{className:"flex items-center justify-between gap-3",children:[e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsx(N,{className:"text-sm",children:"Automatically restart after update"}),e.jsx("p",{className:"text-[11px] text-shell-text-tertiary mt-0.5",children:c.auto_restart?"Server will restart automatically once an update is pulled.":"We'll remind you every 6 hours when a restart is pending."})]}),e.jsx(T,{checked:c.auto_restart??!1,onCheckedChange:y=>S({...c,auto_restart:y}),"aria-label":"Automatically restart after update"})]})]})]}),o&&e.jsx(W,{onClose:()=>j(!1)})]})}function Ce(){const[r,l]=s.useState(`# taOS Configuration
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Default YAML template is malformed (embedded line numbers).

At Line 1 (function Ce), the initial config string includes literal lines 2, 3, 4, … 15, which makes the default YAML invalid and can lead to bad saves when backend config load fails.

Proposed fix
-const[r,l]=s.useState(`# taOS Configuration
-2
-# Edit YAML below
-3
-
-4
-server:
-5
-  port: 3000
-6
-  host: 0.0.0.0
-7
-
-8
-agents:
-9
-  max_concurrent: 5
-10
-  default_model: qwen2.5-7b
-11
-
-12
-providers:
-13
-  - name: rkllama
-14
-    url: http://localhost:8080
-15
-`)
+const[r,l]=s.useState(`# taOS Configuration
+# Edit YAML below
+
+server:
+  port: 3000
+  host: 0.0.0.0
+
+agents:
+  max_concurrent: 5
+  default_model: qwen2.5-7b
+
+providers:
+  - name: rkllama
+    url: http://localhost:8080
+`)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@static/desktop/assets/SettingsApp-Dxe4E4Jg.js` at line 1, The default YAML
template in function Ce contains embedded literal line numbers (e.g. "2", "3",
…) making the config invalid; replace the initial useState string in Ce with a
clean, valid YAML template (no line-number markers) that reflects expected keys
(e.g. top-level comments and example fields) so backend saves/loads valid YAML
when defaults are used.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
docs/superpowers/notes/2026-04-19-multi-framework-chat-routing.md (1)

44-50: Convert ephemeral “not yet in git” notes into durable tracking references.

This section will age quickly and become misleading. Prefer linking to an issue/branch name or attaching a patch reference so the note stays actionable.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/superpowers/notes/2026-04-19-multi-framework-chat-routing.md` around
lines 44 - 50, Replace the ephemeral “not yet in git” note with durable tracking
references: create a short-lived branch (e.g., pi-experimental-install-script)
containing the changes to deployer.py and scripts/install_hermes.sh, open an
issue summarizing the experiment and link that branch, and/or attach a patch
(git format-patch or the Pi’s stash/commit hash) to the issue; update this
document to point to the issue/branch/patch reference so readers can find the
exact changes later rather than relying on transient local edits.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/superpowers/notes/2026-04-19-multi-framework-chat-routing.md`:
- Around line 46-50: Replace the exposed private LAN IP and personal home path
in the note with redacted placeholders (e.g., "<REDACTED_IP>" and
"<REDACTED_USER_PATH>") so the lines that mention the Pi and local edits no
longer contain "192.168.6.123" or "/home/jay/tinyagentos"; update the text in
the note file(s) (deployer.py mention and install_hermes.sh reference) to use
those placeholders and commit the change, and if the sensitive strings were
already committed to history run an appropriate history-rewrite (interactive
rebase or git-filter-repo) to purge them from the repo history before pushing.

In `@static/desktop/assets/StoreApp-CaxvIj4e.js`:
- Line 1: The catalog parsing in the be component sets compat unconditionally to
"green" when the server response lacks compat (inside the h callback where it
builds y with compat:m.compat??"green"), causing all items to appear compatible;
update the parsing logic in the h callback (the fetch("/api/store/catalog")
handler in function be) to derive compat from m.hardware_tiers (or respect a
provided compat field) instead of defaulting to "green" — compute a resolved
compat value (green/yellow/red) from hardware_tiers (or treat missing compat as
unknown and show a neutral badge) and assign that to the compat property in the
mapped object so the UI reflects real compatibility.
- Line 1: Summary: The fallback catalog `ae` hardcodes installed:true for some
apps, causing incorrect UI/uninstall calls; replace it with metadata-only
entries that default installed state to false (or remove the installed field)
and use that in the fallback path inside be (where g(ae) is called). Fix: create
a metadata-only fallback constant (e.g., fallbackCatalogMetadata or sanitize
`ae`) that ensures every entry has installed:false (or no installed property),
then update the effect in be (the useEffect that does `!await
h()&&!s&&(g(ae),x(!1))`) to call g with the sanitized fallback; keep other
symbols the same (ae, be, g) so lookup is straightforward.

---

Nitpick comments:
In `@docs/superpowers/notes/2026-04-19-multi-framework-chat-routing.md`:
- Around line 44-50: Replace the ephemeral “not yet in git” note with durable
tracking references: create a short-lived branch (e.g.,
pi-experimental-install-script) containing the changes to deployer.py and
scripts/install_hermes.sh, open an issue summarizing the experiment and link
that branch, and/or attach a patch (git format-patch or the Pi’s stash/commit
hash) to the issue; update this document to point to the issue/branch/patch
reference so readers can find the exact changes later rather than relying on
transient local edits.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8b70aa92-37b0-4975-9845-11eb79c35c78

📥 Commits

Reviewing files that changed from the base of the PR and between 3ccd512 and a36837f.

📒 Files selected for processing (11)
  • desktop/src/components/agent-settings/FrameworkTab.tsx
  • docs/superpowers/notes/2026-04-19-multi-framework-chat-routing.md
  • static/desktop/assets/AgentsApp-D8hlDDmJ.js
  • static/desktop/assets/MCPApp-Ci8OBr3I.js
  • static/desktop/assets/ProvidersApp-_hGWvOIX.js
  • static/desktop/assets/SettingsApp-B0L5VU3t.js
  • static/desktop/assets/StoreApp-CaxvIj4e.js
  • static/desktop/assets/framework-api-B6uX1y0T.js
  • static/desktop/assets/main-BPhVBqDl.js
  • static/desktop/index.html
  • tinyagentos/frameworks.py
✅ Files skipped from review due to trivial changes (3)
  • static/desktop/assets/MCPApp-Ci8OBr3I.js
  • static/desktop/assets/SettingsApp-B0L5VU3t.js
  • static/desktop/assets/ProvidersApp-_hGWvOIX.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • desktop/src/components/agent-settings/FrameworkTab.tsx

Comment thread docs/superpowers/notes/2026-04-19-multi-framework-chat-routing.md Outdated
@@ -0,0 +1 @@
import{r,j as t}from"./vendor-react-l6srOxy7.js";import{B as A,I as D,C as B,a as F,c as W,k as G}from"./toolbar-UW6q5pkx.js";import{a as E}from"./framework-api-B6uX1y0T.js";import{am as z,S as _,Y as I,an as T,v as K,ao as L,ap as V,C as P,u as R,aq as U,I as H,ar as $,as as Y,_ as Z,H as J,f as X,y as ee,ad as te}from"./vendor-icons-DcMSPw1y.js";import"./vendor-radix-BhM7AEEG.js";import"./vendor-layout-B-pp9n1f.js";const S=[{id:"all",label:"All Apps",icon:t.jsx(z,{size:16}),types:[],description:"Browse everything"},{id:"frameworks",label:"Agent Frameworks",icon:t.jsx(K,{size:16}),types:["agent-framework"],description:"Execution engines for your AI agents"},{id:"models",label:"Models",icon:t.jsx(L,{size:16}),types:["model"],description:"Language models for inference"},{id:"memory",label:"Memory",icon:t.jsx(L,{size:16}),types:["memory"],description:"Memory backends and knowledge stores for agents"},{id:"plugins",label:"Plugins",icon:t.jsx(V,{size:16}),types:["plugin"],description:"Tools and capabilities for agents"},{id:"mcp-server",label:"MCP Servers",icon:t.jsx(P,{size:16}),types:["mcp"],description:"Model Context Protocol servers"},{id:"services",label:"Services",icon:t.jsx(R,{size:16}),types:["service"],description:"Infrastructure and backends"},{id:"streaming",label:"Streaming Apps",icon:t.jsx(U,{size:16}),types:["streaming-app"],description:"Desktop apps streamed via KasmVNC"},{id:"image",label:"Image Generation",icon:t.jsx(H,{size:16}),types:["image-gen","image-model"],description:"Stable Diffusion and image models"},{id:"audio",label:"Audio & Voice",icon:t.jsx($,{size:16}),types:["voice","audio"],description:"TTS, STT, and music generation"},{id:"video",label:"Video",icon:t.jsx(Y,{size:16}),types:["video-gen"],description:"Video generation tools"},{id:"devtools",label:"Dev Tools",icon:t.jsx(Z,{size:16}),types:["dev-tool"],description:"Development and coding tools"},{id:"home",label:"Home & Monitor",icon:t.jsx(J,{size:16}),types:["home","monitoring"],description:"Home automation and monitoring"},{id:"infra",label:"Infrastructure",icon:t.jsx(P,{size:16}),types:["infrastructure"],description:"System services and networking"}],ae=[{id:"smolagents",name:"SmolAgents",type:"agent-framework",version:"1.0.0",description:"HuggingFace code-based agents — well-documented, 26k stars",installed:!1,compat:"green"},{id:"pocketflow",name:"PocketFlow",type:"agent-framework",version:"1.0.0",description:"Minimal 100-line framework, zero deps, graph-based",installed:!1,compat:"green"},{id:"openclaw",name:"OpenClaw",type:"agent-framework",version:"1.0.0",description:"Full-featured multi-channel agent framework",installed:!0,compat:"green"},{id:"langroid",name:"Langroid",type:"agent-framework",version:"1.0.0",description:"Multi-agent message-passing framework",installed:!1,compat:"green"},{id:"openai-agents-sdk",name:"OpenAI Agents SDK",type:"agent-framework",version:"1.0.0",description:"Provider-agnostic agent SDK from OpenAI",installed:!1,compat:"green"},{id:"qwen3-4b",name:"Qwen3 4B",type:"model",version:"3.0.0",description:"Good balance of speed and capability for most tasks",installed:!0,compat:"green"},{id:"qwen3-1.7b",name:"Qwen3 1.7B",type:"model",version:"3.0.0",description:"Fast, fits comfortably in 8GB RAM",installed:!1,compat:"green"},{id:"qwen3-8b",name:"Qwen3 8B",type:"model",version:"3.0.0",description:"Most capable local model for 16GB devices",installed:!1,compat:"yellow"},{id:"mcp-pandoc",name:"MCP Pandoc",type:"mcp",version:"0.1.0",description:"Document format conversion — markdown, docx, pdf, 30+ formats",installed:!1,compat:"green"},{id:"mcp-server-office",name:"MCP Office Docs",type:"mcp",version:"0.1.0",description:"Read, write, and edit .docx files programmatically",installed:!1,compat:"green"},{id:"playwright-mcp",name:"Playwright MCP",type:"mcp",version:"1.0.0",description:"Browser automation for agents via Playwright",installed:!1,compat:"green"},{id:"github-mcp-server",name:"GitHub MCP",type:"mcp",version:"1.0.0",description:"Issues, PRs, repos, search — official GitHub MCP",installed:!1,compat:"green"},{id:"mcp-memory",name:"MCP Memory",type:"mcp",version:"1.0.0",description:"Knowledge graph memory for persistent context",installed:!1,compat:"green"},{id:"web-search",name:"Web Search",type:"plugin",version:"0.3.0",description:"Search the web via SearXNG or Perplexica",installed:!1,compat:"green"},{id:"image-generation-tool",name:"Image Generation",type:"plugin",version:"0.1.0",description:"Generate images via Stable Diffusion",installed:!1,compat:"green"},{id:"searxng",name:"SearXNG",type:"service",version:"latest",description:"Privacy-respecting metasearch engine",installed:!1,compat:"green"},{id:"gitea",name:"Gitea",type:"service",version:"latest",description:"Lightweight self-hosted Git service",installed:!1,compat:"green"},{id:"n8n",name:"n8n",type:"service",version:"latest",description:"Workflow automation platform",installed:!1,compat:"green"},{id:"code-server-kasm",name:"Code Server (Streamed)",type:"streaming-app",version:"latest",description:"VS Code in the browser via KasmVNC",installed:!1,compat:"green"},{id:"blender",name:"Blender",type:"streaming-app",version:"latest",description:"3D creation suite streamed via KasmVNC",installed:!1,compat:"yellow"},{id:"libreoffice",name:"LibreOffice",type:"streaming-app",version:"latest",description:"Full office suite streamed via KasmVNC",installed:!1,compat:"green"},{id:"comfyui",name:"ComfyUI",type:"image-gen",version:"latest",description:"Node-based Stable Diffusion workflow editor",installed:!1,compat:"yellow"},{id:"fooocus",name:"Fooocus",type:"image-gen",version:"latest",description:"Simple Stable Diffusion with minimal setup",installed:!1,compat:"yellow"},{id:"kokoro-tts",name:"Kokoro TTS",type:"voice",version:"latest",description:"High-quality text-to-speech",installed:!1,compat:"green"},{id:"whisper-stt",name:"Whisper STT",type:"voice",version:"latest",description:"OpenAI Whisper speech-to-text",installed:!1,compat:"green"},{id:"animatediff",name:"AnimateDiff",type:"video-gen",version:"latest",description:"AI video generation from text and images",installed:!1,compat:"yellow"},{id:"corridorkey",name:"CorridorKey",type:"video-gen",version:"latest",description:"AI video generation via ComfyUI workflows",installed:!1,compat:"yellow"},{id:"code-server",name:"Code Server",type:"dev-tool",version:"latest",description:"VS Code in the browser — remote development environment",installed:!1,compat:"green"},{id:"jupyter-lab",name:"JupyterLab",type:"dev-tool",version:"latest",description:"Interactive notebooks for data science and experimentation",installed:!1,compat:"green"},{id:"home-assistant",name:"Home Assistant",type:"home",version:"latest",description:"Open-source home automation platform",installed:!1,compat:"green"},{id:"uptime-kuma",name:"Uptime Kuma",type:"monitoring",version:"latest",description:"Self-hosted monitoring tool — track uptime for services and APIs",installed:!1,compat:"green"},{id:"tailscale",name:"Tailscale",type:"infrastructure",version:"latest",description:"Zero-config mesh VPN for secure networking between devices",installed:!1,compat:"green"},{id:"caddy",name:"Caddy",type:"infrastructure",version:"latest",description:"Automatic HTTPS reverse proxy and web server",installed:!1,compat:"green"}],se={"agent-framework":"bg-blue-500/20 text-blue-400",model:"bg-slate-500/20 text-slate-400",service:"bg-amber-500/20 text-amber-400",plugin:"bg-teal-500/20 text-teal-400",mcp:"bg-violet-500/20 text-violet-400","streaming-app":"bg-indigo-500/20 text-indigo-400","image-gen":"bg-pink-500/20 text-pink-400","image-model":"bg-pink-500/20 text-pink-400",voice:"bg-orange-500/20 text-orange-400",audio:"bg-orange-500/20 text-orange-400","video-gen":"bg-red-500/20 text-red-400","dev-tool":"bg-cyan-500/20 text-cyan-400",home:"bg-green-500/20 text-green-400",monitoring:"bg-green-500/20 text-green-400",infrastructure:"bg-slate-500/20 text-slate-400"},ie={"agent-framework":"Framework",model:"Model",service:"Service",plugin:"Plugin",mcp:"MCP Server","streaming-app":"Streaming","image-gen":"Image Gen","image-model":"Image Model",voice:"Voice",audio:"Audio","video-gen":"Video","dev-tool":"Dev Tool",home:"Home",monitoring:"Monitor",infrastructure:"Infra"},ne={green:"bg-emerald-400",yellow:"bg-amber-400",red:"bg-red-400"},re={green:"Compatible",yellow:"Partial",red:"Unsupported"},oe={"agent-framework":"linear-gradient(135deg, rgba(59,130,246,0.3), rgba(59,130,246,0.1))",model:"linear-gradient(135deg, rgba(139,92,246,0.3), rgba(139,92,246,0.1))",service:"linear-gradient(135deg, rgba(245,158,11,0.3), rgba(245,158,11,0.1))",plugin:"linear-gradient(135deg, rgba(20,184,166,0.3), rgba(20,184,166,0.1))","streaming-app":"linear-gradient(135deg, rgba(99,102,241,0.3), rgba(99,102,241,0.1))","image-gen":"linear-gradient(135deg, rgba(236,72,153,0.3), rgba(236,72,153,0.1))",voice:"linear-gradient(135deg, rgba(249,115,22,0.3), rgba(249,115,22,0.1))","dev-tool":"linear-gradient(135deg, rgba(6,182,212,0.3), rgba(6,182,212,0.1))"},a=i=>`https://cdn.simpleicons.org/${i}/ffffff`,e=i=>`https://github.com/${i}.png?size=96`,q={smolagents:e("huggingface"),pocketflow:e("The-Pocket"),openclaw:"/static/store-icons/openclaw.jpg",nanoclaw:e("openclaw"),picoclaw:"https://raw.githubusercontent.com/sipeed/picoclaw/main/assets/logo.webp",zeroclaw:e("nicholasgasior"),microclaw:e("nicholasgasior"),ironclaw:e("nicholasgasior"),nullclaw:e("nicholasgasior"),shibaclaw:"https://raw.githubusercontent.com/RikyZ90/ShibaClaw/main/assets/shibaclaw_logo.png",moltis:e("moltis-ai"),hermes:e("NousResearch"),"agent-zero":e("frdel"),"openai-agents-sdk":a("openai"),langroid:e("langroid"),"qwen2.5-0.5b":e("QwenLM"),"qwen2.5-1.5b":e("QwenLM"),"qwen2.5-3b":e("QwenLM"),"qwen2.5-7b":e("QwenLM"),"qwen2.5-14b":e("QwenLM"),"qwen2.5-32b":e("QwenLM"),"qwen2.5-72b":e("QwenLM"),"qwen2.5-1.5b-rkllm":e("QwenLM"),"qwen2.5-3b-rkllm":e("QwenLM"),"qwen2.5-7b-rkllm":e("QwenLM"),"qwen2.5-14b-rkllm":e("QwenLM"),"qwen2.5-coder-7b":e("QwenLM"),"qwen2.5-coder-14b":e("QwenLM"),"qwen2.5-vl-7b":e("QwenLM"),"qwen2-vl-7b":e("QwenLM"),"qwen3-1.7b":e("QwenLM"),"qwen3-4b":e("QwenLM"),"qwen3-8b":e("QwenLM"),"qwen3-14b":e("QwenLM"),"qwen3-30b-a3b":e("QwenLM"),"qwen3-32b":e("QwenLM"),"qwen3-embedding-0.6b":e("QwenLM"),"qwen3-reranker-0.6b":e("QwenLM"),"llama-3.1-8b":a("meta"),"llama-3.2-1b":a("meta"),"llama-3.2-3b":a("meta"),"llama-3.3-70b":a("meta"),"llama-3-70b":a("meta"),"gemma-2-2b":a("googlegemini"),"gemma-2-9b":a("googlegemini"),"gemma-3-1b":a("googlegemini"),"gemma-3-4b":a("googlegemini"),"gemma-3-12b":a("googlegemini"),"phi-3.5-mini":e("microsoft"),"phi-4":e("microsoft"),"phi-4-mini":e("microsoft"),"mistral-7b-v0.3":e("mistralai"),"mistral-nemo-12b":e("mistralai"),"mixtral-8x7b":e("mistralai"),"ministral-3b":e("mistralai"),"deepseek-r1-14b":e("deepseek-ai"),"deepseek-coder-v2-lite":e("deepseek-ai"),"granite-3.1-2b":e("ibm-granite"),"granite-3.1-8b":e("ibm-granite"),"command-r-35b":e("cohere"),smollm2:e("huggingface"),"smollm2-135m":e("huggingface"),"smollm2-360m":e("huggingface"),"tinyllama-1.1b":e("jzhang38"),"nemotron-mini-4b":e("NVIDIA"),"pelochus-qwen-1.8b-rkllm":e("pelochus"),"llava-1.6-mistral-7b":e("haotian-liu"),"llava-phi-3-mini":e("haotian-liu"),"minicpm-v-2.6":e("OpenBMB"),moondream2:e("vikhyat"),"florence-2-base":e("microsoft"),"bge-large-en-v1.5":e("FlagOpen"),"bge-small-en-v1.5":e("FlagOpen"),"bge-m3":e("FlagOpen"),"bge-reranker-v2-m3":e("FlagOpen"),"nomic-embed-text-v1.5":e("nomic-ai"),"mxbai-embed-large":e("mixedbread-ai"),"snowflake-arctic-embed-m":e("Snowflake-Labs"),"whisper-tiny":a("openai"),"whisper-base":a("openai"),"whisper-small":a("openai"),"whisper-medium":a("openai"),"whisper-large-v3":a("openai"),"whisper-large-v3-turbo":a("openai"),"kokoro-tts":e("hexgrad"),"piper-en-lessac":e("rhasspy"),"parakeet-tdt-0.6b":e("NVIDIA"),"sd-v1.5-lcm":e("Stability-AI"),"dreamshaper-8-lcm":e("Lykon"),"lcm-dreamshaper-v7":e("Lykon"),"sdxl-turbo":e("Stability-AI"),"sdxl-lightning":e("ByteDance"),"sd3.5-large-turbo-gguf":e("Stability-AI"),"flux-dev-gguf":e("black-forest-labs"),"flux-schnell-gguf":e("black-forest-labs"),"flux-schnell-unsloth":e("black-forest-labs"),"pixart-sigma-512":e("PixArt-alpha"),"sdxs-512":e("IDKiro"),"playground-v2.5":e("playgroundai"),kolors:e("Kwai-Kolors"),"auraflow-v0.3":e("cloneofsimo"),"stable-cascade":e("Stability-AI"),"rmbg-1.4":e("briaai"),birefnet:e("ZhengPeng7"),"real-esrgan-x4":e("xinntao"),"4x-ultrasharp":e("xinntao"),"gfpgan-v1.4":e("TencentARC"),codeformer:e("sczhou"),"controlnet-canny":e("lllyasviel"),"controlnet-depth":e("lllyasviel"),"controlnet-openpose":e("lllyasviel"),"controlnet-openpose-sdxl":e("lllyasviel"),comfyui:e("comfyanonymous"),fooocus:e("lllyasviel"),"stable-diffusion-webui":e("AUTOMATIC1111"),"stable-diffusion-cpp":e("leejet"),fastsdcpu:e("rupeshs"),"rk-llama-cpp":e("marty1885"),"rk3588-sd-gpu":e("happyme531"),"rknn-stable-diffusion":e("happyme531"),"lcm-dreamshaper-rknn":e("happyme531"),"ltx-video":e("Lightricks"),wan2gp:e("alibaba"),musicgpt:e("gabotechs"),searxng:a("searxng"),gitea:a("gitea"),"code-server":e("coder"),n8n:a("n8n"),"home-assistant":a("homeassistant"),"uptime-kuma":a("uptimekuma"),filebrowser:e("filebrowser"),excalidraw:a("excalidraw"),memos:e("usememos"),linkwarden:e("linkwarden"),"open-webui":e("open-webui"),dify:e("langgenius"),perplexica:e("ItzCrazyKns"),litellm:e("BerriAI"),"stirling-pdf":e("Stirling-Tools"),"paperless-ngx":e("paperless-ngx"),docling:e("DS4SD"),libretranslate:e("LibreTranslate"),mailserver:e("docker-mailserver"),"chatterbox-tts":e("resemble-ai"),"piper-tts":e("rhasspy"),"kokoro-tts-server":e("remsky"),tailscale:a("tailscale"),ddns:e("ddclient"),exo:e("exo-explore"),"github-mcp-server":a("github"),"git-mcp":a("git"),"mcp-git":a("git"),"mcp-filesystem":e("modelcontextprotocol"),"mcp-fetch":e("modelcontextprotocol"),"mcp-memory":e("modelcontextprotocol"),"mcp-time":e("modelcontextprotocol"),"mcp-sequential-thinking":e("modelcontextprotocol"),"playwright-mcp":a("playwright"),"mcp-server-docker":a("docker"),"mcp-server-kubernetes":a("kubernetes"),"mongodb-mcp-server":a("mongodb"),"mcp-redis":a("redis"),"chroma-mcp":e("chroma-core"),"supabase-mcp":a("supabase"),dbhub:a("postgresql"),"mcp-toolbox-databases":e("googleapis"),"notion-mcp-server":a("notion"),"mcp-obsidian":a("obsidian"),"mcp-atlassian":a("atlassian"),"google-workspace-mcp":a("google"),"slack-mcp-server":a("slack"),"whatsapp-mcp":a("whatsapp"),"ha-mcp":a("homeassistant"),"mcp-email-server":e("modelcontextprotocol"),"aws-mcp":a("amazonaws"),"cloudflare-mcp":a("cloudflare"),"mcp-grafana":a("grafana"),"arxiv-mcp-server":a("arxiv"),"firecrawl-mcp":e("mendableai"),"exa-mcp-server":e("exa-labs"),"context7-mcp":e("upstash"),supergateway:e("supercorp-ai"),"browser-use-mcp":e("browser-use"),camoufox:e("daijro"),engram:e("engramhq"),"mcp-pandoc":e("jgeorgeson"),"mcp-server-office":e("GongRzhe"),"mcp-server-spreadsheet":e("GongRzhe"),"excel-mcp-server":e("haris-musa"),"markdownify-mcp":e("zcaceres"),"desktop-commander-mcp":e("wonderwhy-er"),mcpo:e("open-webui"),"youtube-transcript-mcp":a("youtube"),"todoist-mcp-server":a("todoist"),playwriter:a("playwright"),"image-generation-tool":e("comfyanonymous"),"code-server-kasm":e("coder"),blender:a("blender"),libreoffice:a("libreoffice"),"jupyter-lab":a("jupyter"),caddy:e("caddyserver"),animatediff:e("guoyww"),corridorkey:e("comfyanonymous"),"whisper-stt":a("openai")};function le(i){return q[i]?q[i]:i.startsWith("qwen")?e("QwenLM"):i.startsWith("llama")?a("meta"):i.startsWith("gemma")?a("googlegemini"):i.startsWith("phi-")?e("microsoft"):i.startsWith("whisper")?a("openai"):i.startsWith("deepseek")?e("deepseek-ai"):i.startsWith("mistral")||i.startsWith("mixtral")?e("mistralai"):i.startsWith("bge-")?e("FlagOpen"):i.startsWith("controlnet")?e("lllyasviel"):i.startsWith("flux-")?e("black-forest-labs"):i.startsWith("sd-")||i.startsWith("sdxl")||i.startsWith("sd3")?e("Stability-AI"):null}function ce({app:i,affected:p,onInstall:g,onUninstall:b}){const[v,c]=r.useState(!1),[u,N]=r.useState(!1),[x,w]=r.useState(null),k=le(i.id),C=async()=>{c(!0),w(null);try{const f=i.installed?"/api/store/uninstall":"/api/store/install",h=await fetch(f,{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify({app_id:i.id})});if(!h.ok){let l=`${i.installed?"Uninstall":"Install"} failed (${h.status})`;try{const d=await h.json();d!=null&&d.error&&(l=String(d.error))}catch{}w(l),c(!1);return}i.installed?b(i.id):g(i.id)}catch(f){w(f instanceof Error?f.message:"Network error")}c(!1)};return t.jsxs(B,{className:"flex flex-col rounded-2xl hover:-translate-y-0.5 hover:shadow-2xl hover:border-white/[0.12] transition-all duration-200",children:[t.jsx(F,{className:"p-5 pb-2",children:t.jsxs("div",{className:"flex items-start justify-between",children:[t.jsxs("div",{className:"flex items-center gap-3",children:[t.jsx("div",{className:"w-11 h-11 rounded-xl flex items-center justify-center shrink-0 overflow-hidden",style:{background:oe[i.type]??"rgba(255,255,255,0.06)"},children:k&&!u?t.jsx("img",{src:k,alt:"",className:"w-7 h-7 object-contain",onError:()=>N(!0),loading:"lazy"}):t.jsx(T,{className:"w-5 h-5 text-white/60"})}),t.jsxs("div",{className:"min-w-0",children:[t.jsxs("div",{className:"flex items-center gap-2 flex-wrap",children:[t.jsx("span",{className:"font-medium text-white/90 truncate text-sm",children:i.name}),i.installed&&t.jsx(X,{className:"w-3.5 h-3.5 text-emerald-400 shrink-0"}),p>0&&t.jsxs("span",{className:"bg-yellow-700/30 text-yellow-200 text-xs px-2 py-0.5 rounded ml-2 shrink-0",children:["Update available · ",p," ",p===1?"agent":"agents"]})]}),t.jsxs("span",{className:"text-[11px] text-white/30",children:["v",i.version]})]})]}),t.jsx("div",{className:"flex items-center gap-1",title:re[i.compat],children:t.jsx("span",{className:`w-1.5 h-1.5 rounded-full ${ne[i.compat]}`})})]})}),t.jsxs(W,{className:"px-5 py-2 flex flex-col gap-3 flex-1",children:[t.jsx("span",{className:`text-[10px] font-medium px-2 py-0.5 rounded-full w-fit ${se[i.type]??"bg-white/10 text-white/50"}`,children:ie[i.type]??i.type}),t.jsx("p",{className:"text-xs text-white/45 leading-relaxed flex-1",children:i.description})]}),t.jsxs(G,{className:"p-5 pt-2 flex flex-col gap-2 items-stretch",children:[x&&t.jsx("div",{role:"alert",className:"text-[11px] text-red-300 bg-red-500/10 border border-red-500/20 rounded px-2 py-1",children:x}),t.jsx(A,{variant:i.installed?"destructive":"default",size:"sm",className:"w-full",onClick:C,disabled:v,children:v?t.jsx(I,{className:"w-3.5 h-3.5 animate-spin"}):i.installed?t.jsxs(t.Fragment,{children:[t.jsx(ee,{className:"w-3.5 h-3.5"})," Uninstall"]}):t.jsxs(t.Fragment,{children:[t.jsx(te,{className:"w-3.5 h-3.5"})," Install"]})})]})]})}function be({windowId:i}){const[p,g]=r.useState([]),[b,v]=r.useState(""),[c,u]=r.useState("all"),[N,x]=r.useState(!0),[w,k]=r.useState({}),[C,f]=r.useState([]),h=r.useCallback(async()=>{try{const s=await fetch("/api/store/catalog",{headers:{Accept:"application/json"}}),n=s.headers.get("content-type")??"";if(s.ok&&n.includes("application/json")){const o=await s.json();if(Array.isArray(o)){const y=o.map(m=>({id:String(m.id),name:String(m.name??m.id),type:String(m.type??"plugin"),version:String(m.version??""),description:String(m.description??""),installed:!!m.installed,compat:m.compat??"green"}));return g(y),x(!1),!0}}}catch{}return!1},[]);r.useEffect(()=>{let s=!1;return(async()=>!await h()&&!s&&(g(ae),x(!1)))(),()=>{s=!0}},[h]),r.useEffect(()=>{const n=new URLSearchParams(window.location.hash.split("?")[1]||"").get("category");n&&u(n)},[]),r.useEffect(()=>{E().then(k).catch(()=>{}),fetch("/api/agents").then(s=>s.ok?s.json():[]).then(s=>f(Array.isArray(s)?s:(s==null?void 0:s.agents)??[])).catch(()=>{})},[]);const l=S.find(s=>s.id===c),d=p.filter(s=>{if(c!=="all"&&l&&!l.types.includes(s.type))return!1;if(b){const n=b.toLowerCase();return s.name.toLowerCase().includes(n)||s.description.toLowerCase().includes(n)}return!0}),O=r.useCallback(s=>{g(n=>n.map(o=>o.id===s?{...o,installed:!0}:o))},[]),Q=r.useCallback(s=>{g(n=>n.map(o=>o.id===s?{...o,installed:!1}:o))},[]),j={};for(const s of S){if(s.id==="all"){j[s.id]=p.length;continue}j[s.id]=p.filter(n=>s.types.includes(n.type)).length}const M=typeof window<"u"&&window.innerWidth<640;return t.jsxs("div",{className:`flex ${M?"flex-col":""} h-full overflow-hidden`,children:[M?t.jsx("div",{className:"flex overflow-x-auto gap-2 px-3 py-2 border-b border-shell-border shrink-0",children:S.map(s=>t.jsx(A,{variant:"outline",size:"sm",onClick:()=>u(s.id),className:`whitespace-nowrap rounded-full ${c===s.id?"bg-accent/15 text-accent border-accent/30":""}`,children:s.label},s.id))}):t.jsxs("div",{className:"w-52 shrink-0 border-r border-shell-border bg-shell-surface/30 flex flex-col overflow-y-auto",children:[t.jsx("div",{className:"px-3 py-3 border-b border-shell-border",children:t.jsxs("div",{className:"flex items-center gap-2",children:[t.jsx(z,{size:16,className:"text-accent"}),t.jsx("span",{className:"text-sm font-medium text-shell-text",children:"Store"})]})}),t.jsx("nav",{className:"flex-1 py-2 px-2 space-y-0.5",children:S.map(s=>t.jsxs(A,{variant:"ghost",size:"sm",onClick:()=>u(s.id),className:`w-full justify-start gap-2.5 text-xs ${c===s.id?"bg-accent/15 text-accent hover:bg-accent/20 hover:text-accent":""}`,children:[t.jsx("span",{className:"shrink-0",children:s.icon}),t.jsx("span",{className:"flex-1 truncate text-left",children:s.label}),j[s.id]?t.jsx("span",{className:"text-[10px] text-shell-text-tertiary",children:j[s.id]}):null]},s.id))})]}),t.jsxs("div",{className:"flex-1 flex flex-col overflow-hidden",children:[t.jsxs("header",{className:"shrink-0 px-5 py-4 border-b border-shell-border",children:[t.jsxs("div",{className:"flex items-center justify-between mb-1",children:[t.jsxs("div",{children:[t.jsx("h2",{className:"text-base font-medium text-shell-text",children:(l==null?void 0:l.label)??"All Apps"}),t.jsx("p",{className:"text-xs text-shell-text-tertiary",children:l==null?void 0:l.description})]}),t.jsxs("span",{className:"text-xs text-shell-text-tertiary",children:[d.length," apps"]})]}),t.jsxs("div",{className:"relative mt-3",children:[t.jsx(_,{className:"absolute left-3 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-shell-text-tertiary pointer-events-none z-10"}),t.jsx(D,{type:"text",placeholder:"Search...",value:b,onChange:s=>v(s.target.value),className:"pl-9","aria-label":"Search apps"})]})]}),t.jsx("div",{className:"flex-1 overflow-y-auto px-5 py-4",children:N?t.jsx("div",{className:"flex items-center justify-center h-40",children:t.jsx(I,{className:"w-6 h-6 text-shell-text-tertiary animate-spin"})}):d.length===0&&c==="memory"?t.jsxs("div",{className:"p-6 text-center opacity-70",children:["No third-party memory plugins yet. ",t.jsx("b",{children:"taOSmd"})," is installed by default and available on every agent."]}):d.length===0?t.jsxs("div",{className:"flex flex-col items-center justify-center h-40 text-shell-text-tertiary text-sm gap-2",children:[t.jsx(T,{className:"w-8 h-8"}),t.jsx("span",{children:"No apps in this category"})]}):t.jsx("div",{className:"grid grid-cols-[repeat(auto-fill,minmax(250px,1fr))] gap-4",children:d.map(s=>{const n=w[s.id],o=s.type==="agent-framework"?C.filter(y=>y.framework===s.id&&y.framework_version_sha&&n&&n.sha!==y.framework_version_sha).length:0;return t.jsx(ce,{app:s,affected:o,onInstall:O,onUninstall:Q},s.id)})})})]})]})}export{be as StoreApp,be as default};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Compatibility is always shown as green with the current catalog mapping.

/api/store/catalog does not return a compat field, so compat:m.compat??"green" makes every server-backed item look fully compatible after load. That masks degraded/unsupported entries and makes the badge misleading. Either compute compatibility from hardware_tiers on the frontend or have the API include a resolved compat value.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@static/desktop/assets/StoreApp-CaxvIj4e.js` at line 1, The catalog parsing in
the be component sets compat unconditionally to "green" when the server response
lacks compat (inside the h callback where it builds y with
compat:m.compat??"green"), causing all items to appear compatible; update the
parsing logic in the h callback (the fetch("/api/store/catalog") handler in
function be) to derive compat from m.hardware_tiers (or respect a provided
compat field) instead of defaulting to "green" — compute a resolved compat value
(green/yellow/red) from hardware_tiers (or treat missing compat as unknown and
show a neutral badge) and assign that to the compat property in the mapped
object so the UI reflects real compatibility.

⚠️ Potential issue | 🟡 Minor

The fallback catalog should not hardcode install state.

If /api/store/catalog fails, ae becomes the rendered source of truth, but it already marks some apps as installed. That can expose uninstall buttons for apps that are not actually installed on this machine and leads to confusing 404s from /api/store/uninstall. The fallback should be metadata-only and default installation state to unknown/false.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@static/desktop/assets/StoreApp-CaxvIj4e.js` at line 1, Summary: The fallback
catalog `ae` hardcodes installed:true for some apps, causing incorrect
UI/uninstall calls; replace it with metadata-only entries that default installed
state to false (or remove the installed field) and use that in the fallback path
inside be (where g(ae) is called). Fix: create a metadata-only fallback constant
(e.g., fallbackCatalogMetadata or sanitize `ae`) that ensures every entry has
installed:false (or no installed property), then update the effect in be (the
useEffect that does `!await h()&&!s&&(g(ae),x(!1))`) to call g with the
sanitized fallback; keep other symbols the same (ae, be, g) so lookup is
straightforward.


log "writing /root/.hermes/.env (gateway env vars)"
mkdir -p /root/.hermes /root/.hermes/gateway
cat > /root/.hermes/.env <<ENVEOF
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CRITICAL: Literal string "$MODEL" instead of variable value. This will set the model to the literal string "$MODEL" instead of the actual model name.

Suggested change
cat > /root/.hermes/.env <<ENVEOF
m["default"] = os.environ.get("TAOS_MODEL", "kilo-auto/free")

Similarly for api_key:

Suggested change
cat > /root/.hermes/.env <<ENVEOF
m["api_key"] = os.environ["LITELLM_API_KEY"]

jaylfc added 4 commits April 19, 2026 03:41
Each install_<framework>.sh deploys a Python framework into an LXC agent
container and starts a small bridge that subscribes to taOS's SSE event
stream (the same one openclaw consumes), runs each user_message through
the framework, and posts replies back via /api/openclaw/sessions/{slug}/reply.
This makes any python-pip-installable framework chat-routable through
existing taOS infrastructure without taOS-side changes.

Frameworks supported:
- hermes (NousResearch/hermes-agent, custom provider → LiteLLM)
- smolagents
- langroid
- pocketflow
- openai-agents-sdk

Picked up by deployer.py's script-install branch (when
tinyagentos/scripts/install_<framework>.sh exists, it runs that instead
of the broken pip3 install <framework> fallback).
…nai-agents-sdk uses explicit OpenAIChatCompletionsModel

Hermes:
- 'hermes gateway start --run-as-user root' is wrong API. The real CLI is 'gateway run' for foreground (recommended for containers per Hermes' own docs). My systemd unit invokes that directly so we own the lifecycle.
- The 'custom' provider's api_key MUST be set in /root/.hermes/config.yaml (model.api_key); env vars and cli-config.yaml are NOT consulted by the gateway's credential pool. Without this, Hermes returns 'Missing Authentication header' from LiteLLM.
- API_SERVER_ENABLED/HOST/PORT are read as env vars to enable the api_server platform.

OpenAI Agents SDK:
- 'kilo-auto/free' breaks the SDK's provider-prefix parser ('Unknown prefix: kilo-auto'). Use OpenAIChatCompletionsModel(model=..., openai_client=AsyncOpenAI(base_url=..., api_key=...)) to bypass.
- set_tracing_disabled(True) avoids 401 noise from the SDK trying to phone home to OpenAI.
…sion prefers body channel_id

Before: reply body didn't carry channel_id; bridge_session._handle_reply resolved each agent's DM channel by member lookup, so group-channel replies silently landed in the agent's DM. Fix threads channel_id from the inbound user_message event through the bridge's post_reply back into bridge_session, which prefers the body-supplied channel_id over its DM fallback.
…edoc

Quote the python heredoc and pass values via env vars so any shell-
special characters can't break the python string literal. Addresses kilo
review on PR #233.
@jaylfc jaylfc force-pushed the feat/framework-update-phase-1 branch from 4831616 to f49d5bd Compare April 19, 2026 02:42
@jaylfc
Copy link
Copy Markdown
Owner Author

jaylfc commented Apr 19, 2026

Triaged review feedback (post force-push to f49d5bd):

Addressed:

  • ✅ FrameworkTab dialog: pins confirmed tag, falls back to "latest", optimistic state flip — already in a668127
  • framework-api.ts: now sends target_version instead of {} — already in a668127, bundle rebuilt to framework-api-B6uX1y0T.js
  • install_hermes.sh python heredoc: hardened to read MODEL/LLM_KEY via os.environ rather than bash interpolation, so shell-special chars can't break the python literal (f49d5bd). The kilo bot's "literal \$MODEL" reading was incorrect (heredoc was unquoted), but the new form is more robust regardless.
  • ✅ Sensitive notes file (LAN IP + home path): commit a36837f dropped from history via interactive rebase. Force-push refreshed the branch.

Deferred (pre-existing, out of scope for framework-update PR):

  • ImportApp embed field (agent vs agent_name)
  • GitHubApp "Open on GitHub" placeholder URL guard
  • SettingsApp malformed default YAML template (line numbers embedded)
  • StoreApp compatibility always-green default

These are real bugs in pre-existing source surfaced because this PR rebuilds the desktop bundle. Tracking separately — fixing them here would expand the PR's blast radius.

The openclaw fork's taos-bridge.ts doesn't forward the inbound user_message
channel_id into its reply payload — the dispatcher only includes kind, id,
trace_id, content. Without this fallback, an openclaw agent's reply to a
group-channel message routed to the agent's DM instead, breaking
multi-framework group chat.

Since trace_id == originating message id (set in agent_chat_router), we
can look up the original message and recover the source channel. Verified
on Pi: john (openclaw) now replies in the roundtable channel alongside
hermes, smolagents, langroid, pocketflow, and openai-agents-sdk.
@jaylfc
Copy link
Copy Markdown
Owner Author

jaylfc commented Apr 19, 2026

Overnight update — 6-framework group chat working

After landing the kilo/CodeRabbit triage commits, end-to-end tested the multi-framework chat in the roundtable channel. All 6 agents replied to the same message in the same channel:

  • john — openclaw → "Hi, I'm John on OpenClaw 🤖"
  • tom — hermes (NousResearch) → "Hermes — kilo-auto/free"
  • don — smolagents (HuggingFace)
  • linus — langroid
  • pat — pocketflow
  • olive — openai-agents-sdk

The john (openclaw) case required one more fix that's now in 05ffa16: the openclaw fork's taos-bridge.ts dispatcher doesn't forward inbound channel_id into its reply payload (it only sends kind, id, trace_id, content). Without a host-side fallback, openclaw replies to group-channel messages were silently routed to the agent's DM. Since trace_id == originating message_id, the host can look up the original message and recover the source channel.

This is a defensive fallback that benefits any framework whose adapter doesn't thread channel_id explicitly. The body-supplied channel_id from 2dbd284 still wins when present.

Demo task #21 (Tom + John group chat) now exceeded — 6 frameworks active, not just 2.

Verified end-to-end: Tom (Hermes) participated in the 6-framework
roundtable group chat alongside openclaw, smolagents, langroid,
pocketflow, and openai-agents-sdk. Per the deploy-flow promotion plan,
moving from alpha to beta to surface it in the recommended-frameworks
list. Updated description to reflect the actual taos-hermes-bridge wiring
that ships in tinyagentos/scripts/install_hermes.sh.
@jaylfc jaylfc merged commit 64ede76 into master Apr 19, 2026
8 checks passed
@jaylfc jaylfc deleted the feat/framework-update-phase-1 branch April 19, 2026 10:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant