Commit Graph
544 Commits
Author SHA1 Message Date
Juan DenisandClaude Opus 4.8 483f1fa4c0 feat(automations): side-nav Settings layout (like core/WordPress settings)
Replace the single long scroll of stacked cards with the core settings-layout:
a left sub-nav (Engine / Events & port / Pack secrets) and a content pane that
shows only the selected section. Reuses the global .settings-layout/.settings-nav
classes so it matches core Settings and WordPress. Drops the redundant per-card
Refresh (the topbar Refresh covers it).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 00:10:19 -04:00
Juan DenisandClaude Opus 4.8 5fce53f779 feat(automations): Templates marketplace tab (search, favorites, import)
Promote 'New from template' from a popup to its own Templates tab, built to grow
like a marketplace:
- Responsive card grid (icon, name, description, integration tags) with a
  'Use template' action per card.
- Search box to filter.
- Per-card favorite star; favorites persist in localStorage and float to the
  top (server-side store can replace this later without UI changes).
- 'Import template' action: paste a tramo workflow JSON or load a .json file to
  create it as a new automation.
Removes the old modal picker and its topbar button.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 00:01:03 -04:00
Juan DenisandClaude Opus 4.8 049235b12d feat(automations): two-column New automation dialog with trigger picker
Replace the name-only New workflow modal with a two-column dialog: an explainer
panel (badge, mini flow diagram, use-case examples) on the left and, on the
right, the name field plus a 'How should it start?' trigger picker. Choices seed
a valid builtin trigger node (manual / cron / panel-event via webhook on
/sk/events / incoming webhook) so the editor opens with the trigger already
placed. Own copy/wording; collapses to a single column on narrow screens.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 23:51:19 -04:00
Juan DenisandClaude Opus 4.8 f4b8630e3b feat(automations): richer template picker
Replace the three plain text rows with proper cards: a brand-tinted icon tile
(Telegram/GitHub/Discord/generic), bold name, muted description, integration
tags, and a hover chevron. Wider modal (size lg) with an intro line, and the
Close button moves to the modal footer slot.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 23:42:56 -04:00
Juan DenisandClaude Opus 4.8 6f4246bf0e fix(automations): un-nest template-picker styles from .tramo-page
The 'New from template' picker renders inside a <Modal>, which portals to
<body> -- outside the .tramo-page subtree -- so the .tramo-template rules
nested under .tramo-page never matched it. The name/description spans fell back
to inline defaults and ran together. Move .tramo-template-list/.tramo-template
to top-level scope so the portaled modal picks them up (name bold on its own
line, muted description below).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 23:33:25 -04:00
Juan DenisandClaude Opus 4.8 52ea07426f fix(automations): center content in sk-tabgroup__inner (match WordPress)
Add the .sk-tabgroup__inner wrapper WordPress/Domains use inside
.sk-tabgroup__content so the content is padded + max-width centered under the
full-bleed header, instead of sitting edge-to-edge.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 23:23:09 -04:00
Juan DenisandClaude Opus 4.8 801fe3bbf6 fix(automations): full-bleed tab-group header to match WordPress/Domains
The topbar was rendered inside a plain padded page-container, so it floated
inset with no header band. Switch the wrapper to the same classes TabGroupLayout
uses for WordPress/Domains -- 'page-container--full-bleed sk-tabgroup' with the
content in 'sk-tabgroup__content' -- so Automations gets the identical
full-width header strip (bordered band, icon+title+tabs+actions) over centered
content.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 23:20:08 -04:00
Juan DenisandClaude Opus 4.8 18a8248fc1 fix(automations): match the clean Domains/WordPress header layout
Drop the '<icon> tramo . workflows . triggers . integrations' topbar subtext
and the redundant inner card-header on the Workflows/Runs tabs. Actions
(Deploy, New from template, New workflow, Refresh) now live in the PageTopbar
and are contextual to the active tab, so each tab shows a single clean header
over its content -- the same pattern as Domains and WordPress.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 23:13:23 -04:00
Juan DenisandClaude Opus 4.8 bd8842faf4 fix(routing): show a Not Found page for unknown/uninstalled-extension routes
Navigating straight to an extension route whose extension isn't installed
(or any unknown URL) rendered a blank page -- the route was never registered
and there was no catch-all. Add a catch-all '*' route inside the dashboard
shell that renders a NotFound page (icon, the missing path, links to the
dashboard + Marketplace).

Contributed routes load async, so on a hard refresh onto an installed
extension route the catch-all can mount for a beat before the route
registers; NotFound keys off a new contributions __ready flag to show a
loading skeleton until the route set is known, avoiding a 'Not found' flash.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 23:06:03 -04:00
Juan DenisandClaude Opus 4.8 e62d969a8b feat(automations): retire React-Flow Workflow Builder UI (Phase 4 frontend)
The Automations extension (tramo) replaces the drag-and-drop builder.

- Delete builtin-extensions/serverkit-workflows + its mirror, pages/
  WorkflowBuilder.jsx, and all of components/workflow/ (its @xyflow nodes,
  edges, and config panels).
- KEEP @xyflow/react (vendor-flow chunk): pages/AppMap.jsx still uses it.
- Redirect /workflow -> /automations in App.jsx (retired-route pattern).
- Drop the pages/_workflow SCSS partial + its main.scss import.
- Refresh the stale serverkit-workflows comments in DashboardLayout +
  sidebarItems to point at the Automations extension.

Build + lint clean; sync-builtin-frontends --check green (14 managed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 22:27:20 -04:00
Juan DenisandClaude Opus 4.8 79be6e082d test(automations): tramo extension suite + host/model refinements
- test_tramo_extension.py: 40 tests (manifest, models+purge, workflow store
  CRUD/materialize/deploy, host argv/control/status/nt-guard, run_sync,
  jobs harvest+health, events bridge + call-back key, blueprint auth/run
  proxy/settings mask/auth-exempt hooks passthrough, Phase 4 ported emitters +
  legacy export + old routes gone).
- host_service: add _is_windows() seam (test platform without touching global
  os.name, which would break pathlib).
- models: dirty detection via deployed_version counter (race-free vs the
  deploy commit's own updated_at onupdate bump).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 22:23:53 -04:00
Juan DenisandClaude Opus 4.8 bde4647fdc feat(k8s): serverkit-k8s extension — manage remote clusters via kubectl
Opt-in builtin extension. Add a cluster by pasting its kubeconfig (stored
encrypted, one row per cluster); the engine shells out to kubectl against a
per-call temp kubeconfig to show cluster health, nodes, namespaces, workloads,
pods with live logs, services, and ingresses, plus scale/restart/delete and
apply-from-paste. Reads need viewer, mutations need admin. Not platform-gated
(kubectl runs anywhere); no cluster needed on the panel host.

Backend: models (ext_serverkit_k8s_clusters), kubectl_service (argv build +
temp-file materialize/cleanup), cluster_service (CRUD + normalizers), k8s
blueprint (/api/v1/k8s), lifecycle. Frontend: K8sPage (7 tabs) mirrored to the
baked plugin tree via sync-builtin-frontends. 30 extension tests green;
manifest/platform suites green; frontend builds; sync-drift clean.

Plan: docs/plans/44_KUBERNETES_EXTENSION_PLAN.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 03:41:34 -04:00
Juan DenisandClaude Opus 4.8 2dfe0d7d54 feat(search): unified entity omnisearch endpoint for the palette
GET /api/v1/search?q= (JWT, 2-char minimum) fans a term out across the core
entity types — services, servers, domains, databases, WordPress sites, cron
jobs (admin-only), installed extensions, and vaults (names only, never secret
values) — reusing each domain's existing access helpers so a member only sees
what their grants allow. Per-type cap of 5; each entity block is isolated so one
failing type degrades gracefully instead of 500-ing the search.

Wires the frontend api.search() async provider (200ms debounce) into the
palette's entity group, replacing the fetch-everything-on-open prefetch. Adds
tests/test_search.py covering shape, min-length, cap, member-vs-admin filtering,
cron admin-only, and the vaults names-only rule.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 00:26:40 -04:00
Juan DenisandClaude Opus 4.8 86bd8d70a9 feat(palette): wrap settings cards for deep-link focus across all tabs
Mechanical wrap-the-card pass: each remaining settings tab component now spreads
useSettingFocus's register(id, class) onto its identifiable cards so a palette
settings result scrolls to and flashes the exact card. Covers Profile,
Appearance, Sidebar, White Label, About, Users, Activity, Site, Modules,
Migrations, SSO, API, AI, System, and Connections. Cards whose roots live in
non-editable subcomponents (login links, invitations, provider cards) register
at the nearest reachable section or fall back to landing on the tab.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 23:47:36 -04:00
Juan DenisandClaude Opus 4.8 e0a47bbb9d feat(palette): settings search — deep-link focus + completeness guard
useSettingFocus rides the useFocusParam rail with a new `setting` kind: a
palette result navigates to /settings/<tab>?focus=setting:<id>, and the target
card scrolls into view and flashes a token-based highlight for ~2s. Wires the
headline Security (2FA card + admin require-2FA policy, now mounted) and
Notifications (email/SMTP, channels, severity, categories, quiet hours) cards.

Adds scripts/check-settings-index.mjs (dependency-free) to the lint step so
every settings tab must carry at least one index entry — a new tab can't ship
invisible to search.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 23:42:57 -04:00
Juan DenisandClaude Opus 4.8 3e0fbd2f81 feat(palette): omnisearch core — providers, ranking, VS Code keys
Rebuild CommandPalette around scored providers (pages, settings, actions,
plugin contributions, docs, async entities) with a single subsequence fuzzy
scorer and shouldFilter={false} so cmdk no longer double-filters. Adds:

- data/palettePages.js, data/settingsIndex.js (84 cards), data/commandActions.js
- utils/paletteScore.js (word-start bonus + category weight) and
  utils/paletteFrecency.js (localStorage frecency: count + time decay)
- hooks/usePaletteAuthz.js — mirrors the sidebar's admin + workspace-nav gate
  so a member's palette never surfaces admin surface
- F1 and Ctrl/Cmd+Shift+P open aliases alongside Ctrl/Cmd+K
- prefix modes (`>` actions, `?` docs), Recently used empty state, kbd footer

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 23:31:25 -04:00
Juan DenisandClaude Opus 4.8 5fc0b881b4 feat(connections): one-click GitHub setup via the GitHub App manifest flow
Connecting GitHub required hand-registering an OAuth app and pasting a client
id + secret — friction, and an open-source panel can't ship a secret anyway.
Now an admin sets it up in one click.

- backend: build_github_app_manifest / complete_github_app_manifest — hand
  GitHub an app manifest, convert the returned code into credentials stored
  locally (client id/secret in settings; PEM + webhook secret encrypted). No
  shared secret in the repo or build.
- connect flow: when an app slug exists, generate_github_authorize_url points at
  the app's install screen (grants repos + authorizes in one hop); repositories
  are listed per-installation since GitHub Apps don't use /user/repos.
- get_github_config surfaces provider_kind/app_slug/app_name/install_url.
- routes: GET/POST /source-connections/admin/github/app-manifest[/complete].
- frontend: Settings -> Connections leads with "Set up in one click" (auto-POSTs
  the manifest form to github.com); a new /connections/github-app/callback page
  finishes setup and chains into connect. Manual OAuth app moves under
  "Advanced". 7 proving tests (manifest shape, conversion+storage, install-url
  routing, app-mode repo listing); ratchet 2498->2505.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 23:06:09 -04:00
Juan DenisandClaude Opus 4.8 4bc5e1af9a feat(new-service): template step is a mini app gallery, not one card
Feedback: the template step showed only AgentSite in a stacked card. Make it a
real gallery of the whole catalog.

- pull the FULL template catalog into the picker (repo + one-click compose), not
  just repo-kind; icon + title tiles ~4 across
- collapsed to two rows; "Show all N" expands into a scrollable grid with a
  simple search box
- curated ordering floats AgentSite + popular apps first; AgentSite badged
  "Featured"
- picking a repo template continues in the wizard; picking a one-click compose
  template routes to its install flow (its real deploy path)
- keep the inline "Bring your own manifest" escape hatch

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 09:32:05 -04:00
Juan DenisandClaude Opus 4.8 48cdd159f3 fix(new-service): center the stepper, richer template step + bring-your-own
Feedback on the template step: it read as sparse (one bare row), the "Browse all
templates" button punted to another page, and the stepper hugged the left.

- center the Source/Connect/Review stepper
- render templates as featured cards: icon (with fallback), name, a "Featured"
  badge, version, description, category chips, and the repo URL — a real preview
  instead of a one-line row
- add an inline "Bring your own manifest" card: deploy any Git repo shipping a
  serverkit.yml / Docker Compose / Dockerfile by switching to the Git-remote
  input in place, no page jump
- demote "Browse all templates" to a subtle text link

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 09:08:48 -04:00
Juan DenisandClaude Opus 4.8 bd87b06a60 feat(new-service): a real three-step wizard, not a form-wall
/services/new rendered everything at once regardless of state — a "Ready to
Import" rail of placeholder rows, a decorative Connect->Detect->Deploy strip, a
numbered guide, and two boilerplate note boxes. It is now a slim stepper over
one centered column.

- three explicit steps: Source (radio-cards) -> Connect (source-specific input
  + API-driven template picker) -> Review (manifest detection, buildpack, env
  list, core fields; nothing renders without real data)
- decomposed the 1,327-line monolith into pages/new-service/: a
  useNewServiceForm hook (step state + submit) with presentational SourceStep /
  ConnectStep / ReviewStep components
- ?template=/?source= deep links land preloaded on the Connect step; deploy
  templates load from the backend catalog (kind: repo) instead of a JS constant
- submit payloads are byte-identical to before (flow re-skin, not a backend
  change); _new-service.scss rewritten for the stepper layout
- deletes the hardcoded frontend catalog (data/deployTemplates.js) now that its
  last consumer is gone, and the old single-file NewService.jsx

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 02:39:23 -04:00
Juan DenisandClaude Opus 4.8 b91b28ae4f feat(templates): one catalog grid with the Marketplace filter treatment
Templates was two pages stacked: a one-row "Deploy templates" section above the
real 106-template grid, plus a category chip wall with a "More +95" spillover.
Now it is one browsing surface.

- repo-kind templates render in the main grid as normal cards with a kind badge
  (Badge info "Git repo" vs outline "One-click")
- topbar SearchField + FilterButton/FilterDrawer (category, kind, sort) with a
  single row of quick category chips; the chip wall and spillover are gone
- repo cards open the same detail shell with a repo-specific body (repo URL,
  detected manifests, env preview from the /manifest endpoint), primary action
  "Deploy" -> /services/new?template=<id>
- `#deploy-templates` anchor redirects to the ?kind=repo view; kind joins
  category/search/sort in the URL params

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 02:39:02 -04:00
Juan DenisandClaude Opus 4.8 abe0fbece9 feat(templates): deploy templates as a backend repo-kind catalog entry
Deploy templates were a hardcoded frontend constant (AgentSite, with a
hand-fabricated manifest and an EXAMPLE badge). They now live in the backend
catalog as `kind: repo` YAML entries that blend with the 105 compose templates.

- template schema gains `kind: compose | repo` (absent = compose, so every
  existing template is untouched); repo templates carry a `repo` block with a
  `url` instead of a compose stack
- backend/templates/agentsite.yaml (kind: repo) replaces the JS constant
- list + detail payloads serve `kind`/`repo`; category derivation picks up
  repo templates automatically
- GET /api/v1/templates/<id>/manifest inspects a PUBLIC repo via
  RepositoryManifestService.analyze_public_repo (raw host, no clone, no OAuth)
  and falls back to the template's declared hints, labeled `template-hints`
- index.json + catalog-cert test recognize repo-kind; ratchet floor 2475->2498

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 02:38:46 -04:00
Juan DenisandClaude Fable 5 a7ddb1423e fix(plugins): re-sync baked builtin manifests through the canonicalizer
The plan-39 em-dash purge hand-wrote six baked plugin.json copies without
the trailing newline that sync-builtin-frontends.mjs emits, tripping the
CI drift gate (--check). Regenerated via the script; content unchanged.
Guard suite test_extension_manifests.py: 28 passed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 09:04:11 -04:00
Juan Denis 4e1b7da5f6 Add NVIDIA GPU monitor plugin (backend + manifests)
Introduce serverkit-gpu extension: backend plugin with GPU API and service (nvidia-smi based) providing per-GPU metrics and best-effort container resolution for GPU processes. Files added: backend/app/plugins/serverkit-gpu/{__init__.py,gpu.py,gpu_service.py,plugin.json}. Also update several frontend plugin.json files (including serverkit-gpu) to match manifest formatting (trailing newline adjustments). The GPU service degrades gracefully when nvidia-smi or Docker info is unavailable.
2026-07-09 08:47:28 -04:00
Juan DenisandClaude Opus 4.8 e2ae436d1a feat(frontend): restore cron CSS, SurveyTab, and runtime proving tests (plan 42 ph4)
Recover three frontend losses from the 2026-07-08 rebuild:
- _cron.scss: merge-restore the dropped .scheduled-tasks-card/-runs run-history
  drawer + .cron-jobcell__name rules from the clean shadow (46 -> 75 blocks);
  restored ONLY classes still referenced in JSX (no dead CSS), kept the plan-39
  .cron-kpis strip.
- SurveyTab.jsx (plan 28): rebuilt against the surviving survey API + wired into
  ServerDetail's tab strip (capability-gated), and fixed main.scss which never
  imported _server-survey.scss (styles had never compiled). Read-only; the
  recovered site-migration/decommission bits were dropped (those APIs don't
  exist in the current backend).
- runtime/__tests__/{sdkGate,sha256}.test.mjs (plan 32): re-created as node:test
  units against the surviving sdkGate.js/semverRange.js + digest.js seams.

Verified: node --test 9/9 pass, npm run lint 0 errors, npm run build green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 04:55:42 -04:00
Juan Denis 817a45cf77 Right-align plugin docs in marketplace page
Add a CSS rule to _marketplace.scss to right-align the .plugin-install-form__docs element inside the plugin install form. This ensures the docs link/button is pushed to the far right for improved layout and spacing in the marketplace plugin card.
2026-07-09 03:38:26 -04:00
Juan DenisandClaude Opus 4.8 6accbf9655 feat(services): New Service wizard clarity — guides, shared templates, docs links
- Per-source explainer strip (SOURCE_GUIDES): the selected source card renders
  what it does, what to have ready, and the 2-3 next steps.
- Extract the hardcoded SERVICE_TEMPLATES into frontend/src/data/deployTemplates.js;
  the Templates tab gains a 'Deploy templates' section (badged 'Example' for the
  demo) with a 'Use template' deep-link (/services/new?template=<id>) that
  preselects it in the wizard. One list, two entry points; Template Library
  button now targets #deploy-templates.
- New docsLinks.js map + <DocsLink> (book icon, opens serverkit.ai docs in a new
  tab, hidden under White Label — the single place that rule lives). Wired into
  the New Service source pane (deploy-sources / manifest) and the Marketplace
  manual-install modal (extensions/installing).

Lint 0 errors; build clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 02:12:24 -04:00
Juan DenisandClaude Opus 4.8 bd6b8eff33 feat(plugins): GitHub-native install with preview + permissions consent
Add POST /api/v1/plugins/preview: resolve a GitHub/zip source, download,
read plugin.json, and return slug/version/permissions/panel-gate + the
resolved URL, sha256, and warnings — without installing. The manual-install
URL flow becomes two-step (Resolve -> consent card -> Install), pinning the
install to the previewed sha256 so installed bytes match previewed bytes.

- owner/repo and owner/repo@tag shorthand normalization (install accepts it too).
- Actionable resolve errors: rate limit (with reset ETA), 404/private, no
  plugin.json at archive root.
- Optional SERVERKIT_GITHUB_TOKEN: Bearer auth on GitHub hosts only (never
  leaked to third-party mirrors, never logged); lifts the anon rate limit and
  enables private-repo installs.
- ManualInstallModal: PreviewConsent card with permission chips + warnings +
  'GitHub · unverified' badge; SCSS added.

Tests (test_plugin_preview.py, 15): shorthand, happy/warning preview paths,
token header injection + host scoping, preview->install checksum continuity +
tamper rejection. Full plugin suites green; frontend lint 0 errors, build clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 01:52:38 -04:00
Juan DenisandClaude Opus 4.8 c7b12b3095 feat(marketplace): surface registry v2 logo/repo/bundled fields
Stop stripping the v2 fields in registry_service normalization: add repo,
logo, bundled to _FIELDS. Repo-relative logos (assets/<slug>/<file>) are
resolved to absolute URLs against the index URL they came from, so one
relative path works for both the raw-GitHub index and the serverkit.ai
/ext endpoint. Bundled entries are excluded from the Browse catalog by
default (they duplicate builtin cards) and reachable via
?include_bundled=true. Detail modal gains a Source repo link.

Tests: v2 normalization, relative-logo resolution, v1 unaffected, bundled
exclusion + include_bundled endpoint flag. Full test_registry.py green (19).
Docs: EXTENSIONS_REGISTRY.md documents schema_version 2 + v2 fields.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 01:43:34 -04:00
Juan DenisandClaude Opus 4.8 8bd9c1a7dd feat(ui): marketplace icons + shared search/filter, jobs tab-group, KPI + terminal fixes
Marketplace:
- Bundle 14 illustrated extension cover icons (ExtensionIcons.js resolver);
  render on a clean light cover, larger & centered (no gradient/white-chip clash)
- Move search into the page top bar and add a reusable schema-driven FilterDrawer
  (SearchField + FilterDrawer/FilterButton ds primitives); drop the always-on
  publisher segmented control and the "N results" count

Jobs:
- Convert to a tab-group page (Activity / Scheduled) so scheduled jobs are a tab,
  not a scroll; reuse the shared SearchField + FilterDrawer in the top bar

KPI fixes (lost in the recovery rebuild):
- Add missing grid rules: .mon-kpis, .fleet-kpis, .ftp-kpis (stacked full-width)
- .sk-kpi__val gets an explicit color: var(--text) so values aren't near-black
  on non-tabgroup shells

Terminal:
- List Log Files first so the default landing matches the highlighted tab
- Drop the top padding so the topbar pins flush like the other pages

gitignore: allow the bundled extension-icons PNGs (real Vite-imported assets).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 01:28:20 -04:00
Juan DenisandClaude Opus 4.8 dcde6ab64a fix(extensions): gate GPU auto-install on a present GPU; purge em dashes from manifests
Move serverkit-gpu from CONVERTED_BUILTIN_SLUGS into GATED_BUILTIN_SLUGS behind
a _gpu_present() predicate (shutil.which('nvidia-smi')), mirroring the email
gate: fresh installs stay marketplace-only, upgraded panels without a GPU no
longer get it, upgraded panels with a GPU keep it (D3 continuity). Rewrite the
12 builtin + baked extension descriptions that used em dashes into plain
sentences, keeping every pair in sync. New test_extension_manifests.py walks
both manifest trees to assert no em/en dashes and per-slug description parity.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 23:12:48 -04:00
Juan DenisandClaude Opus 4.8 f391723bb3 feat(marketplace): density pass, Installed top-bar tab, per-extension cover art
Promote Installed to a real top-bar tab (/marketplace/installed, route-driven
via useLocation; legacy ?tab=installed still resolves) and drop the inner Tabs.
Collapse the two chip rows + SectionHeader into one .marketplace-toolbar row
(search + category Select + publisher SegControl + reset + count). Compact the
cards (one badge row, 2-line clamp, tighter grid/padding, ~5 cols) and give
installed rows a matching density pass. Add ExtensionBrands.jsx: deterministic
per-slug gradient covers with brand marks, with a logo/brand/manifest-icon/
category fallback chain, plus optional registry logo passthrough.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 23:11:39 -04:00
Juan DenisandClaude Opus 4.8 7ca2b77364 feat(email): restyle Email Server page onto the sk-email design system
The recovery-rebuild Email.jsx emitted sk-email__* classes while _email.scss
still styled the dead .email-page selectors. Rewrite _email.scss for the
sk-email namespace (drop the dead block), give the not-installed state a
designed EmptyState hero with a deliverability-preflight note, and move the
services/accounts/queue tables to DataTable with client-side search on the
account and queue lists.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 23:05:46 -04:00
Juan DenisandClaude Opus 4.8 634f21340d feat(jobs): host-idiom Jobs page + server-side search/paging + job retention
Frontend: rebuild /jobs to the Servers/Domains table-with-search idiom —
SegControl status filter, kind Select, debounced search, DataTable rows,
clickable compact KPIs, Prev/Next paging with 'Showing X-Y of N'. Rewrite
_jobs.scss for the sk-jobs namespace (dead .jobs-* selectors removed).

Backend: GET /api/v1/jobs gains a case-insensitive `q` (kind/owner) filter
and a `total` count; JobService.prune_terminal + a builtin.job_retention
ScheduledJob prune scheduler-tick jobs (succeeded/cancelled > jobs.retention_days
default 14, failed > 3x) so the '107k Total' stops growing at the source.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 23:01:12 -04:00
Juan DenisandClaude Opus 4.8 104d45dc16 feat(ui): compact KPI numbers so 6-digit Queue Bus counts stop overflowing
Add formatCompact/formatFull (Intl.NumberFormat) util, an opt-in `compact`
prop on MetricCard (value >= 1000 renders compact with the exact number as
the hover title), and CSS hardening (tabular-nums + truncation guard on
.sk-kpi__val, minmax(0,1fr) rail grid). Apply to the Queue Bus rail tiles
and status/group nav count badges.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 22:53:36 -04:00
Juan DenisandClaude Opus 4.8 271f925951 fix(settings): drop About under White Label + show panel version in System Info
- Remove the flex spacer that bottom-pinned the About nav item, leaving a large
  empty gap; About now sits directly under White Label in My Account.
- System Info's System Details card now shows the ServerKit (panel) version,
  white-label-aware (uses the brand name when white-labeling is enabled) — it
  previously showed host/OS details but never the panel version.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 22:28:16 -04:00
Juan DenisandClaude Opus 4.8 64508c5f39 fix(recovery): rebuild lost frontend components + recover corrupted files
Phase 3 of the recovery rebuild (plan 38). `npm run build` green again.

Reconstructed feature components (plans 20/21/25/27/31/32):
- ExtensionBoundary.jsx, serverkit-gpu/GpuMonitor.jsx — recovered clean from
  the D: backup verbatim
- ds/KpiBand.jsx (+ SCSS) — rebuilt from plan 20 + importer call-sites
- SchedulePicker.jsx — cron picker rebuilt against ScheduleCard + existing SCSS
- pages/Coexistence.jsx — adoption/observed-server page (PageTopbar)

Recovered 5 pre-existing corrupted files (content had been overwritten with
unrelated log/JSON data in an earlier recovery, committed to history):
- WorkspaceSwitcher.jsx, _shared-resources.scss — spliced (real content
  survived; garbage tail truncated)
- Jobs.jsx, Email.jsx, _backups.scss — reconstructed from ApiService contracts
  + surviving markup (functional; noted as reconstructions in-file)

Gate: npm run lint (0 errors) && npm run build (built, 3058 modules) both pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 01:02:29 -04:00
Juan Denis 6e32519f7e restore: remaining reconstructed July 5-7 changes (clean recovered files not attributable to one plan) 2026-07-07 20:29:31 -04:00
Juan Denis 59e92c64a6 feat(manifest): appliance tier — ports/volumes/multi-container units (plan 35) 2026-07-07 20:29:30 -04:00
Juan Denis 45942339eb feat(notifications): polish — digests, prefs, deep-links (plans 24/33) 2026-07-07 20:29:29 -04:00
Juan Denis c96a476a74 feat(extensions): runtime-frontend hardening + GPU core-slim (plan 32) 2026-07-07 20:29:28 -04:00
Juan Denis a882d35db9 feat(adoption): DNS cutover+verify, snapshots, SSH import (plan 31) 2026-07-07 20:29:26 -04:00
Juan Denis 7d045f32af feat(backup): trust round 2 — restore drills, offsite/verify (plan 30) 2026-07-07 20:29:25 -04:00
Juan Denis 6b6237bc17 feat(security): authz hardening round 2 (plan 29) 2026-07-07 20:29:24 -04:00
Juan Denis 44892d3314 feat(agent): V2 capability pack — survey/doctor/fleet sweep+repair (plan 28) 2026-07-07 20:29:24 -04:00
Juan Denis 782ea70c8b feat(workspace): scoping audit + per-scope matrix (plan 19) 2026-07-07 20:29:22 -04:00
Juan DenisandClaude Opus 4.8 0fef6825c6 chore(plugins): re-sync bundled serverkit-mail plugin.json
Regenerate the pre-bundled frontend/src/plugins copy from its
builtin-extensions source (node scripts/sync-builtin-frontends.mjs) to clear the
builtin-frontend drift CI check. Content-identical re-serialization; no
behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 02:03:37 -04:00
Juan DenisandClaude Opus 4.8 57ca8617da feat(manifest): wizard summary, Settings section, project status strip (Phase 5 frontend)
Add manifest UI: the repo-import wizard shows a multi-service summary when a
version:1 serverkit.yaml is detected; Service detail Settings gains a Manifest
section (managed-by badge, status pill, scaffold download, Plan/Apply); the
project view shows a manifest status strip. New api/manifests.js client module.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 01:49:07 -04:00
Juan DenisandClaude Opus 4.8 427492cb4b fix(mail): pin Stalwart engine against live 0.16.11 (WSL+Docker)
Ran the real container and drove its API; corrected what only a live box
reveals:

- image is stalwartlabs/stalwart:latest — the assumed stalwartlabs/mail-server
  no longer resolves on Docker Hub (404 on pull)
- config lives at /etc/stalwart/config.json and data at /opt/stalwart, so
  install() now bind-mounts two host dirs instead of one /opt/stalwart-mail
- admin credential is pinned via a single STALWART_RECOVERY_ADMIN=user:secret
  env var (not FALLBACK_ADMIN_USER/SECRET)
- a fresh container boots into BOOTSTRAP mode: only the /account setup UI is up
  and every /api/* returns 404 (RFC-7807 problem+json) until the one-time setup
  completes. get_status now probes /api/session and reports needs_setup +
  setup_url; the Overview UI shows a "finish setup" banner. Verified live:
  GET /api/session -> 404 problem+json.
- _api parses the problem+json detail/title error shape

Tests updated to the real install argv + two new cases (bootstrap needs_setup,
problem+json parsing): 58 pass. Post-setup CRUD sub-paths (/api/principal etc.)
remain best-effort constants until confirmed against a fully-initialized
instance on a real box.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 00:48:01 -04:00
Juan DenisandClaude Opus 4.8 d0d55fc2ea feat(mail): serverkit-mail frontend tab-group UI
Self-contained multi-tab Mail Server page mirroring the serverkit-dns-server
plugin shape (named MailPage export, no default export; own PageTopbar tabs;
generic api.request calls). Tabs: Overview (status/install/service + a
prominent deliverability preflight banner that makes clear sending is blocked
until PTR/port-25/RBL pass), Domains, Mailboxes (+ autoresponder), Forwarders,
DNS & DKIM (generate/deploy records + copy-to-clipboard + LE cert), Queue.

Canonical source under builtin-extensions/serverkit-mail/frontend/ with a
byte-identical synced copy (+ manifest) under frontend/src/plugins/serverkit-mail/.
SCSS-only, host components reused, no inline styles. Verified: eslint 0 errors,
npm run build succeeds (Vite compiles the plugin).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 00:27:39 -04:00