From c65674965f49c399ec0f31fd49ee177b22256833 Mon Sep 17 00:00:00 2001 From: Jeffrey Smith Date: Fri, 27 Mar 2026 16:45:45 +0000 Subject: [PATCH] Feat v0.2.9 builtin extension retirement Stop auto-seeding 6 chat-centric extensions on startup. Move them to standard packages with js/ layout and "requires": ["chat"] metadata. Delete SeedBuiltinPackages, seed tests, Dockerfile COPY, and extensions/builtin/ directory. Co-Authored-By: Claude Opus 4.6 (1M context) --- CHANGELOG.md | 23 ++ Dockerfile | 3 - ROADMAP.md | 4 +- VERSION | 2 +- .../csv-table/js}/script.js | 0 .../csv-table/manifest.json | 2 + .../diff-viewer/js}/script.js | 0 .../diff-viewer/manifest.json | 2 + .../js-sandbox/js}/script.js | 0 .../js-sandbox/manifest.json | 2 + .../katex-renderer/js}/script.js | 0 .../katex-renderer/manifest.json | 2 + .../mermaid-renderer/js}/script.js | 0 .../mermaid-renderer/manifest.json | 2 + .../regex-tester/js}/script.js | 0 .../regex-tester/manifest.json | 2 + server/handlers/extension_test.go | 220 +----------------- server/handlers/seed_packages.go | 208 ----------------- server/handlers/trigger_sync.go | 2 +- server/main.go | 2 - server/store/package_iface.go | 5 +- 21 files changed, 42 insertions(+), 439 deletions(-) rename {extensions/builtin/csv-table => packages/csv-table/js}/script.js (100%) rename {extensions/builtin => packages}/csv-table/manifest.json (85%) rename {extensions/builtin/diff-viewer => packages/diff-viewer/js}/script.js (100%) rename {extensions/builtin => packages}/diff-viewer/manifest.json (86%) rename {extensions/builtin/js-sandbox => packages/js-sandbox/js}/script.js (100%) rename {extensions/builtin => packages}/js-sandbox/manifest.json (96%) rename {extensions/builtin/katex-renderer => packages/katex-renderer/js}/script.js (100%) rename {extensions/builtin => packages}/katex-renderer/manifest.json (86%) rename {extensions/builtin/mermaid-renderer => packages/mermaid-renderer/js}/script.js (100%) rename {extensions/builtin => packages}/mermaid-renderer/manifest.json (87%) rename {extensions/builtin/regex-tester => packages/regex-tester/js}/script.js (100%) rename {extensions/builtin => packages}/regex-tester/manifest.json (96%) delete mode 100644 server/handlers/seed_packages.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 07baef8..f635bb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,29 @@ All notable changes to Switchboard Core are documented here. +## v0.2.9 — Builtin Extension Retirement + +### Changed + +- **6 builtin extensions → standard packages**: csv-table, diff-viewer, + js-sandbox, katex-renderer, mermaid-renderer, and regex-tester moved from + `extensions/builtin/` to `packages/` with standard `js/` layout. Each + manifest now declares `"requires": ["chat"]` and `"type": "extension"`. + Built via `build.sh` like all other packages — no auto-install. + +### Removed + +- **`SeedBuiltinPackages` seeder**: Deleted `seed_packages.go` (function, + `builtinManifest` struct, `buildFullManifest` helper) and the startup call + in `main.go`. Extensions are no longer auto-seeded into the DB. +- **`extensions/builtin/` directory**: Removed from repo and Dockerfile COPY. +- **Seed tests**: Removed 8 `TestSeed_*` functions and `makeSeedDir` helper + from `extension_test.go` (~220 lines). +- **Stale comments**: Removed `SeedBuiltinPackages` references from + `package_iface.go` and `trigger_sync.go`. + +--- + ## v0.2.8 — Team Admin Settings Audit (Pass 1) ### Removed diff --git a/Dockerfile b/Dockerfile index c110790..ae22a02 100644 --- a/Dockerfile +++ b/Dockerfile @@ -85,9 +85,6 @@ COPY --from=vendor /vendor/katex/katex.min.css /usr/share/nginx/html/vendor/kate COPY --from=vendor /vendor/katex/fonts/ /usr/share/nginx/html/vendor/katex/fonts/ COPY --from=cm6-build /build/dist/ /usr/share/nginx/html/vendor/codemirror/ -# Builtin extensions (seeded into DB on startup by backend) -COPY extensions/builtin/ /app/extensions/builtin/ - # nginx config (template — envsubst injects BASE_PATH at runtime) COPY nginx.conf.template /etc/nginx/templates/default.conf.template # Only substitute BASE_PATH — preserve nginx variables ($host, $uri, etc.) diff --git a/ROADMAP.md b/ROADMAP.md index 884babb..aa6a59f 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -114,8 +114,8 @@ SDK stabilization, and the first rebuilt extension (tasks). | Step | Status | Description | |------|--------|-------------| -| Retire builtin seeder | ⬚ | Stop auto-seeding chat-centric extensions (csv-table, diff-viewer, js-sandbox, katex-renderer, mermaid-renderer, regex-tester). Remove `SeedBuiltinPackages`, Dockerfile COPY, and `extensions/builtin/` directory. These extensions are not OBE — they're dormant until a chat surface exists to consume them. | -| Convert to regular packages | ⬚ | Repackage the 6 extensions as standard `.pkg` archives in `packages/`. Add chat dependency metadata to manifests so they can be installed when the chat extension ships post-MVP. No auto-install — explicit install only, matching the distribution model. | +| Retire builtin seeder | ✅ | Removed `SeedBuiltinPackages`, `seed_packages.go`, Dockerfile COPY, and `extensions/builtin/` directory. These extensions are dormant until a chat surface exists to consume them. | +| Convert to regular packages | ✅ | Repackaged 6 extensions as standard directories in `packages/` with `js/` layout and `"requires": ["chat"]` manifest metadata. Built via `build.sh` like all other packages. No auto-install — explicit install only. | ## v0.3.x — Workflow Architecture diff --git a/VERSION b/VERSION index a45be46..1866a36 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.2.8 +0.2.9 diff --git a/extensions/builtin/csv-table/script.js b/packages/csv-table/js/script.js similarity index 100% rename from extensions/builtin/csv-table/script.js rename to packages/csv-table/js/script.js diff --git a/extensions/builtin/csv-table/manifest.json b/packages/csv-table/manifest.json similarity index 85% rename from extensions/builtin/csv-table/manifest.json rename to packages/csv-table/manifest.json index 96a1ab5..4b72abd 100644 --- a/extensions/builtin/csv-table/manifest.json +++ b/packages/csv-table/manifest.json @@ -2,9 +2,11 @@ "id": "csv-table", "name": "CSV Table Viewer", "version": "1.0.0", + "type": "extension", "tier": "browser", "author": "switchboard", "description": "Renders ```csv code blocks as sortable, interactive HTML tables", + "requires": ["chat"], "permissions": [], "tools": [], "surfaces": [], diff --git a/extensions/builtin/diff-viewer/script.js b/packages/diff-viewer/js/script.js similarity index 100% rename from extensions/builtin/diff-viewer/script.js rename to packages/diff-viewer/js/script.js diff --git a/extensions/builtin/diff-viewer/manifest.json b/packages/diff-viewer/manifest.json similarity index 86% rename from extensions/builtin/diff-viewer/manifest.json rename to packages/diff-viewer/manifest.json index fc17adb..18ffaec 100644 --- a/extensions/builtin/diff-viewer/manifest.json +++ b/packages/diff-viewer/manifest.json @@ -2,9 +2,11 @@ "id": "diff-viewer", "name": "Diff Viewer", "version": "1.0.0", + "type": "extension", "tier": "browser", "author": "switchboard", "description": "Renders ```diff code blocks with syntax-highlighted additions, deletions, and context lines", + "requires": ["chat"], "permissions": [], "tools": [], "surfaces": [], diff --git a/extensions/builtin/js-sandbox/script.js b/packages/js-sandbox/js/script.js similarity index 100% rename from extensions/builtin/js-sandbox/script.js rename to packages/js-sandbox/js/script.js diff --git a/extensions/builtin/js-sandbox/manifest.json b/packages/js-sandbox/manifest.json similarity index 96% rename from extensions/builtin/js-sandbox/manifest.json rename to packages/js-sandbox/manifest.json index b01b539..c3227c6 100644 --- a/extensions/builtin/js-sandbox/manifest.json +++ b/packages/js-sandbox/manifest.json @@ -2,9 +2,11 @@ "id": "js-sandbox", "name": "JavaScript Sandbox", "version": "1.0.0", + "type": "extension", "tier": "browser", "author": "switchboard", "description": "Executes JavaScript in a sandboxed iframe. Allows the LLM to run code, verify calculations, and transform data — no server compute required.", + "requires": ["chat"], "permissions": [], "tools": [ { diff --git a/extensions/builtin/katex-renderer/script.js b/packages/katex-renderer/js/script.js similarity index 100% rename from extensions/builtin/katex-renderer/script.js rename to packages/katex-renderer/js/script.js diff --git a/extensions/builtin/katex-renderer/manifest.json b/packages/katex-renderer/manifest.json similarity index 86% rename from extensions/builtin/katex-renderer/manifest.json rename to packages/katex-renderer/manifest.json index 6b20a23..0a0e184 100644 --- a/extensions/builtin/katex-renderer/manifest.json +++ b/packages/katex-renderer/manifest.json @@ -2,9 +2,11 @@ "id": "katex-renderer", "name": "KaTeX Math", "version": "1.0.0", + "type": "extension", "tier": "browser", "author": "switchboard", "description": "Renders LaTeX math expressions: ```latex blocks and inline $...$ / $$...$$ syntax", + "requires": ["chat"], "permissions": [], "tools": [], "surfaces": [], diff --git a/extensions/builtin/mermaid-renderer/script.js b/packages/mermaid-renderer/js/script.js similarity index 100% rename from extensions/builtin/mermaid-renderer/script.js rename to packages/mermaid-renderer/js/script.js diff --git a/extensions/builtin/mermaid-renderer/manifest.json b/packages/mermaid-renderer/manifest.json similarity index 87% rename from extensions/builtin/mermaid-renderer/manifest.json rename to packages/mermaid-renderer/manifest.json index 53eb43b..524c41b 100644 --- a/extensions/builtin/mermaid-renderer/manifest.json +++ b/packages/mermaid-renderer/manifest.json @@ -2,9 +2,11 @@ "id": "mermaid-renderer", "name": "Mermaid Diagrams", "version": "2.0.0", + "type": "extension", "tier": "browser", "author": "switchboard", "description": "Renders ```mermaid code blocks as interactive SVG diagrams with zoom/pan, SVG/PNG export, and source copy", + "requires": ["chat"], "permissions": [], "tools": [], "surfaces": [], diff --git a/extensions/builtin/regex-tester/script.js b/packages/regex-tester/js/script.js similarity index 100% rename from extensions/builtin/regex-tester/script.js rename to packages/regex-tester/js/script.js diff --git a/extensions/builtin/regex-tester/manifest.json b/packages/regex-tester/manifest.json similarity index 96% rename from extensions/builtin/regex-tester/manifest.json rename to packages/regex-tester/manifest.json index 24259a3..b92a0d2 100644 --- a/extensions/builtin/regex-tester/manifest.json +++ b/packages/regex-tester/manifest.json @@ -2,9 +2,11 @@ "id": "regex-tester", "name": "Regex Tester", "version": "1.0.0", + "type": "extension", "tier": "browser", "author": "switchboard", "description": "Tests regular expressions against input strings. Allows the LLM to verify regex patterns and see matches, groups, and indices.", + "requires": ["chat"], "permissions": [], "tools": [ { diff --git a/server/handlers/extension_test.go b/server/handlers/extension_test.go index c724df7..b5450c4 100644 --- a/server/handlers/extension_test.go +++ b/server/handlers/extension_test.go @@ -5,9 +5,7 @@ import ( "database/sql" "encoding/json" "net/http" - "os" - "path/filepath" - "testing" +"testing" "github.com/gin-gonic/gin" @@ -806,219 +804,3 @@ func TestExtension_StoreDeleteUserSettings(t *testing.T) { } } -// ═══════════════════════════════════════════════ -// Seed Logic (F26/F27) -// ═══════════════════════════════════════════════ - -// makeSeedDir creates a temp directory with extension subdirectories. -// Each entry in exts maps a dir name to {manifest, script}. -func makeSeedDir(t *testing.T, exts map[string]struct{ manifest, script string }) string { - t.Helper() - dir := t.TempDir() - for name, files := range exts { - extDir := filepath.Join(dir, name) - if err := os.MkdirAll(extDir, 0755); err != nil { - t.Fatalf("mkdir %s: %v", extDir, err) - } - if files.manifest != "" { - if err := os.WriteFile(filepath.Join(extDir, "manifest.json"), []byte(files.manifest), 0644); err != nil { - t.Fatalf("write manifest: %v", err) - } - } - if files.script != "" { - if err := os.WriteFile(filepath.Join(extDir, "script.js"), []byte(files.script), 0644); err != nil { - t.Fatalf("write script: %v", err) - } - } - } - return dir -} - -func TestSeed_NewExtension(t *testing.T) { - h := setupExtensionHarness(t) - ctx := context.Background() - - dir := makeSeedDir(t, map[string]struct{ manifest, script string }{ - "test-seeded": { - manifest: `{"id":"test-seeded","name":"Seeded Extension","version":"1.0.0","tier":"browser","author":"test"}`, - script: `console.log("seeded");`, - }, - }) - - SeedBuiltinPackages(h.stores, dir) - - // Should be created as system extension - pkg, err := h.stores.Packages.Get(ctx, "test-seeded") - if err != nil { - t.Fatalf("seeded extension not found: %v", err) - } - if pkg.Title != "Seeded Extension" { - t.Fatalf("name: got %q", pkg.Title) - } - if pkg.Version != "1.0.0" { - t.Fatalf("version: got %q", pkg.Version) - } - if !pkg.IsSystem { - t.Fatal("should be system extension") - } - if !pkg.Enabled { - t.Fatal("should be enabled") - } - // Verify _script is inlined in manifest - if _, ok := pkg.Manifest["_script"]; !ok { - t.Fatal("manifest should contain _script") - } -} - -func TestSeed_SameVersionSkips(t *testing.T) { - h := setupExtensionHarness(t) - ctx := context.Background() - - dir := makeSeedDir(t, map[string]struct{ manifest, script string }{ - "skip-ext": { - manifest: `{"id":"skip-ext","name":"Skip Test","version":"1.0.0"}`, - script: `console.log("v1");`, - }, - }) - - // Seed twice - SeedBuiltinPackages(h.stores, dir) - SeedBuiltinPackages(h.stores, dir) - - // Should still have exactly one - pkgs, err := h.stores.Packages.List(ctx) - if err != nil { - t.Fatalf("list: %v", err) - } - count := 0 - for _, p := range pkgs { - if p.ID == "skip-ext" { - count++ - } - } - if count != 1 { - t.Fatalf("expected 1 skip-ext, got %d", count) - } -} - -func TestSeed_VersionChangeUpdates(t *testing.T) { - h := setupExtensionHarness(t) - ctx := context.Background() - - // Seed v1 - dir1 := makeSeedDir(t, map[string]struct{ manifest, script string }{ - "versioned-ext": { - manifest: `{"id":"versioned-ext","name":"V1 Name","version":"1.0.0","author":"original"}`, - script: `console.log("v1");`, - }, - }) - SeedBuiltinPackages(h.stores, dir1) - - pkg, _ := h.stores.Packages.Get(ctx, "versioned-ext") - if pkg.Version != "1.0.0" { - t.Fatalf("v1 version: got %q", pkg.Version) - } - - // Seed v2 (different version triggers update) - dir2 := makeSeedDir(t, map[string]struct{ manifest, script string }{ - "versioned-ext": { - manifest: `{"id":"versioned-ext","name":"V2 Name","version":"2.0.0","author":"updated"}`, - script: `console.log("v2");`, - }, - }) - SeedBuiltinPackages(h.stores, dir2) - - pkg, _ = h.stores.Packages.Get(ctx, "versioned-ext") - if pkg.Version != "2.0.0" { - t.Fatalf("v2 version: got %q", pkg.Version) - } - if pkg.Title != "V2 Name" { - t.Fatalf("v2 name: got %q", pkg.Title) - } -} - -func TestSeed_MissingManifestSkips(t *testing.T) { - h := setupExtensionHarness(t) - ctx := context.Background() - - // Directory with no manifest.json - dir := makeSeedDir(t, map[string]struct{ manifest, script string }{ - "no-manifest": {script: `console.log("orphan");`}, - }) - - SeedBuiltinPackages(h.stores, dir) - - pkgs, err := h.stores.Packages.List(ctx) - if err != nil { - t.Fatalf("list: %v", err) - } - if len(pkgs) != 0 { - t.Fatalf("should skip dir without manifest, got %d extensions", len(pkgs)) - } -} - -func TestSeed_InvalidManifestSkips(t *testing.T) { - h := setupExtensionHarness(t) - ctx := context.Background() - - dir := makeSeedDir(t, map[string]struct{ manifest, script string }{ - "bad-manifest": {manifest: `{not valid json`, script: `console.log("bad");`}, - }) - - SeedBuiltinPackages(h.stores, dir) - - pkgs, _ := h.stores.Packages.List(ctx) - if len(pkgs) != 0 { - t.Fatalf("should skip invalid manifest, got %d extensions", len(pkgs)) - } -} - -func TestSeed_MissingIDSkips(t *testing.T) { - h := setupExtensionHarness(t) - ctx := context.Background() - - // Valid JSON but no "id" field - dir := makeSeedDir(t, map[string]struct{ manifest, script string }{ - "no-id": {manifest: `{"name":"No ID Extension","version":"1.0.0"}`, script: `console.log("no id");`}, - }) - - SeedBuiltinPackages(h.stores, dir) - - pkgs, _ := h.stores.Packages.List(ctx) - if len(pkgs) != 0 { - t.Fatalf("should skip manifest without id, got %d extensions", len(pkgs)) - } -} - -func TestSeed_NonexistentDirectorySkips(t *testing.T) { - h := setupExtensionHarness(t) - - // Should not panic on missing directory - SeedBuiltinPackages(h.stores, "/nonexistent/path/extensions") - - pkgs, _ := h.stores.Packages.List(context.Background()) - if len(pkgs) != 0 { - t.Fatalf("should seed nothing from missing dir, got %d", len(pkgs)) - } -} - -func TestSeed_ScriptOptional(t *testing.T) { - h := setupExtensionHarness(t) - ctx := context.Background() - - // Manifest-only extension (no script.js) - dir := makeSeedDir(t, map[string]struct{ manifest, script string }{ - "manifest-only": {manifest: `{"id":"manifest-only","name":"Manifest Only","version":"1.0.0"}`}, - }) - - SeedBuiltinPackages(h.stores, dir) - - pkg, err := h.stores.Packages.Get(ctx, "manifest-only") - if err != nil { - t.Fatalf("should create manifest-only ext: %v", err) - } - // Manifest should NOT contain _script - if _, ok := pkg.Manifest["_script"]; ok { - t.Fatal("manifest should not contain _script when no script.js exists") - } -} diff --git a/server/handlers/seed_packages.go b/server/handlers/seed_packages.go deleted file mode 100644 index 1508d64..0000000 --- a/server/handlers/seed_packages.go +++ /dev/null @@ -1,208 +0,0 @@ -package handlers - -import ( - "context" - "encoding/json" - "log" - "os" - "path/filepath" - - "switchboard-core/store" -) - -// builtinManifest is the on-disk manifest.json shape. -type builtinManifest struct { - ID string `json:"id"` - Name string `json:"name"` - Version string `json:"version"` - Tier string `json:"tier"` - Author string `json:"author"` - Description string `json:"description"` - Permissions json.RawMessage `json:"permissions"` - Tools json.RawMessage `json:"tools"` - Surfaces json.RawMessage `json:"surfaces"` - Settings json.RawMessage `json:"settings"` -} - -// SeedBuiltinPackages scans a directory for builtin extension bundles -// (each subdirectory contains manifest.json + script.js) and upserts them -// into the packages table as system packages. -// -// Layout: -// -// extensions/builtin/ -// mermaid-renderer/ -// manifest.json -// script.js -// -// Idempotent: skips packages whose version matches what's already installed, -// updates those with a newer version, creates new ones. -func SeedBuiltinPackages(stores store.Stores, extensionsDir string) { - if stores.Packages == nil { - return - } - - entries, err := os.ReadDir(extensionsDir) - if err != nil { - if os.IsNotExist(err) { - log.Printf(" 📦 No builtin extensions directory at %s (skipping)", extensionsDir) - return - } - log.Printf("⚠ Failed to read extensions directory %s: %v", extensionsDir, err) - return - } - - ctx := context.Background() - seeded, updated, skipped := 0, 0, 0 - - for _, entry := range entries { - if !entry.IsDir() { - continue - } - - extDir := filepath.Join(extensionsDir, entry.Name()) - manifestPath := filepath.Join(extDir, "manifest.json") - scriptPath := filepath.Join(extDir, "script.js") - - // Read manifest - manifestData, err := os.ReadFile(manifestPath) - if err != nil { - log.Printf("⚠ Skipping %s: no manifest.json: %v", entry.Name(), err) - continue - } - - var manifest builtinManifest - if err := json.Unmarshal(manifestData, &manifest); err != nil { - log.Printf("⚠ Skipping %s: invalid manifest.json: %v", entry.Name(), err) - continue - } - - if manifest.ID == "" { - log.Printf("⚠ Skipping %s: manifest missing 'id'", entry.Name()) - continue - } - - // Read script (optional — some extensions might be manifest-only) - var script string - if scriptData, err := os.ReadFile(scriptPath); err == nil { - script = string(scriptData) - } - - // Build the full manifest with _script inlined - fullManifest := buildFullManifest(manifestData, script) - - // Parse into map for Seed/Update - var manifestMap map[string]any - json.Unmarshal(fullManifest, &manifestMap) - - // Check if already installed - existing, err := stores.Packages.Get(ctx, manifest.ID) - if err == nil && existing != nil { - // Resolve tier for fixup + update paths - tier := manifest.Tier - if tier == "" { - tier = "browser" - } - - // Same version — skip, but fixup type/tier if wrong - // (v0.28.7 initial seed created builtins with type='surface' - // because Seed() uses schema DEFAULT and Update() didn't set type) - if existing.Version == manifest.Version { - if existing.Type != "extension" || existing.Tier != tier { - pkg := &store.PackageRegistration{ - Title: existing.Title, Type: "extension", Version: existing.Version, - Description: existing.Description, Author: existing.Author, - Tier: tier, Manifest: existing.Manifest, - IsSystem: true, Enabled: existing.Enabled, - } - if err := stores.Packages.Update(ctx, manifest.ID, pkg); err != nil { - log.Printf("⚠ Failed to fixup builtin package %s: %v", manifest.ID, err) - } - } - skipped++ - continue - } - // Version changed — update - pkg := &store.PackageRegistration{ - Title: manifest.Name, - Type: "extension", - Version: manifest.Version, - Description: manifest.Description, - Author: manifest.Author, - Tier: tier, - Manifest: manifestMap, - IsSystem: true, - Enabled: true, - } - if err := stores.Packages.Update(ctx, manifest.ID, pkg); err != nil { - log.Printf("⚠ Failed to update builtin package %s: %v", manifest.ID, err) - continue - } - // Sync triggers on version update (v0.2.2) - SyncManifestTriggers(ctx, stores, nil, manifest.ID, manifestMap) - updated++ - continue - } - - // New package — seed with full metadata - if err := stores.Packages.Seed(ctx, manifest.ID, manifest.Name, "builtin", manifestMap); err != nil { - log.Printf("⚠ Failed to seed builtin package %s: %v", manifest.ID, err) - continue - } - - // Set extension-specific fields that Seed doesn't cover - tier := manifest.Tier - if tier == "" { - tier = "browser" - } - pkg := &store.PackageRegistration{ - Title: manifest.Name, - Type: "extension", - Version: manifest.Version, - Description: manifest.Description, - Author: manifest.Author, - Tier: tier, - Manifest: manifestMap, - IsSystem: true, - Enabled: true, - } - if err := stores.Packages.Update(ctx, manifest.ID, pkg); err != nil { - log.Printf("⚠ Failed to update builtin package metadata %s: %v", manifest.ID, err) - } - // Sync triggers from manifest (v0.2.2) - SyncManifestTriggers(ctx, stores, nil, manifest.ID, manifestMap) - - seeded++ - } - - if seeded+updated > 0 { - log.Printf(" 📦 Builtin packages: %d seeded, %d updated, %d unchanged", seeded, updated, skipped) - } else if skipped > 0 { - log.Printf(" 📦 Builtin packages: %d up to date", skipped) - } -} - -// buildFullManifest merges the raw manifest JSON with an inline _script field. -func buildFullManifest(rawManifest []byte, script string) json.RawMessage { - if script == "" { - return json.RawMessage(rawManifest) - } - - var m map[string]json.RawMessage - if err := json.Unmarshal(rawManifest, &m); err != nil { - return json.RawMessage(rawManifest) - } - - scriptJSON, err := json.Marshal(script) - if err != nil { - return json.RawMessage(rawManifest) - } - - m["_script"] = json.RawMessage(scriptJSON) - result, err := json.Marshal(m) - if err != nil { - return json.RawMessage(rawManifest) - } - - return json.RawMessage(result) -} diff --git a/server/handlers/trigger_sync.go b/server/handlers/trigger_sync.go index b9cd84d..48a1594 100644 --- a/server/handlers/trigger_sync.go +++ b/server/handlers/trigger_sync.go @@ -25,7 +25,7 @@ type manifestTrigger struct { // manifest (declarative sync). For webhooks, generates an HMAC secret on // first create. // -// Called from SeedBuiltinPackages, admin install, and package install. +// Called from admin install and package install. func SyncManifestTriggers(ctx context.Context, stores store.Stores, engine *triggers.Engine, pkgID string, manifest map[string]any) { if stores.Triggers == nil { return diff --git a/server/main.go b/server/main.go index 651513d..3349e48 100644 --- a/server/main.go +++ b/server/main.go @@ -116,8 +116,6 @@ func main() { // Seed additional users from env (dev/test only, skipped in production) handlers.SeedUsers(cfg, stores, uekCache) - // Seed builtin extensions from disk (idempotent, version-aware) - handlers.SeedBuiltinPackages(stores, "extensions/builtin") } defer database.Close() diff --git a/server/store/package_iface.go b/server/store/package_iface.go index 361ee2b..a642272 100644 --- a/server/store/package_iface.go +++ b/server/store/package_iface.go @@ -12,8 +12,7 @@ type PackageStore interface { // Seed upserts a package at startup. Does NOT overwrite the enabled // flag if the row already exists (admin toggle survives restarts). - // Called by the page engine for core surfaces and by - // SeedBuiltinPackages() for builtin extensions. + // Called by the page engine for core surfaces. Seed(ctx context.Context, id, title, source string, manifest map[string]any) error // List returns all registered packages, ordered by source then title. @@ -39,7 +38,7 @@ type PackageStore interface { // ── Extended lifecycle ─────────────────────────────── // Create inserts a new package with all fields. Used by the install - // handler and by SeedBuiltinPackages() for first-time seeding. + // handler for .pkg uploads. Create(ctx context.Context, pkg *PackageRegistration) error // Update modifies mutable fields on an existing package.