Feat v0.6.3 dead code sweep #38
@@ -113,7 +113,7 @@ func TestRouteFor(t *testing.T) {
|
||||
{"pong", DirToClient},
|
||||
// Tool bridge routes
|
||||
{"tool.call.abc123", DirToClient},
|
||||
{"tool.result.abc123", DirBoth}, // v0.32.0: DirBoth for cross-pod WaitFor
|
||||
{"tool.result.abc123", DirBoth},
|
||||
// Extension lifecycle
|
||||
{"extension.loaded", DirLocal},
|
||||
{"extension.error", DirLocal},
|
||||
@@ -227,7 +227,6 @@ func TestToolCallRouteToClient(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
|
||||
// prevent re-sending to clients (see subscribeToBus in ws.go).
|
||||
if !ShouldAcceptFromClient("tool.result.abc123") {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package events
|
||||
|
||||
// v0.32.0: TicketValidatorAdapter bridges the context-aware store.TicketStore
|
||||
// to the middleware.TicketValidator interface (which has no context parameter).
|
||||
// Replaces the in-memory TicketStore that lived in this file previously.
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ type Event struct {
|
||||
Payload json.RawMessage `json:"payload"`
|
||||
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
|
||||
// (harmless if clients see it — they ignore unknown fields).
|
||||
TargetUserID string `json:"target_user_id,omitempty"`
|
||||
@@ -38,8 +37,8 @@ var routeTable = map[string]Direction{
|
||||
// User/presence
|
||||
"user.presence": DirToClient,
|
||||
"user.status": DirToClient,
|
||||
"user.mentioned": DirToClient, // v0.23.2: targeted @mention notification
|
||||
"typing.user": DirToClient, // v0.23.2: human typing in DM/channel
|
||||
"user.mentioned": DirToClient,
|
||||
"typing.user": DirToClient,
|
||||
|
||||
// System
|
||||
"system.notify": DirToClient,
|
||||
@@ -77,7 +76,7 @@ var routeTable = map[string]Direction{
|
||||
|
||||
// Tool execution (browser tools bridge)
|
||||
"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.loaded": DirLocal, // Client-only
|
||||
|
||||
@@ -174,7 +174,6 @@ func (h *Hub) IsConnected(userID string) bool {
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (h *Hub) PublishToUser(userID string, event Event) {
|
||||
event.TargetUserID = userID
|
||||
@@ -238,13 +237,11 @@ func (c *Conn) subscribeToBus() {
|
||||
return
|
||||
}
|
||||
|
||||
// v0.32.0: Targeted delivery — only deliver to the intended user.
|
||||
// Targeted events skip room filtering (user-scoped, not room-scoped).
|
||||
if e.TargetUserID != "" && e.TargetUserID != c.userID {
|
||||
return
|
||||
}
|
||||
|
||||
// v0.32.0: tool.result travels cross-pod for WaitFor (DirBoth) but
|
||||
// must not be forwarded to WebSocket clients — they sent it.
|
||||
if strings.HasPrefix(e.Label, "tool.result.") {
|
||||
return
|
||||
@@ -316,7 +313,6 @@ func (c *Conn) readPump() {
|
||||
continue
|
||||
}
|
||||
|
||||
// v0.5.0: Room management — intercept before bus publish (like ping)
|
||||
if event.Label == "room.subscribe" {
|
||||
var req struct {
|
||||
Room string `json:"room"`
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package handlers
|
||||
|
||||
// v0.38.1: Extension connection handlers — admin/global scope.
|
||||
// Methods on AdminHandler, mirrors admin provider config pattern.
|
||||
|
||||
import (
|
||||
|
||||
@@ -345,7 +345,6 @@ func hashToken(token string) string {
|
||||
// - AdminHandler.destroyVault (admin-initiated reset)
|
||||
//
|
||||
// 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) {
|
||||
if err := stores.Users.ClearVaultKeys(ctx, userID); err != nil {
|
||||
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.
|
||||
//
|
||||
// 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) {
|
||||
vaultSet, encryptedUEK, salt, nonce, err := stores.Users.GetVaultKeys(ctx, userID)
|
||||
if err != nil || !vaultSet {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package handlers
|
||||
|
||||
// v0.38.1: ConnectionResolver bridges the sandbox connections module
|
||||
// to the store + vault layers. Implements sandbox.ConnectionResolver.
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package handlers
|
||||
|
||||
// v0.38.4: Connection type discovery endpoint.
|
||||
// Returns merged connection types from all active packages.
|
||||
// Library declarations take precedence over non-library declarations.
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package handlers
|
||||
|
||||
// v0.38.1: Extension connection handlers — personal scope + resolution.
|
||||
// Pattern follows apiconfigs.go (ProviderConfigHandler).
|
||||
|
||||
import (
|
||||
@@ -28,7 +27,6 @@ func NewConnectionHandler(s store.Stores, vault *crypto.KeyResolver) *Connection
|
||||
// ListConnections returns the user's personal connections.
|
||||
func (h *ConnectionHandler) ListConnections(c *gin.Context) {
|
||||
userID := getUserID(c)
|
||||
// v0.38.5: Show all accessible connections (personal + team + global)
|
||||
// so users can see which connections are available to them.
|
||||
conns, err := h.stores.Connections.ListAccessible(c.Request.Context(), userID)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// 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
|
||||
// /s/:slug/api/*path with JWT auth.
|
||||
//
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package handlers
|
||||
|
||||
// ext_db_schema.go — v0.29.2
|
||||
// ext_db_schema.go
|
||||
//
|
||||
// DDL generation and lifecycle management for extension-owned database tables.
|
||||
// Tables are namespaced as ext_{pkg_slug}_{logical_name} and tracked in the
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
)
|
||||
|
||||
// ExtPermHandler serves extension permission management endpoints.
|
||||
// v0.29.0 CS2: Admin reviews and grants/revokes declared permissions.
|
||||
type ExtPermHandler struct {
|
||||
stores store.Stores
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
)
|
||||
|
||||
// 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.
|
||||
//
|
||||
// Secrets are stored in GlobalConfig under key "ext_secrets:{packageID}"
|
||||
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
)
|
||||
|
||||
// ExtensionHandler serves extension management endpoints.
|
||||
// v0.28.7: Backed by PackageStore (packages table) instead of ExtensionStore.
|
||||
type ExtensionHandler struct {
|
||||
stores store.Stores
|
||||
}
|
||||
@@ -94,7 +93,6 @@ func (h *ExtensionHandler) UpdateUserExtensionSettings(c *gin.Context) {
|
||||
|
||||
settings := json.RawMessage("{}")
|
||||
if body.Settings != nil {
|
||||
// v0.2.0: enforce user_overridable — strip locked keys before saving
|
||||
schema := store.ParseSettingsSchema(pkg.Manifest)
|
||||
if len(schema) > 0 {
|
||||
var incoming map[string]any
|
||||
@@ -229,18 +227,15 @@ func (h *ExtensionHandler) AdminInstallExtension(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// v0.29.0: Parse manifest permissions and declare them.
|
||||
// If permissions are declared, package moves to pending_review.
|
||||
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 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)
|
||||
}
|
||||
}
|
||||
|
||||
// v0.2.2: Sync triggers from manifest
|
||||
SyncManifestTriggers(c.Request.Context(), h.stores, triggers.GlobalEngine(), pkg.ID, manifestMap)
|
||||
|
||||
c.JSON(201, gin.H{"data": pkg})
|
||||
@@ -403,7 +398,6 @@ func (h *ExtensionHandler) AdminUninstallExtension(c *gin.Context) {
|
||||
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 {
|
||||
log.Printf("[ext-db] schema drop failed for %s: %v", id, err)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// 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
|
||||
// path items; others get auto-generated stubs.
|
||||
//
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package handlers
|
||||
|
||||
// package_registry.go — v0.30.0 CS4
|
||||
// package_registry.go
|
||||
//
|
||||
// Package registry (marketplace discovery). Admin browses available
|
||||
// packages from an external JSON registry and installs them by URL.
|
||||
|
||||
@@ -31,8 +31,8 @@ var validPackageID = regexp.MustCompile(`^[a-z0-9][a-z0-9-]{0,62}[a-z0-9]$`)
|
||||
type PackageHandler struct {
|
||||
stores store.Stores
|
||||
packagesDir string // e.g. /data/packages — where archives are extracted
|
||||
sandbox *sandbox.Sandbox // v0.30.0: for schema migration scripts
|
||||
runner *sandbox.Runner // v0.38.2: for test-tool execution
|
||||
sandbox *sandbox.Sandbox
|
||||
runner *sandbox.Runner
|
||||
}
|
||||
|
||||
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) {
|
||||
id := c.Param("id")
|
||||
|
||||
// v0.3.7: Block enabling dormant packages (unmet requires).
|
||||
pkg, err := h.stores.Packages.Get(c.Request.Context(), id)
|
||||
if err != nil || pkg == nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "package not found"})
|
||||
@@ -149,7 +148,6 @@ func (h *PackageHandler) DeletePackage(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// v0.38.2: Libraries with active consumers cannot be uninstalled.
|
||||
if pkg.Type == "library" && h.stores.Dependencies != nil {
|
||||
hasConsumers, _ := h.stores.Dependencies.HasConsumers(c.Request.Context(), id)
|
||||
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 {
|
||||
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 {
|
||||
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 cleanupTmp bool
|
||||
|
||||
// v0.30.0: Check for pre-downloaded file from registry install
|
||||
if regFile, ok := c.Get("_registry_file"); ok {
|
||||
tmpPath = regFile.(string)
|
||||
// Don't remove — caller manages lifecycle
|
||||
@@ -278,7 +273,6 @@ func (h *PackageHandler) InstallPackage(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// v0.38.0: Entry point validation for starlark-tier packages.
|
||||
// Scripts are loaded from disk at runtime — no _starlark_script injection.
|
||||
if manifestTier, _ := manifest["tier"].(string); manifestTier == "starlark" {
|
||||
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"})
|
||||
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
|
||||
// (e.g. editor: browser-tier surface + admin-configurable settings).
|
||||
if !hasExtBehavior && !hasSettings {
|
||||
@@ -393,7 +386,6 @@ func (h *PackageHandler) InstallPackage(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
case "library":
|
||||
// v0.38.2: Libraries must declare exports, cannot have tools/pipes/route.
|
||||
exports, _ := manifest["exports"].([]any)
|
||||
if len(exports) == 0 {
|
||||
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")
|
||||
|
||||
// v0.30.0: use registry source if set by registry install handler
|
||||
pkgSource := "extension"
|
||||
if src, ok := c.Get("_registry_source"); ok {
|
||||
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)
|
||||
}
|
||||
|
||||
// v0.29.2: Create namespaced DB tables declared in the manifest.
|
||||
if tables, ok := ParseDBTables(manifest); ok {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// v0.38.5: Declare manifest permissions (same as AdminInstallExtension).
|
||||
// This creates the permission rows and sets status to pending_review
|
||||
// if the package declares permissions.
|
||||
SyncManifestPermissions(c, h.stores, pkgID, manifest)
|
||||
|
||||
// v0.2.2: Sync triggers from manifest
|
||||
SyncManifestTriggers(c.Request.Context(), h.stores, triggers.GlobalEngine(), pkgID, manifest)
|
||||
|
||||
// v0.30.0: Run schema migrations if declared.
|
||||
newSchemaVersion := ParseSchemaVersion(manifest)
|
||||
if newSchemaVersion > 0 && h.sandbox != nil {
|
||||
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 err := InstallWorkflowFromManifest(c, h.stores, pkgID, manifest); err != nil {
|
||||
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.
|
||||
if deps, ok := manifest["dependencies"].(map[string]any); ok && len(deps) > 0 {
|
||||
// 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))
|
||||
}
|
||||
|
||||
// v0.2.1: Auto-set default_surface when the first extension surface is installed.
|
||||
if (pkgType == "surface" || pkgType == "full") && pkgSource != "core" {
|
||||
if raw, err := h.stores.GlobalConfig.Get(c.Request.Context(), "default_surface"); err != nil || raw == nil {
|
||||
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).
|
||||
knownCaps := map[string]bool{}
|
||||
var unmetReqs []string
|
||||
|
||||
@@ -6,7 +6,6 @@ package handlers
|
||||
// GET /api/v1/presence?users=id1,id2 returns current status.
|
||||
// Online = last_seen within 90s.
|
||||
//
|
||||
// v0.29.0: Raw SQL replaced with PresenceStore + UserStore methods.
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
@@ -8,7 +8,6 @@ package handlers
|
||||
// (profile, permissions, teams/mine, settings) into one call.
|
||||
// The SDK calls this at startup and on token refresh.
|
||||
//
|
||||
// v0.37.15
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
@@ -2,7 +2,6 @@ package handlers
|
||||
|
||||
// settings.go — User profile, preferences, and password management.
|
||||
//
|
||||
// v0.29.0: Raw SQL replaced with UserStore methods.
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package handlers
|
||||
|
||||
// team_package_settings.go — v0.2.0
|
||||
// team_package_settings.go
|
||||
//
|
||||
// Team-admin endpoints for managing team-scoped package settings.
|
||||
// These sit in the settings cascade between global (admin) and user.
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
)
|
||||
|
||||
// ═══════════════════════════════════════════════
|
||||
// Upgrade Tests — v0.5.5
|
||||
// Upgrade Tests
|
||||
//
|
||||
// Schema edge cases, settings migration, and
|
||||
// package compatibility across kernel upgrades.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package handlers
|
||||
|
||||
// user_packages.go — v0.30.0 CS5
|
||||
// user_packages.go
|
||||
//
|
||||
// Non-admin package management. Users install personal-scoped packages;
|
||||
// team admins install team-scoped packages.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package handlers
|
||||
|
||||
// workflow_packages.go — v0.30.2 CS1
|
||||
// workflow_packages.go
|
||||
//
|
||||
// Handles workflow-specific package operations:
|
||||
// - ExportWorkflowPackage: bundles a workflow definition + stages into a .pkg
|
||||
|
||||
@@ -106,7 +106,6 @@ func (h *WorkflowHandler) Create(c *gin.Context) {
|
||||
|
||||
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 {
|
||||
tid := ftid.(string)
|
||||
w.TeamID = &tid
|
||||
@@ -417,7 +416,6 @@ func (h *WorkflowHandler) Clone(c *gin.Context) {
|
||||
CreatedBy: c.GetString("user_id"),
|
||||
}
|
||||
|
||||
// v0.31.2: team-scoped route injects force_team_id
|
||||
if ftid, ok := c.Get("force_team_id"); ok {
|
||||
tid := ftid.(string)
|
||||
clone.TeamID = &tid
|
||||
|
||||
@@ -36,7 +36,6 @@ import (
|
||||
sqliteStore "switchboard-core/store/sqlite"
|
||||
)
|
||||
|
||||
// v0.33.0: Embedded OpenAPI spec and Swagger UI for /api/docs.
|
||||
//
|
||||
//go:embed static/openapi.yaml
|
||||
var openapiSpec []byte
|
||||
@@ -106,7 +105,6 @@ func main() {
|
||||
stores = postgres.NewStores(database.DB)
|
||||
}
|
||||
|
||||
// v0.33.0: Start Prometheus DB pool collector
|
||||
metrics.StartDBCollector(database.DB, 15*time.Second)
|
||||
|
||||
// Bootstrap admin from env (K8s secret) — upserts on every restart
|
||||
@@ -158,17 +156,12 @@ func main() {
|
||||
sandbox.New(sandbox.DefaultConfig()),
|
||||
stores,
|
||||
)
|
||||
// v0.38.1: connections module — extension connection resolution
|
||||
starlarkRunner.SetConnectionResolver(handlers.NewConnectionResolverAdapter(stores, keyResolver))
|
||||
// v0.29.2: db module — extension namespaced table access
|
||||
starlarkRunner.SetDB(database.DB, database.IsPostgres())
|
||||
// v0.38.0: disk-based script loading + load() support
|
||||
if cfg.StoragePath != "" {
|
||||
starlarkRunner.SetPackagesDir(cfg.StoragePath + "/packages")
|
||||
}
|
||||
// v0.5.0: realtime module — extension publish to WebSocket channels
|
||||
starlarkRunner.SetBus(bus)
|
||||
// v0.38.5: allow extensions to reach private IPs (self-hosted Gitea, etc.)
|
||||
if os.Getenv("EXT_ALLOW_PRIVATE_IPS") == "true" {
|
||||
starlarkRunner.SetAllowPrivateIPs(true)
|
||||
log.Printf(" ⚠️ Extension SSRF protection relaxed: private IPs allowed")
|
||||
@@ -274,7 +267,6 @@ func main() {
|
||||
c.JSON(200, info)
|
||||
})
|
||||
|
||||
// v0.32.0: Kubernetes probe endpoints
|
||||
// Liveness: process is alive and serving (no dependency checks).
|
||||
base.GET("/healthz/live", func(c *gin.Context) {
|
||||
c.JSON(200, gin.H{"status": "ok"})
|
||||
@@ -296,10 +288,8 @@ func main() {
|
||||
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()))
|
||||
|
||||
// v0.33.0: OpenAPI spec + Swagger UI (no auth — documentation)
|
||||
base.GET("/api/docs", func(c *gin.Context) {
|
||||
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)
|
||||
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) {
|
||||
spec, err := handlers.BuildOpenAPISpec(stores, openapiSpec, Version)
|
||||
if err != nil {
|
||||
@@ -501,7 +490,7 @@ func main() {
|
||||
connTypeH := handlers.NewConnectionTypeHandler(stores)
|
||||
protected.GET("/connection-types", connTypeH.ListConnectionTypes)
|
||||
|
||||
// Extension Connections (personal scope — v0.38.1)
|
||||
// Extension Connections (personal scope
|
||||
connH := handlers.NewConnectionHandler(stores, keyResolver)
|
||||
protected.GET("/connections", connH.ListConnections)
|
||||
protected.POST("/connections", connH.CreateConnection)
|
||||
@@ -559,7 +548,7 @@ func main() {
|
||||
teams := handlers.NewTeamHandler(stores, keyResolver)
|
||||
protected.GET("/teams/mine", teams.MyTeams)
|
||||
|
||||
// Groups (user: my groups — v0.16.0)
|
||||
// Groups (user: my groups
|
||||
groupH := handlers.NewGroupHandler(stores)
|
||||
protected.GET("/groups/mine", groupH.MyGroups)
|
||||
|
||||
@@ -697,7 +686,7 @@ func main() {
|
||||
admin.GET("/audit", adm.ListAuditLog)
|
||||
admin.GET("/audit/actions", adm.ListAuditActions)
|
||||
|
||||
// Groups (admin — v0.16.0)
|
||||
// Groups (admin
|
||||
groupAdm := handlers.NewGroupHandler(stores)
|
||||
admin.GET("/groups", groupAdm.ListGroups)
|
||||
admin.POST("/groups", groupAdm.CreateGroup)
|
||||
@@ -712,7 +701,7 @@ func main() {
|
||||
admin.GET("/permissions", groupAdm.ListPermissions)
|
||||
admin.GET("/users/:id/permissions", groupAdm.GetUserPermissions)
|
||||
|
||||
// Resource Grants (admin — v0.16.0)
|
||||
// Resource Grants (admin
|
||||
admin.GET("/grants/:type/:id", groupAdm.GetResourceGrant)
|
||||
admin.PUT("/grants/:type/:id", groupAdm.SetResourceGrant)
|
||||
|
||||
@@ -736,7 +725,7 @@ func main() {
|
||||
admin.PUT("/extensions/:id", extAdm.AdminUpdateExtension)
|
||||
admin.DELETE("/extensions/:id", extAdm.AdminUninstallExtension)
|
||||
|
||||
// Extension permissions (admin — v0.29.0)
|
||||
// Extension permissions (admin
|
||||
extPermH := handlers.NewExtPermHandler(stores)
|
||||
admin.GET("/extensions/:id/permissions", extPermH.ListPackagePermissions)
|
||||
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/grant-all", extPermH.GrantAllPermissions)
|
||||
|
||||
// Extension secrets (admin — v0.29.0 CS3)
|
||||
// Extension secrets (admin
|
||||
extSecH := handlers.NewExtSecretsHandler(stores)
|
||||
admin.GET("/extensions/:id/secrets", extSecH.GetSecrets)
|
||||
admin.PUT("/extensions/:id/secrets", extSecH.SetSecrets)
|
||||
@@ -756,7 +745,7 @@ func main() {
|
||||
packagesDir = cfg.StoragePath + "/packages"
|
||||
}
|
||||
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)
|
||||
registryH := handlers.NewRegistryHandler(stores, packagesDir, pkgAdm)
|
||||
@@ -769,14 +758,14 @@ func main() {
|
||||
admin.PUT("/packages/:id/enable", pkgAdm.EnablePackage)
|
||||
admin.PUT("/packages/:id/disable", pkgAdm.DisablePackage)
|
||||
admin.DELETE("/packages/:id", pkgAdm.DeletePackage)
|
||||
admin.GET("/packages/:id/settings", pkgAdm.GetPackageSettings) // v0.30.0
|
||||
admin.PUT("/packages/:id/settings", pkgAdm.UpdatePackageSettings) // v0.30.0
|
||||
admin.GET("/packages/:id/dependencies", pkgAdm.ListDependencies) // v0.38.2
|
||||
admin.GET("/packages/:id/consumers", pkgAdm.ListConsumers) // v0.38.2
|
||||
admin.POST("/packages/:id/test-tool", pkgAdm.TestTool) // v0.38.2
|
||||
admin.POST("/packages/:id/update", pkgAdm.UpdatePackage) // v0.5.4
|
||||
admin.GET("/packages/:id/export", pkgAdm.ExportPackage) // v0.5.4
|
||||
admin.GET("/dependencies", pkgAdm.ListAllDependencies) // v0.38.2
|
||||
admin.GET("/packages/:id/settings", pkgAdm.GetPackageSettings)
|
||||
admin.PUT("/packages/:id/settings", pkgAdm.UpdatePackageSettings)
|
||||
admin.GET("/packages/:id/dependencies", pkgAdm.ListDependencies)
|
||||
admin.GET("/packages/:id/consumers", pkgAdm.ListConsumers)
|
||||
admin.POST("/packages/:id/test-tool", pkgAdm.TestTool)
|
||||
admin.POST("/packages/:id/update", pkgAdm.UpdatePackage)
|
||||
admin.GET("/packages/:id/export", pkgAdm.ExportPackage)
|
||||
admin.GET("/dependencies", pkgAdm.ListAllDependencies)
|
||||
|
||||
// Workflow package export (v0.30.2)
|
||||
wfPkgH := handlers.NewWorkflowPackageHandler(stores)
|
||||
@@ -825,9 +814,7 @@ func main() {
|
||||
}
|
||||
|
||||
// ── 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).
|
||||
// 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.
|
||||
if cfg.StoragePath != "" {
|
||||
packagesStaticDir := cfg.StoragePath + "/packages"
|
||||
@@ -853,7 +840,6 @@ func main() {
|
||||
// Login page — no auth required
|
||||
base.GET("/login", pageEngine.RenderLogin())
|
||||
|
||||
// v0.25.0: Surface routes generated from manifest registry.
|
||||
// Replaces manual per-surface route blocks.
|
||||
pageEngine.RegisterPageRoutes(base, pages.PageRouteMiddleware{
|
||||
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)
|
||||
})
|
||||
|
||||
// v0.29.1: Extension API routes — Starlark packages serve JSON endpoints.
|
||||
// Mounted at /s/:slug/api/* with JWT auth (returns 401, not redirect).
|
||||
{
|
||||
extAPIH := handlers.NewExtAPIHandler(stores, starlarkRunner)
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
)
|
||||
|
||||
// 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.
|
||||
// Auth endpoints have their own protections (bcrypt, lockout); blocking
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
|
||||
// RequireTeamAdmin returns middleware that restricts access to team admins.
|
||||
// System admins are always allowed through.
|
||||
// v0.29.0: accepts TeamStore instead of using database.DB directly.
|
||||
func RequireTeamAdmin(teams store.TeamStore) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
role, _ := c.Get("role")
|
||||
@@ -43,7 +42,6 @@ func RequireTeamAdmin(teams store.TeamStore) gin.HandlerFunc {
|
||||
// RequireTeamMember returns middleware that restricts access to users who
|
||||
// belong to the team identified by :teamId (any role).
|
||||
// System admins are always allowed through.
|
||||
// v0.29.0: accepts TeamStore instead of using database.DB directly.
|
||||
func RequireTeamMember(teams store.TeamStore) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
role, _ := c.Get("role")
|
||||
|
||||
@@ -31,11 +31,11 @@ const (
|
||||
ExtPermDBRead = "db.read"
|
||||
ExtPermDBWrite = "db.write"
|
||||
ExtPermAPIHTTP = "api.http"
|
||||
ExtPermFormValidate = "forms.validate" // v0.29.3: form validation hooks
|
||||
ExtPermWorkflowAccess = "workflow.access" // v0.30.2: workflow definition + stage data access
|
||||
ExtPermConnectionsRead = "connections.read" // v0.38.1: extension connection resolution
|
||||
ExtPermTriggersRegister = "triggers.register" // v0.2.2: event/webhook trigger registration
|
||||
ExtPermRealtimePublish = "realtime.publish" // v0.5.0: publish events to realtime channels
|
||||
ExtPermFormValidate = "forms.validate"
|
||||
ExtPermWorkflowAccess = "workflow.access"
|
||||
ExtPermConnectionsRead = "connections.read"
|
||||
ExtPermTriggersRegister = "triggers.register"
|
||||
ExtPermRealtimePublish = "realtime.publish"
|
||||
)
|
||||
|
||||
// ValidExtensionPermissions is the set of recognized permission keys.
|
||||
|
||||
@@ -121,10 +121,9 @@ var ValidAudiences = map[string]bool{
|
||||
// ── Typed Form Template ─────────────────────
|
||||
|
||||
// TypedFormTemplate is the structured form_template schema (v0.29.3).
|
||||
// v0.35.0: added Fieldsets for progressive multi-step forms.
|
||||
type TypedFormTemplate struct {
|
||||
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"`
|
||||
}
|
||||
|
||||
@@ -153,7 +152,7 @@ type FormField struct {
|
||||
Default interface{} `json:"default,omitempty"`
|
||||
Options []FormOption `json:"options,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.
|
||||
@@ -200,7 +199,6 @@ func ParseTypedFormTemplate(raw json.RawMessage) *TypedFormTemplate {
|
||||
return nil
|
||||
}
|
||||
|
||||
// v0.35.0: If fieldsets present, flatten into fields for backward compat
|
||||
if len(tpl.Fieldsets) > 0 {
|
||||
var allFields []FormField
|
||||
for _, fs := range tpl.Fieldsets {
|
||||
@@ -237,7 +235,6 @@ type FieldError struct {
|
||||
func ValidateFormData(tpl *TypedFormTemplate, data map[string]interface{}) []FieldError {
|
||||
var errs []FieldError
|
||||
for _, f := range tpl.Fields {
|
||||
// v0.35.0: Skip fields whose condition is not met
|
||||
if f.Condition != nil && !evaluateFieldCondition(f.Condition, data) {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ type AdminPageData struct {
|
||||
// Feature gates (BYOK, personas) moved to sw.auth.policies in v0.37.19.
|
||||
type SettingsPageData struct {
|
||||
Section string `json:"section"`
|
||||
ConfigSections []ConfigSectionEntry `json:"config_sections,omitempty"` // v0.38.3
|
||||
ConfigSections []ConfigSectionEntry `json:"config_sections,omitempty"`
|
||||
}
|
||||
|
||||
// ── Loader registration ──────────────────────
|
||||
@@ -39,7 +39,7 @@ type SettingsPageData struct {
|
||||
// TeamAdminPageData is what the team-admin surface receives.
|
||||
type TeamAdminPageData struct {
|
||||
Section string `json:"section"`
|
||||
ConfigSections []ConfigSectionEntry `json:"config_sections,omitempty"` // v0.38.3
|
||||
ConfigSections []ConfigSectionEntry `json:"config_sections,omitempty"`
|
||||
}
|
||||
|
||||
func (e *Engine) registerLoaders() {
|
||||
@@ -91,7 +91,6 @@ func sectionCategory(section string) string {
|
||||
}
|
||||
|
||||
// ── Settings loader ──────────────────────────
|
||||
// v0.22.7: Reads feature gates from GlobalConfig to control
|
||||
// which nav links/tabs are visible (BYOK, User Personas).
|
||||
|
||||
func (e *Engine) teamAdminLoader(c *gin.Context, s store.Stores) (any, error) {
|
||||
|
||||
@@ -38,7 +38,7 @@ type Engine struct {
|
||||
cfg *config.Config
|
||||
stores store.Stores
|
||||
loaders map[string]DataLoaderFunc
|
||||
surfaces []SurfaceManifest // v0.25.0: registered surface definitions
|
||||
surfaces []SurfaceManifest
|
||||
devMode bool
|
||||
}
|
||||
|
||||
@@ -95,25 +95,20 @@ type PageData struct {
|
||||
User *UserContext
|
||||
Data any // surface-specific data from loader
|
||||
|
||||
// v0.22.7: Theme + settings injection
|
||||
Theme string // "dark", "light", or "" (= use default)
|
||||
SurfaceSettings any // JSON-serialized to window.__SETTINGS__
|
||||
|
||||
// v0.25.0: Surface manifest — layout preset, components, etc.
|
||||
Manifest *SurfaceManifest `json:"manifest,omitempty"`
|
||||
|
||||
// v0.25.0: Enabled surface IDs for conditional nav rendering.
|
||||
EnabledSurfaces []string `json:"-"`
|
||||
|
||||
// v0.27.0: Extension surfaces for sidebar nav rendering.
|
||||
ExtensionSurfaces []ExtensionNavItem `json:"-"`
|
||||
|
||||
// v0.22.7: Login/splash page fields
|
||||
InstanceName string // branding: instance display name
|
||||
LogoURL string // branding: custom logo URL
|
||||
Tagline string // branding: tagline under instance name
|
||||
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.
|
||||
@@ -137,7 +132,6 @@ type ExtensionNavItem struct {
|
||||
|
||||
// ConfigSectionEntry describes a package-declared config section for
|
||||
// lazy-loading in Settings, Admin, or Team Admin surfaces.
|
||||
// v0.38.3: manifest-driven discovery — no new tables or endpoints.
|
||||
type ConfigSectionEntry struct {
|
||||
PackageID string `json:"package_id"` // section key & asset path prefix
|
||||
Label string `json:"label"` // nav link text
|
||||
@@ -207,7 +201,7 @@ func New(cfg *config.Config, stores store.Stores) *Engine {
|
||||
e.parseTemplates()
|
||||
e.registerLoaders()
|
||||
e.registerCoreSurfaces()
|
||||
e.SeedSurfaces() // v0.25.0: Write core manifests to DB (preserves admin toggle state)
|
||||
e.SeedSurfaces()
|
||||
return e
|
||||
}
|
||||
|
||||
@@ -322,7 +316,6 @@ func (e *Engine) Render(c *gin.Context, name string, data PageData) {
|
||||
data.Message = e.loadMessage()
|
||||
data.Footer = e.loadFooter()
|
||||
|
||||
// v0.22.7: Default theme if not set
|
||||
if data.Theme == "" {
|
||||
data.Theme = "dark"
|
||||
}
|
||||
@@ -477,7 +470,6 @@ func (e *Engine) RegisterPageRoutes(base *gin.RouterGroup, mw PageRouteMiddlewar
|
||||
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.
|
||||
group.GET("/s/:slug", e.RenderExtensionSurface())
|
||||
}
|
||||
@@ -592,7 +584,6 @@ func stripPrefix(route, prefix string) string {
|
||||
// RenderLogin serves the standalone login page.
|
||||
func (e *Engine) RenderLogin() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
// v0.22.7: Populate splash/login fields from global config
|
||||
instanceName, logoURL, tagline := e.loadBranding()
|
||||
regOpen := e.isRegistrationOpen()
|
||||
|
||||
@@ -619,8 +610,8 @@ type WorkflowPageData struct {
|
||||
FormTemplateJSON string // typed form template JSON (empty if custom)
|
||||
TotalStages int
|
||||
CurrentStage int
|
||||
SurfacePkgID string // v0.30.2: custom package surface override (empty = use StageMode)
|
||||
BrandingJSON string // v0.35.0: workflow branding JSON (accent_color, logo_url, tagline)
|
||||
SurfacePkgID string
|
||||
BrandingJSON string
|
||||
}
|
||||
|
||||
// WorkflowLandingPageData is passed to workflow-landing.html.
|
||||
|
||||
@@ -35,7 +35,6 @@ func (e *Engine) SeedSurfaces() {
|
||||
}
|
||||
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
|
||||
// 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" {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// Package sandbox — connections_module.go
|
||||
//
|
||||
// v0.38.1: Connections module for Starlark extensions.
|
||||
// Requires permission: connections.read
|
||||
//
|
||||
// Starlark API:
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// Package sandbox — db_module.go
|
||||
//
|
||||
// v0.29.2 CS1: Structured db module for Starlark extensions.
|
||||
//
|
||||
// Permissions:
|
||||
// db.read → db.query(), db.view(), db.list_tables()
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// Package sandbox — http_module.go
|
||||
//
|
||||
// v0.29.1 CS0: HTTP outbound module for Starlark extensions.
|
||||
// Requires permission: api.http
|
||||
//
|
||||
// Starlark API:
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// Package sandbox — lib_module.go
|
||||
//
|
||||
// v0.38.2: Library loading module for Starlark extensions.
|
||||
// No permission required — any starlark package can use lib.require().
|
||||
//
|
||||
// Starlark API:
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// Package sandbox — modules.go
|
||||
//
|
||||
// v0.29.0 CS3: Module factories for Starlark sandbox.
|
||||
// Each factory returns a starlarkstruct.Module that the runner
|
||||
// injects into the script namespace based on granted permissions.
|
||||
package sandbox
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// Package sandbox — realtime_module.go
|
||||
//
|
||||
// v0.5.0: Starlark realtime.publish() module.
|
||||
//
|
||||
// Allows extensions to publish events to WebSocket channels.
|
||||
// Events are scoped to rooms — only clients subscribed to the
|
||||
|
||||
@@ -1,19 +1,14 @@
|
||||
// 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.
|
||||
//
|
||||
// v0.29.1 CS0: Wires api.http permission to BuildHTTPModule with
|
||||
// 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.
|
||||
//
|
||||
// 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
|
||||
// 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.
|
||||
//
|
||||
// The runner is the bridge between the package/permission system
|
||||
@@ -57,13 +52,13 @@ type RunContext struct {
|
||||
type Runner struct {
|
||||
sandbox *Sandbox
|
||||
stores store.Stores
|
||||
packagesDir string // v0.38.0: disk path for load() support
|
||||
packagesDir string
|
||||
notifier NotificationSender // nil = notifications module unavailable
|
||||
connResolver ConnectionResolver // nil = connections module unavailable (v0.38.1)
|
||||
db *sql.DB // nil = db module unavailable
|
||||
dbPostgres bool // true = use $N placeholders; false = use ?
|
||||
allowPrivateIPs bool // v0.38.5: disable SSRF private IP check
|
||||
bus *events.Bus // v0.5.0: event bus for realtime module
|
||||
allowPrivateIPs bool
|
||||
bus *events.Bus
|
||||
}
|
||||
|
||||
// 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.
|
||||
// Required for load() support — scripts are read from disk at runtime.
|
||||
// v0.38.0.
|
||||
func (r *Runner) SetPackagesDir(dir string) {
|
||||
r.packagesDir = dir
|
||||
}
|
||||
@@ -114,7 +108,6 @@ func (r *Runner) SetAllowPrivateIPs(allow bool) {
|
||||
// ExecPackage loads a package's script from disk (primary) or manifest
|
||||
// (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
|
||||
// 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
|
||||
}
|
||||
|
||||
// v0.38.2: per-invocation lib context for lib.load() caching + cycle detection
|
||||
lc := newLibContext()
|
||||
|
||||
// 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).
|
||||
userID := ""
|
||||
teamID := ""
|
||||
@@ -362,7 +353,6 @@ func (r *Runner) buildModulesWithLibCtx(ctx context.Context, packageID string, m
|
||||
}
|
||||
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.
|
||||
if lc != nil {
|
||||
modules["lib"] = BuildLibModule(ctx, r, packageID, rc, lc)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// Package sandbox — sandboxed Starlark interpreter.
|
||||
//
|
||||
// v0.29.0 CS1: Provides execution with timeout, step limits,
|
||||
// captured output, and injectable predeclared modules.
|
||||
//
|
||||
// The sandbox is intentionally restrictive:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package sandbox
|
||||
|
||||
// settings_module.go — v0.2.0
|
||||
// settings_module.go
|
||||
//
|
||||
// The settings module lets extensions read their resolved settings via
|
||||
// the three-tier cascade: global → team → user, respecting the
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// Package sandbox — unicode_scan.go
|
||||
//
|
||||
// v0.5.1: Invisible Unicode scanning gate.
|
||||
//
|
||||
// Defends against GlassWorm (variation selector payloads), Trojan Source
|
||||
// (bidi override attacks / CVE-2021-42574), and other invisible Unicode
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
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,
|
||||
// manage instances, and programmatically advance workflow stages.
|
||||
@@ -27,7 +27,6 @@ import (
|
||||
// BuildWorkflowModule creates the "workflow" Starlark module for a package.
|
||||
// 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;
|
||||
// Starlark builtins for them will be added when the engine interface is
|
||||
// extracted to a shared package to avoid circular imports.
|
||||
|
||||
@@ -42,19 +42,19 @@ type Stores struct {
|
||||
ResourceGrants ResourceGrantStore
|
||||
Notifications NotificationStore
|
||||
NotifPrefs NotificationPreferenceStore
|
||||
Presence PresenceStore // v0.29.0: User online/offline status
|
||||
Health HealthStore // v0.29.0: Provider health window management
|
||||
Connections ConnectionStore // v0.38.1: Extension connection credentials
|
||||
Dependencies DependencyStore // v0.38.2: Library package dependency graph
|
||||
Packages PackageStore // v0.28.7: Unified package registry (surfaces + extensions)
|
||||
Workflows WorkflowStore // v0.26.1: Workflow definitions + stages
|
||||
ExtPermissions ExtensionPermissionStore // v0.29.0: Extension declared/granted capabilities
|
||||
ExtData ExtDataStore // v0.29.2: Extension namespaced table catalog
|
||||
Tickets TicketStore // v0.32.0: WS auth tickets (PG-backed for cross-pod)
|
||||
RateLimits RateLimitStore // v0.32.0: Distributed rate limiting
|
||||
Triggers TriggerStore // v0.2.2: Extension event/webhook triggers
|
||||
ScheduledTasks ScheduledTaskStore // v0.2.2: User-created cron tasks
|
||||
Cluster ClusterStore // v0.6.0: PG-backed cluster node registry (nil on SQLite)
|
||||
Presence PresenceStore
|
||||
Health HealthStore
|
||||
Connections ConnectionStore
|
||||
Dependencies DependencyStore
|
||||
Packages PackageStore
|
||||
Workflows WorkflowStore
|
||||
ExtPermissions ExtensionPermissionStore
|
||||
ExtData ExtDataStore
|
||||
Tickets TicketStore
|
||||
RateLimits RateLimitStore
|
||||
Triggers TriggerStore
|
||||
ScheduledTasks ScheduledTaskStore
|
||||
Cluster ClusterStore
|
||||
}
|
||||
|
||||
// TeamAvailableModel is returned by CatalogStore.ListTeamAvailable.
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
)
|
||||
|
||||
// RateLimitStore manages distributed rate limit counters in Postgres.
|
||||
// v0.32.0: fixed-window counter — upsert per-second bucket, check burst.
|
||||
type RateLimitStore struct{}
|
||||
|
||||
func NewRateLimitStore() *RateLimitStore { return &RateLimitStore{} }
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
)
|
||||
|
||||
// TicketStore manages WS auth tickets in Postgres.
|
||||
// v0.32.0: replaces in-memory sync.Map for cross-pod validation.
|
||||
type TicketStore struct{}
|
||||
|
||||
func NewTicketStore() *TicketStore { return &TicketStore{} }
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
)
|
||||
|
||||
// RateLimitStore manages distributed rate limit counters.
|
||||
// v0.32.0: replaces in-memory token bucket for cross-pod rate limiting.
|
||||
type RateLimitStore interface {
|
||||
// Allow checks if a request is within the rate limit.
|
||||
// key is "{scope}:{identifier}" e.g. "auth:192.168.1.1".
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package store
|
||||
|
||||
// settings_cascade.go — v0.2.0
|
||||
// settings_cascade.go
|
||||
//
|
||||
// Pure functions for three-tier settings resolution:
|
||||
// global (admin) → team → user
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
)
|
||||
|
||||
// RateLimitStore manages rate limit counters in SQLite.
|
||||
// v0.32.0: functional parity for single-process test coverage.
|
||||
type RateLimitStore struct{}
|
||||
|
||||
func NewRateLimitStore() *RateLimitStore { return &RateLimitStore{} }
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
)
|
||||
|
||||
// TicketStore manages WS auth tickets in SQLite.
|
||||
// v0.32.0: functional parity for single-process test coverage.
|
||||
type TicketStore struct{}
|
||||
|
||||
func NewTicketStore() *TicketStore { return &TicketStore{} }
|
||||
|
||||
@@ -3,7 +3,6 @@ package store
|
||||
import "context"
|
||||
|
||||
// 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 {
|
||||
// Issue creates a single-use ticket for the given user.
|
||||
// Returns the opaque ticket ID (128-bit hex).
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// Package triggers — engine.go
|
||||
//
|
||||
// v0.2.2: Core trigger engine. Manages extension-declared triggers
|
||||
// (event bus subscriptions + webhook receivers) and user-created
|
||||
// scheduled tasks (cron). All three converge to sandbox.Runner.CallEntryPoint.
|
||||
package triggers
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
// v0.28.0: Added RunID, TokensUsed fields (D1 audit fix).
|
||||
package webhook
|
||||
|
||||
import (
|
||||
|
||||
@@ -243,7 +243,6 @@ describe('init() profile gate', () => {
|
||||
});
|
||||
|
||||
// ── 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,
|
||||
// the REST client's 401 handler must NOT redirect to /login (we're
|
||||
// already there). boot() handles 401 gracefully on its own.
|
||||
|
||||
@@ -5,7 +5,6 @@ const path = require('path');
|
||||
const { createBrowserContext, loadSource, SRC } = require('./helpers');
|
||||
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.
|
||||
const EXTENSIONS_EXISTS = fs.existsSync(path.join(SRC, 'extensions.js'));
|
||||
const maybeDescribe = EXTENSIONS_EXISTS ? describe : describe.skip;
|
||||
|
||||
@@ -5,7 +5,6 @@ const path = require('path');
|
||||
const { createBrowserContext, loadSource, SRC } = require('./helpers');
|
||||
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.
|
||||
const EXTENSIONS_EXISTS = fs.existsSync(path.join(SRC, 'extensions.js'));
|
||||
const maybeDescribe = EXTENSIONS_EXISTS ? describe : describe.skip;
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
// simulated browser environment so tests run
|
||||
// against the ACTUAL frontend code.
|
||||
//
|
||||
// v0.37.10: Removed loadAppModules (api.js + app.js deleted).
|
||||
// ==========================================
|
||||
|
||||
const fs = require('fs');
|
||||
|
||||
@@ -7,13 +7,9 @@
|
||||
// policy exists but the frontend doesn't
|
||||
// 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.
|
||||
// v0.37.10: Legacy SPA source audit removed (ui-core.js, app.js,
|
||||
// pages.js, settings-handlers.js, ui-admin.js all deleted).
|
||||
// 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
|
||||
// at the bottom of this file cover the same policy keys.
|
||||
//
|
||||
@@ -130,7 +126,6 @@ describe('Team member dropdown population', () => {
|
||||
});
|
||||
|
||||
// ── Kernel surface template ──────────────────
|
||||
// v0.1.0: Chat surface removed. Verify admin mount exists and
|
||||
// old SPA scaffold is gone.
|
||||
|
||||
describe('Kernel surface templates (v0.1.0)', () => {
|
||||
@@ -153,7 +148,6 @@ describe('Kernel surface templates (v0.1.0)', () => {
|
||||
});
|
||||
|
||||
// ── Admin Preact surface ─────────────────────
|
||||
// v0.37.6: Admin surface handles settings save via Preact.
|
||||
// Verify the admin surface has policy key handling.
|
||||
|
||||
describe('Admin Preact surface handles settings', () => {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
// Bug badge that shows error count. Subscribes to engine
|
||||
// for reactive updates.
|
||||
//
|
||||
// v0.37.18: Preact rebuild from debug.js _updateBadge().
|
||||
|
||||
const html = window.html;
|
||||
const { useEffect } = window.hooks;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
// Filterable console log display with type coloring,
|
||||
// elapsed timestamps, and auto-scroll.
|
||||
//
|
||||
// v0.37.18: Preact rebuild from debug.js _renderConsoleTab().
|
||||
|
||||
const html = window.html;
|
||||
const { useState, useMemo, useRef, useEffect } = window.hooks;
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
// Singleton — init() must run before SDK boot to capture early errors.
|
||||
// UI-agnostic: Preact components subscribe via .subscribe().
|
||||
//
|
||||
// v0.37.18: Extracted from debug.js (v0.37.14).
|
||||
//
|
||||
// Exports: debugEngine (singleton)
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
// Global overlay — not a routed surface.
|
||||
// Mounts via mountDebugModal(el, engine).
|
||||
//
|
||||
// v0.37.18: Preact rebuild of debug modal (CR P2-5).
|
||||
|
||||
import { debugEngine } from './engine.js';
|
||||
import { ConsoleTab } from './console-tab.js';
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
// Fetch log with expandable request/response details.
|
||||
// Newest entries first.
|
||||
//
|
||||
// v0.37.18: Preact rebuild from debug.js _renderNetworkTab().
|
||||
|
||||
const html = window.html;
|
||||
const { useState } = window.hooks;
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
// command history, and collapsible JSON output.
|
||||
// Admin-gated OR ?debug=1 URL param.
|
||||
//
|
||||
// v0.37.18: Preact rebuild from repl.js (v0.37.14).
|
||||
|
||||
const html = window.html;
|
||||
const { useState, useRef, useEffect, useCallback } = window.hooks;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// ==========================================
|
||||
// Displays current application state snapshot as formatted JSON.
|
||||
//
|
||||
// v0.37.18: Preact rebuild from debug.js _renderStateTab().
|
||||
|
||||
const html = window.html;
|
||||
const { useMemo } = window.hooks;
|
||||
|
||||
@@ -239,7 +239,6 @@ export function createDomains(restClient) {
|
||||
del: (id) => rc.del(`/api/v1/admin/extensions/${id}`),
|
||||
},
|
||||
|
||||
// v0.38.1: Global connections
|
||||
connections: crud(rc, '/api/v1/admin/connections'),
|
||||
|
||||
packages: {
|
||||
@@ -251,20 +250,18 @@ export function createDomains(restClient) {
|
||||
del: (id) => rc.del(`/api/v1/admin/packages/${id}`),
|
||||
settings: (id) => rc.get(`/api/v1/admin/packages/${id}/settings`),
|
||||
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
|
||||
consumers: (id) => rc.get(`/api/v1/admin/packages/${id}/consumers`), // v0.38.2
|
||||
update: (id, file) => rc.upload(`/api/v1/admin/packages/${id}/update`, file), // v0.5.4
|
||||
exportPkg: (id) => `/api/v1/admin/packages/${id}/export`, // v0.5.4 (URL for window.open)
|
||||
dependencies: (id) => rc.get(`/api/v1/admin/packages/${id}/dependencies`),
|
||||
consumers: (id) => rc.get(`/api/v1/admin/packages/${id}/consumers`),
|
||||
update: (id, file) => rc.upload(`/api/v1/admin/packages/${id}/update`, file),
|
||||
exportPkg: (id) => `/api/v1/admin/packages/${id}/export`,
|
||||
registry: () => rc.get('/api/v1/admin/packages/registry'),
|
||||
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`),
|
||||
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`, {}),
|
||||
grantAllPerms: (id) => rc.post(`/api/v1/admin/extensions/${id}/permissions/grant-all`, {}),
|
||||
},
|
||||
|
||||
// v0.38.2: Full dependency graph
|
||||
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}`),
|
||||
},
|
||||
|
||||
// v0.6.1: Backup/Restore
|
||||
backup: {
|
||||
create: (opts) => rc.post('/api/v1/admin/backup' + _qs(opts)),
|
||||
list: () => rc.get('/api/v1/admin/backups'),
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* Reads globals:
|
||||
* __SECTION__ — active section name (string)
|
||||
* __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).
|
||||
* All 24+ sections are native Preact components loaded lazily.
|
||||
@@ -72,7 +72,6 @@ const sectionModules = {
|
||||
audit: () => import(`./audit.js${_v}`),
|
||||
};
|
||||
|
||||
// v0.38.3: Register dynamic section loaders for extension config sections
|
||||
for (const cs of _configSections) {
|
||||
const pkgId = cs.package_id;
|
||||
const component = cs.component || 'js/config.js';
|
||||
|
||||
@@ -46,7 +46,7 @@ export default function PackagesSection() {
|
||||
const [registryPkgs, setRegistryPkgs] = useState([]);
|
||||
const [registryLoading, setRegistryLoading] = 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 BASE = window.__BASE__ || '';
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* Reads globals:
|
||||
* __SECTION__ — active section name (string)
|
||||
* __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).
|
||||
* All sections are native Preact components loaded lazily.
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* Reads globals:
|
||||
* __SECTION__ — active section name (string)
|
||||
* __BASE__ — base path
|
||||
* __CONFIG_SECTIONS__ — v0.38.3: extension config sections (array|null)
|
||||
* __CONFIG_SECTIONS__
|
||||
*
|
||||
* Layout: topbar (back + team name) + sidebar nav + content area.
|
||||
* All 10+ sections are native Preact components loaded lazily.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Team Admin > Workflows — v0.37.15 rewrite
|
||||
* Team Admin > Workflows
|
||||
*
|
||||
* Tab layout: Workflows | Assignments | Monitor
|
||||
* - 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)) : ''
|
||||
);
|
||||
|
||||
// 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 [requiredRole, setRequiredRole] = useState(sc.required_role || '');
|
||||
const [valApprovals, setValApprovals] = useState(sc.validation?.required_approvals || '');
|
||||
|
||||
Reference in New Issue
Block a user