feat(plugins): unified Plugins surface (tenant-built + marketplace) + MIB service auth#76
Open
Nirpat3 wants to merge 53 commits into
Open
feat(plugins): unified Plugins surface (tenant-built + marketplace) + MIB service auth#76Nirpat3 wants to merge 53 commits into
Nirpat3 wants to merge 53 commits into
Conversation
Restyle /login and /signup to the AROS chat-first design (Claude Design export): warm cream/gold palette (accent #B8842A), Newsreader serif display, JetBrains Mono brand mark, "by ShreAI", light/dark toggle. Auth logic is unchanged — Supabase email+password sign-in, the hosted-issuer OTP/workspace branch, and the signup form/verify/done flow with intent picker + password strength all preserved. - aros-design.css: reconstructed light/dark design tokens + shared auth classes - useArosTheme: persisted light/dark control via <html data-aros-theme> - index.html: add Newsreader / JetBrains Mono / Hanken Grotesk fonts Verified locally in both themes (typecheck clean). Not deployed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Refine the token system + auth primitives (class names unchanged, so Login/ Signup inherit automatically): clean white cards on a soft warm radial ground, hairline borders (--line-strong), layered soft card shadows, precise focus rings (--focus-ring), restrained depth on the primary button (shadow + active press), antialiased type, refined dark-mode surface elevation. Warm gold accent (#B8842A light / #CD9F43 dark) preserved. Both light and dark verified. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds the redesigned app shell behind a preview route (/preview/app, no auth): left nav (Chat/Stores/Apps/Skills/Agents/Models/Permissions/Connection Health + Workspace section), connection-health widget, role switcher, user row, and a themed topbar — all on the Phase-1 token system, light + dark. - Concierge chat home (canned reply in preview; wires to router /v1/chat). - Skills and Agents render real capability cards (wire to CapabilityCatalog kind="skills"/"agents"); other sections are placeholders mapping to existing surfaces (Stores/Apps→ConnectionsHub, Models→AIModels, Health→ConnectionHealth). - Namespaced shell classes to rsx-* to avoid collision with aros.css (.aros-nav/.aros-card there leaked flex-direction:column into the nav). Preview only — the live post-login app is unchanged. Web typecheck clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Every nav section now renders real content in the chat-first design via a data-driven panel system (stats / cards / rows / form / note), driven by shellData.SECTIONS which mirrors the app's actual catalogs: - Stores/Apps: real POS + app providers (RapidRMS connected w/ 5 stores, Verifone needs-attention, Clover/Square/NCR/Gilbarco; QuickBooks/Xero/Slack/…). - Skills/Agents: the real CapabilityCatalog items (Daily Sales Summary, Inventory Reorder, …; Store Operations Agent, Inventory Agent, …) with tags + states. - Models: the real AIModels providers (Shre local default, Anthropic, OpenAI, Gemini, Ollama) with model ids. - Connection Health, Permissions, Team, Billing, Usage, Settings: real structure. Static specs are cutover-ready — swap SECTIONS for live fetches and the render layer is unchanged. Preview only, web typecheck clean, verified light + dark. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… (Phase 4) 4-step modal wizard (provider → credentials → stores & access → review), scoped to the two POS we support today: RapidRMS (HTTPS API key) and Verifone Commander (secure tunnel: site controller IP + service creds + tunnel agent). Faithful to the Claude Design export (provider blurbs, field labels, read/write access copy). Opens from the Stores "Connect POS" CTA and the Concierge "Connect Store" chip; finishes with a success toast. Light + dark verified. Also narrowed the real ConnectionsHub POS list to RapidRMS + Verifone Commander to match, and trimmed the Stores panel to the two providers. Preview only (/preview/app). Web typecheck clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… (Phase 5)
- Concierge chat now POSTs the real shre-router /v1/chat ({agentId,messages,
stream}), renders the reply, typing indicator + graceful error bubble.
- Connect wizard now POSTs the real connectors API (POST /api/connectors:
{type,name,config,secrets} then /api/connectors/test) with the auth session
(Bearer + x-aros-tenant-id). Provider fields realigned to the backend
contract: rapidrms-api (clientId/email/password), verifone-commander
(commanderIp/username/password). Inline error surfaced on failure.
- Cutover behind a reversible flag: ?redesign=1 mounts AppShell as the real
authed post-login app (?redesign=0 reverts); off by default, live app unchanged.
Validated in preview via network capture — both flows emit the correct request
shape; full round-trip needs a real session + running router/backend (staging).
Web typecheck clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…se 6) Two-mode shell driven from a top bar (chat icon + menu): - Chat mode: chat pane (left) + conversation canvas (right) — the canvas shows the conversation's outputs (KPI tiles, sales-by-store bar chart, low-stock table); new Canvas.tsx (example data; wired build receives mib-widget blocks). - App mode: picking a section from the top menu closes chat and shows the nav sidebar (left) + section content (right). - Chat pane gets a "New chat" affordance (resets the thread). - Role switcher + all workspace menus (Team/Billing/Usage/Settings) moved out of the rail into a slide-in profile sidebar opened from the profile avatar. goSection() routes 'chat' back to chat mode (no SectionPanel for chat). Verified: menu→app→chat round-trips with zero page errors, light + dark. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…label, responsive (Phase 7) Design consulted (subagent) then implemented: - Home "Command Home" (Home.tsx): time-of-day greeting (Newsreader), Ask-Shre hero that opens chat, suggestion chips, 4 live KPI tiles, "Needs your approval" + "What Shre did" band, quick actions. Default view. - Chat is now a toggle: clicking the chat icon SLIDES the chat rail in (animate grid-template-columns 440ms open / 320ms close, cubic-bezier(.16,1,.3,1)) with a fixed-width clipped rail (no text reflow) and a home<->canvas cross-fade; clicking again slides back to Home. prefers-reduced-motion fallback + focus mgmt. - User info restored to BOTTOM-LEFT of the app-mode side menu (opens profile). - Chat pane gains a model picker (Shre/Anthropic/OpenAI/Gemini) + New chat; Home's Ask-Shre and chips seed + focus the chat input. - Whitelabel structure: branding.ts centralizes product/byline/mark/concierge (+ applyBrandTokens for runtime theme tokens); shell/chat consume it. - Responsive: tablet (KPI/grid reflow) + phone (chat = full-screen slide-over, nav collapses to top menu, top-bar trims) breakpoints; verified 390px + desktop. Web typecheck clean; verified home/chat/app + mobile, light + dark. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fixes nav discoverability: the top-bar menu (hamburger) icon now slides out the complete sidebar as a left drawer from ANY mode (Home/Chat/section) — brand header, full nav (Home, Chat, Stores, Apps, Skills, Agents, Models, Permissions, Connection Health), and the user info row at the bottom (opens profile). Replaces the small dropdown. App-mode docked sidebar unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…r panes scroll The home<->chat slide grid defined columns but no row track, so its implicit row was content-sized instead of viewport-height. Inner panes then got height:100% of content, pushing the chat composer (and lower section content) off-screen with no scroll. Pin grid-template-rows: minmax(0,1fr) on .rsx2-slide + .rsx2-stage (and min-height:0 on rail/stage) so the thread scrolls and the composer stays visible. Verified: 6-message thread scrolls, composer visible at 593/760px. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ark-mode fix - Right pane in chat is now two tabs: Canvas (conversation outputs) and History (recallable past conversations). Clicking a conversation loads it into the chat (ConciergeChat gains an `initial` prop); New chat clears it. CONVERSATIONS + HistoryPanel added. - Layout consistency: Home and every section now share ONE docked left sidebar (nav + user info bottom-left). Chat is the only exception — its rail replaces the sidebar (slides in). The menu icon closes chat and returns to Home (desktop) / opens the nav drawer (mobile). - Dark-mode fix: button wrappers (.rsx2-brand/.rsx2-ask/.rsx2-quick/.rsx2-userrow) now set color:var(--ink); their labels were falling back to the UA default (black) and were unreadable on dark surfaces. Verified: sidebar hidden during chat, menu returns to home+sidebar, dark text readable, no page errors. Web typecheck clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nt left panel Data layer (redesign/data.ts) — THE GUARANTEE: demo persona/figures/catalogs render ONLY when there is no real session (the public /preview/app). A logged-in build shows real data + empty states, never demo content. - useDemo() = !session. useIdentity() = real session name/tenant (live) or demo persona (preview). AppShell + UserRow now use identity, not the hardcoded USER. - useSection(): stores/apps -> GET /api/connectors, models -> GET /api/settings/ models (mapped to rows); other sections empty until wired. Demo -> static specs. - useHomeData/useCanvasDemo/useConversations: demo content in preview, empty states live. SectionPanel/Home/Canvas/HistoryPanel render clean empty states. Profile menu -> a LEFT panel (rsx2-profilenav) that overlaps the primary sidebar with Home on top + Role + Workspace nav + Sign out, and PERSISTS while you click through the account pages (was a right drawer that closed on each selection). Verified in preview: demo data intact, profile is a left panel that survives navigation, no page errors. Web typecheck clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Live data (still gated behind a real session; preview stays demo): - Skills/Agents -> GET /api/resources/skill|agent (tenant_resources) as cards. - Models/Apps -> GET /api/resources/model|app as rows. - Stores -> GET /api/connectors (RapidRMS/Verifone). - Home -> GET /api/dashboard (activity + tasks->approvals) + /api/store/summary (KPIs). Mapping is defensive: a field that's absent yields an empty state, never a fabricated number. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ConciergeChat surfaces the raw reply via onReply; AppShell extracts structured blocks (data.blocks / canvas.blocks / content_blocks) and feeds the Canvas, which renders kpis/bars/table/text blocks defensively (unknown types skipped) — reset on new/recalled chats. Demo canvas unchanged in preview; live shows blocks when a reply carries them, else the empty state. Ready for the backend to emit mib-widget content blocks on /v1/chat. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…s/permissions) Live data for the last sections (demo path unchanged): - Connection Health -> derived from /api/connectors (healthy/degraded/down + rows). - Billing / Usage -> /api/billing/status?tenantId (plan/status; requests/spend). - Team -> the logged-in user as a member row. - Settings -> workspace-name prefilled from the tenant. - Permissions -> role scopes shown always (product policy, not tenant data). Absent fields still yield empty states, never fabricated values. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…(converge) Council-directed convergence after syncing main (which shipped the mib-widget canvas in PR #38). Drop the redesign's divergent kpis/bars/text data.blocks scheme; instead: ConciergeChat renders assistant messages via the shared ChatMessageRenderer (markdown + inline mib-widgets) and derives canvas items from the transcript via itemsFromMessages(); Canvas renders CanvasWidgetItems with the shared WidgetRenderer. warmPalette() feeds the renderer the live design tokens. Web typecheck clean; preview verified (ChatMessageRenderer active, demo canvas intact, no page errors). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
# Conflicts: # apps/web/src/app/App.tsx # apps/web/src/redesign/AppShell.tsx
…ign (soft launch) The changelog had lapsed after 0.1.0 while tags advanced to v0.4.0. Backfilled 0.2.0/0.3.0/0.3.1/0.4.0 from their tags, added [0.5.0] for the flag-gated redesign soft-launch (live 2026-07-16, default unchanged), and expanded [Unreleased] with the flip-to-default + convergence follow-ups. Bumped root version 0.3.1 -> 0.5.0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ect 0.5.0 note) Concurrent workstream inverted the gating on the live fork — the redesign now renders by default for authenticated users; ?redesign=0 sets aros-shell-legacy as the opt-out. Updated the [0.5.0] note + [Unreleased] (pre-flip checks are now post-flip verification). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add `Cache-Control: private, no-store` to the app-shell HTML (and the index.html SPA fallback) so no shared cache/CDN stores the authenticated bootstrap. Fingerprinted /assets/* stay `public, immutable`. Closes the post-flip cache-control hardening item. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Record the app-shell no-store fix and the verified tenant-isolation / demo-leak audit results. Clears the post-flip verification item. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ace plugins
Repoint the AROS Plugins section from marketplace-only to a UNIFIED view of
two sources (DECIDED: unified), grouped in one page:
* Built for your business — apps this tenant generated via the App Factory
(public.tenant_apps). Preview apps carry a "Confirm & publish" gate that
promotes preview -> live (the human approval step). This is the
"StorePulse-like internal app I built" case.
* Installed from marketplace — marketplace apps enabled with source=plugin
(the rows the page showed before).
Backend (src/server.ts):
* GET /api/plugins — merges tenant_apps ('built') + marketplace
source=plugin ('installed'); degrades to
installed-only if tenant_apps isn't applied
yet (staged-apply migration).
* POST /api/plugins/:id/confirm — preview -> live via appfactory.approveGoLive,
service-role carrying the approver uuid;
tenant-scoped before promote; owner/admin only.
Frontend:
* api.ts: BuiltPlugin/InstalledPlugin types, listPlugins(), confirmPlugin().
* InstalledResourcesPage: PluginsPage split out into a grouped Built/Installed
view with status pills and a Confirm & publish action; Connectors unchanged.
* aros-shell.css: rsx-group__label + rsx-row__btn--primary.
Contract is shared: MIB surfaces read the same GET /api/plugins.
Verified: apps/web tsc --noEmit clean; server tsc shows no new errors in the
changed files (the lone src/server.ts baseline error, discoveredStores x3, is
pre-existing connector code). Live HTTP smoke is gated on the tenant_apps
migration (20260715) being applied + Supabase creds — an operator/deploy step.
MIB surfaces (mib007 / shre-command-center, mib-desktop) can't present an
end-user's AROS session — they read a tenant's plugins service-to-service.
Add a shared resolvePluginAccess() that authorizes a plugins request as EITHER
a workspace user (cookie/bearer -> tenant membership) OR a trusted service
(AROS_SERVICE_TOKEN + allow-listed x-service-source + explicit ?tenantId),
reusing the existing readServiceToken()/tokensMatch() primitives.
* GET /api/plugins — service path lists any tenant's plugins by
?tenantId (same as the store-summary path).
* POST /api/plugins/:id/confirm — service path is pre-authorized (the MIB
surface already gated the human) and MUST
carry the approving user's uuid in body.userId,
which preview->live records as the approver.
Allow-list: shre-router, mib007, shre-command-center, mib-desktop. Token is the
real gate; the source header is allow-listed for audit clarity.
Verified: apps/web tsc clean; server tsc shows no new errors in changed files.
GET /api/plugins/all — SERVICE AUTH ONLY (no end-user path); returns every tenant's plugins grouped by tenant, with tenant display names. Powers the mib-desktop ops console's cross-tenant Plugins view. Capped (default 500, max 2000) and degrades gracefully if tenant_apps isn't applied yet. Verified: server tsc shows no new errors in the changed file.
Nirpat3
force-pushed
the
feat/plugins-unified-surface
branch
from
July 17, 2026 04:12
175c8f1 to
b35ac19
Compare
…uilt app
POST /api/plugins (SERVICE AUTH ONLY) closes the App Factory loop: it creates a
tenant_apps row (status 'draft') + the app's private schema + scoped role via
appfactory.provisionApp, so a built app appears under Plugins and can walk the
draft -> preview -> live lifecycle.
* appfactory/sql-executor.ts — config-gated privileged Postgres executor
(APP_FACTORY_DATABASE_URL or ~/.shre/vault/app-factory-db.json) using the
same pg-Pool + file-vault conventions as the rapidrms analytics connector.
Null when unconfigured -> the endpoint 501s (same operator-wiring posture as
the staged-apply tenant_apps migration). Also vaults the generated role
password to ~/.shre/vault/app-<subdomain>.json (0600) — never over the wire.
* src/server.ts — handlePluginProvision: validates tenantId/slug/displayName,
provisions, vaults the credential, returns the created app (no secret).
This is the AROS half of the loop; the Sia App Factory write-back (call this on
build/plan approval) is the remaining connective step.
Verified: server+appfactory tsc show no new errors in changed files.
The previous commit shipped appfactory/sql-executor.ts but its server.ts wiring (import, handlePluginProvision, POST /api/plugins route) was dropped. Restore it so the provision write-path is actually reachable. Verified: server tsc shows no new errors in changed files.
Covers the staged tenant_apps migration, the provisioning DB config (APP_FACTORY_DATABASE_URL / vault), the MIB service token + allow-list, curl verification for every endpoint, the lifecycle guardrails, and rollback. Each item degrades safely until configured.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Turns the AROS Plugins section into a unified view of two sources (grouped in one page):
tenant_apps). Preview apps carry a Confirm & publish gate (preview → live, the human approval step).source = 'plugin'(what the page showed before).Backend (
src/server.ts)GET /api/plugins— mergestenant_apps(built) + marketplacesource=plugin(installed). Degrades to installed-only iftenant_appsisn't applied yet (staged-apply migration).POST /api/plugins/:id/confirm— preview → live viaappfactory.approveGoLive, service-role carrying the approver uuid; tenant-scoped before promote; owner/admin only.resolvePluginAccess) — MIB surfaces read a tenant's plugins service-to-service (AROS_SERVICE_TOKEN+ allow-listedx-service-source+ explicit?tenantId), reusing the existingreadServiceToken/tokensMatchprimitives.Frontend
api.ts:BuiltPlugin/InstalledPlugintypes,listPlugins(),confirmPlugin().InstalledResourcesPage:PluginsPagesplit into a grouped Built/Installed view with status pills + Confirm & publish; Connectors unchanged.aros-shell.css:rsx-group__label+rsx-row__btn--primary.Verification
apps/webtsc --noEmitclean.tscshows no new errors in changed files (the lonesrc/server.tsbaseline error —discoveredStores×3 — is pre-existing connector code).tenant_appsmigration (20260715) being applied + Supabase creds — an operator/deploy step, since the migration is staged-apply.Companion
MIB read-side lands in
Shreai/shre-command-center#<plugins-surface>(reads thisGET /api/pluginscontract).🤖 Generated with Claude Code