1231 Commits
Author SHA1 Message Date
Juan Denis 000099407c Merge pull request #59 from jhd3197/dev
Appliance-tier manifests, GitHub-native extensions, and the July recovery rebuild
v1.7.25
2026-07-09 20:58:22 -04:00
github-actions[bot] 33b21c8ba6 chore: bump version to 1.7.25 [skip ci] 2026-07-09 13:33:06 +00:00
Juan Denis d96acd24e9 Merge branch 'dev' of https://github.com/jhd3197/ServerKit into dev 2026-07-09 09:32:41 -04:00
Juan DenisandClaude Fable 5 4420c406f8 fix(release): stop blanket-excluding images from the release tarball
build-release.sh excluded *.png/*.jpg/*.jpeg globally — fine when the only
images were screenshots, fatal once ExtensionIcons.js (8bd9c1a) started
importing frontend/src/assets/extension-icons/*.png as build inputs: the
/tmp build tree lost them and rollup failed to resolve. The blanket rule
was also silently stripping frontend/public/favicon-*.png from every
release build.

Replace with targeted excludes: docs/screenshots, frontend/dist,
.codex-screenshots, template-icons, and (via --no-wildcards-match-slash)
root-level ./*.png|jpg|jpeg strays + dev/*.png. Verified by listing the
tar: 14 extension icons + 4 favicons included, zero stray images, *.log/
*.tmp/*.pyc still excluded; shellcheck clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 09:32:09 -04:00
github-actions[bot] e03795b707 chore: bump version to 1.7.24 [skip ci] 2026-07-09 13:07:35 +00:00
Juan Denis b46b86505d Merge branch 'dev' of https://github.com/jhd3197/ServerKit into dev 2026-07-09 09:05:53 -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
github-actions[bot] 01c1fba433 chore: bump version to 1.7.23 [skip ci] 2026-07-09 12:49:07 +00:00
Juan Denis 326079f21c Merge branch 'dev' of https://github.com/jhd3197/ServerKit into dev 2026-07-09 08:47:35 -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 297c1e5864 ci(tests): add test-count ratchet + changelog recovery-parity (plan 42 ph6)
Install the guard the data loss taught us to want: code and its proving tests
died together, so the suite stayed green (2572->2209 passing) while whole
features silently vanished — nobody diffs a green number.

- backend/tests/BASELINE_COUNT (2475) + check_test_count.py: compares pytest's
  collected count to the floor; CI (backend-ci.yml new step) fails if it drops.
  Lowering the floor requires editing BASELINE_COUNT in the same commit, so a
  silent test loss surfaces as a red X instead of a still-green suite. Verified:
  passes at baseline, exits 1 on a simulated drop.
- CHANGELOG: document the recovery-parity restore (Cloudflare R2 engine,
  email-bounce, DNS-cutover staging, survey routes, cron CSS, 18 test suites)
  and the ratchet.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 05:27:22 -04:00
Juan DenisandClaude Opus 4.8 b9ee243ba2 feat(cloudflare): restore Round 2 engine — DNSSEC, Origin CA, rules, activity (plan 42 ph2)
Restore the plan-36 Cloudflare Round 2 backend lost in the rebuild (migrations
073/074 survived; the engine + test_cloudflare_round2.py did not):

- CfOpsChange model (verbatim from clean pyc dump, matches mig 074) +
  CfOpsChangeService.record (never-raises) / list.
- Core client (dns/cloudflare.py): get/set_dnssec; create/list/revoke Origin
  certificates; X-Auth-User-Service-Key header mode (origin_ca_key, mig 073).
- Extension service: DNSSEC get/set; Origin CA issue (panel-side CSR, key never
  leaves the box, proxied-hostname warning, scoped-cred 400 not 502) /list/revoke
  + Linux-guarded install via upload_custom_cert; Redirect+Transform rules
  (RULE_PHASES/ACTIONS/PRESETS reusing _find_custom_ruleset + injection guard);
  best-effort _record wired into every mutating method; scope_check probe;
  list_activity.
- Extension routes: /zones/<id>/dnssec, origin-ca, /rules/<slug>, /activity,
  /scope-check (admin-gated, CloudflareError->400, provider->502).
- ssl_service.list_certificates walks /etc/ssl/serverkit + "Origin CA
  (proxy-only)" badge; fixed certbot-branch early-return hiding custom certs.

Also registers EmailBounceState (plan 42 ph3) in models/__init__ (shared file).
Deviation: origin_ca_key implemented as the Fernet Origin CA *service key* (plan
Decision 3), not mig-073's docstring "generated private key" — the generated key
is used inline for install and never persisted. R2 preset backend-untouched (it
lives in the storage-provider + frontend layers). Verified: 83 passed
(round2 30 + zone_settings 42 + extraction 8 + ssl 3), no regression.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 05:02:54 -04:00
Juan DenisandClaude Opus 4.8 4833f91451 feat(notifications): restore email-bounce webhook slice (plan 42 ph3)
Restore the plan-33 bounce slice lost in the rebuild (migration 069 survived;
the model/route/service did not). EmailBounceState model reconstructed verbatim
from the clean pyc dump. New bounce_service.BounceService maps provider payloads
(generic/SendGrid/Postmark/SES/Mailgun), records bounces correlated to
NotificationDelivery by provider_message_id, auto-mutes a recipient past
MUTE_THRESHOLD (complaints mute immediately), and the email channel skips muted
targets. Inbound webhook POST /api/v1/notifications/inbound/email (HMAC-SHA256,
404 until notify.inbound_secret set, 401 on bad sig) + operator unmute + admin
bouncing list + email_status in GET /preferences.

Route/service names/paths faithful to the salvaged test string-index; logic
reconstructed from the plan-33 doc (the route+test pycs were corrupt). Model
registration in app/models/__init__.py lands with the Phase-2 commit (shared
file). Verified: test_email_bounce.py 16 passed; notification suite no regressions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 04:55:53 -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 DenisandClaude Opus 4.8 ba271a6850 fix(dns-cutover): restore snapshot() staging + provider-named 501 (plan 42 D7/ph5)
The recovery rebuild left the provider-registry cutover path a subset: the
explicit-records staging classmethod DnsCutoverService.snapshot(domain, records,
provider=, provider_zone_id=) was gone (only the server-sourced create_snapshot,
which needs a live client, survived), and the 501 NO_PROVIDER body didn't name
the provider. Re-add snapshot() (sibling of create_snapshot; stages caller-
supplied records with no client so the provider guard fires at cutover time) and
thread DnsCutoverError.provider through _resolve_dns_client into the API bodies.

Un-skips test_dns_cutover_providers (plan 31 #12). Kept the rebuilt contract's
UNWRAPPED /cutover success shape (proven by the surviving test_dns_cutover_api.py)
rather than the pre-loss 'cutover'-wrapped shape, adapting that one assertion.
23 passed (providers + api + service), no regression.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 04:50:16 -04:00
Juan DenisandClaude Opus 4.8 e25d4926e7 fix(survey): register orphaned survey_bp dropped in recovery rebuild (plan 42 ph1)
app/api/survey.py defines survey_bp + its /api/v1/servers/<id>/survey routes
(plan 27/28) and its own docstring says "Mounted at /api/v1/servers (see
app/__init__.py)", but the 2026-07-08 recovery rebuild dropped the
register_blueprint call, leaving every survey/management-mode/observed-status
route a 404. The survey_service logic survived intact. Re-register it under
/api/v1/servers (same prefix as servers_bp, no route conflict). Un-blocks the
restored test_survey_api catalog/snapshot route coverage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 04:41:09 -04:00
Juan DenisandClaude Opus 4.8 63ede5fe46 test(recovery): restore 18 lost proving-test suites from pyc trove (plan 42 ph1)
Re-arm the proving surface the data loss silently took: 18 backend test
files reconstructed from winfr-carved .pyc fragments (marshal+dis extractor
for clean pycs; string-salvage + surviving-source reconstruction for
fragmented ones). Restores coverage for backup drills/offsite (plan 30),
fleet sweep + fleet-doctor DNS + DNS cutover + ssh import + observe
enforcement (plans 26/31), survey catalog/api + setup security + workspace
scoping (plans 28/22/19), and sdk contract + cron preview + notification
prefs + runtime frontend (plans 32/25/34/24).

Where a restored test proved its feature HOLLOW (code died with its test),
the affected cases are @pytest.mark.skip(reason="plan 42: ...") naming the
exact missing symbol, so the canary stays green while flagging the gap for a
named follow-up. Full suite: 2373 passed, 56 skipped, 0 failed; collected
2322 -> 2429. test_gpu dropped as redundant (coverage survives in
test_gpu_extension.py post core-slim).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 04:41:00 -04:00
github-actions[bot] ffedcbed15 chore: bump version to 1.7.22 [skip ci] 2026-07-09 07:38:42 +00: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 80e1fdca44 feat(manifest): reference appliance + adoption hardening (plan 35 ph6)
Prove the reference-appliance-media.yaml applies end-to-end (one Application,
udp+local ports recorded, prosody bootstrap once, JVB fromServer IP bound,
web-config backup policy, 4-service generated compose, idempotent re-plan).
Scaffold now round-trips typed ports + BYO image + disk size; drift_pair tracks
raw ports and image; a schema-copy structural-parity test guards the docs copy.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 03:35:53 -04:00
Juan DenisandClaude Opus 4.8 0b21ef93f5 feat(manifest): network identity — fromServer, SERVER_PUBLIC_IP, shared net (plan 35 ph5)
Add a fromServer env binder ({property: publicIp|hostname}) resolved against the
target server or panel host, with a fromserver_no_ip plan-time blocker. Add the
${SERVER_PUBLIC_IP} template magic variable. Attach manifest-generated app
projects to a shared external `serverkit` docker network (DockerService.
ensure_network + networks param on create_docker_app/buildpack generate_compose)
so a fromService host reference resolves cross-app by container name at runtime.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 03:28:08 -04:00
Juan DenisandClaude Opus 4.8 619b2215da feat(manifest): BYO image + host requirements (plan 35 ph4)
Add first-class `image:`/`registry:` (apply stamps Application.docker_image +
registry_id, resolved by registry name; registry_credential blocker on
unknown/uncredentialed registries, ECR accepted) and `hostRequirements`
(privileged/capAdd/sysctls/devices/kernelModules). create_docker_app emits the
host-requirement fields; every requirement is listed in plain words as a plan
issue and audited via manifest.host_requirements on apply. kernelModules get an
advisory /proc/modules check that warns but never silently passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 03:21:14 -04:00
Juan DenisandClaude Opus 4.8 fdc3d45749 feat(manifest): multi-container units (plan 35 ph3)
Add a `containers:` map that turns one service into one Application rendered as
one compose project. Normalizer validates mutual-exclusion with buildpack keys,
dependsOn references, and rejects cycles (DFS). New UnitComposeService renders
container_name prefixing, healthcheck blocks (cmd->CMD-SHELL, httpPath->wget),
depends_on.condition mapping, raw port publishes, and per-container synthetic
named volumes. resolve_service flattens container ports/bootstraps/host-reqs/env
to the unit (unified `bootstraps` list under one bootstrap_done flag); unit disk
backups route at the docker-volume name. Seams: unit_compose/unit_container_names.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 03:13:54 -04:00
Juan DenisandClaude Opus 4.8 be3a9f9bcd feat(manifest): finish appliance volumes + one-shot bootstrap (plan 35 ph2)
Persist manifest disk `size` onto AppVolume.declared_size; route disk backups to
the docker volume's live host mountpoint (VolumeService.host_path_for_mount +
volume_mount meta in BackupPolicyService._resolve_target). Add `bootstrap`
schema + BootstrapService.run_once (docker compose run --rm, injectable runner
seam), a bootstrap apply step that stamps applications.bootstrap_done once, and
POST /manifests/bootstrap/reset to re-arm it. Scaffold round-trips disk size.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 03:05:08 -04:00
Juan DenisandClaude Opus 4.8 06c3fcf28c feat(manifest): appliance-tier typed L4 ports + plan-time blockers rail (plan 35 ph1)
Rebuild of plan 35 Phase 1 against the surviving migrations (ports as a JSON
column on applications, not a separate table). Adds typed `ports[]` to the
serverkit.yaml schema/normalizer, AppPortService (persist/compose-emit/firewall),
open_port apply step + firewall open on apply / close on app delete, and the
blockers engine (port_conflict/remote_target/observed_server/firewall_undetected)
that refuses apply instead of half-deploying.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 02:57:23 -04:00
Juan Denis 7f46787aee Merge branch 'dev' of https://github.com/jhd3197/ServerKit into dev 2026-07-09 02:18:47 -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 9062f1d47a docs(extensions): document the installable-straight-from-GitHub contract
EXTENSIONS.md gains an 'Installable straight from GitHub' section: plugin.json
at archive root, release .zip preferred over zipball fallback, accepted URL
forms incl. owner/repo[@tag], and how the preview/consent flow presents your
declared permissions (checksum-pinned install). Keeps the GitHub docs and the
serverkit.ai Installing page telling the same story.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 02:05:32 -04:00
Juan DenisandClaude Opus 4.8 dc628aabba docs(manifest): point serverkit.yaml $schema at serverkit.ai
Single home for the schema on the product domain (serverkit.dev -> serverkit.ai
in SERVERKIT_YAML.md examples and the schema $id), matching the new
serverkit.ai/serverkit-yaml.schema.json endpoint. Also documents the deferred
registry-URL flip (plan 40 task 11): DEFAULT_REGISTRY_URL stays raw-GitHub
until the serverkit.ai /ext endpoints are deployed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 01:57:11 -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 f0fe9d833e feat(registry): add builtin-extension catalog export script
scripts/export-registry-entries.mjs reads builtin-extensions/*/plugin.json
and emits index-v2 bundled entries for the serverkit-extensions registry.
Normalizes panel categories onto the registry enum and optionally attaches
repo-relative logo paths when art exists. Run and paste into index.json via
PR whenever the bundled set changes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 01:37:20 -04:00
github-actions[bot] 4fe8da21c8 chore: bump version to 1.7.21 [skip ci] 2026-07-09 05:34:36 +00: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
github-actions[bot] bc19039c5b chore: bump version to 1.7.20 [skip ci] 2026-07-09 03:59:15 +00:00
Juan Denis cfcf5bb3c6 Merge branch 'dev' of https://github.com/jhd3197/ServerKit into dev 2026-07-08 23:59:11 -04:00
Juan DenisandClaude Opus 4.8 50587fe06b test(ssh-import): redirect APPS_DIR to tmp so create_app step passes on CI
test_copy_files_uses_staged_docroot drove _step_create_app, which provisions
the app root under paths.APPS_DIR (defaults to /var/serverkit/apps). That path
is writable on a local dev box but denied on the CI runner, so the test failed
only in CI with PermissionError. Monkeypatch app.paths.APPS_DIR to the tmp
import base, matching the apps_dir pattern already used in test_site_import.py.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 23:58:45 -04:00
github-actions[bot] ffc89242c8 chore: bump version to 1.7.19 [skip ci] 2026-07-09 03:24:37 +00:00
Juan Denis fc7199e2d5 Merge branch 'dev' of https://github.com/jhd3197/ServerKit into dev 2026-07-08 23:24:31 -04:00
Juan DenisandClaude Opus 4.8 7c68277e13 docs(changelog): record the plan-39 UI consistency round under Unreleased
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 23:13:53 -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 Fable 5 80d3758394 fix(scripts): restore agent install.sh and full-stack-test.ps1 clobbered in recovery
Commit 6e32519 (the July 5-7 recovery restore) wrote git-reflog garbage
over scripts/install.sh (the agent installer served at
GET /api/v1/servers/install.sh) and whitespace-mangled content over
scripts/test/full-stack-test.ps1, breaking scripts-ci: shellcheck SC2148
(no shebang) and 15/18 test_agent_install.sh failures. Restore both files
verbatim from 6e32519~1, the last good revision.

Verified: test_agent_install.sh 18/18, test_install.sh 62 passed,
shellcheck --severity=error clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 23:00:50 -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
github-actions[bot] fd45f25ede chore: bump version to 1.7.18 [skip ci] 2026-07-09 02:43:25 +00: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 95871c2ba8 fix(staging): verify migration-head check works with descriptive revision IDs
The staging verify's migration-head check grepped for Alembic hex hashes
([0-9a-f]{8,}), but ServerKit revision IDs are descriptive slugs (e.g.
074_cf_ops_changes). The empty grep exited non-zero and, unguarded under
set -e, aborted verify before the VERDICT line ever printed. Take the first
token of `flask db current`/`heads` instead, and guard with `|| true`.

Surfaced driving plan-38 Phase 5 (staged deploy of the recovered dev to the DO
test box via Faro): deploy migrated cleanly to head 074 but verify crashed
pre-verdict. With this fix, `verify --full` emits a green VERDICT (pass:true).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 09:26:18 -04:00
Juan DenisandClaude Opus 4.8 4bd36574c9 docs(changelog): record the July-5-7 recovery rebuild under Unreleased
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 02:31:13 -04:00