chore: strip pre-fork version comments across 72 files

Removes standalone "// v0.X.X:" comment lines and inline trailing
version annotations. Keeps version references in config field docs
and compatibility notes where they describe requirements.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-31 12:17:48 +00:00
parent 2a0c242d1e
commit 2f31a69756
72 changed files with 69 additions and 199 deletions

View File

@@ -113,7 +113,7 @@ func TestRouteFor(t *testing.T) {
{"pong", DirToClient}, {"pong", DirToClient},
// Tool bridge routes // Tool bridge routes
{"tool.call.abc123", DirToClient}, {"tool.call.abc123", DirToClient},
{"tool.result.abc123", DirBoth}, // v0.32.0: DirBoth for cross-pod WaitFor {"tool.result.abc123", DirBoth},
// Extension lifecycle // Extension lifecycle
{"extension.loaded", DirLocal}, {"extension.loaded", DirLocal},
{"extension.error", DirLocal}, {"extension.error", DirLocal},
@@ -227,7 +227,6 @@ func TestToolCallRouteToClient(t *testing.T) {
} }
func TestToolResultRouteBoth(t *testing.T) { func TestToolResultRouteBoth(t *testing.T) {
// v0.32.0: tool.result is DirBoth so results cross pods for WaitFor.
// The WS subscriber explicitly filters out tool.result events to // The WS subscriber explicitly filters out tool.result events to
// prevent re-sending to clients (see subscribeToBus in ws.go). // prevent re-sending to clients (see subscribeToBus in ws.go).
if !ShouldAcceptFromClient("tool.result.abc123") { if !ShouldAcceptFromClient("tool.result.abc123") {

View File

@@ -1,6 +1,5 @@
package events package events
// v0.32.0: TicketValidatorAdapter bridges the context-aware store.TicketStore
// to the middleware.TicketValidator interface (which has no context parameter). // to the middleware.TicketValidator interface (which has no context parameter).
// Replaces the in-memory TicketStore that lived in this file previously. // Replaces the in-memory TicketStore that lived in this file previously.

View File

@@ -9,7 +9,6 @@ type Event struct {
Payload json.RawMessage `json:"payload"` Payload json.RawMessage `json:"payload"`
Ts int64 `json:"ts"` Ts int64 `json:"ts"`
// v0.32.0: Cross-pod targeted delivery. When set, only connections
// belonging to this user receive the event. Serialized for pg_broadcast // belonging to this user receive the event. Serialized for pg_broadcast
// (harmless if clients see it — they ignore unknown fields). // (harmless if clients see it — they ignore unknown fields).
TargetUserID string `json:"target_user_id,omitempty"` TargetUserID string `json:"target_user_id,omitempty"`
@@ -38,8 +37,8 @@ var routeTable = map[string]Direction{
// User/presence // User/presence
"user.presence": DirToClient, "user.presence": DirToClient,
"user.status": DirToClient, "user.status": DirToClient,
"user.mentioned": DirToClient, // v0.23.2: targeted @mention notification "user.mentioned": DirToClient,
"typing.user": DirToClient, // v0.23.2: human typing in DM/channel "typing.user": DirToClient,
// System // System
"system.notify": DirToClient, "system.notify": DirToClient,
@@ -77,7 +76,7 @@ var routeTable = map[string]Direction{
// Tool execution (browser tools bridge) // Tool execution (browser tools bridge)
"tool.call.": DirToClient, // Server → specific client (browser tool invocation) "tool.call.": DirToClient, // Server → specific client (browser tool invocation)
"tool.result.": DirBoth, // v0.32.0: DirBoth — result must cross pods for WaitFor "tool.result.": DirBoth,
// Extension lifecycle // Extension lifecycle
"extension.loaded": DirLocal, // Client-only "extension.loaded": DirLocal, // Client-only

View File

@@ -174,7 +174,6 @@ func (h *Hub) IsConnected(userID string) bool {
} }
// PublishToUser sends an event to a specific user via the bus. // PublishToUser sends an event to a specific user via the bus.
// v0.32.0: Cross-pod safe — the bus broadcast hook fans out via pg_notify.
// All replicas receive the event; only the one with the user's connection delivers it. // All replicas receive the event; only the one with the user's connection delivers it.
func (h *Hub) PublishToUser(userID string, event Event) { func (h *Hub) PublishToUser(userID string, event Event) {
event.TargetUserID = userID event.TargetUserID = userID
@@ -238,13 +237,11 @@ func (c *Conn) subscribeToBus() {
return return
} }
// v0.32.0: Targeted delivery — only deliver to the intended user.
// Targeted events skip room filtering (user-scoped, not room-scoped). // Targeted events skip room filtering (user-scoped, not room-scoped).
if e.TargetUserID != "" && e.TargetUserID != c.userID { if e.TargetUserID != "" && e.TargetUserID != c.userID {
return return
} }
// v0.32.0: tool.result travels cross-pod for WaitFor (DirBoth) but
// must not be forwarded to WebSocket clients — they sent it. // must not be forwarded to WebSocket clients — they sent it.
if strings.HasPrefix(e.Label, "tool.result.") { if strings.HasPrefix(e.Label, "tool.result.") {
return return
@@ -316,7 +313,6 @@ func (c *Conn) readPump() {
continue continue
} }
// v0.5.0: Room management — intercept before bus publish (like ping)
if event.Label == "room.subscribe" { if event.Label == "room.subscribe" {
var req struct { var req struct {
Room string `json:"room"` Room string `json:"room"`

View File

@@ -1,6 +1,5 @@
package handlers package handlers
// v0.38.1: Extension connection handlers — admin/global scope.
// Methods on AdminHandler, mirrors admin provider config pattern. // Methods on AdminHandler, mirrors admin provider config pattern.
import ( import (

View File

@@ -345,7 +345,6 @@ func hashToken(token string) string {
// - AdminHandler.destroyVault (admin-initiated reset) // - AdminHandler.destroyVault (admin-initiated reset)
// //
// Does NOT evict from UEK cache or write audit logs — callers handle that. // Does NOT evict from UEK cache or write audit logs — callers handle that.
// v0.29.0: accepts stores instead of using database.DB directly.
func DestroyVaultDB(ctx context.Context, stores store.Stores, userID string) (deleted int64) { func DestroyVaultDB(ctx context.Context, stores store.Stores, userID string) (deleted int64) {
if err := stores.Users.ClearVaultKeys(ctx, userID); err != nil { if err := stores.Users.ClearVaultKeys(ctx, userID); err != nil {
log.Printf("⚠ DestroyVaultDB: failed to clear vault columns for user %s: %v", userID, err) log.Printf("⚠ DestroyVaultDB: failed to clear vault columns for user %s: %v", userID, err)
@@ -366,8 +365,6 @@ func DestroyVaultDB(ctx context.Context, stores store.Stores, userID string) (de
// sessions survive server restarts. // sessions survive server restarts.
// //
// Used by BootstrapAdmin and SeedUsers where the password is known at startup. // Used by BootstrapAdmin and SeedUsers where the password is known at startup.
// v0.29.0: accepts stores instead of using database.DB directly.
// v0.30.2: accepts optional uekCache to pre-warm vault on restart.
func ProbeAndRepairVault(ctx context.Context, stores store.Stores, userID, password string, uekCache ...*crypto.UEKCache) { func ProbeAndRepairVault(ctx context.Context, stores store.Stores, userID, password string, uekCache ...*crypto.UEKCache) {
vaultSet, encryptedUEK, salt, nonce, err := stores.Users.GetVaultKeys(ctx, userID) vaultSet, encryptedUEK, salt, nonce, err := stores.Users.GetVaultKeys(ctx, userID)
if err != nil || !vaultSet { if err != nil || !vaultSet {

View File

@@ -1,6 +1,5 @@
package handlers package handlers
// v0.38.1: ConnectionResolver bridges the sandbox connections module
// to the store + vault layers. Implements sandbox.ConnectionResolver. // to the store + vault layers. Implements sandbox.ConnectionResolver.
import ( import (

View File

@@ -1,6 +1,5 @@
package handlers package handlers
// v0.38.4: Connection type discovery endpoint.
// Returns merged connection types from all active packages. // Returns merged connection types from all active packages.
// Library declarations take precedence over non-library declarations. // Library declarations take precedence over non-library declarations.

View File

@@ -1,6 +1,5 @@
package handlers package handlers
// v0.38.1: Extension connection handlers — personal scope + resolution.
// Pattern follows apiconfigs.go (ProviderConfigHandler). // Pattern follows apiconfigs.go (ProviderConfigHandler).
import ( import (
@@ -28,7 +27,6 @@ func NewConnectionHandler(s store.Stores, vault *crypto.KeyResolver) *Connection
// ListConnections returns the user's personal connections. // ListConnections returns the user's personal connections.
func (h *ConnectionHandler) ListConnections(c *gin.Context) { func (h *ConnectionHandler) ListConnections(c *gin.Context) {
userID := getUserID(c) userID := getUserID(c)
// v0.38.5: Show all accessible connections (personal + team + global)
// so users can see which connections are available to them. // so users can see which connections are available to them.
conns, err := h.stores.Connections.ListAccessible(c.Request.Context(), userID) conns, err := h.stores.Connections.ListAccessible(c.Request.Context(), userID)
if err != nil { if err != nil {

View File

@@ -1,6 +1,5 @@
// Package handlers — ext_api.go // Package handlers — ext_api.go
// //
// v0.29.1 CS1: Extension API routes. Starlark packages serve custom
// JSON endpoints via the existing Gin router. Mounted at // JSON endpoints via the existing Gin router. Mounted at
// /s/:slug/api/*path with JWT auth. // /s/:slug/api/*path with JWT auth.
// //

View File

@@ -1,6 +1,6 @@
package handlers package handlers
// ext_db_schema.go — v0.29.2 // ext_db_schema.go
// //
// DDL generation and lifecycle management for extension-owned database tables. // DDL generation and lifecycle management for extension-owned database tables.
// Tables are namespaced as ext_{pkg_slug}_{logical_name} and tracked in the // Tables are namespaced as ext_{pkg_slug}_{logical_name} and tracked in the

View File

@@ -10,7 +10,6 @@ import (
) )
// ExtPermHandler serves extension permission management endpoints. // ExtPermHandler serves extension permission management endpoints.
// v0.29.0 CS2: Admin reviews and grants/revokes declared permissions.
type ExtPermHandler struct { type ExtPermHandler struct {
stores store.Stores stores store.Stores
} }

View File

@@ -10,7 +10,6 @@ import (
) )
// ExtSecretsHandler serves extension secret management endpoints. // ExtSecretsHandler serves extension secret management endpoints.
// v0.29.0 CS3: Admin sets key-value secrets that Starlark extensions
// can read via the secrets.get() module. // can read via the secrets.get() module.
// //
// Secrets are stored in GlobalConfig under key "ext_secrets:{packageID}" // Secrets are stored in GlobalConfig under key "ext_secrets:{packageID}"

View File

@@ -16,7 +16,6 @@ import (
) )
// ExtensionHandler serves extension management endpoints. // ExtensionHandler serves extension management endpoints.
// v0.28.7: Backed by PackageStore (packages table) instead of ExtensionStore.
type ExtensionHandler struct { type ExtensionHandler struct {
stores store.Stores stores store.Stores
} }
@@ -94,7 +93,6 @@ func (h *ExtensionHandler) UpdateUserExtensionSettings(c *gin.Context) {
settings := json.RawMessage("{}") settings := json.RawMessage("{}")
if body.Settings != nil { if body.Settings != nil {
// v0.2.0: enforce user_overridable — strip locked keys before saving
schema := store.ParseSettingsSchema(pkg.Manifest) schema := store.ParseSettingsSchema(pkg.Manifest)
if len(schema) > 0 { if len(schema) > 0 {
var incoming map[string]any var incoming map[string]any
@@ -229,18 +227,15 @@ func (h *ExtensionHandler) AdminInstallExtension(c *gin.Context) {
return return
} }
// v0.29.0: Parse manifest permissions and declare them.
// If permissions are declared, package moves to pending_review. // If permissions are declared, package moves to pending_review.
SyncManifestPermissions(c, h.stores, pkg.ID, manifestMap) SyncManifestPermissions(c, h.stores, pkg.ID, manifestMap)
// v0.29.2: Create namespaced DB tables declared in the manifest.
if tables, ok := ParseDBTables(manifestMap); ok { if tables, ok := ParseDBTables(manifestMap); ok {
if err := CreateExtTables(c.Request.Context(), database.DB, database.IsPostgres(), h.stores, pkg.ID, tables); err != nil { if err := CreateExtTables(c.Request.Context(), database.DB, database.IsPostgres(), h.stores, pkg.ID, tables); err != nil {
log.Printf("[ext-db] schema create failed for %s: %v", pkg.ID, err) log.Printf("[ext-db] schema create failed for %s: %v", pkg.ID, err)
} }
} }
// v0.2.2: Sync triggers from manifest
SyncManifestTriggers(c.Request.Context(), h.stores, triggers.GlobalEngine(), pkg.ID, manifestMap) SyncManifestTriggers(c.Request.Context(), h.stores, triggers.GlobalEngine(), pkg.ID, manifestMap)
c.JSON(201, gin.H{"data": pkg}) c.JSON(201, gin.H{"data": pkg})
@@ -403,7 +398,6 @@ func (h *ExtensionHandler) AdminUninstallExtension(c *gin.Context) {
return return
} }
// v0.29.2: Drop namespaced DB tables before removing the package record.
if err := DropExtTables(c.Request.Context(), database.DB, h.stores, id); err != nil { if err := DropExtTables(c.Request.Context(), database.DB, h.stores, id); err != nil {
log.Printf("[ext-db] schema drop failed for %s: %v", id, err) log.Printf("[ext-db] schema drop failed for %s: %v", id, err)
} }

View File

@@ -1,6 +1,5 @@
// Package handlers — openapi.go // Package handlers — openapi.go
// //
// v0.6.2: Dynamic OpenAPI spec generation. Merges the static kernel spec
// with extension-declared API routes. Extensions with api_schema get rich // with extension-declared API routes. Extensions with api_schema get rich
// path items; others get auto-generated stubs. // path items; others get auto-generated stubs.
// //

View File

@@ -1,6 +1,6 @@
package handlers package handlers
// package_registry.go — v0.30.0 CS4 // package_registry.go
// //
// Package registry (marketplace discovery). Admin browses available // Package registry (marketplace discovery). Admin browses available
// packages from an external JSON registry and installs them by URL. // packages from an external JSON registry and installs them by URL.

View File

@@ -31,8 +31,8 @@ var validPackageID = regexp.MustCompile(`^[a-z0-9][a-z0-9-]{0,62}[a-z0-9]$`)
type PackageHandler struct { type PackageHandler struct {
stores store.Stores stores store.Stores
packagesDir string // e.g. /data/packages — where archives are extracted packagesDir string // e.g. /data/packages — where archives are extracted
sandbox *sandbox.Sandbox // v0.30.0: for schema migration scripts sandbox *sandbox.Sandbox
runner *sandbox.Runner // v0.38.2: for test-tool execution runner *sandbox.Runner
} }
func NewPackageHandler(s store.Stores, packagesDir ...string) *PackageHandler { func NewPackageHandler(s store.Stores, packagesDir ...string) *PackageHandler {
@@ -95,7 +95,6 @@ func (h *PackageHandler) GetPackage(c *gin.Context) {
func (h *PackageHandler) EnablePackage(c *gin.Context) { func (h *PackageHandler) EnablePackage(c *gin.Context) {
id := c.Param("id") id := c.Param("id")
// v0.3.7: Block enabling dormant packages (unmet requires).
pkg, err := h.stores.Packages.Get(c.Request.Context(), id) pkg, err := h.stores.Packages.Get(c.Request.Context(), id)
if err != nil || pkg == nil { if err != nil || pkg == nil {
c.JSON(http.StatusNotFound, gin.H{"error": "package not found"}) c.JSON(http.StatusNotFound, gin.H{"error": "package not found"})
@@ -149,7 +148,6 @@ func (h *PackageHandler) DeletePackage(c *gin.Context) {
return return
} }
// v0.38.2: Libraries with active consumers cannot be uninstalled.
if pkg.Type == "library" && h.stores.Dependencies != nil { if pkg.Type == "library" && h.stores.Dependencies != nil {
hasConsumers, _ := h.stores.Dependencies.HasConsumers(c.Request.Context(), id) hasConsumers, _ := h.stores.Dependencies.HasConsumers(c.Request.Context(), id)
if hasConsumers { if hasConsumers {
@@ -158,12 +156,10 @@ func (h *PackageHandler) DeletePackage(c *gin.Context) {
} }
} }
// v0.38.2: Clean up dependency records when uninstalling a consumer.
if h.stores.Dependencies != nil { if h.stores.Dependencies != nil {
h.stores.Dependencies.DeleteAllForConsumer(c.Request.Context(), id) h.stores.Dependencies.DeleteAllForConsumer(c.Request.Context(), id)
} }
// v0.29.2: Drop namespaced DB tables before removing the package record.
if err := DropExtTables(c.Request.Context(), database.DB, h.stores, id); err != nil { if err := DropExtTables(c.Request.Context(), database.DB, h.stores, id); err != nil {
log.Printf("[ext-db] schema drop failed for %s: %v", id, err) log.Printf("[ext-db] schema drop failed for %s: %v", id, err)
} }
@@ -195,7 +191,6 @@ func (h *PackageHandler) InstallPackage(c *gin.Context) {
var tmpPath string var tmpPath string
var cleanupTmp bool var cleanupTmp bool
// v0.30.0: Check for pre-downloaded file from registry install
if regFile, ok := c.Get("_registry_file"); ok { if regFile, ok := c.Get("_registry_file"); ok {
tmpPath = regFile.(string) tmpPath = regFile.(string)
// Don't remove — caller manages lifecycle // Don't remove — caller manages lifecycle
@@ -278,7 +273,6 @@ func (h *PackageHandler) InstallPackage(c *gin.Context) {
return return
} }
// v0.38.0: Entry point validation for starlark-tier packages.
// Scripts are loaded from disk at runtime — no _starlark_script injection. // Scripts are loaded from disk at runtime — no _starlark_script injection.
if manifestTier, _ := manifest["tier"].(string); manifestTier == "starlark" { if manifestTier, _ := manifest["tier"].(string); manifestTier == "starlark" {
entryPoint := "script.star" entryPoint := "script.star"
@@ -380,7 +374,6 @@ func (h *PackageHandler) InstallPackage(c *gin.Context) {
c.JSON(http.StatusBadRequest, gin.H{"error": "full packages require a route"}) c.JSON(http.StatusBadRequest, gin.H{"error": "full packages require a route"})
return return
} }
// v0.31.0: full packages need either server-side behavior (tools/pipes/hooks)
// or a settings schema. A surface-with-settings is a valid "full" package // or a settings schema. A surface-with-settings is a valid "full" package
// (e.g. editor: browser-tier surface + admin-configurable settings). // (e.g. editor: browser-tier surface + admin-configurable settings).
if !hasExtBehavior && !hasSettings { if !hasExtBehavior && !hasSettings {
@@ -393,7 +386,6 @@ func (h *PackageHandler) InstallPackage(c *gin.Context) {
return return
} }
case "library": case "library":
// v0.38.2: Libraries must declare exports, cannot have tools/pipes/route.
exports, _ := manifest["exports"].([]any) exports, _ := manifest["exports"].([]any)
if len(exports) == 0 { if len(exports) == 0 {
c.JSON(http.StatusBadRequest, gin.H{"error": "library packages require an 'exports' array"}) c.JSON(http.StatusBadRequest, gin.H{"error": "library packages require an 'exports' array"})
@@ -471,7 +463,6 @@ func (h *PackageHandler) InstallPackage(c *gin.Context) {
userID := c.GetString("user_id") userID := c.GetString("user_id")
// v0.30.0: use registry source if set by registry install handler
pkgSource := "extension" pkgSource := "extension"
if src, ok := c.Get("_registry_source"); ok { if src, ok := c.Get("_registry_source"); ok {
pkgSource = src.(string) pkgSource = src.(string)
@@ -509,22 +500,18 @@ func (h *PackageHandler) InstallPackage(c *gin.Context) {
log.Printf("[packages] Failed to update package metadata for %s: %v", pkgID, err) log.Printf("[packages] Failed to update package metadata for %s: %v", pkgID, err)
} }
// v0.29.2: Create namespaced DB tables declared in the manifest.
if tables, ok := ParseDBTables(manifest); ok { if tables, ok := ParseDBTables(manifest); ok {
if err := CreateExtTables(c.Request.Context(), database.DB, database.IsPostgres(), h.stores, pkgID, tables); err != nil { if err := CreateExtTables(c.Request.Context(), database.DB, database.IsPostgres(), h.stores, pkgID, tables); err != nil {
log.Printf("[ext-db] schema create failed for %s: %v", pkgID, err) log.Printf("[ext-db] schema create failed for %s: %v", pkgID, err)
} }
} }
// v0.38.5: Declare manifest permissions (same as AdminInstallExtension).
// This creates the permission rows and sets status to pending_review // This creates the permission rows and sets status to pending_review
// if the package declares permissions. // if the package declares permissions.
SyncManifestPermissions(c, h.stores, pkgID, manifest) SyncManifestPermissions(c, h.stores, pkgID, manifest)
// v0.2.2: Sync triggers from manifest
SyncManifestTriggers(c.Request.Context(), h.stores, triggers.GlobalEngine(), pkgID, manifest) SyncManifestTriggers(c.Request.Context(), h.stores, triggers.GlobalEngine(), pkgID, manifest)
// v0.30.0: Run schema migrations if declared.
newSchemaVersion := ParseSchemaVersion(manifest) newSchemaVersion := ParseSchemaVersion(manifest)
if newSchemaVersion > 0 && h.sandbox != nil { if newSchemaVersion > 0 && h.sandbox != nil {
oldSchemaVersion := 0 oldSchemaVersion := 0
@@ -553,7 +540,6 @@ func (h *PackageHandler) InstallPackage(c *gin.Context) {
} }
} }
// v0.30.2: Install workflow definition from package manifest.
if pkgType == "workflow" { if pkgType == "workflow" {
if err := InstallWorkflowFromManifest(c, h.stores, pkgID, manifest); err != nil { if err := InstallWorkflowFromManifest(c, h.stores, pkgID, manifest); err != nil {
log.Printf("[packages] workflow install failed for %s: %v", pkgID, err) log.Printf("[packages] workflow install failed for %s: %v", pkgID, err)
@@ -562,7 +548,6 @@ func (h *PackageHandler) InstallPackage(c *gin.Context) {
} }
} }
// v0.38.2: Create dependency records from manifest "dependencies" map.
// Libraries must be installed first; consumers declare them. // Libraries must be installed first; consumers declare them.
if deps, ok := manifest["dependencies"].(map[string]any); ok && len(deps) > 0 { if deps, ok := manifest["dependencies"].(map[string]any); ok && len(deps) > 0 {
// Clear stale dependency records from a previous install of the same consumer. // Clear stale dependency records from a previous install of the same consumer.
@@ -603,7 +588,6 @@ func (h *PackageHandler) InstallPackage(c *gin.Context) {
log.Printf("[packages] %s: %d dependencies recorded", pkgID, len(deps)) log.Printf("[packages] %s: %d dependencies recorded", pkgID, len(deps))
} }
// v0.2.1: Auto-set default_surface when the first extension surface is installed.
if (pkgType == "surface" || pkgType == "full") && pkgSource != "core" { if (pkgType == "surface" || pkgType == "full") && pkgSource != "core" {
if raw, err := h.stores.GlobalConfig.Get(c.Request.Context(), "default_surface"); err != nil || raw == nil { if raw, err := h.stores.GlobalConfig.Get(c.Request.Context(), "default_surface"); err != nil || raw == nil {
dflt := models.JSONMap{"id": pkgID} dflt := models.JSONMap{"id": pkgID}
@@ -615,7 +599,6 @@ func (h *PackageHandler) InstallPackage(c *gin.Context) {
} }
} }
// v0.3.7: Auto-set dormant for packages with unmet requires.
// Known capabilities: (none yet — chat and legacy-sdk are future/removed). // Known capabilities: (none yet — chat and legacy-sdk are future/removed).
knownCaps := map[string]bool{} knownCaps := map[string]bool{}
var unmetReqs []string var unmetReqs []string

View File

@@ -6,7 +6,6 @@ package handlers
// GET /api/v1/presence?users=id1,id2 returns current status. // GET /api/v1/presence?users=id1,id2 returns current status.
// Online = last_seen within 90s. // Online = last_seen within 90s.
// //
// v0.29.0: Raw SQL replaced with PresenceStore + UserStore methods.
import ( import (
"net/http" "net/http"

View File

@@ -8,7 +8,6 @@ package handlers
// (profile, permissions, teams/mine, settings) into one call. // (profile, permissions, teams/mine, settings) into one call.
// The SDK calls this at startup and on token refresh. // The SDK calls this at startup and on token refresh.
// //
// v0.37.15
import ( import (
"net/http" "net/http"

View File

@@ -2,7 +2,6 @@ package handlers
// settings.go — User profile, preferences, and password management. // settings.go — User profile, preferences, and password management.
// //
// v0.29.0: Raw SQL replaced with UserStore methods.
import ( import (
"context" "context"

View File

@@ -1,6 +1,6 @@
package handlers package handlers
// team_package_settings.go — v0.2.0 // team_package_settings.go
// //
// Team-admin endpoints for managing team-scoped package settings. // Team-admin endpoints for managing team-scoped package settings.
// These sit in the settings cascade between global (admin) and user. // These sit in the settings cascade between global (admin) and user.

View File

@@ -14,7 +14,7 @@ import (
) )
// ═══════════════════════════════════════════════ // ═══════════════════════════════════════════════
// Upgrade Tests — v0.5.5 // Upgrade Tests
// //
// Schema edge cases, settings migration, and // Schema edge cases, settings migration, and
// package compatibility across kernel upgrades. // package compatibility across kernel upgrades.

View File

@@ -1,6 +1,6 @@
package handlers package handlers
// user_packages.go — v0.30.0 CS5 // user_packages.go
// //
// Non-admin package management. Users install personal-scoped packages; // Non-admin package management. Users install personal-scoped packages;
// team admins install team-scoped packages. // team admins install team-scoped packages.

View File

@@ -1,6 +1,6 @@
package handlers package handlers
// workflow_packages.go — v0.30.2 CS1 // workflow_packages.go
// //
// Handles workflow-specific package operations: // Handles workflow-specific package operations:
// - ExportWorkflowPackage: bundles a workflow definition + stages into a .pkg // - ExportWorkflowPackage: bundles a workflow definition + stages into a .pkg

View File

@@ -106,7 +106,6 @@ func (h *WorkflowHandler) Create(c *gin.Context) {
w.CreatedBy = c.GetString("user_id") w.CreatedBy = c.GetString("user_id")
// v0.31.2: team-scoped route injects force_team_id via CreateTeamWorkflow
if ftid, ok := c.Get("force_team_id"); ok { if ftid, ok := c.Get("force_team_id"); ok {
tid := ftid.(string) tid := ftid.(string)
w.TeamID = &tid w.TeamID = &tid
@@ -417,7 +416,6 @@ func (h *WorkflowHandler) Clone(c *gin.Context) {
CreatedBy: c.GetString("user_id"), CreatedBy: c.GetString("user_id"),
} }
// v0.31.2: team-scoped route injects force_team_id
if ftid, ok := c.Get("force_team_id"); ok { if ftid, ok := c.Get("force_team_id"); ok {
tid := ftid.(string) tid := ftid.(string)
clone.TeamID = &tid clone.TeamID = &tid

View File

@@ -36,7 +36,6 @@ import (
sqliteStore "switchboard-core/store/sqlite" sqliteStore "switchboard-core/store/sqlite"
) )
// v0.33.0: Embedded OpenAPI spec and Swagger UI for /api/docs.
// //
//go:embed static/openapi.yaml //go:embed static/openapi.yaml
var openapiSpec []byte var openapiSpec []byte
@@ -106,7 +105,6 @@ func main() {
stores = postgres.NewStores(database.DB) stores = postgres.NewStores(database.DB)
} }
// v0.33.0: Start Prometheus DB pool collector
metrics.StartDBCollector(database.DB, 15*time.Second) metrics.StartDBCollector(database.DB, 15*time.Second)
// Bootstrap admin from env (K8s secret) — upserts on every restart // Bootstrap admin from env (K8s secret) — upserts on every restart
@@ -158,17 +156,12 @@ func main() {
sandbox.New(sandbox.DefaultConfig()), sandbox.New(sandbox.DefaultConfig()),
stores, stores,
) )
// v0.38.1: connections module — extension connection resolution
starlarkRunner.SetConnectionResolver(handlers.NewConnectionResolverAdapter(stores, keyResolver)) starlarkRunner.SetConnectionResolver(handlers.NewConnectionResolverAdapter(stores, keyResolver))
// v0.29.2: db module — extension namespaced table access
starlarkRunner.SetDB(database.DB, database.IsPostgres()) starlarkRunner.SetDB(database.DB, database.IsPostgres())
// v0.38.0: disk-based script loading + load() support
if cfg.StoragePath != "" { if cfg.StoragePath != "" {
starlarkRunner.SetPackagesDir(cfg.StoragePath + "/packages") starlarkRunner.SetPackagesDir(cfg.StoragePath + "/packages")
} }
// v0.5.0: realtime module — extension publish to WebSocket channels
starlarkRunner.SetBus(bus) starlarkRunner.SetBus(bus)
// v0.38.5: allow extensions to reach private IPs (self-hosted Gitea, etc.)
if os.Getenv("EXT_ALLOW_PRIVATE_IPS") == "true" { if os.Getenv("EXT_ALLOW_PRIVATE_IPS") == "true" {
starlarkRunner.SetAllowPrivateIPs(true) starlarkRunner.SetAllowPrivateIPs(true)
log.Printf(" ⚠️ Extension SSRF protection relaxed: private IPs allowed") log.Printf(" ⚠️ Extension SSRF protection relaxed: private IPs allowed")
@@ -274,7 +267,6 @@ func main() {
c.JSON(200, info) c.JSON(200, info)
}) })
// v0.32.0: Kubernetes probe endpoints
// Liveness: process is alive and serving (no dependency checks). // Liveness: process is alive and serving (no dependency checks).
base.GET("/healthz/live", func(c *gin.Context) { base.GET("/healthz/live", func(c *gin.Context) {
c.JSON(200, gin.H{"status": "ok"}) c.JSON(200, gin.H{"status": "ok"})
@@ -296,10 +288,8 @@ func main() {
c.JSON(200, gin.H{"status": "ok"}) c.JSON(200, gin.H{"status": "ok"})
}) })
// v0.33.0: Prometheus metrics endpoint (no auth — Prometheus scrapes directly)
base.GET("/metrics", gin.WrapH(promhttp.Handler())) base.GET("/metrics", gin.WrapH(promhttp.Handler()))
// v0.33.0: OpenAPI spec + Swagger UI (no auth — documentation)
base.GET("/api/docs", func(c *gin.Context) { base.GET("/api/docs", func(c *gin.Context) {
c.Data(http.StatusOK, "text/html; charset=utf-8", swaggerHTML) c.Data(http.StatusOK, "text/html; charset=utf-8", swaggerHTML)
}) })
@@ -308,7 +298,6 @@ func main() {
patched := bytes.Replace(openapiSpec, []byte("${VERSION}"), []byte(Version), 1) patched := bytes.Replace(openapiSpec, []byte("${VERSION}"), []byte(Version), 1)
c.Data(http.StatusOK, "application/yaml", patched) c.Data(http.StatusOK, "application/yaml", patched)
}) })
// v0.6.2: Dynamic OpenAPI spec with extension routes merged in
base.GET("/api/docs/openapi.json", func(c *gin.Context) { base.GET("/api/docs/openapi.json", func(c *gin.Context) {
spec, err := handlers.BuildOpenAPISpec(stores, openapiSpec, Version) spec, err := handlers.BuildOpenAPISpec(stores, openapiSpec, Version)
if err != nil { if err != nil {
@@ -501,7 +490,7 @@ func main() {
connTypeH := handlers.NewConnectionTypeHandler(stores) connTypeH := handlers.NewConnectionTypeHandler(stores)
protected.GET("/connection-types", connTypeH.ListConnectionTypes) protected.GET("/connection-types", connTypeH.ListConnectionTypes)
// Extension Connections (personal scope — v0.38.1) // Extension Connections (personal scope
connH := handlers.NewConnectionHandler(stores, keyResolver) connH := handlers.NewConnectionHandler(stores, keyResolver)
protected.GET("/connections", connH.ListConnections) protected.GET("/connections", connH.ListConnections)
protected.POST("/connections", connH.CreateConnection) protected.POST("/connections", connH.CreateConnection)
@@ -559,7 +548,7 @@ func main() {
teams := handlers.NewTeamHandler(stores, keyResolver) teams := handlers.NewTeamHandler(stores, keyResolver)
protected.GET("/teams/mine", teams.MyTeams) protected.GET("/teams/mine", teams.MyTeams)
// Groups (user: my groups — v0.16.0) // Groups (user: my groups
groupH := handlers.NewGroupHandler(stores) groupH := handlers.NewGroupHandler(stores)
protected.GET("/groups/mine", groupH.MyGroups) protected.GET("/groups/mine", groupH.MyGroups)
@@ -697,7 +686,7 @@ func main() {
admin.GET("/audit", adm.ListAuditLog) admin.GET("/audit", adm.ListAuditLog)
admin.GET("/audit/actions", adm.ListAuditActions) admin.GET("/audit/actions", adm.ListAuditActions)
// Groups (admin — v0.16.0) // Groups (admin
groupAdm := handlers.NewGroupHandler(stores) groupAdm := handlers.NewGroupHandler(stores)
admin.GET("/groups", groupAdm.ListGroups) admin.GET("/groups", groupAdm.ListGroups)
admin.POST("/groups", groupAdm.CreateGroup) admin.POST("/groups", groupAdm.CreateGroup)
@@ -712,7 +701,7 @@ func main() {
admin.GET("/permissions", groupAdm.ListPermissions) admin.GET("/permissions", groupAdm.ListPermissions)
admin.GET("/users/:id/permissions", groupAdm.GetUserPermissions) admin.GET("/users/:id/permissions", groupAdm.GetUserPermissions)
// Resource Grants (admin — v0.16.0) // Resource Grants (admin
admin.GET("/grants/:type/:id", groupAdm.GetResourceGrant) admin.GET("/grants/:type/:id", groupAdm.GetResourceGrant)
admin.PUT("/grants/:type/:id", groupAdm.SetResourceGrant) admin.PUT("/grants/:type/:id", groupAdm.SetResourceGrant)
@@ -736,7 +725,7 @@ func main() {
admin.PUT("/extensions/:id", extAdm.AdminUpdateExtension) admin.PUT("/extensions/:id", extAdm.AdminUpdateExtension)
admin.DELETE("/extensions/:id", extAdm.AdminUninstallExtension) admin.DELETE("/extensions/:id", extAdm.AdminUninstallExtension)
// Extension permissions (admin — v0.29.0) // Extension permissions (admin
extPermH := handlers.NewExtPermHandler(stores) extPermH := handlers.NewExtPermHandler(stores)
admin.GET("/extensions/:id/permissions", extPermH.ListPackagePermissions) admin.GET("/extensions/:id/permissions", extPermH.ListPackagePermissions)
admin.GET("/extensions/:id/review", extPermH.ReviewPackage) admin.GET("/extensions/:id/review", extPermH.ReviewPackage)
@@ -744,7 +733,7 @@ func main() {
admin.POST("/extensions/:id/permissions/:perm/revoke", extPermH.RevokePermission) admin.POST("/extensions/:id/permissions/:perm/revoke", extPermH.RevokePermission)
admin.POST("/extensions/:id/permissions/grant-all", extPermH.GrantAllPermissions) admin.POST("/extensions/:id/permissions/grant-all", extPermH.GrantAllPermissions)
// Extension secrets (admin — v0.29.0 CS3) // Extension secrets (admin
extSecH := handlers.NewExtSecretsHandler(stores) extSecH := handlers.NewExtSecretsHandler(stores)
admin.GET("/extensions/:id/secrets", extSecH.GetSecrets) admin.GET("/extensions/:id/secrets", extSecH.GetSecrets)
admin.PUT("/extensions/:id/secrets", extSecH.SetSecrets) admin.PUT("/extensions/:id/secrets", extSecH.SetSecrets)
@@ -756,7 +745,7 @@ func main() {
packagesDir = cfg.StoragePath + "/packages" packagesDir = cfg.StoragePath + "/packages"
} }
pkgAdm := handlers.NewPackageHandler(stores, packagesDir) pkgAdm := handlers.NewPackageHandler(stores, packagesDir)
pkgAdm.SetSandbox(sandbox.New(sandbox.DefaultConfig())) // v0.30.0: schema migrations pkgAdm.SetSandbox(sandbox.New(sandbox.DefaultConfig()))
// Package registry — must be registered before /packages/:id (v0.30.0) // Package registry — must be registered before /packages/:id (v0.30.0)
registryH := handlers.NewRegistryHandler(stores, packagesDir, pkgAdm) registryH := handlers.NewRegistryHandler(stores, packagesDir, pkgAdm)
@@ -769,14 +758,14 @@ func main() {
admin.PUT("/packages/:id/enable", pkgAdm.EnablePackage) admin.PUT("/packages/:id/enable", pkgAdm.EnablePackage)
admin.PUT("/packages/:id/disable", pkgAdm.DisablePackage) admin.PUT("/packages/:id/disable", pkgAdm.DisablePackage)
admin.DELETE("/packages/:id", pkgAdm.DeletePackage) admin.DELETE("/packages/:id", pkgAdm.DeletePackage)
admin.GET("/packages/:id/settings", pkgAdm.GetPackageSettings) // v0.30.0 admin.GET("/packages/:id/settings", pkgAdm.GetPackageSettings)
admin.PUT("/packages/:id/settings", pkgAdm.UpdatePackageSettings) // v0.30.0 admin.PUT("/packages/:id/settings", pkgAdm.UpdatePackageSettings)
admin.GET("/packages/:id/dependencies", pkgAdm.ListDependencies) // v0.38.2 admin.GET("/packages/:id/dependencies", pkgAdm.ListDependencies)
admin.GET("/packages/:id/consumers", pkgAdm.ListConsumers) // v0.38.2 admin.GET("/packages/:id/consumers", pkgAdm.ListConsumers)
admin.POST("/packages/:id/test-tool", pkgAdm.TestTool) // v0.38.2 admin.POST("/packages/:id/test-tool", pkgAdm.TestTool)
admin.POST("/packages/:id/update", pkgAdm.UpdatePackage) // v0.5.4 admin.POST("/packages/:id/update", pkgAdm.UpdatePackage)
admin.GET("/packages/:id/export", pkgAdm.ExportPackage) // v0.5.4 admin.GET("/packages/:id/export", pkgAdm.ExportPackage)
admin.GET("/dependencies", pkgAdm.ListAllDependencies) // v0.38.2 admin.GET("/dependencies", pkgAdm.ListAllDependencies)
// Workflow package export (v0.30.2) // Workflow package export (v0.30.2)
wfPkgH := handlers.NewWorkflowPackageHandler(stores) wfPkgH := handlers.NewWorkflowPackageHandler(stores)
@@ -825,9 +814,7 @@ func main() {
} }
// ── Page Routes ────────────────────────── // ── Page Routes ──────────────────────────
// v0.27.0: Extension surface static assets (JS, CSS, images).
// Served without auth — same rationale as extension assets (script tags can't send headers). // Served without auth — same rationale as extension assets (script tags can't send headers).
// v0.28.7: on-disk path moved to /data/packages/, URL stays /surfaces/:id/ for stability.
// In split deployment, nginx serves these from /data/packages/ directly. // In split deployment, nginx serves these from /data/packages/ directly.
if cfg.StoragePath != "" { if cfg.StoragePath != "" {
packagesStaticDir := cfg.StoragePath + "/packages" packagesStaticDir := cfg.StoragePath + "/packages"
@@ -853,7 +840,6 @@ func main() {
// Login page — no auth required // Login page — no auth required
base.GET("/login", pageEngine.RenderLogin()) base.GET("/login", pageEngine.RenderLogin())
// v0.25.0: Surface routes generated from manifest registry.
// Replaces manual per-surface route blocks. // Replaces manual per-surface route blocks.
pageEngine.RegisterPageRoutes(base, pages.PageRouteMiddleware{ pageEngine.RegisterPageRoutes(base, pages.PageRouteMiddleware{
Authenticated: middleware.AuthOrRedirect(cfg, stores.Users, userCache), Authenticated: middleware.AuthOrRedirect(cfg, stores.Users, userCache),
@@ -864,7 +850,6 @@ func main() {
Session: middleware.AuthOrRedirect(cfg, stores.Users, userCache), // TODO: session middleware for workflow visitors (v0.2.0) Session: middleware.AuthOrRedirect(cfg, stores.Users, userCache), // TODO: session middleware for workflow visitors (v0.2.0)
}) })
// v0.29.1: Extension API routes — Starlark packages serve JSON endpoints.
// Mounted at /s/:slug/api/* with JWT auth (returns 401, not redirect). // Mounted at /s/:slug/api/* with JWT auth (returns 401, not redirect).
{ {
extAPIH := handlers.NewExtAPIHandler(stores, starlarkRunner) extAPIH := handlers.NewExtAPIHandler(stores, starlarkRunner)

View File

@@ -10,7 +10,6 @@ import (
) )
// RateLimiter implements per-IP rate limiting backed by a shared store. // RateLimiter implements per-IP rate limiting backed by a shared store.
// v0.32.0: replaces in-memory token bucket for cross-pod consistency.
// //
// Fail-open: if the store is unavailable (PG down), requests are allowed. // Fail-open: if the store is unavailable (PG down), requests are allowed.
// Auth endpoints have their own protections (bcrypt, lockout); blocking // Auth endpoints have their own protections (bcrypt, lockout); blocking

View File

@@ -10,7 +10,6 @@ import (
// RequireTeamAdmin returns middleware that restricts access to team admins. // RequireTeamAdmin returns middleware that restricts access to team admins.
// System admins are always allowed through. // System admins are always allowed through.
// v0.29.0: accepts TeamStore instead of using database.DB directly.
func RequireTeamAdmin(teams store.TeamStore) gin.HandlerFunc { func RequireTeamAdmin(teams store.TeamStore) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
role, _ := c.Get("role") role, _ := c.Get("role")
@@ -43,7 +42,6 @@ func RequireTeamAdmin(teams store.TeamStore) gin.HandlerFunc {
// RequireTeamMember returns middleware that restricts access to users who // RequireTeamMember returns middleware that restricts access to users who
// belong to the team identified by :teamId (any role). // belong to the team identified by :teamId (any role).
// System admins are always allowed through. // System admins are always allowed through.
// v0.29.0: accepts TeamStore instead of using database.DB directly.
func RequireTeamMember(teams store.TeamStore) gin.HandlerFunc { func RequireTeamMember(teams store.TeamStore) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
role, _ := c.Get("role") role, _ := c.Get("role")

View File

@@ -31,11 +31,11 @@ const (
ExtPermDBRead = "db.read" ExtPermDBRead = "db.read"
ExtPermDBWrite = "db.write" ExtPermDBWrite = "db.write"
ExtPermAPIHTTP = "api.http" ExtPermAPIHTTP = "api.http"
ExtPermFormValidate = "forms.validate" // v0.29.3: form validation hooks ExtPermFormValidate = "forms.validate"
ExtPermWorkflowAccess = "workflow.access" // v0.30.2: workflow definition + stage data access ExtPermWorkflowAccess = "workflow.access"
ExtPermConnectionsRead = "connections.read" // v0.38.1: extension connection resolution ExtPermConnectionsRead = "connections.read"
ExtPermTriggersRegister = "triggers.register" // v0.2.2: event/webhook trigger registration ExtPermTriggersRegister = "triggers.register"
ExtPermRealtimePublish = "realtime.publish" // v0.5.0: publish events to realtime channels ExtPermRealtimePublish = "realtime.publish"
) )
// ValidExtensionPermissions is the set of recognized permission keys. // ValidExtensionPermissions is the set of recognized permission keys.

View File

@@ -121,10 +121,9 @@ var ValidAudiences = map[string]bool{
// ── Typed Form Template ───────────────────── // ── Typed Form Template ─────────────────────
// TypedFormTemplate is the structured form_template schema (v0.29.3). // TypedFormTemplate is the structured form_template schema (v0.29.3).
// v0.35.0: added Fieldsets for progressive multi-step forms.
type TypedFormTemplate struct { type TypedFormTemplate struct {
Fields []FormField `json:"fields"` Fields []FormField `json:"fields"`
Fieldsets []FormFieldset `json:"fieldsets,omitempty"` // v0.35.0: multi-step form groups Fieldsets []FormFieldset `json:"fieldsets,omitempty"`
Hooks *FormHooks `json:"hooks,omitempty"` Hooks *FormHooks `json:"hooks,omitempty"`
} }
@@ -153,7 +152,7 @@ type FormField struct {
Default interface{} `json:"default,omitempty"` Default interface{} `json:"default,omitempty"`
Options []FormOption `json:"options,omitempty"` Options []FormOption `json:"options,omitempty"`
Validation *FormValidation `json:"validation,omitempty"` Validation *FormValidation `json:"validation,omitempty"`
Condition *FieldCondition `json:"condition,omitempty"` // v0.35.0: conditional visibility Condition *FieldCondition `json:"condition,omitempty"`
} }
// FormOption is a choice for select fields. // FormOption is a choice for select fields.
@@ -200,7 +199,6 @@ func ParseTypedFormTemplate(raw json.RawMessage) *TypedFormTemplate {
return nil return nil
} }
// v0.35.0: If fieldsets present, flatten into fields for backward compat
if len(tpl.Fieldsets) > 0 { if len(tpl.Fieldsets) > 0 {
var allFields []FormField var allFields []FormField
for _, fs := range tpl.Fieldsets { for _, fs := range tpl.Fieldsets {
@@ -237,7 +235,6 @@ type FieldError struct {
func ValidateFormData(tpl *TypedFormTemplate, data map[string]interface{}) []FieldError { func ValidateFormData(tpl *TypedFormTemplate, data map[string]interface{}) []FieldError {
var errs []FieldError var errs []FieldError
for _, f := range tpl.Fields { for _, f := range tpl.Fields {
// v0.35.0: Skip fields whose condition is not met
if f.Condition != nil && !evaluateFieldCondition(f.Condition, data) { if f.Condition != nil && !evaluateFieldCondition(f.Condition, data) {
continue continue
} }

View File

@@ -31,7 +31,7 @@ type AdminPageData struct {
// Feature gates (BYOK, personas) moved to sw.auth.policies in v0.37.19. // Feature gates (BYOK, personas) moved to sw.auth.policies in v0.37.19.
type SettingsPageData struct { type SettingsPageData struct {
Section string `json:"section"` Section string `json:"section"`
ConfigSections []ConfigSectionEntry `json:"config_sections,omitempty"` // v0.38.3 ConfigSections []ConfigSectionEntry `json:"config_sections,omitempty"`
} }
// ── Loader registration ────────────────────── // ── Loader registration ──────────────────────
@@ -39,7 +39,7 @@ type SettingsPageData struct {
// TeamAdminPageData is what the team-admin surface receives. // TeamAdminPageData is what the team-admin surface receives.
type TeamAdminPageData struct { type TeamAdminPageData struct {
Section string `json:"section"` Section string `json:"section"`
ConfigSections []ConfigSectionEntry `json:"config_sections,omitempty"` // v0.38.3 ConfigSections []ConfigSectionEntry `json:"config_sections,omitempty"`
} }
func (e *Engine) registerLoaders() { func (e *Engine) registerLoaders() {
@@ -91,7 +91,6 @@ func sectionCategory(section string) string {
} }
// ── Settings loader ────────────────────────── // ── Settings loader ──────────────────────────
// v0.22.7: Reads feature gates from GlobalConfig to control
// which nav links/tabs are visible (BYOK, User Personas). // which nav links/tabs are visible (BYOK, User Personas).
func (e *Engine) teamAdminLoader(c *gin.Context, s store.Stores) (any, error) { func (e *Engine) teamAdminLoader(c *gin.Context, s store.Stores) (any, error) {

View File

@@ -38,7 +38,7 @@ type Engine struct {
cfg *config.Config cfg *config.Config
stores store.Stores stores store.Stores
loaders map[string]DataLoaderFunc loaders map[string]DataLoaderFunc
surfaces []SurfaceManifest // v0.25.0: registered surface definitions surfaces []SurfaceManifest
devMode bool devMode bool
} }
@@ -95,25 +95,20 @@ type PageData struct {
User *UserContext User *UserContext
Data any // surface-specific data from loader Data any // surface-specific data from loader
// v0.22.7: Theme + settings injection
Theme string // "dark", "light", or "" (= use default) Theme string // "dark", "light", or "" (= use default)
SurfaceSettings any // JSON-serialized to window.__SETTINGS__ SurfaceSettings any // JSON-serialized to window.__SETTINGS__
// v0.25.0: Surface manifest — layout preset, components, etc.
Manifest *SurfaceManifest `json:"manifest,omitempty"` Manifest *SurfaceManifest `json:"manifest,omitempty"`
// v0.25.0: Enabled surface IDs for conditional nav rendering.
EnabledSurfaces []string `json:"-"` EnabledSurfaces []string `json:"-"`
// v0.27.0: Extension surfaces for sidebar nav rendering.
ExtensionSurfaces []ExtensionNavItem `json:"-"` ExtensionSurfaces []ExtensionNavItem `json:"-"`
// v0.22.7: Login/splash page fields
InstanceName string // branding: instance display name InstanceName string // branding: instance display name
LogoURL string // branding: custom logo URL LogoURL string // branding: custom logo URL
Tagline string // branding: tagline under instance name Tagline string // branding: tagline under instance name
RegistrationOpen bool // whether self-registration is enabled RegistrationOpen bool // whether self-registration is enabled
AuthMode string // v0.24.1: "builtin", "mtls", "oidc" AuthMode string // "builtin", "mtls", "oidc"
} }
// SurfaceEnabled returns true if the given surface ID is in the EnabledSurfaces list. // SurfaceEnabled returns true if the given surface ID is in the EnabledSurfaces list.
@@ -137,7 +132,6 @@ type ExtensionNavItem struct {
// ConfigSectionEntry describes a package-declared config section for // ConfigSectionEntry describes a package-declared config section for
// lazy-loading in Settings, Admin, or Team Admin surfaces. // lazy-loading in Settings, Admin, or Team Admin surfaces.
// v0.38.3: manifest-driven discovery — no new tables or endpoints.
type ConfigSectionEntry struct { type ConfigSectionEntry struct {
PackageID string `json:"package_id"` // section key & asset path prefix PackageID string `json:"package_id"` // section key & asset path prefix
Label string `json:"label"` // nav link text Label string `json:"label"` // nav link text
@@ -207,7 +201,7 @@ func New(cfg *config.Config, stores store.Stores) *Engine {
e.parseTemplates() e.parseTemplates()
e.registerLoaders() e.registerLoaders()
e.registerCoreSurfaces() e.registerCoreSurfaces()
e.SeedSurfaces() // v0.25.0: Write core manifests to DB (preserves admin toggle state) e.SeedSurfaces()
return e return e
} }
@@ -322,7 +316,6 @@ func (e *Engine) Render(c *gin.Context, name string, data PageData) {
data.Message = e.loadMessage() data.Message = e.loadMessage()
data.Footer = e.loadFooter() data.Footer = e.loadFooter()
// v0.22.7: Default theme if not set
if data.Theme == "" { if data.Theme == "" {
data.Theme = "dark" data.Theme = "dark"
} }
@@ -477,7 +470,6 @@ func (e *Engine) RegisterPageRoutes(base *gin.RouterGroup, mw PageRouteMiddlewar
registerRoutes(group, s, handler) registerRoutes(group, s, handler)
} }
// v0.27.0: Extension surface catch-all — DB lookup at request time.
// No restart needed after installing new surfaces via admin API. // No restart needed after installing new surfaces via admin API.
group.GET("/s/:slug", e.RenderExtensionSurface()) group.GET("/s/:slug", e.RenderExtensionSurface())
} }
@@ -592,7 +584,6 @@ func stripPrefix(route, prefix string) string {
// RenderLogin serves the standalone login page. // RenderLogin serves the standalone login page.
func (e *Engine) RenderLogin() gin.HandlerFunc { func (e *Engine) RenderLogin() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
// v0.22.7: Populate splash/login fields from global config
instanceName, logoURL, tagline := e.loadBranding() instanceName, logoURL, tagline := e.loadBranding()
regOpen := e.isRegistrationOpen() regOpen := e.isRegistrationOpen()
@@ -619,8 +610,8 @@ type WorkflowPageData struct {
FormTemplateJSON string // typed form template JSON (empty if custom) FormTemplateJSON string // typed form template JSON (empty if custom)
TotalStages int TotalStages int
CurrentStage int CurrentStage int
SurfacePkgID string // v0.30.2: custom package surface override (empty = use StageMode) SurfacePkgID string
BrandingJSON string // v0.35.0: workflow branding JSON (accent_color, logo_url, tagline) BrandingJSON string
} }
// WorkflowLandingPageData is passed to workflow-landing.html. // WorkflowLandingPageData is passed to workflow-landing.html.

View File

@@ -35,7 +35,6 @@ func (e *Engine) SeedSurfaces() {
} }
log.Printf("[pages] Seeded %d core surfaces into registry", len(e.surfaces)) log.Printf("[pages] Seeded %d core surfaces into registry", len(e.surfaces))
// v0.31.0: Clean up old "editor" core surface row.
// Editor is now an installable package — the old seed row would show a // Editor is now an installable package — the old seed row would show a
// broken nav link until the package .pkg is uploaded via admin UI. // broken nav link until the package .pkg is uploaded via admin UI.
if sr, err := e.stores.Packages.Get(ctx, "editor"); err == nil && sr != nil && sr.Source == "extension" { if sr, err := e.stores.Packages.Get(ctx, "editor"); err == nil && sr != nil && sr.Source == "extension" {

View File

@@ -1,6 +1,5 @@
// Package sandbox — connections_module.go // Package sandbox — connections_module.go
// //
// v0.38.1: Connections module for Starlark extensions.
// Requires permission: connections.read // Requires permission: connections.read
// //
// Starlark API: // Starlark API:

View File

@@ -1,6 +1,5 @@
// Package sandbox — db_module.go // Package sandbox — db_module.go
// //
// v0.29.2 CS1: Structured db module for Starlark extensions.
// //
// Permissions: // Permissions:
// db.read → db.query(), db.view(), db.list_tables() // db.read → db.query(), db.view(), db.list_tables()

View File

@@ -1,6 +1,5 @@
// Package sandbox — http_module.go // Package sandbox — http_module.go
// //
// v0.29.1 CS0: HTTP outbound module for Starlark extensions.
// Requires permission: api.http // Requires permission: api.http
// //
// Starlark API: // Starlark API:

View File

@@ -1,6 +1,5 @@
// Package sandbox — lib_module.go // Package sandbox — lib_module.go
// //
// v0.38.2: Library loading module for Starlark extensions.
// No permission required — any starlark package can use lib.require(). // No permission required — any starlark package can use lib.require().
// //
// Starlark API: // Starlark API:

View File

@@ -1,6 +1,5 @@
// Package sandbox — modules.go // Package sandbox — modules.go
// //
// v0.29.0 CS3: Module factories for Starlark sandbox.
// Each factory returns a starlarkstruct.Module that the runner // Each factory returns a starlarkstruct.Module that the runner
// injects into the script namespace based on granted permissions. // injects into the script namespace based on granted permissions.
package sandbox package sandbox

View File

@@ -1,6 +1,5 @@
// Package sandbox — realtime_module.go // Package sandbox — realtime_module.go
// //
// v0.5.0: Starlark realtime.publish() module.
// //
// Allows extensions to publish events to WebSocket channels. // Allows extensions to publish events to WebSocket channels.
// Events are scoped to rooms — only clients subscribed to the // Events are scoped to rooms — only clients subscribed to the

View File

@@ -1,19 +1,14 @@
// Package sandbox — runner.go // Package sandbox — runner.go
// //
// v0.29.0 CS3: Runner loads a package's Starlark script, assembles
// the module set based on granted permissions, and executes it. // the module set based on granted permissions, and executes it.
// //
// v0.29.1 CS0: Wires api.http permission to BuildHTTPModule with
// network_access config from package manifest. // network_access config from package manifest.
// //
// v0.29.1 CS2: Adds RunContext for per-invocation state (user_id),
// vault for provider key decryption, and provider.complete module. // vault for provider key decryption, and provider.complete module.
// //
// v0.29.2 CS1: Adds db *sql.DB for the db module. SetDB() wires it
// at startup. db.read grants query/view/list_tables; db.write adds // at startup. db.read grants query/view/list_tables; db.write adds
// insert/update/delete. If both are granted, db.write wins (superset). // insert/update/delete. If both are granted, db.write wins (superset).
// //
// v0.38.2: Adds lib.load() for library packages. Libraries run with
// their own permission context. Per-invocation lib cache + cycle detection. // their own permission context. Per-invocation lib cache + cycle detection.
// //
// The runner is the bridge between the package/permission system // The runner is the bridge between the package/permission system
@@ -57,13 +52,13 @@ type RunContext struct {
type Runner struct { type Runner struct {
sandbox *Sandbox sandbox *Sandbox
stores store.Stores stores store.Stores
packagesDir string // v0.38.0: disk path for load() support packagesDir string
notifier NotificationSender // nil = notifications module unavailable notifier NotificationSender // nil = notifications module unavailable
connResolver ConnectionResolver // nil = connections module unavailable (v0.38.1) connResolver ConnectionResolver // nil = connections module unavailable (v0.38.1)
db *sql.DB // nil = db module unavailable db *sql.DB // nil = db module unavailable
dbPostgres bool // true = use $N placeholders; false = use ? dbPostgres bool // true = use $N placeholders; false = use ?
allowPrivateIPs bool // v0.38.5: disable SSRF private IP check allowPrivateIPs bool
bus *events.Bus // v0.5.0: event bus for realtime module bus *events.Bus
} }
// NewRunner creates a runner with the given sandbox and dependencies. // NewRunner creates a runner with the given sandbox and dependencies.
@@ -94,7 +89,6 @@ func (r *Runner) SetDB(db *sql.DB, isPostgres bool) {
// SetPackagesDir sets the disk path where package archives are extracted. // SetPackagesDir sets the disk path where package archives are extracted.
// Required for load() support — scripts are read from disk at runtime. // Required for load() support — scripts are read from disk at runtime.
// v0.38.0.
func (r *Runner) SetPackagesDir(dir string) { func (r *Runner) SetPackagesDir(dir string) {
r.packagesDir = dir r.packagesDir = dir
} }
@@ -114,7 +108,6 @@ func (r *Runner) SetAllowPrivateIPs(allow bool) {
// ExecPackage loads a package's script from disk (primary) or manifest // ExecPackage loads a package's script from disk (primary) or manifest
// (legacy) and executes it with modules gated by granted permissions. // (legacy) and executes it with modules gated by granted permissions.
// //
// v0.38.0: Disk-based loading + package-scoped load() support.
// //
// The RunContext carries per-invocation state (user_id for provider // The RunContext carries per-invocation state (user_id for provider
// resolution). Pass nil if no per-invocation context is needed. // resolution). Pass nil if no per-invocation context is needed.
@@ -136,7 +129,6 @@ func (r *Runner) ExecPackage(ctx context.Context, pkg *store.PackageRegistration
return nil, err return nil, err
} }
// v0.38.2: per-invocation lib context for lib.load() caching + cycle detection
lc := newLibContext() lc := newLibContext()
// Build modules based on granted permissions // Build modules based on granted permissions
@@ -352,7 +344,6 @@ func (r *Runner) buildModulesWithLibCtx(ctx context.Context, packageID string, m
}) })
} }
// v0.30.0: settings module — always injected, no permission required.
// A package reads its own admin + team + user settings (v0.2.0 cascade). // A package reads its own admin + team + user settings (v0.2.0 cascade).
userID := "" userID := ""
teamID := "" teamID := ""
@@ -362,7 +353,6 @@ func (r *Runner) buildModulesWithLibCtx(ctx context.Context, packageID string, m
} }
modules["settings"] = BuildSettingsModule(ctx, r.stores, packageID, userID, teamID) modules["settings"] = BuildSettingsModule(ctx, r.stores, packageID, userID, teamID)
// v0.38.2: lib module — always injected, no permission required.
// Allows any starlark package to load declared library dependencies. // Allows any starlark package to load declared library dependencies.
if lc != nil { if lc != nil {
modules["lib"] = BuildLibModule(ctx, r, packageID, rc, lc) modules["lib"] = BuildLibModule(ctx, r, packageID, rc, lc)

View File

@@ -1,6 +1,5 @@
// Package sandbox — sandboxed Starlark interpreter. // Package sandbox — sandboxed Starlark interpreter.
// //
// v0.29.0 CS1: Provides execution with timeout, step limits,
// captured output, and injectable predeclared modules. // captured output, and injectable predeclared modules.
// //
// The sandbox is intentionally restrictive: // The sandbox is intentionally restrictive:

View File

@@ -1,6 +1,6 @@
package sandbox package sandbox
// settings_module.go — v0.2.0 // settings_module.go
// //
// The settings module lets extensions read their resolved settings via // The settings module lets extensions read their resolved settings via
// the three-tier cascade: global → team → user, respecting the // the three-tier cascade: global → team → user, respecting the

View File

@@ -1,6 +1,5 @@
// Package sandbox — unicode_scan.go // Package sandbox — unicode_scan.go
// //
// v0.5.1: Invisible Unicode scanning gate.
// //
// Defends against GlassWorm (variation selector payloads), Trojan Source // Defends against GlassWorm (variation selector payloads), Trojan Source
// (bidi override attacks / CVE-2021-42574), and other invisible Unicode // (bidi override attacks / CVE-2021-42574), and other invisible Unicode

View File

@@ -1,6 +1,6 @@
package sandbox package sandbox
// workflow_module.go — v0.30.2 CS1, v0.3.2 instance API // workflow_module.go
// //
// The workflow module lets extensions read workflow definitions, // The workflow module lets extensions read workflow definitions,
// manage instances, and programmatically advance workflow stages. // manage instances, and programmatically advance workflow stages.
@@ -27,7 +27,6 @@ import (
// BuildWorkflowModule creates the "workflow" Starlark module for a package. // BuildWorkflowModule creates the "workflow" Starlark module for a package.
// Requires the workflow.access permission. // Requires the workflow.access permission.
// //
// v0.3.2: added get_instance and list_instances for read-only instance access.
// Mutating operations (start/advance/cancel) are available via HTTP API; // Mutating operations (start/advance/cancel) are available via HTTP API;
// Starlark builtins for them will be added when the engine interface is // Starlark builtins for them will be added when the engine interface is
// extracted to a shared package to avoid circular imports. // extracted to a shared package to avoid circular imports.

View File

@@ -42,19 +42,19 @@ type Stores struct {
ResourceGrants ResourceGrantStore ResourceGrants ResourceGrantStore
Notifications NotificationStore Notifications NotificationStore
NotifPrefs NotificationPreferenceStore NotifPrefs NotificationPreferenceStore
Presence PresenceStore // v0.29.0: User online/offline status Presence PresenceStore
Health HealthStore // v0.29.0: Provider health window management Health HealthStore
Connections ConnectionStore // v0.38.1: Extension connection credentials Connections ConnectionStore
Dependencies DependencyStore // v0.38.2: Library package dependency graph Dependencies DependencyStore
Packages PackageStore // v0.28.7: Unified package registry (surfaces + extensions) Packages PackageStore
Workflows WorkflowStore // v0.26.1: Workflow definitions + stages Workflows WorkflowStore
ExtPermissions ExtensionPermissionStore // v0.29.0: Extension declared/granted capabilities ExtPermissions ExtensionPermissionStore
ExtData ExtDataStore // v0.29.2: Extension namespaced table catalog ExtData ExtDataStore
Tickets TicketStore // v0.32.0: WS auth tickets (PG-backed for cross-pod) Tickets TicketStore
RateLimits RateLimitStore // v0.32.0: Distributed rate limiting RateLimits RateLimitStore
Triggers TriggerStore // v0.2.2: Extension event/webhook triggers Triggers TriggerStore
ScheduledTasks ScheduledTaskStore // v0.2.2: User-created cron tasks ScheduledTasks ScheduledTaskStore
Cluster ClusterStore // v0.6.0: PG-backed cluster node registry (nil on SQLite) Cluster ClusterStore
} }
// TeamAvailableModel is returned by CatalogStore.ListTeamAvailable. // TeamAvailableModel is returned by CatalogStore.ListTeamAvailable.

View File

@@ -7,7 +7,6 @@ import (
) )
// RateLimitStore manages distributed rate limit counters in Postgres. // RateLimitStore manages distributed rate limit counters in Postgres.
// v0.32.0: fixed-window counter — upsert per-second bucket, check burst.
type RateLimitStore struct{} type RateLimitStore struct{}
func NewRateLimitStore() *RateLimitStore { return &RateLimitStore{} } func NewRateLimitStore() *RateLimitStore { return &RateLimitStore{} }

View File

@@ -8,7 +8,6 @@ import (
) )
// TicketStore manages WS auth tickets in Postgres. // TicketStore manages WS auth tickets in Postgres.
// v0.32.0: replaces in-memory sync.Map for cross-pod validation.
type TicketStore struct{} type TicketStore struct{}
func NewTicketStore() *TicketStore { return &TicketStore{} } func NewTicketStore() *TicketStore { return &TicketStore{} }

View File

@@ -6,7 +6,6 @@ import (
) )
// RateLimitStore manages distributed rate limit counters. // RateLimitStore manages distributed rate limit counters.
// v0.32.0: replaces in-memory token bucket for cross-pod rate limiting.
type RateLimitStore interface { type RateLimitStore interface {
// Allow checks if a request is within the rate limit. // Allow checks if a request is within the rate limit.
// key is "{scope}:{identifier}" e.g. "auth:192.168.1.1". // key is "{scope}:{identifier}" e.g. "auth:192.168.1.1".

View File

@@ -1,6 +1,6 @@
package store package store
// settings_cascade.go — v0.2.0 // settings_cascade.go
// //
// Pure functions for three-tier settings resolution: // Pure functions for three-tier settings resolution:
// global (admin) → team → user // global (admin) → team → user

View File

@@ -7,7 +7,6 @@ import (
) )
// RateLimitStore manages rate limit counters in SQLite. // RateLimitStore manages rate limit counters in SQLite.
// v0.32.0: functional parity for single-process test coverage.
type RateLimitStore struct{} type RateLimitStore struct{}
func NewRateLimitStore() *RateLimitStore { return &RateLimitStore{} } func NewRateLimitStore() *RateLimitStore { return &RateLimitStore{} }

View File

@@ -8,7 +8,6 @@ import (
) )
// TicketStore manages WS auth tickets in SQLite. // TicketStore manages WS auth tickets in SQLite.
// v0.32.0: functional parity for single-process test coverage.
type TicketStore struct{} type TicketStore struct{}
func NewTicketStore() *TicketStore { return &TicketStore{} } func NewTicketStore() *TicketStore { return &TicketStore{} }

View File

@@ -3,7 +3,6 @@ package store
import "context" import "context"
// TicketStore manages short-lived, single-use WebSocket auth tickets. // TicketStore manages short-lived, single-use WebSocket auth tickets.
// v0.32.0: replaces in-memory sync.Map for cross-pod ticket validation.
type TicketStore interface { type TicketStore interface {
// Issue creates a single-use ticket for the given user. // Issue creates a single-use ticket for the given user.
// Returns the opaque ticket ID (128-bit hex). // Returns the opaque ticket ID (128-bit hex).

View File

@@ -1,6 +1,5 @@
// Package triggers — engine.go // Package triggers — engine.go
// //
// v0.2.2: Core trigger engine. Manages extension-declared triggers
// (event bus subscriptions + webhook receivers) and user-created // (event bus subscriptions + webhook receivers) and user-created
// scheduled tasks (cron). All three converge to sandbox.Runner.CallEntryPoint. // scheduled tasks (cron). All three converge to sandbox.Runner.CallEntryPoint.
package triggers package triggers

View File

@@ -1,8 +1,6 @@
// Package webhook delivers HTTP POST notifications on task/workflow completion. // Package webhook delivers HTTP POST notifications on task/workflow completion.
// //
// v0.27.3: Retry (3 attempts, exponential backoff), HMAC-SHA256 signature,
// 10s timeout per attempt. // 10s timeout per attempt.
// v0.28.0: Added RunID, TokensUsed fields (D1 audit fix).
package webhook package webhook
import ( import (

View File

@@ -243,7 +243,6 @@ describe('init() profile gate', () => {
}); });
// ── Boot-time 401 redirect suppression ────── // ── Boot-time 401 redirect suppression ──────
// v0.37.14: Stale cookie blank-login-page fix.
// When the login page boots the SDK with stale localStorage tokens, // When the login page boots the SDK with stale localStorage tokens,
// the REST client's 401 handler must NOT redirect to /login (we're // the REST client's 401 handler must NOT redirect to /login (we're
// already there). boot() handles 401 gracefully on its own. // already there). boot() handles 401 gracefully on its own.

View File

@@ -5,7 +5,6 @@ const path = require('path');
const { createBrowserContext, loadSource, SRC } = require('./helpers'); const { createBrowserContext, loadSource, SRC } = require('./helpers');
const vm = require('vm'); const vm = require('vm');
// v0.37.10: extensions.js deleted — extension system moves to Preact in v0.37.12.
// Skip all tests if the source file doesn't exist. // Skip all tests if the source file doesn't exist.
const EXTENSIONS_EXISTS = fs.existsSync(path.join(SRC, 'extensions.js')); const EXTENSIONS_EXISTS = fs.existsSync(path.join(SRC, 'extensions.js'));
const maybeDescribe = EXTENSIONS_EXISTS ? describe : describe.skip; const maybeDescribe = EXTENSIONS_EXISTS ? describe : describe.skip;

View File

@@ -5,7 +5,6 @@ const path = require('path');
const { createBrowserContext, loadSource, SRC } = require('./helpers'); const { createBrowserContext, loadSource, SRC } = require('./helpers');
const vm = require('vm'); const vm = require('vm');
// v0.37.10: extensions.js deleted — extension system moves to Preact in v0.37.12.
// Skip all tests if the source file doesn't exist. // Skip all tests if the source file doesn't exist.
const EXTENSIONS_EXISTS = fs.existsSync(path.join(SRC, 'extensions.js')); const EXTENSIONS_EXISTS = fs.existsSync(path.join(SRC, 'extensions.js'));
const maybeDescribe = EXTENSIONS_EXISTS ? describe : describe.skip; const maybeDescribe = EXTENSIONS_EXISTS ? describe : describe.skip;

View File

@@ -5,7 +5,6 @@
// simulated browser environment so tests run // simulated browser environment so tests run
// against the ACTUAL frontend code. // against the ACTUAL frontend code.
// //
// v0.37.10: Removed loadAppModules (api.js + app.js deleted).
// ========================================== // ==========================================
const fs = require('fs'); const fs = require('fs');

View File

@@ -7,13 +7,9 @@
// policy exists but the frontend doesn't // policy exists but the frontend doesn't
// check it. // check it.
// //
// v0.22.5: Updated for server-rendered Go templates.
// v0.37.5: Settings surface moved to Preact — legacy SPA tests
// replaced with component source audits. // replaced with component source audits.
// v0.37.10: Legacy SPA source audit removed (ui-core.js, app.js,
// pages.js, settings-handlers.js, ui-admin.js all deleted). // pages.js, settings-handlers.js, ui-admin.js all deleted).
// Policy gating now verified via Preact surfaces + admin templates. // Policy gating now verified via Preact surfaces + admin templates.
// v0.37.12: Admin Go templates deleted (Preact since v0.37.6). Template
// element ID assertions removed — Preact component source audits // element ID assertions removed — Preact component source audits
// at the bottom of this file cover the same policy keys. // at the bottom of this file cover the same policy keys.
// //
@@ -130,7 +126,6 @@ describe('Team member dropdown population', () => {
}); });
// ── Kernel surface template ────────────────── // ── Kernel surface template ──────────────────
// v0.1.0: Chat surface removed. Verify admin mount exists and
// old SPA scaffold is gone. // old SPA scaffold is gone.
describe('Kernel surface templates (v0.1.0)', () => { describe('Kernel surface templates (v0.1.0)', () => {
@@ -153,7 +148,6 @@ describe('Kernel surface templates (v0.1.0)', () => {
}); });
// ── Admin Preact surface ───────────────────── // ── Admin Preact surface ─────────────────────
// v0.37.6: Admin surface handles settings save via Preact.
// Verify the admin surface has policy key handling. // Verify the admin surface has policy key handling.
describe('Admin Preact surface handles settings', () => { describe('Admin Preact surface handles settings', () => {

View File

@@ -4,7 +4,6 @@
// Bug badge that shows error count. Subscribes to engine // Bug badge that shows error count. Subscribes to engine
// for reactive updates. // for reactive updates.
// //
// v0.37.18: Preact rebuild from debug.js _updateBadge().
const html = window.html; const html = window.html;
const { useEffect } = window.hooks; const { useEffect } = window.hooks;

View File

@@ -4,7 +4,6 @@
// Filterable console log display with type coloring, // Filterable console log display with type coloring,
// elapsed timestamps, and auto-scroll. // elapsed timestamps, and auto-scroll.
// //
// v0.37.18: Preact rebuild from debug.js _renderConsoleTab().
const html = window.html; const html = window.html;
const { useState, useMemo, useRef, useEffect } = window.hooks; const { useState, useMemo, useRef, useEffect } = window.hooks;

View File

@@ -5,7 +5,6 @@
// Singleton — init() must run before SDK boot to capture early errors. // Singleton — init() must run before SDK boot to capture early errors.
// UI-agnostic: Preact components subscribe via .subscribe(). // UI-agnostic: Preact components subscribe via .subscribe().
// //
// v0.37.18: Extracted from debug.js (v0.37.14).
// //
// Exports: debugEngine (singleton) // Exports: debugEngine (singleton)

View File

@@ -5,7 +5,6 @@
// Global overlay — not a routed surface. // Global overlay — not a routed surface.
// Mounts via mountDebugModal(el, engine). // Mounts via mountDebugModal(el, engine).
// //
// v0.37.18: Preact rebuild of debug modal (CR P2-5).
import { debugEngine } from './engine.js'; import { debugEngine } from './engine.js';
import { ConsoleTab } from './console-tab.js'; import { ConsoleTab } from './console-tab.js';

View File

@@ -4,7 +4,6 @@
// Fetch log with expandable request/response details. // Fetch log with expandable request/response details.
// Newest entries first. // Newest entries first.
// //
// v0.37.18: Preact rebuild from debug.js _renderNetworkTab().
const html = window.html; const html = window.html;
const { useState } = window.hooks; const { useState } = window.hooks;

View File

@@ -5,7 +5,6 @@
// command history, and collapsible JSON output. // command history, and collapsible JSON output.
// Admin-gated OR ?debug=1 URL param. // Admin-gated OR ?debug=1 URL param.
// //
// v0.37.18: Preact rebuild from repl.js (v0.37.14).
const html = window.html; const html = window.html;
const { useState, useRef, useEffect, useCallback } = window.hooks; const { useState, useRef, useEffect, useCallback } = window.hooks;

View File

@@ -3,7 +3,6 @@
// ========================================== // ==========================================
// Displays current application state snapshot as formatted JSON. // Displays current application state snapshot as formatted JSON.
// //
// v0.37.18: Preact rebuild from debug.js _renderStateTab().
const html = window.html; const html = window.html;
const { useMemo } = window.hooks; const { useMemo } = window.hooks;

View File

@@ -239,7 +239,6 @@ export function createDomains(restClient) {
del: (id) => rc.del(`/api/v1/admin/extensions/${id}`), del: (id) => rc.del(`/api/v1/admin/extensions/${id}`),
}, },
// v0.38.1: Global connections
connections: crud(rc, '/api/v1/admin/connections'), connections: crud(rc, '/api/v1/admin/connections'),
packages: { packages: {
@@ -251,20 +250,18 @@ export function createDomains(restClient) {
del: (id) => rc.del(`/api/v1/admin/packages/${id}`), del: (id) => rc.del(`/api/v1/admin/packages/${id}`),
settings: (id) => rc.get(`/api/v1/admin/packages/${id}/settings`), settings: (id) => rc.get(`/api/v1/admin/packages/${id}/settings`),
updateSettings: (id, data) => rc.put(`/api/v1/admin/packages/${id}/settings`, data), updateSettings: (id, data) => rc.put(`/api/v1/admin/packages/${id}/settings`, data),
dependencies: (id) => rc.get(`/api/v1/admin/packages/${id}/dependencies`), // v0.38.2 dependencies: (id) => rc.get(`/api/v1/admin/packages/${id}/dependencies`),
consumers: (id) => rc.get(`/api/v1/admin/packages/${id}/consumers`), // v0.38.2 consumers: (id) => rc.get(`/api/v1/admin/packages/${id}/consumers`),
update: (id, file) => rc.upload(`/api/v1/admin/packages/${id}/update`, file), // v0.5.4 update: (id, file) => rc.upload(`/api/v1/admin/packages/${id}/update`, file),
exportPkg: (id) => `/api/v1/admin/packages/${id}/export`, // v0.5.4 (URL for window.open) exportPkg: (id) => `/api/v1/admin/packages/${id}/export`,
registry: () => rc.get('/api/v1/admin/packages/registry'), registry: () => rc.get('/api/v1/admin/packages/registry'),
registryInstall: (url) => rc.post('/api/v1/admin/packages/registry/install', { download_url: url }), registryInstall: (url) => rc.post('/api/v1/admin/packages/registry/install', { download_url: url }),
// v0.5.0: Extension permissions
permissions: (id) => rc.get(`/api/v1/admin/extensions/${id}/permissions`), permissions: (id) => rc.get(`/api/v1/admin/extensions/${id}/permissions`),
grantPerm: (id, perm) => rc.post(`/api/v1/admin/extensions/${id}/permissions/${perm}/grant`, {}), grantPerm: (id, perm) => rc.post(`/api/v1/admin/extensions/${id}/permissions/${perm}/grant`, {}),
revokePerm: (id, perm) => rc.post(`/api/v1/admin/extensions/${id}/permissions/${perm}/revoke`, {}), revokePerm: (id, perm) => rc.post(`/api/v1/admin/extensions/${id}/permissions/${perm}/revoke`, {}),
grantAllPerms: (id) => rc.post(`/api/v1/admin/extensions/${id}/permissions/grant-all`, {}), grantAllPerms: (id) => rc.post(`/api/v1/admin/extensions/${id}/permissions/grant-all`, {}),
}, },
// v0.38.2: Full dependency graph
dependencies: { dependencies: {
list: () => rc.get('/api/v1/admin/dependencies'), list: () => rc.get('/api/v1/admin/dependencies'),
}, },
@@ -278,7 +275,6 @@ export function createDomains(restClient) {
del: (id) => rc.del(`/api/v1/admin/surfaces/${id}`), del: (id) => rc.del(`/api/v1/admin/surfaces/${id}`),
}, },
// v0.6.1: Backup/Restore
backup: { backup: {
create: (opts) => rc.post('/api/v1/admin/backup' + _qs(opts)), create: (opts) => rc.post('/api/v1/admin/backup' + _qs(opts)),
list: () => rc.get('/api/v1/admin/backups'), list: () => rc.get('/api/v1/admin/backups'),

View File

@@ -4,7 +4,7 @@
* Reads globals: * Reads globals:
* __SECTION__ — active section name (string) * __SECTION__ — active section name (string)
* __BASE__ — base path * __BASE__ — base path
* __CONFIG_SECTIONS__ — v0.38.3: extension config sections (array|null) * __CONFIG_SECTIONS__
* *
* Layout: topbar (back + category tabs) + body (sidebar nav + content area). * Layout: topbar (back + category tabs) + body (sidebar nav + content area).
* All 24+ sections are native Preact components loaded lazily. * All 24+ sections are native Preact components loaded lazily.
@@ -72,7 +72,6 @@ const sectionModules = {
audit: () => import(`./audit.js${_v}`), audit: () => import(`./audit.js${_v}`),
}; };
// v0.38.3: Register dynamic section loaders for extension config sections
for (const cs of _configSections) { for (const cs of _configSections) {
const pkgId = cs.package_id; const pkgId = cs.package_id;
const component = cs.component || 'js/config.js'; const component = cs.component || 'js/config.js';

View File

@@ -46,7 +46,7 @@ export default function PackagesSection() {
const [registryPkgs, setRegistryPkgs] = useState([]); const [registryPkgs, setRegistryPkgs] = useState([]);
const [registryLoading, setRegistryLoading] = useState(false); const [registryLoading, setRegistryLoading] = useState(false);
const [installing, setInstalling] = useState(false); const [installing, setInstalling] = useState(false);
const [permsId, setPermsId] = useState(null); // v0.5.0: permissions drawer const [permsId, setPermsId] = useState(null);
const [perms, setPerms] = useState([]); const [perms, setPerms] = useState([]);
const BASE = window.__BASE__ || ''; const BASE = window.__BASE__ || '';

View File

@@ -4,7 +4,7 @@
* Reads globals: * Reads globals:
* __SECTION__ — active section name (string) * __SECTION__ — active section name (string)
* __BASE__ — base path * __BASE__ — base path
* __CONFIG_SECTIONS__ — v0.38.3: extension config sections (array|null) * __CONFIG_SECTIONS__
* *
* Layout: topbar + left nav + content area (same CSS classes as before). * Layout: topbar + left nav + content area (same CSS classes as before).
* All sections are native Preact components loaded lazily. * All sections are native Preact components loaded lazily.

View File

@@ -4,7 +4,7 @@
* Reads globals: * Reads globals:
* __SECTION__ — active section name (string) * __SECTION__ — active section name (string)
* __BASE__ — base path * __BASE__ — base path
* __CONFIG_SECTIONS__ — v0.38.3: extension config sections (array|null) * __CONFIG_SECTIONS__
* *
* Layout: topbar (back + team name) + sidebar nav + content area. * Layout: topbar (back + team name) + sidebar nav + content area.
* All 10+ sections are native Preact components loaded lazily. * All 10+ sections are native Preact components loaded lazily.

View File

@@ -1,5 +1,5 @@
/** /**
* Team Admin > Workflows — v0.37.15 rewrite * Team Admin > Workflows
* *
* Tab layout: Workflows | Assignments | Monitor * Tab layout: Workflows | Assignments | Monitor
* - Workflows: CRUD + inline stage editor (E2) * - Workflows: CRUD + inline stage editor (E2)
@@ -365,7 +365,6 @@ function StageForm({ stage, teams, onSave, onCancel }) {
stage?.branch_rules ? (typeof stage.branch_rules === 'string' ? stage.branch_rules : JSON.stringify(stage.branch_rules, null, 2)) : '' stage?.branch_rules ? (typeof stage.branch_rules === 'string' ? stage.branch_rules : JSON.stringify(stage.branch_rules, null, 2)) : ''
); );
// v0.3.4: stage_config fields
const sc = stage?.stage_config ? (typeof stage.stage_config === 'string' ? JSON.parse(stage.stage_config || '{}') : stage.stage_config) : {}; const sc = stage?.stage_config ? (typeof stage.stage_config === 'string' ? JSON.parse(stage.stage_config || '{}') : stage.stage_config) : {};
const [requiredRole, setRequiredRole] = useState(sc.required_role || ''); const [requiredRole, setRequiredRole] = useState(sc.required_role || '');
const [valApprovals, setValApprovals] = useState(sc.validation?.required_approvals || ''); const [valApprovals, setValApprovals] = useState(sc.validation?.required_approvals || '');