@@ -49,6 +49,7 @@ type Server struct {
4949 teams map [string ]* team.Team
5050 teamsMu sync.RWMutex
5151 agentsDir string
52+ agentsPath string // For local files: specific file path to reload (instead of scanning agentsDir)
5253 rootFS * os.Root
5354}
5455
@@ -67,6 +68,13 @@ func WithAgentsDir(dir string) Opt {
6768 }
6869}
6970
71+ func WithAgentsPath (agentPath string ) Opt {
72+ return func (s * Server ) error {
73+ s .agentsPath = agentPath
74+ return nil
75+ }
76+ }
77+
7078func New (sessionStore session.Store , runConfig * config.RuntimeConfig , teams map [string ]* team.Team , opts ... Opt ) (* Server , error ) {
7179 e := echo .New ()
7280 e .Use (middleware .CORS ())
@@ -156,6 +164,12 @@ func (s *Server) Serve(ctx context.Context, ln net.Listener) error {
156164 return nil
157165}
158166
167+ // hasAgentsDir returns true if the server has a local agents directory.
168+ // Returns false for OCI reference-based servers, which load from memory.
169+ func (s * Server ) hasAgentsDir () bool {
170+ return s .agentsDir != "" && s .rootFS != nil
171+ }
172+
159173// getTeam retrieves a team by key with read lock
160174func (s * Server ) getTeam (key string ) (* team.Team , bool ) {
161175 s .teamsMu .RLock ()
@@ -259,6 +273,10 @@ func (s *Server) getAgentConfigYAML(c echo.Context) error {
259273}
260274
261275func (s * Server ) editAgentConfigYAML (c echo.Context ) error {
276+ if ! s .hasAgentsDir () {
277+ return echo .NewHTTPError (http .StatusMethodNotAllowed , "write operations not supported for read-only OCI references" )
278+ }
279+
262280 agentID := c .Param ("id" )
263281 p := addYamlExt (agentID )
264282
@@ -311,6 +329,10 @@ func (s *Server) editAgentConfigYAML(c echo.Context) error {
311329}
312330
313331func (s * Server ) editAgentConfig (c echo.Context ) error {
332+ if ! s .hasAgentsDir () {
333+ return echo .NewHTTPError (http .StatusMethodNotAllowed , "write operations not supported for read-only OCI references" )
334+ }
335+
314336 var req api.EditAgentConfigRequest
315337 if err := c .Bind (& req ); err != nil {
316338 return echo .NewHTTPError (http .StatusBadRequest , "invalid request body" )
@@ -397,6 +419,10 @@ func (s *Server) editAgentConfig(c echo.Context) error {
397419}
398420
399421func (s * Server ) createAgent (c echo.Context ) error {
422+ if ! s .hasAgentsDir () {
423+ return echo .NewHTTPError (http .StatusMethodNotAllowed , "write operations not supported for read-only OCI references" )
424+ }
425+
400426 var req api.CreateAgentRequest
401427 if err := c .Bind (& req ); err != nil {
402428 return echo .NewHTTPError (http .StatusBadRequest , "invalid request body" )
@@ -427,6 +453,10 @@ func (s *Server) createAgent(c echo.Context) error {
427453}
428454
429455func (s * Server ) createAgentConfig (c echo.Context ) error {
456+ if ! s .hasAgentsDir () {
457+ return echo .NewHTTPError (http .StatusMethodNotAllowed , "write operations not supported for read-only OCI references" )
458+ }
459+
430460 var req api.CreateAgentConfigRequest
431461 if err := c .Bind (& req ); err != nil {
432462 return echo .NewHTTPError (http .StatusBadRequest , "invalid request body" )
@@ -519,6 +549,10 @@ func (s *Server) createAgentConfig(c echo.Context) error {
519549}
520550
521551func (s * Server ) importAgent (c echo.Context ) error {
552+ if ! s .hasAgentsDir () {
553+ return echo .NewHTTPError (http .StatusMethodNotAllowed , "write operations not supported for read-only OCI references" )
554+ }
555+
522556 var req api.ImportAgentRequest
523557 if err := c .Bind (& req ); err != nil {
524558 return echo .NewHTTPError (http .StatusBadRequest , "invalid request body" )
@@ -613,6 +647,10 @@ func (s *Server) importAgent(c echo.Context) error {
613647}
614648
615649func (s * Server ) exportAgents (c echo.Context ) error {
650+ if ! s .hasAgentsDir () {
651+ return echo .NewHTTPError (http .StatusMethodNotAllowed , "write operations not supported for read-only OCI references" )
652+ }
653+
616654 // Create zip file in the agents directory
617655 zipFileName := fmt .Sprintf ("agents_export_%d.zip" , time .Now ().Unix ())
618656 zipPath := filepath .Join (s .agentsDir , zipFileName )
@@ -793,6 +831,10 @@ func (s *Server) pushAgent(c echo.Context) error {
793831}
794832
795833func (s * Server ) deleteAgent (c echo.Context ) error {
834+ if ! s .hasAgentsDir () {
835+ return echo .NewHTTPError (http .StatusMethodNotAllowed , "write operations not supported for read-only OCI references" )
836+ }
837+
796838 var req api.DeleteAgentRequest
797839 if err := c .Bind (& req ); err != nil {
798840 return echo .NewHTTPError (http .StatusBadRequest , "invalid request body" )
@@ -851,8 +893,12 @@ func (s *Server) deleteAgent(c echo.Context) error {
851893
852894func (s * Server ) getAgents (c echo.Context ) error {
853895 // Refresh agents from disk to get the latest configurations
854- if err := s .refreshAgentsFromDisk (c .Request ().Context ()); err != nil {
855- slog .Error ("Failed to refresh agents from disk" , "error" , err )
896+ // Only refresh for local files/dirs (hasAgentsDir == true)
897+ // OCI refs are immutable and loaded into memory, updates handled via ReloadTeams()
898+ if s .hasAgentsDir () {
899+ if err := s .refreshAgentsFromDisk (c .Request ().Context ()); err != nil {
900+ slog .Error ("Failed to refresh agents from disk" , "error" , err )
901+ }
856902 }
857903
858904 // DO NOT, under any circumstance, replace this with "var agents []api.Agent",
@@ -904,13 +950,21 @@ func (s *Server) getAgents(c echo.Context) error {
904950}
905951
906952func (s * Server ) refreshAgentsFromDisk (ctx context.Context ) error {
907- if s .agentsDir == "" {
953+ // Use agentsPath if set (for specific files), otherwise fall back to agentsDir
954+ // Note: OCI references are handled via ReloadTeams() and never call this function
955+ // (see getAgents() which skips refresh for OCI refs)
956+ pathToLoad := s .agentsPath
957+ if pathToLoad == "" {
958+ pathToLoad = s .agentsDir
959+ }
960+
961+ if pathToLoad == "" {
908962 return nil
909963 }
910964
911- slog .Debug ("Refreshing agents from disk" , "agentsDir " , s . agentsDir )
965+ slog .Debug ("Refreshing agents from disk" , "path " , pathToLoad )
912966
913- newTeams , err := teamloader .LoadTeams (ctx , s . agentsDir , s .runConfig )
967+ newTeams , err := teamloader .LoadTeams (ctx , pathToLoad , s .runConfig )
914968 if err != nil {
915969 return fmt .Errorf ("failed to load teams: %w" , err )
916970 }
@@ -1147,9 +1201,28 @@ func (s *Server) runAgent(c echo.Context) error {
11471201 rc := s .runConfig
11481202 rc .WorkingDir = sess .WorkingDir
11491203
1150- t , err := teamloader .Load (c .Request ().Context (), filepath .Join (s .agentsDir , p ), rc )
1151- if err != nil {
1152- return echo .NewHTTPError (http .StatusInternalServerError , "failed to load agent for session" )
1204+ // Load team - either reload from disk (local) or use in-memory team (OCI refs)
1205+ var t * team.Team
1206+
1207+ if s .hasAgentsDir () {
1208+ // Has local agents path or directory: reload from disk to pick up changes
1209+ loadPath := s .agentsPath
1210+ if loadPath == "" {
1211+ loadPath = filepath .Join (s .agentsDir , p )
1212+ }
1213+
1214+ var loadErr error
1215+ t , loadErr = teamloader .Load (c .Request ().Context (), loadPath , rc )
1216+ if loadErr != nil {
1217+ return echo .NewHTTPError (http .StatusInternalServerError , "failed to load agent for session" )
1218+ }
1219+ } else {
1220+ // No local directory: use the already-loaded team from memory (OCI refs)
1221+ var exists bool
1222+ t , exists = s .getTeam (p )
1223+ if ! exists {
1224+ return echo .NewHTTPError (http .StatusNotFound , fmt .Sprintf ("agent not found: %s" , agentFilename ))
1225+ }
11531226 }
11541227
11551228 agent , err := t .Agent (currentAgent )
0 commit comments