Feat v0.8.2 capability negotiation (#69)
Some checks failed
CI/CD / detect-changes (pull_request) Successful in 23s
CI/CD / test-frontend (pull_request) Has been skipped
CI/CD / test-runners (pull_request) Has been skipped
CI/CD / e2e-smoke (pull_request) Has been skipped
CI/CD / test-go-pg (pull_request) Failing after 2m59s
CI/CD / test-sqlite (pull_request) Failing after 3m22s
CI/CD / build-and-deploy (pull_request) Has been skipped

Extensions declare environment requirements via capabilities.required and
capabilities.optional in their manifest. The kernel validates at install
time — required capabilities reject with HTTP 422 and rollback, optional
capabilities log a warning. Runtime query via settings.has_capability().
Admin endpoint GET /api/v1/admin/capabilities re-probes live.

Detected capabilities: postgres, pgvector, object_storage, s3, workspace.

18 new tests, all passing with -race.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-03 08:38:37 +00:00
parent 435f972ded
commit 44bf63e5fe
16 changed files with 567 additions and 64 deletions

View File

@@ -40,7 +40,7 @@ var defaultBundledPackages = map[string]bool{}
//
// Design: install-once, skip-if-present. If an admin uninstalls a bundled
// package, it won't be re-installed on the next restart.
func InstallBundledPackages(bundledDir, packagesDir, allowlist string, stores store.Stores, runner *sandbox.Runner) {
func InstallBundledPackages(bundledDir, packagesDir, allowlist string, stores store.Stores, runner *sandbox.Runner, detectedCaps map[string]bool) {
entries, err := os.ReadDir(bundledDir)
if err != nil {
if os.IsNotExist(err) {
@@ -82,7 +82,7 @@ func InstallBundledPackages(bundledDir, packagesDir, allowlist string, stores st
}
pkgPath := filepath.Join(bundledDir, entry.Name())
result, err := installBundledPackage(pkgPath, packagesDir, stores, runner, adminUserID)
result, err := installBundledPackage(pkgPath, packagesDir, stores, runner, adminUserID, detectedCaps)
if err != nil {
log.Printf("[bundled] ⚠️ Failed to install %s: %v", entry.Name(), err)
continue
@@ -129,7 +129,7 @@ func parseBundleAllowlist(s string) map[string]bool {
// installBundledPackage installs a single .pkg archive if its package ID
// doesn't already exist in the database. Returns "installed" or "skipped".
func installBundledPackage(pkgPath, packagesDir string, stores store.Stores, runner *sandbox.Runner, adminUserID string) (string, error) {
func installBundledPackage(pkgPath, packagesDir string, stores store.Stores, runner *sandbox.Runner, adminUserID string, detectedCaps map[string]bool) (string, error) {
ctx := context.Background()
// Open as zip
@@ -237,20 +237,17 @@ func installBundledPackage(pkgPath, packagesDir string, stores store.Stores, run
}
}
// Handle dormant status for packages with unmet requires
knownCaps := map[string]bool{}
if reqs, ok := manifest["requires"].([]any); ok && len(reqs) > 0 {
var unmetReqs []string
for _, r := range reqs {
req, _ := r.(string)
if req != "" && !knownCaps[req] {
unmetReqs = append(unmetReqs, req)
}
// Validate required capabilities — skip package if unmet
if caps, ok := ParseCapabilities(manifest); ok {
missing := CheckRequiredCapabilities(caps.Required, detectedCaps)
if len(missing) > 0 {
stores.Packages.Delete(ctx, pkgID)
log.Printf("[bundled] %s: skipped (missing required capabilities: %v)", pkgID, missing)
return "skipped", nil
}
if len(unmetReqs) > 0 {
stores.Packages.SetStatus(ctx, pkgID, "dormant")
stores.Packages.SetEnabled(ctx, pkgID, false)
log.Printf("[bundled] %s: dormant (unmet requires: %v)", pkgID, unmetReqs)
unmetOpt := CheckRequiredCapabilities(caps.Optional, detectedCaps)
if len(unmetOpt) > 0 {
log.Printf("[bundled] %s: optional capabilities unavailable: %v", pkgID, unmetOpt)
}
}