From 59377e08005626ada90cb9d428b857fc589aaba9 Mon Sep 17 00:00:00 2001 From: Steve Yoon Date: Tue, 7 Apr 2026 13:17:09 -0400 Subject: [PATCH 1/7] cloud-agents environment support --- cmd/lk/agent.go | 49 +++++++++++++++++++++++++++++++++++++++++++------ go.mod | 10 +++++++--- go.sum | 16 ++++++---------- 3 files changed, 56 insertions(+), 19 deletions(-) diff --git a/cmd/lk/agent.go b/cmd/lk/agent.go index 84b6f462..93f427a9 100644 --- a/cmd/lk/agent.go +++ b/cmd/lk/agent.go @@ -109,6 +109,11 @@ var ( agentPrebuiltImageTarFlag = &cli.StringFlag{ Name: "image-tar", Usage: "Pre-built image from an OCI tar file (e.g. ./image.tar). No Docker daemon required.", + + envFlag = &cli.StringSliceFlag{ + Name: "env", + Usage: "Deployment environment(s). For create/deploy, specifies the target environment (defaults to 'production'). For update-secrets, assigns environment(s) to the secret. Can be specified multiple times (e.g. --env staging --env production).", + Required: false, } skipSDKCheckFlag = &cli.BoolFlag{ @@ -182,6 +187,7 @@ var ( ignoreEmptySecretsFlag, silentFlag, regionFlag, + envFlag, skipSDKCheckFlag, agentPrebuiltImageFlag, agentPrebuiltImageTarFlag, @@ -228,6 +234,7 @@ var ( secretsMountFlag, silentFlag, regionFlag, + envFlag, ignoreEmptySecretsFlag, skipSDKCheckFlag, agentPrebuiltImageFlag, @@ -353,6 +360,7 @@ var ( secretsFileFlag, secretsMountFlag, ignoreEmptySecretsFlag, + envFlag, idFlag(false), &cli.BoolFlag{ Name: "overwrite", @@ -633,12 +641,17 @@ func createAgent(ctx context.Context, cmd *cli.Command) error { return err } + environment := "production" + if cmd.IsSet("env") { + environment = cmd.StringSlice("env")[0] + } + buildContext, cancel := context.WithTimeout(ctx, buildTimeout) defer cancel() regions := []string{region} excludeFiles := []string{fmt.Sprintf("**/%s", config.LiveKitTOMLFile)} - resp, err := agentsClient.CreateAgent(buildContext, os.DirFS(workingDir), secrets, regions, excludeFiles, os.Stderr) + resp, err := agentsClient.CreateAgent(buildContext, os.DirFS(workingDir), secrets, regions, environment, excludeFiles, os.Stderr) if err != nil { if errors.Is(err, context.DeadlineExceeded) { return fmt.Errorf("build timed out possibly due to large image size") @@ -797,8 +810,15 @@ func deployAgent(ctx context.Context, cmd *cli.Command) error { } } + environment := "production" + if cmd.IsSet("env") { + environment = cmd.StringSlice("env")[0] + } + + buildContext, cancel := context.WithTimeout(ctx, buildTimeout) + defer cancel() excludeFiles := []string{fmt.Sprintf("**/%s", config.LiveKitTOMLFile)} - if err := agentsClient.DeployAgent(buildContext, agentId, os.DirFS(workingDir), secrets, excludeFiles, os.Stderr); err != nil { + if err := agentsClient.DeployAgent(buildContext, agentId, os.DirFS(workingDir), secrets, environment, excludeFiles, os.Stderr); err != nil { if twerr, ok := err.(twirp.Error); ok { return fmt.Errorf("unable to deploy agent: %s", twerr.Msg()) } @@ -890,6 +910,7 @@ func getAgentStatus(ctx context.Context, cmd *cli.Command) error { agent.AgentId, agent.Version, regionalAgent.Region, + regionalAgent.Environment, regionalAgent.Status, fmt.Sprintf("%s / %s", curCPU, regionalAgent.CpuLimit), fmt.Sprintf("%s / %s", curMem, memLimit), @@ -900,7 +921,7 @@ func getAgentStatus(ctx context.Context, cmd *cli.Command) error { } t := util.CreateTable(). - Headers("ID", "Version", "Region", "Status", "CPU", "Mem", "Replicas", "Deployed At"). + Headers("ID", "Version", "Region", "Environment", "Status", "CPU", "Mem", "Replicas", "Deployed At"). Rows(rows...) fmt.Println(t) @@ -1175,20 +1196,25 @@ func listAgents(ctx context.Context, cmd *cli.Command) error { var rows [][]string for _, agent := range items { var regions []string + var environments []string for _, regionalAgent := range agent.AgentDeployments { regions = append(regions, regionalAgent.Region) + if !slices.Contains(environments, regionalAgent.Environment) { + environments = append(environments, regionalAgent.Environment) + } } rows = append(rows, []string{ agent.AgentId, agent.AgentName, strings.Join(regions, ","), + strings.Join(environments, ","), agent.Version, agent.DeployedAt.AsTime().Format(time.RFC3339), }) } t := util.CreateTable(). - Headers("ID", "Dispatch Name", "Regions", "Version", "Deployed At"). + Headers("ID", "Dispatch Name", "Regions", "Environment", "Version", "Deployed At"). Rows(rows...) fmt.Println(t) @@ -1215,14 +1241,18 @@ func listAgentSecrets(ctx context.Context, cmd *cli.Command) error { // TODO (steveyoon): show secret.Kind.String() once cloud-agents is released table := util.CreateTable(). - Headers("Name", "Created At", "Updated At") + Headers("Name", "Environments", "Created At", "Updated At") for _, secret := range secrets.Secrets { // NOTE: Maybe these should be omitted on the server side? if slices.Contains(ignoredSecrets, secret.Name) { continue } - table.Row(secret.Name, secret.CreatedAt.AsTime().Format(time.RFC3339), secret.UpdatedAt.AsTime().Format(time.RFC3339)) + envs := strings.Join(secret.Environments, ", ") + if envs == "" { + envs = "(all)" + } + table.Row(secret.Name, envs, secret.CreatedAt.AsTime().Format(time.RFC3339), secret.UpdatedAt.AsTime().Format(time.RFC3339)) } fmt.Println(table) @@ -1240,6 +1270,13 @@ func updateAgentSecrets(ctx context.Context, cmd *cli.Command) error { return err } + if cmd.IsSet("env") { + envs := cmd.StringSlice("env") + for _, s := range secrets { + s.Environments = envs + } + } + var confirmOverwrite bool if cmd.Bool("overwrite") { confirmOverwrite = SkipPrompts(cmd) diff --git a/go.mod b/go.mod index 28027440..2592647c 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,7 @@ require ( github.com/pelletier/go-toml v1.9.5 github.com/pion/rtcp v1.2.16 github.com/pion/rtp v1.10.1 - github.com/pion/webrtc/v4 v4.2.9 + github.com/pion/webrtc/v4 v4.2.11 github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.11.1 @@ -40,6 +40,8 @@ require ( k8s.io/apimachinery v0.34.1 ) +replace github.com/livekit/protocol => /Users/syoon/src/protocol + require ( buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260209202127-80ab13bee0bf.1 // indirect buf.build/go/protovalidate v1.1.3 // indirect @@ -189,12 +191,12 @@ require ( github.com/pierrec/lz4/v4 v4.1.26 // indirect github.com/pion/datachannel v1.6.0 // indirect github.com/pion/dtls/v3 v3.1.2 // indirect - github.com/pion/ice/v4 v4.2.1 // indirect + github.com/pion/ice/v4 v4.2.2 // indirect github.com/pion/interceptor v0.1.44 // indirect github.com/pion/logging v0.2.4 // indirect github.com/pion/mdns/v2 v2.1.0 // indirect github.com/pion/randutil v0.1.0 // indirect - github.com/pion/sctp v1.9.3 // indirect + github.com/pion/sctp v1.9.4 // indirect github.com/pion/sdp/v3 v3.0.18 // indirect github.com/pion/srtp/v3 v3.0.10 // indirect github.com/pion/stun/v3 v3.1.1 // indirect @@ -267,3 +269,5 @@ require ( mvdan.cc/sh/v3 v3.13.1 // indirect sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect ) + +replace github.com/livekit/server-sdk-go/v2 => /Users/syoon/src/server-sdk-go diff --git a/go.sum b/go.sum index 18dd9cfa..636ce978 100644 --- a/go.sum +++ b/go.sum @@ -366,12 +366,8 @@ github.com/livekit/mageutil v0.0.0-20250511045019-0f1ff63f7731 h1:9x+U2HGLrSw5AT github.com/livekit/mageutil v0.0.0-20250511045019-0f1ff63f7731/go.mod h1:Rs3MhFwutWhGwmY1VQsygw28z5bWcnEYmS1OG9OxjOQ= github.com/livekit/mediatransportutil v0.0.0-20260309115634-0e2e24b36ee8 h1:coWig9fKxdb/nwOaIoGUUAogso12GblAJh/9SA9hcxk= github.com/livekit/mediatransportutil v0.0.0-20260309115634-0e2e24b36ee8/go.mod h1:RCd46PT+6sEztld6XpkCrG1xskb0u3SqxIjy4G897Ss= -github.com/livekit/protocol v1.45.9-0.20260508203311-a249893d6a5d h1:mE0/AjgGnvsF3q0ipiNGe1HZ3CUKUG7Y+zqey/2JrBE= -github.com/livekit/protocol v1.45.9-0.20260508203311-a249893d6a5d/go.mod h1:KEPIJ/ZdMFQ9tmmfv/uT9TjQEuEcZupCZBabuRGEC1k= github.com/livekit/psrpc v0.7.1 h1:ms37az0QTD3UXIWuUC5D/SkmKOlRMVRsI261eBWu/Vw= github.com/livekit/psrpc v0.7.1/go.mod h1:bZ4iHFQptTkbPnB0LasvRNu/OBYXEu1NA6O5BMFo9kk= -github.com/livekit/server-sdk-go/v2 v2.16.2 h1:eQe24cka3X+5zUivezyL72nwtAJTWFXgibeiyJ/Jm+Y= -github.com/livekit/server-sdk-go/v2 v2.16.2/go.mod h1:/HOUG9AXJeCbMCdtw0dr37AB+3xXUlj/OLeXS/0p7rA= github.com/lucasb-eyer/go-colorful v1.4.0 h1:UtrWVfLdarDgc44HcS7pYloGHJUjHV/4FwW4TvVgFr4= github.com/lucasb-eyer/go-colorful v1.4.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/magefile/mage v1.17.0 h1:dS4tkq997Ism03akafC8509iqDjeE7TNTexI25Y7sXM= @@ -452,8 +448,8 @@ github.com/pion/datachannel v1.6.0 h1:XecBlj+cvsxhAMZWFfFcPyUaDZtd7IJvrXqlXD/53i github.com/pion/datachannel v1.6.0/go.mod h1:ur+wzYF8mWdC+Mkis5Thosk+u/VOL287apDNEbFpsIk= github.com/pion/dtls/v3 v3.1.2 h1:gqEdOUXLtCGW+afsBLO0LtDD8GnuBBjEy6HRtyofZTc= github.com/pion/dtls/v3 v3.1.2/go.mod h1:Hw/igcX4pdY69z1Hgv5x7wJFrUkdgHwAn/Q/uo7YHRo= -github.com/pion/ice/v4 v4.2.1 h1:XPRYXaLiFq3LFDG7a7bMrmr3mFr27G/gtXN3v/TVfxY= -github.com/pion/ice/v4 v4.2.1/go.mod h1:2quLV1S5v1tAx3VvAJaH//KGitRXvo4RKlX6D3tnN+c= +github.com/pion/ice/v4 v4.2.2 h1:dQJzzcgTFHDYyV3BoCfjPeX+JEtr58BWPi4PGyo6Vjg= +github.com/pion/ice/v4 v4.2.2/go.mod h1:2quLV1S5v1tAx3VvAJaH//KGitRXvo4RKlX6D3tnN+c= github.com/pion/interceptor v0.1.44 h1:sNlZwM8dWXU9JQAkJh8xrarC0Etn8Oolcniukmuy0/I= github.com/pion/interceptor v0.1.44/go.mod h1:4atVlBkcgXuUP+ykQF0qOCGU2j7pQzX2ofvPRFsY5RY= github.com/pion/logging v0.2.4 h1:tTew+7cmQ+Mc1pTBLKH2puKsOvhm32dROumOZ655zB8= @@ -466,8 +462,8 @@ github.com/pion/rtcp v1.2.16 h1:fk1B1dNW4hsI78XUCljZJlC4kZOPk67mNRuQ0fcEkSo= github.com/pion/rtcp v1.2.16/go.mod h1:/as7VKfYbs5NIb4h6muQ35kQF/J0ZVNz2Z3xKoCBYOo= github.com/pion/rtp v1.10.1 h1:xP1prZcCTUuhO2c83XtxyOHJteISg6o8iPsE2acaMtA= github.com/pion/rtp v1.10.1/go.mod h1:rF5nS1GqbR7H/TCpKwylzeq6yDM+MM6k+On5EgeThEM= -github.com/pion/sctp v1.9.3 h1:tjuOX9K/U4udMR2I7QDqr4sLE0tFzegtou7OF4a7p8A= -github.com/pion/sctp v1.9.3/go.mod h1:N20Dq6LY+JvJDAh9VVh1JELngb2rQ8dPgds5yBWiPgw= +github.com/pion/sctp v1.9.4 h1:cMxEu0F5tbP4qH07bKf1Zjf4rUih9LIo0qQt424e258= +github.com/pion/sctp v1.9.4/go.mod h1:N20Dq6LY+JvJDAh9VVh1JELngb2rQ8dPgds5yBWiPgw= github.com/pion/sdp/v3 v3.0.18 h1:l0bAXazKHpepazVdp+tPYnrsy9dfh7ZbT8DxesH5ZnI= github.com/pion/sdp/v3 v3.0.18/go.mod h1:ZREGo6A9ZygQ9XkqAj5xYCQtQpif0i6Pa81HOiAdqQ8= github.com/pion/srtp/v3 v3.0.10 h1:tFirkpBb3XccP5VEXLi50GqXhv5SKPxqrdlhDCJlZrQ= @@ -480,8 +476,8 @@ github.com/pion/transport/v4 v4.0.1 h1:sdROELU6BZ63Ab7FrOLn13M6YdJLY20wldXW2Cu2k github.com/pion/transport/v4 v4.0.1/go.mod h1:nEuEA4AD5lPdcIegQDpVLgNoDGreqM/YqmEx3ovP4jM= github.com/pion/turn/v4 v4.1.4 h1:EU11yMXKIsK43FhcUnjLlrhE4nboHZq+TXBIi3QpcxQ= github.com/pion/turn/v4 v4.1.4/go.mod h1:ES1DXVFKnOhuDkqn9hn5VJlSWmZPaRJLyBXoOeO/BmQ= -github.com/pion/webrtc/v4 v4.2.9 h1:DZIh1HAhPIL3RvwEDFsmL5hfPSLEpxsQk9/Jir2vkJE= -github.com/pion/webrtc/v4 v4.2.9/go.mod h1:9EmLZve0H76eTzf8v2FmchZ6tcBXtDgpfTEu+drW6SY= +github.com/pion/webrtc/v4 v4.2.11 h1:QUX1QZKlNIn4O7U5JxLPGP0sV5RTncZkzu9SPR3jVNU= +github.com/pion/webrtc/v4 v4.2.11/go.mod h1:s/rAiyy77GyRFrZMx+Ls6aua26dIBPudH8/ZHYbIRWY= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= From d8d89efc01e5ef3689d8d76f3794d118a1f30cee Mon Sep 17 00:00:00 2001 From: Steve Yoon Date: Tue, 14 Apr 2026 12:32:28 -0400 Subject: [PATCH 2/7] remove env from create agent --- cmd/lk/agent.go | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/cmd/lk/agent.go b/cmd/lk/agent.go index 93f427a9..bbf9dec0 100644 --- a/cmd/lk/agent.go +++ b/cmd/lk/agent.go @@ -306,6 +306,7 @@ var ( Flags: []cli.Flag{ idFlag(false), logTypeFlag, + envFlag, }, ArgsUsage: "[working-dir]", }, @@ -641,17 +642,12 @@ func createAgent(ctx context.Context, cmd *cli.Command) error { return err } - environment := "production" - if cmd.IsSet("env") { - environment = cmd.StringSlice("env")[0] - } - buildContext, cancel := context.WithTimeout(ctx, buildTimeout) defer cancel() regions := []string{region} excludeFiles := []string{fmt.Sprintf("**/%s", config.LiveKitTOMLFile)} - resp, err := agentsClient.CreateAgent(buildContext, os.DirFS(workingDir), secrets, regions, environment, excludeFiles, os.Stderr) + resp, err := agentsClient.CreateAgent(buildContext, os.DirFS(workingDir), secrets, regions, excludeFiles, os.Stderr) if err != nil { if errors.Is(err, context.DeadlineExceeded) { return fmt.Errorf("build timed out possibly due to large image size") @@ -685,7 +681,7 @@ func createAgent(ctx context.Context, cmd *cli.Command) error { return err } else if viewLogs { fmt.Println("Tailing runtime logs...safe to exit at any time") - return agentsClient.StreamLogs(ctx, "deploy", lkConfig.Agent.ID, os.Stdout, resp.ServerRegions[0]) + return agentsClient.StreamLogs(ctx, "deploy", lkConfig.Agent.ID, "", os.Stdout, resp.ServerRegions[0]) } } return nil @@ -1043,7 +1039,11 @@ func getLogs(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("no agent deployments found") } - return agentsClient.StreamLogs(ctx, cmd.String("log-type"), agentID, os.Stdout, response.Agents[0].AgentDeployments[0].ServerRegion) + var agentEnvironment string + if envs := cmd.StringSlice("env"); len(envs) > 0 { + agentEnvironment = envs[0] + } + return agentsClient.StreamLogs(ctx, cmd.String("log-type"), agentID, agentEnvironment, os.Stdout, response.Agents[0].AgentDeployments[0].ServerRegion) } func deleteAgent(ctx context.Context, cmd *cli.Command) error { @@ -1195,13 +1195,16 @@ func listAgents(ctx context.Context, cmd *cli.Command) error { var rows [][]string for _, agent := range items { - var regions []string + // Determine region: use production deployment's region as the canonical region. + var region string var environments []string for _, regionalAgent := range agent.AgentDeployments { - regions = append(regions, regionalAgent.Region) if !slices.Contains(environments, regionalAgent.Environment) { environments = append(environments, regionalAgent.Environment) } + if region == "" || regionalAgent.Environment == "production" { + region = regionalAgent.Region + } } rows = append(rows, []string{ agent.AgentId, From 1d488521790f79549c3d8c46db03c810b0c7f1ae Mon Sep 17 00:00:00 2001 From: Steve Yoon Date: Fri, 17 Apr 2026 16:15:23 -0400 Subject: [PATCH 3/7] support environment-scoped delete and draining status --- cmd/lk/agent.go | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/cmd/lk/agent.go b/cmd/lk/agent.go index bbf9dec0..d8922c14 100644 --- a/cmd/lk/agent.go +++ b/cmd/lk/agent.go @@ -312,13 +312,14 @@ var ( }, { Name: "delete", - Usage: "Delete an agent", + Usage: "Delete an agent or a specific environment", Before: createAgentClient, Action: deleteAgent, Aliases: []string{"destroy"}, Flags: []cli.Flag{ silentFlag, idFlag(false), + envFlag, }, ArgsUsage: "[working-dir]", }, @@ -810,6 +811,7 @@ func deployAgent(ctx context.Context, cmd *cli.Command) error { if cmd.IsSet("env") { environment = cmd.StringSlice("env")[0] } + fmt.Printf("Using environment [%s]\n", util.Accented(environment)) buildContext, cancel := context.WithTimeout(ctx, buildTimeout) defer cancel() @@ -902,9 +904,13 @@ func getAgentStatus(ctx context.Context, cmd *cli.Command) error { logger.Errorw("error parsing mem req", err) } + version := regionalAgent.Version + if version == "" { + version = agent.Version + } rows = append(rows, []string{ agent.AgentId, - agent.Version, + version, regionalAgent.Region, regionalAgent.Environment, regionalAgent.Status, @@ -1053,12 +1059,26 @@ func deleteAgent(ctx context.Context, cmd *cli.Command) error { return err } + var environment string + if envs := cmd.StringSlice("env"); len(envs) > 0 { + environment = envs[0] + } + + confirmMsg := fmt.Sprintf("Are you sure you want to delete agent [%s]?", agentID) + deletingMsg := "Deleting agent [" + util.Accented(agentID) + "]" + deletedMsg := fmt.Sprintf("Deleted agent [%s]", util.Accented(agentID)) + if environment != "" { + confirmMsg = fmt.Sprintf("Are you sure you want to delete environment [%s] from agent [%s]?", environment, agentID) + deletingMsg = "Deleting environment [" + util.Accented(environment) + "] from agent [" + util.Accented(agentID) + "]" + deletedMsg = fmt.Sprintf("Deleted environment [%s] from agent [%s]", util.Accented(environment), util.Accented(agentID)) + } + if !silent && !SkipPrompts(cmd) { var confirmDelete bool if err := huh.NewForm( huh.NewGroup( huh.NewConfirm(). - Title(fmt.Sprintf("Are you sure you want to delete agent [%s]?", agentID)). + Title(confirmMsg). Value(&confirmDelete). Inline(false). WithTheme(util.Theme), @@ -1074,12 +1094,13 @@ func deleteAgent(ctx context.Context, cmd *cli.Command) error { var res *lkproto.DeleteAgentResponse err = util.Await( - "Deleting agent ["+util.Accented(agentID)+"]", + deletingMsg, ctx, func(ctx context.Context) error { var clientErr error res, clientErr = agentsClient.DeleteAgent(ctx, &lkproto.DeleteAgentRequest{ - AgentId: agentID, + AgentId: agentID, + Environment: environment, }) return clientErr }, @@ -1096,7 +1117,7 @@ func deleteAgent(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("failed to delete agent %s", res.Message) } - fmt.Printf("Deleted agent [%s]\n", util.Accented(agentID)) + fmt.Printf("%s\n", deletedMsg) return nil } @@ -1118,6 +1139,9 @@ func listAgentVersions(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("unable to list agent versions: %w", err) } + table := util.CreateTable(). + Headers("Version", "Current", "Draining", "Status", "Created At", "Deployed At") + // Sort versions by created date descending slices.SortFunc(versions.Versions, func(a, b *lkproto.AgentVersion) int { return b.CreatedAt.AsTime().Compare(a.CreatedAt.AsTime()) @@ -1141,6 +1165,7 @@ func listAgentVersions(ctx context.Context, cmd *cli.Command) error { row := []string{ version.Version, fmt.Sprintf("%t", version.Current), + fmt.Sprintf("%t", version.Draining), version.Status, version.CreatedAt.AsTime().Format(time.RFC3339), version.DeployedAt.AsTime().Format(time.RFC3339), From 6a1b5b8193c0b2dcfa2b7f13181a3641c2bbff62 Mon Sep 17 00:00:00 2001 From: Steve Yoon Date: Thu, 23 Apr 2026 11:48:02 -0400 Subject: [PATCH 4/7] support agent name per env --- cmd/lk/agent.go | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/cmd/lk/agent.go b/cmd/lk/agent.go index d8922c14..038e98f6 100644 --- a/cmd/lk/agent.go +++ b/cmd/lk/agent.go @@ -816,7 +816,7 @@ func deployAgent(ctx context.Context, cmd *cli.Command) error { buildContext, cancel := context.WithTimeout(ctx, buildTimeout) defer cancel() excludeFiles := []string{fmt.Sprintf("**/%s", config.LiveKitTOMLFile)} - if err := agentsClient.DeployAgent(buildContext, agentId, os.DirFS(workingDir), secrets, environment, excludeFiles, os.Stderr); err != nil { + if err := agentsClient.DeployAgentV2(buildContext, agentId, os.DirFS(workingDir), secrets, environment, excludeFiles, os.Stderr); err != nil { if twerr, ok := err.(twirp.Error); ok { return fmt.Errorf("unable to deploy agent: %s", twerr.Msg()) } @@ -910,6 +910,7 @@ func getAgentStatus(ctx context.Context, cmd *cli.Command) error { } rows = append(rows, []string{ agent.AgentId, + regionalAgent.AgentName, version, regionalAgent.Region, regionalAgent.Environment, @@ -923,7 +924,7 @@ func getAgentStatus(ctx context.Context, cmd *cli.Command) error { } t := util.CreateTable(). - Headers("ID", "Version", "Region", "Environment", "Status", "CPU", "Mem", "Replicas", "Deployed At"). + Headers("ID", "Name", "Version", "Region", "Environment", "Status", "CPU", "Mem", "Replicas", "Deployed At"). Rows(rows...) fmt.Println(t) @@ -1140,7 +1141,14 @@ func listAgentVersions(ctx context.Context, cmd *cli.Command) error { } table := util.CreateTable(). - Headers("Version", "Current", "Draining", "Status", "Created At", "Deployed At") + Headers("Version", "Prod", "Draining", "Active", "Status", "Created At", "Deployed At") + + flag := func(b bool) string { + if b { + return "✓" + } + return "---" + } // Sort versions by created date descending slices.SortFunc(versions.Versions, func(a, b *lkproto.AgentVersion) int { @@ -1164,8 +1172,9 @@ func listAgentVersions(ctx context.Context, cmd *cli.Command) error { for _, version := range versions.Versions { row := []string{ version.Version, - fmt.Sprintf("%t", version.Current), - fmt.Sprintf("%t", version.Draining), + flag(version.Current), + flag(version.Draining), + flag(version.Active), version.Status, version.CreatedAt.AsTime().Format(time.RFC3339), version.DeployedAt.AsTime().Format(time.RFC3339), From f9f746ca813a6bdea45969000ff465fce0ace14d Mon Sep 17 00:00:00 2001 From: Steve Yoon Date: Thu, 23 Apr 2026 13:02:47 -0400 Subject: [PATCH 5/7] resolve merge conflicts --- cmd/lk/agent.go | 44 +++++++++++++++++++++----------------------- go.mod | 4 ---- go.sum | 4 ++++ 3 files changed, 25 insertions(+), 27 deletions(-) diff --git a/cmd/lk/agent.go b/cmd/lk/agent.go index 038e98f6..fcc96f84 100644 --- a/cmd/lk/agent.go +++ b/cmd/lk/agent.go @@ -109,7 +109,7 @@ var ( agentPrebuiltImageTarFlag = &cli.StringFlag{ Name: "image-tar", Usage: "Pre-built image from an OCI tar file (e.g. ./image.tar). No Docker daemon required.", - + } envFlag = &cli.StringSliceFlag{ Name: "env", Usage: "Deployment environment(s). For create/deploy, specifies the target environment (defaults to 'production'). For update-secrets, assigns environment(s) to the secret. Can be specified multiple times (e.g. --env staging --env production).", @@ -813,8 +813,6 @@ func deployAgent(ctx context.Context, cmd *cli.Command) error { } fmt.Printf("Using environment [%s]\n", util.Accented(environment)) - buildContext, cancel := context.WithTimeout(ctx, buildTimeout) - defer cancel() excludeFiles := []string{fmt.Sprintf("**/%s", config.LiveKitTOMLFile)} if err := agentsClient.DeployAgentV2(buildContext, agentId, os.DirFS(workingDir), secrets, environment, excludeFiles, os.Stderr); err != nil { if twerr, ok := err.(twirp.Error); ok { @@ -1140,16 +1138,6 @@ func listAgentVersions(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("unable to list agent versions: %w", err) } - table := util.CreateTable(). - Headers("Version", "Prod", "Draining", "Active", "Status", "Created At", "Deployed At") - - flag := func(b bool) string { - if b { - return "✓" - } - return "---" - } - // Sort versions by created date descending slices.SortFunc(versions.Versions, func(a, b *lkproto.AgentVersion) int { return b.CreatedAt.AsTime().Compare(a.CreatedAt.AsTime()) @@ -1163,7 +1151,14 @@ func listAgentVersions(ctx context.Context, cmd *cli.Command) error { } } - headers := []string{"Version", "Current", "Status", "Created At", "Deployed At"} + flag := func(b bool) string { + if b { + return "✓" + } + return "---" + } + + headers := []string{"Version", "Prod", "Draining", "Active", "Status", "Created At", "Deployed At"} if showDigest { headers = append(headers, "Digest") } @@ -1229,16 +1224,19 @@ func listAgents(ctx context.Context, cmd *cli.Command) error { var rows [][]string for _, agent := range items { - // Determine region: use production deployment's region as the canonical region. - var region string - var environments []string + regionSet := map[string]struct{}{} + envSet := map[string]struct{}{} for _, regionalAgent := range agent.AgentDeployments { - if !slices.Contains(environments, regionalAgent.Environment) { - environments = append(environments, regionalAgent.Environment) - } - if region == "" || regionalAgent.Environment == "production" { - region = regionalAgent.Region - } + regionSet[regionalAgent.Region] = struct{}{} + envSet[regionalAgent.Environment] = struct{}{} + } + regions := make([]string, 0, len(regionSet)) + for region := range regionSet { + regions = append(regions, region) + } + environments := make([]string, 0, len(envSet)) + for environment := range envSet { + environments = append(environments, environment) } rows = append(rows, []string{ agent.AgentId, diff --git a/go.mod b/go.mod index 2592647c..7778a610 100644 --- a/go.mod +++ b/go.mod @@ -40,8 +40,6 @@ require ( k8s.io/apimachinery v0.34.1 ) -replace github.com/livekit/protocol => /Users/syoon/src/protocol - require ( buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260209202127-80ab13bee0bf.1 // indirect buf.build/go/protovalidate v1.1.3 // indirect @@ -269,5 +267,3 @@ require ( mvdan.cc/sh/v3 v3.13.1 // indirect sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect ) - -replace github.com/livekit/server-sdk-go/v2 => /Users/syoon/src/server-sdk-go diff --git a/go.sum b/go.sum index 636ce978..a52b207f 100644 --- a/go.sum +++ b/go.sum @@ -366,8 +366,12 @@ github.com/livekit/mageutil v0.0.0-20250511045019-0f1ff63f7731 h1:9x+U2HGLrSw5AT github.com/livekit/mageutil v0.0.0-20250511045019-0f1ff63f7731/go.mod h1:Rs3MhFwutWhGwmY1VQsygw28z5bWcnEYmS1OG9OxjOQ= github.com/livekit/mediatransportutil v0.0.0-20260309115634-0e2e24b36ee8 h1:coWig9fKxdb/nwOaIoGUUAogso12GblAJh/9SA9hcxk= github.com/livekit/mediatransportutil v0.0.0-20260309115634-0e2e24b36ee8/go.mod h1:RCd46PT+6sEztld6XpkCrG1xskb0u3SqxIjy4G897Ss= +github.com/livekit/protocol v1.45.9-0.20260508203311-a249893d6a5d h1:mE0/AjgGnvsF3q0ipiNGe1HZ3CUKUG7Y+zqey/2JrBE= +github.com/livekit/protocol v1.45.9-0.20260508203311-a249893d6a5d/go.mod h1:KEPIJ/ZdMFQ9tmmfv/uT9TjQEuEcZupCZBabuRGEC1k= github.com/livekit/psrpc v0.7.1 h1:ms37az0QTD3UXIWuUC5D/SkmKOlRMVRsI261eBWu/Vw= github.com/livekit/psrpc v0.7.1/go.mod h1:bZ4iHFQptTkbPnB0LasvRNu/OBYXEu1NA6O5BMFo9kk= +github.com/livekit/server-sdk-go/v2 v2.16.2 h1:eQe24cka3X+5zUivezyL72nwtAJTWFXgibeiyJ/Jm+Y= +github.com/livekit/server-sdk-go/v2 v2.16.2/go.mod h1:/HOUG9AXJeCbMCdtw0dr37AB+3xXUlj/OLeXS/0p7rA= github.com/lucasb-eyer/go-colorful v1.4.0 h1:UtrWVfLdarDgc44HcS7pYloGHJUjHV/4FwW4TvVgFr4= github.com/lucasb-eyer/go-colorful v1.4.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/magefile/mage v1.17.0 h1:dS4tkq997Ism03akafC8509iqDjeE7TNTexI25Y7sXM= From 181ce46e9a784bf7a2333ad6b70fb90771cc3919 Mon Sep 17 00:00:00 2001 From: Steve Yoon Date: Thu, 23 Apr 2026 15:31:54 -0400 Subject: [PATCH 6/7] format timestamp --- cmd/lk/agent.go | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/cmd/lk/agent.go b/cmd/lk/agent.go index fcc96f84..3c9cbda9 100644 --- a/cmd/lk/agent.go +++ b/cmd/lk/agent.go @@ -400,6 +400,13 @@ func createAgentClient(ctx context.Context, cmd *cli.Command) (context.Context, return createAgentClientWithOpts(ctx, cmd) } +func formatTime(t time.Time) string { + if t.IsZero() { + return "---" + } + return t.Format(time.RFC3339) +} + func createAgentClientWithOpts(ctx context.Context, cmd *cli.Command, opts ...loadOption) (context.Context, error) { var err error @@ -916,7 +923,7 @@ func getAgentStatus(ctx context.Context, cmd *cli.Command) error { fmt.Sprintf("%s / %s", curCPU, regionalAgent.CpuLimit), fmt.Sprintf("%s / %s", curMem, memLimit), fmt.Sprintf("%d / %d / %d", regionalAgent.Replicas, regionalAgent.MinReplicas, regionalAgent.MaxReplicas), - agent.DeployedAt.AsTime().Format(time.RFC3339), + formatTime(agent.DeployedAt.AsTime()), }) } } @@ -1171,8 +1178,8 @@ func listAgentVersions(ctx context.Context, cmd *cli.Command) error { flag(version.Draining), flag(version.Active), version.Status, - version.CreatedAt.AsTime().Format(time.RFC3339), - version.DeployedAt.AsTime().Format(time.RFC3339), + formatTime(version.CreatedAt.AsTime()), + formatTime(version.DeployedAt.AsTime()), } if showDigest { row = append(row, version.Attributes["image_digest"]) @@ -1244,7 +1251,7 @@ func listAgents(ctx context.Context, cmd *cli.Command) error { strings.Join(regions, ","), strings.Join(environments, ","), agent.Version, - agent.DeployedAt.AsTime().Format(time.RFC3339), + formatTime(agent.DeployedAt.AsTime()), }) } From 509b3aae99268d9d781e80068a6f7502584490d1 Mon Sep 17 00:00:00 2001 From: Steve Yoon Date: Thu, 14 May 2026 17:28:11 -0400 Subject: [PATCH 7/7] fix livekit-cli --- cmd/lk/agent.go | 85 +++++++++++++++++++++++++------------------------ go.mod | 5 ++- go.sum | 10 +++--- 3 files changed, 51 insertions(+), 49 deletions(-) diff --git a/cmd/lk/agent.go b/cmd/lk/agent.go index 3c9cbda9..edd086b3 100644 --- a/cmd/lk/agent.go +++ b/cmd/lk/agent.go @@ -110,10 +110,11 @@ var ( Name: "image-tar", Usage: "Pre-built image from an OCI tar file (e.g. ./image.tar). No Docker daemon required.", } - envFlag = &cli.StringSliceFlag{ - Name: "env", - Usage: "Deployment environment(s). For create/deploy, specifies the target environment (defaults to 'production'). For update-secrets, assigns environment(s) to the secret. Can be specified multiple times (e.g. --env staging --env production).", + deploymentFlag = &cli.StringSliceFlag{ + Name: "deployment", + Usage: "Agent deployments. For create/deploy, specifies the target deployment (defaults to 'production'). For update-secrets, assigns deployment(s) to the secret. Can be specified multiple times (e.g. --deployment staging --deployment production).", Required: false, + Aliases: []string{"d"}, } skipSDKCheckFlag = &cli.BoolFlag{ @@ -187,7 +188,7 @@ var ( ignoreEmptySecretsFlag, silentFlag, regionFlag, - envFlag, + deploymentFlag, skipSDKCheckFlag, agentPrebuiltImageFlag, agentPrebuiltImageTarFlag, @@ -234,7 +235,7 @@ var ( secretsMountFlag, silentFlag, regionFlag, - envFlag, + deploymentFlag, ignoreEmptySecretsFlag, skipSDKCheckFlag, agentPrebuiltImageFlag, @@ -306,20 +307,20 @@ var ( Flags: []cli.Flag{ idFlag(false), logTypeFlag, - envFlag, + deploymentFlag, }, ArgsUsage: "[working-dir]", }, { Name: "delete", - Usage: "Delete an agent or a specific environment", + Usage: "Delete an agent or a specific deployment", Before: createAgentClient, Action: deleteAgent, Aliases: []string{"destroy"}, Flags: []cli.Flag{ silentFlag, idFlag(false), - envFlag, + deploymentFlag, }, ArgsUsage: "[working-dir]", }, @@ -362,7 +363,7 @@ var ( secretsFileFlag, secretsMountFlag, ignoreEmptySecretsFlag, - envFlag, + deploymentFlag, idFlag(false), &cli.BoolFlag{ Name: "overwrite", @@ -814,14 +815,14 @@ func deployAgent(ctx context.Context, cmd *cli.Command) error { } } - environment := "production" - if cmd.IsSet("env") { - environment = cmd.StringSlice("env")[0] + agentDeployment := "production" + if cmd.IsSet("deployment") { + agentDeployment = cmd.StringSlice("deployment")[0] } - fmt.Printf("Using environment [%s]\n", util.Accented(environment)) + fmt.Printf("Using deployment [%s]\n", util.Accented(agentDeployment)) excludeFiles := []string{fmt.Sprintf("**/%s", config.LiveKitTOMLFile)} - if err := agentsClient.DeployAgentV2(buildContext, agentId, os.DirFS(workingDir), secrets, environment, excludeFiles, os.Stderr); err != nil { + if err := agentsClient.DeployAgentV2(buildContext, agentId, os.DirFS(workingDir), secrets, agentDeployment, excludeFiles, os.Stderr); err != nil { if twerr, ok := err.(twirp.Error); ok { return fmt.Errorf("unable to deploy agent: %s", twerr.Msg()) } @@ -918,7 +919,7 @@ func getAgentStatus(ctx context.Context, cmd *cli.Command) error { regionalAgent.AgentName, version, regionalAgent.Region, - regionalAgent.Environment, + regionalAgent.Deployment, regionalAgent.Status, fmt.Sprintf("%s / %s", curCPU, regionalAgent.CpuLimit), fmt.Sprintf("%s / %s", curMem, memLimit), @@ -929,7 +930,7 @@ func getAgentStatus(ctx context.Context, cmd *cli.Command) error { } t := util.CreateTable(). - Headers("ID", "Name", "Version", "Region", "Environment", "Status", "CPU", "Mem", "Replicas", "Deployed At"). + Headers("ID", "Name", "Version", "Region", "Deployment", "Status", "CPU", "Mem", "Replicas", "Deployed At"). Rows(rows...) fmt.Println(t) @@ -1052,8 +1053,8 @@ func getLogs(ctx context.Context, cmd *cli.Command) error { } var agentEnvironment string - if envs := cmd.StringSlice("env"); len(envs) > 0 { - agentEnvironment = envs[0] + if deployments := cmd.StringSlice("deployment"); len(deployments) > 0 { + agentEnvironment = deployments[0] } return agentsClient.StreamLogs(ctx, cmd.String("log-type"), agentID, agentEnvironment, os.Stdout, response.Agents[0].AgentDeployments[0].ServerRegion) } @@ -1065,18 +1066,18 @@ func deleteAgent(ctx context.Context, cmd *cli.Command) error { return err } - var environment string - if envs := cmd.StringSlice("env"); len(envs) > 0 { - environment = envs[0] + var deployment string + if deployments := cmd.StringSlice("deployment"); len(deployments) > 0 { + deployment = deployments[0] } confirmMsg := fmt.Sprintf("Are you sure you want to delete agent [%s]?", agentID) deletingMsg := "Deleting agent [" + util.Accented(agentID) + "]" deletedMsg := fmt.Sprintf("Deleted agent [%s]", util.Accented(agentID)) - if environment != "" { - confirmMsg = fmt.Sprintf("Are you sure you want to delete environment [%s] from agent [%s]?", environment, agentID) - deletingMsg = "Deleting environment [" + util.Accented(environment) + "] from agent [" + util.Accented(agentID) + "]" - deletedMsg = fmt.Sprintf("Deleted environment [%s] from agent [%s]", util.Accented(environment), util.Accented(agentID)) + if deployment != "" { + confirmMsg = fmt.Sprintf("Are you sure you want to delete deployment [%s] from agent [%s]?", deployment, agentID) + deletingMsg = "Deleting deployment [" + util.Accented(deployment) + "] from agent [" + util.Accented(agentID) + "]" + deletedMsg = fmt.Sprintf("Deleted deployment [%s] from agent [%s]", util.Accented(deployment), util.Accented(agentID)) } if !silent && !SkipPrompts(cmd) { @@ -1105,8 +1106,8 @@ func deleteAgent(ctx context.Context, cmd *cli.Command) error { func(ctx context.Context) error { var clientErr error res, clientErr = agentsClient.DeleteAgent(ctx, &lkproto.DeleteAgentRequest{ - AgentId: agentID, - Environment: environment, + AgentId: agentID, + Deployment: deployment, }) return clientErr }, @@ -1232,31 +1233,31 @@ func listAgents(ctx context.Context, cmd *cli.Command) error { var rows [][]string for _, agent := range items { regionSet := map[string]struct{}{} - envSet := map[string]struct{}{} + deploymentSet := map[string]struct{}{} for _, regionalAgent := range agent.AgentDeployments { regionSet[regionalAgent.Region] = struct{}{} - envSet[regionalAgent.Environment] = struct{}{} + deploymentSet[regionalAgent.Deployment] = struct{}{} } regions := make([]string, 0, len(regionSet)) for region := range regionSet { regions = append(regions, region) } - environments := make([]string, 0, len(envSet)) - for environment := range envSet { - environments = append(environments, environment) + deployments := make([]string, 0, len(deploymentSet)) + for deployment := range deploymentSet { + deployments = append(deployments, deployment) } rows = append(rows, []string{ agent.AgentId, agent.AgentName, strings.Join(regions, ","), - strings.Join(environments, ","), + strings.Join(deployments, ","), agent.Version, formatTime(agent.DeployedAt.AsTime()), }) } t := util.CreateTable(). - Headers("ID", "Dispatch Name", "Regions", "Environment", "Version", "Deployed At"). + Headers("ID", "Dispatch Name", "Regions", "Deployment", "Version", "Deployed At"). Rows(rows...) fmt.Println(t) @@ -1283,18 +1284,18 @@ func listAgentSecrets(ctx context.Context, cmd *cli.Command) error { // TODO (steveyoon): show secret.Kind.String() once cloud-agents is released table := util.CreateTable(). - Headers("Name", "Environments", "Created At", "Updated At") + Headers("Name", "Deployments", "Created At", "Updated At") for _, secret := range secrets.Secrets { // NOTE: Maybe these should be omitted on the server side? if slices.Contains(ignoredSecrets, secret.Name) { continue } - envs := strings.Join(secret.Environments, ", ") - if envs == "" { - envs = "(all)" + deployments := strings.Join(secret.Deployments, ", ") + if deployments == "" { + deployments = "(all)" } - table.Row(secret.Name, envs, secret.CreatedAt.AsTime().Format(time.RFC3339), secret.UpdatedAt.AsTime().Format(time.RFC3339)) + table.Row(secret.Name, deployments, secret.CreatedAt.AsTime().Format(time.RFC3339), secret.UpdatedAt.AsTime().Format(time.RFC3339)) } fmt.Println(table) @@ -1312,10 +1313,10 @@ func updateAgentSecrets(ctx context.Context, cmd *cli.Command) error { return err } - if cmd.IsSet("env") { - envs := cmd.StringSlice("env") + if cmd.IsSet("deployment") { + deployments := cmd.StringSlice("deployment") for _, s := range secrets { - s.Environments = envs + s.Deployments = deployments } } diff --git a/go.mod b/go.mod index 7778a610..ca265f82 100644 --- a/go.mod +++ b/go.mod @@ -18,8 +18,8 @@ require ( github.com/google/go-containerregistry v0.20.6 github.com/google/go-querystring v1.2.0 github.com/joho/godotenv v1.5.1 - github.com/livekit/protocol v1.45.9-0.20260508203311-a249893d6a5d - github.com/livekit/server-sdk-go/v2 v2.16.2 + github.com/livekit/protocol v1.45.9-0.20260514181713-80e20dbf4fc6 + github.com/livekit/server-sdk-go/v2 v2.16.4-0.20260514210357-b7fd17c1f63e github.com/mattn/go-isatty v0.0.21 github.com/moby/patternmatcher v0.6.1 github.com/modelcontextprotocol/go-sdk v1.4.1 @@ -251,7 +251,6 @@ require ( go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/crypto v0.50.0 // indirect golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 // indirect - golang.org/x/mod v0.34.0 // indirect golang.org/x/net v0.53.0 // indirect golang.org/x/oauth2 v0.36.0 // indirect golang.org/x/sys v0.43.0 // indirect diff --git a/go.sum b/go.sum index a52b207f..1db05bf2 100644 --- a/go.sum +++ b/go.sum @@ -366,12 +366,14 @@ github.com/livekit/mageutil v0.0.0-20250511045019-0f1ff63f7731 h1:9x+U2HGLrSw5AT github.com/livekit/mageutil v0.0.0-20250511045019-0f1ff63f7731/go.mod h1:Rs3MhFwutWhGwmY1VQsygw28z5bWcnEYmS1OG9OxjOQ= github.com/livekit/mediatransportutil v0.0.0-20260309115634-0e2e24b36ee8 h1:coWig9fKxdb/nwOaIoGUUAogso12GblAJh/9SA9hcxk= github.com/livekit/mediatransportutil v0.0.0-20260309115634-0e2e24b36ee8/go.mod h1:RCd46PT+6sEztld6XpkCrG1xskb0u3SqxIjy4G897Ss= -github.com/livekit/protocol v1.45.9-0.20260508203311-a249893d6a5d h1:mE0/AjgGnvsF3q0ipiNGe1HZ3CUKUG7Y+zqey/2JrBE= -github.com/livekit/protocol v1.45.9-0.20260508203311-a249893d6a5d/go.mod h1:KEPIJ/ZdMFQ9tmmfv/uT9TjQEuEcZupCZBabuRGEC1k= +github.com/livekit/protocol v1.45.9-0.20260514181713-80e20dbf4fc6 h1:eCK2ZuTrnG6i4sQ0h9tYCs/bUKK9J+hZvotluycaQkA= +github.com/livekit/protocol v1.45.9-0.20260514181713-80e20dbf4fc6/go.mod h1:KEPIJ/ZdMFQ9tmmfv/uT9TjQEuEcZupCZBabuRGEC1k= github.com/livekit/psrpc v0.7.1 h1:ms37az0QTD3UXIWuUC5D/SkmKOlRMVRsI261eBWu/Vw= github.com/livekit/psrpc v0.7.1/go.mod h1:bZ4iHFQptTkbPnB0LasvRNu/OBYXEu1NA6O5BMFo9kk= -github.com/livekit/server-sdk-go/v2 v2.16.2 h1:eQe24cka3X+5zUivezyL72nwtAJTWFXgibeiyJ/Jm+Y= -github.com/livekit/server-sdk-go/v2 v2.16.2/go.mod h1:/HOUG9AXJeCbMCdtw0dr37AB+3xXUlj/OLeXS/0p7rA= +github.com/livekit/server-sdk-go/v2 v2.16.4-0.20260513132820-9c0ccebaf488 h1:LIvtlD67bUE9J1sXtovUXgS5w4bs7H8FquLV0HBru+k= +github.com/livekit/server-sdk-go/v2 v2.16.4-0.20260513132820-9c0ccebaf488/go.mod h1:Ua6WRLYw8U+27pm+FPN68ogW+KsMXTQ9tPVGfTPjDCg= +github.com/livekit/server-sdk-go/v2 v2.16.4-0.20260514210357-b7fd17c1f63e h1:4vYTJ7y0xjn6GKUYGAZVuDdD1D24IrpQcC/y2A/Gs4A= +github.com/livekit/server-sdk-go/v2 v2.16.4-0.20260514210357-b7fd17c1f63e/go.mod h1:9tj49a7cWLKeLG8DUw17LPVqLxuwAD2IqL5kn92EUd0= github.com/lucasb-eyer/go-colorful v1.4.0 h1:UtrWVfLdarDgc44HcS7pYloGHJUjHV/4FwW4TvVgFr4= github.com/lucasb-eyer/go-colorful v1.4.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/magefile/mage v1.17.0 h1:dS4tkq997Ism03akafC8509iqDjeE7TNTexI25Y7sXM=