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
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:
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user