The release workflow builds linux/amd64,linux/arm64 via buildx + QEMU on
an amd64 runner, so the frontend-builder stage ran `npm ci`/`npm run
build` under emulation for arm64 -- 20-40 minutes when it did not hang
outright. Its output (frontend/dist) is static, arch-independent assets,
so pin the stage to $BUILDPLATFORM: buildx runs it natively, once, shared
by both platform images. Final images are unchanged.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Backstop for the updater-side skip: remove_retired_extensions() deletes a
retired extension''s copy-installed files and InstalledPlugin row on every
boot, BEFORE the plugin loader, so it is never loaded, never repaired
back, and never present for an older/offline updater to carry forward
into the next frontend build. Also drops serverkit-workflows from
CONVERTED_BUILTIN_SLUGS -- it no longer ships, so the auto-install
candidate was a permanent no-op.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The 1.7.25 -> 1.7.33 update broke on panels that still had the retired
serverkit-workflows extension on disk: preserve_installed_plugins carried
it into the fresh tree, where its reactflow import (removed with plan 45)
sank the whole bundle build and blocked the update.
- RETIRED_PLUGIN_SLUGS: the carry-forward now skips slugs the product
deliberately deleted, and docker in-place mode strips their leftovers
after the hard reset (untracked dirs survive it).
- Quarantine-and-retry: when the bundle build fails and plugins were
carried forward, move them into /var/backups/serverkit/quarantined-
plugins-<ts> and retry once -- fail the plugin, never the update.
The updater self-refreshes from main before running, so this protects
every panel on its next `serverkit update` once merged.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Track the in-flight plugin id/action so uninstall/update/enable/disable can't be double-fired and each row shows live progress; await loadExtensions() before clearing busy. Includes matching tramo editor and workflow_store tweaks (source + live copies) and tests.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the logo image at the top of the English and translated READMEs with the new docs/screenshots/poster.png banner, and change the tagline to "Deploy, manage, and monitor servers." (translated per language).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Updated both K8sPage copies to use the full-bleed `sk-tabgroup` page shell and content wrappers, including the loading state. This moves warning/empty/main tab content into `sk-tabgroup__content`/`__inner` for consistent spacing and tabbed-page structure, and removes the extra topbar meta text to match the new header pattern.
Regenerate frontend/src/plugins/<slug>/plugin.json from source via
scripts/sync-builtin-frontends.mjs. Resolves the drift check: k8s was a
missing trailing newline; tramo/wordpress/cloudflare-ops/gpu are the new
featured/feature_score fields normalized to canonical form.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Introduces the new `serverkit-k8s` backend plugin with full extension wiring: plugin metadata, model registration, lifecycle hooks, kubectl execution service, cluster CRUD/default handling, connectivity checks, normalized read endpoints (nodes/namespaces/workloads/pods/services/ingresses/logs/overview), and admin mutations (scale/restart/delete/apply). It also registers the plugin in the frontend plugins manifest (including plugin styles) so the Kubernetes page can be surfaced by the plugin system.
- Featured order: Faro (100), Automations (90), WordPress (80),
Cloudflare Zone Ops (70), GPU Monitor (60). Scores set in each bundled
extension's manifest (both copies); Faro's lives in the registry index.
- Add illustrated covers for Kubernetes and WordPress (extension-icons +
resolver rules) so they stop falling back to generic category glyphs.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
"Marketplace" reads as a store; the page is where you browse and install
extensions, so call it Extensions.
- Nav label + page/tab title + route /marketplace -> /extensions
(first tab drives the topbar title via TabGroupLayout).
- /marketplace and /marketplace/installed now redirect to /extensions.
- Update command palette, page-context map, NotFound, empty-state copy.
- Keep the internal nav id, CSS classes, component/file names, and the
backend /api/v1/marketplace/registry endpoints unchanged; palette keeps
"marketplace" as a search keyword so the old term still finds it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- registry_service: keep featured/feature_score through normalization so
registry (bundled:false) extensions like Faro actually float to the top
and show the Featured ribbon (they were being stripped).
- ExtensionIcons: give the Postfix/Dovecot "Email Server" the envelope and
the Stalwart "Mail Server" the server-rack icon so they stop looking like
duplicates ('email' matched before the bare 'mail' rule).
- Drop Faro's logo override so it uses its full-bleed illustrated cover
(92px) instead of the 44px brand-logo slot.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Match the published serverkit-faro v1.0.2 release (lean zip) in the
offline registry fallback.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Add Faro illustrated cover (extension-icons/faro.png) + resolver rule so
serverkit-faro shows a 3D lighthouse instead of a flat glyph.
- Mark Cloudflare Zone Ops featured (score 95) in both plugin.json copies.
- Add a wordpress.detail.panel PluginSlot so the Faro (and future WP)
extensions can render on WordPress site pages.
- Sync the registry offline fallback with the Faro v1.0.1 entry (featured).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Node-based automation builder powered by the tramo engine, embedded as an
opt-in builtin plugin (Automations, /automations). Panel is system of record
for workflow docs; a managed @tramo/server Docker container on the host runs
them headless with run history, approvals, and inbound webhooks proxied
through the panel. Replaces the older Workflow Builder.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Add an illustrated 3D cover icon for the Automations extension
(assets/extension-icons/automations.png, transparent bg, 256px) and map
tramo/automation to it in the extension icon resolver.
- Add manifest 'featured' + 'feature_score' (Automations = featured, score 100).
- Sort featured extensions to the top of the Marketplace by score (score is
hidden; used only for ordering) and show a gold 'Featured' ribbon on the
cover + a subtle featured border.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
The React-Flow Workflow Builder is replaced by the Automations extension
(tramo). Remove its core Python engine and port its four event emitters to
the panel event bus so tramo workflows can trigger on them via the events
bridge.
- event_service.py: new EVENT_CATALOG entries health.check_failed, git.push,
monitor.high_cpu, monitor.high_memory.
- docker_service.py: drop the app_stopped emit (app.stopped already fires via
the audit trail -- no double-emit).
- environment_health_service/git_service/monitoring_service: emit the new
panel events via EventService instead of WorkflowEventBus.
- Delete app/api/workflows.py, app/services/workflow_engine.py,
app/services/workflow_service.py; drop the blueprint + startup job
registration; neutralize the cron workflow-scheduler handler to a no-op.
- KEEP app/models/workflow.py + its tables (read-only legacy export lives in
the tramo blueprint; table drop is a future cleanup).
- Remove the two workflow-engine test classes from test_jobs.py.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Three seed WorkflowDocs (backup-failed->Telegram, nightly health->GitHub
issue, panel event->Discord) served at GET /tramo/templates, plus
POST /tramo/workflows/from-template/<id> to instantiate one.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>
Live test on the dev box caught 'serverkit list-apps --json' printing
the table: cmd_list_apps' hand-rolled flag mapping dropped everything
but --all, and cmd_list_users/cmd_list_servers dropped all args. All
three now forward "$@" (with -a kept as the legacy --all alias), and
test_cli.sh T20 drives the real executed-mode dispatch through a fake
venv with a recording python stub so any wrapper verb that eats flags
fails the suite.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
serverkit migrate-db forwarded the wrapper's own name to cli.py, which
only defines db-migrate — the command has failed with 'No such command'
since the rename. Fix the forward and stop the drift class entirely:
- cmd_panel_cli forwards Click-only verbs verbatim (manifest, services,
apps, repair, login-url, support-bundle, db-migrate/status/history;
panel-status/panel-doctor where a native bash name collides)
- serverkit completion prints a bash completion script; install.sh and
update.sh drop it into /etc/bash_completion.d (best-effort), the
uninstall lib removes it
- test_cli.sh T17-T19: migrate-db forwarding, dispatch reachability,
and a two-way parity guard (cli.py verbs <-> dispatch <-> completion);
test_update.sh T29 covers refresh_bash_completion
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
status, doctor, list-users, list-apps, list-servers, db-status,
deployment-status, services list, apps list, and manifest plan gain a
--json flag emitting machine-readable output, so the CLI can feed
scripts, cron checks, and external monitors. Table-only extras refuse
the combination loudly (--all, --repair).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Email Server builtin extension's backend package was present on disk (and
exercised by the green test suite) but never tracked. Adds it alongside the
already-tracked serverkit-gpu / serverkit-gui backend plugins: the Flask
blueprint (email.py) plus the Postfix/Dovecot/DKIM/SpamAssassin/Roundcube
service modules. __pycache__ stays gitignored.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>