Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cli/azd/extensions/azure.ai.agents/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ require (
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerregistry/armcontainerregistry v1.3.0-beta.3
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions v1.3.0
github.com/azure/azure-dev/cli/azd v1.23.14
github.com/azure/azure-dev/cli/azd v1.24.3
github.com/braydonk/yaml v0.9.0
github.com/drone/envsubst v1.0.3
github.com/fatih/color v1.18.0
Expand Down
6 changes: 2 additions & 4 deletions cli/azd/extensions/azure.ai.agents/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthoriza
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v3 v3.0.0-beta.2/go.mod h1:jVRrRDLCOuif95HDYC23ADTMlvahB7tMdl519m9Iyjc=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/cognitiveservices/armcognitiveservices/v2 v2.0.0 h1:pxphC/uRZKNHNPbZ0duDDgKkefju2F03OkG5xF6byHQ=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/cognitiveservices/armcognitiveservices/v2 v2.0.0/go.mod h1:twcwRey+l1znKBL5TEzYiZMtiVkWfM7Pq8a9vY04xYc=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerregistry/armcontainerregistry v1.2.0 h1:DWlwvVV5r/Wy1561nZ3wrpI1/vDIBRY/Wd1HWaRBZWA=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerregistry/armcontainerregistry v1.2.0/go.mod h1:E7ltexgRDmeJ0fJWv0D/HLwY2xbDdN+uv+X2uZtOx3w=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerregistry/armcontainerregistry v1.3.0-beta.3 h1:4qfc7os3wRQcl+ImfeH9z0abWJzuV9IGcN1B9olmPTU=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerregistry/armcontainerregistry v1.3.0-beta.3/go.mod h1:NlNAngH4e++mzPTN0+1EEvyUmwFmR91u/MQUVV230Z4=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v2 v2.0.0 h1:PTFGRSlMKCQelWwxUyYVEUqseBJVemLyqWJjvMyt0do=
Expand Down Expand Up @@ -61,8 +59,8 @@ github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWp
github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/azure/azure-dev/cli/azd v1.23.14 h1:yfmEPu3T+FqFuHhHHX9gEc+yflAu0k1aDOkN00HlFzo=
github.com/azure/azure-dev/cli/azd v1.23.14/go.mod h1:mS/n9XZcwRrTaDcFxWFfgcsmB6AuuTNarB2IDCS5QzI=
github.com/azure/azure-dev/cli/azd v1.24.3 h1:r2kEr2YYLu4ImKo6nR/WjhHg/1SliN1uwmAVqnM8t3o=
github.com/azure/azure-dev/cli/azd v1.24.3/go.mod h1:YANepMw36aWA8/mQyXau6JCAG84oK0ZgfvLF8rN5asU=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package cmd

import "github.com/azure/azure-dev/cli/azd/pkg/azdext"

// ensureExtensionContext returns a non-nil [azdext.ExtensionContext] so command
// constructors can be safely invoked from tests with a nil receiver. The SDK's
// [azdext.NewExtensionRootCommand] populates the real context (and its env-var
// fallback) before any leaf RunE runs, so tests that don't exercise RunE can
// safely pass nil here.
func ensureExtensionContext(extCtx *azdext.ExtensionContext) *azdext.ExtensionContext {
if extCtx == nil {
return &azdext.ExtensionContext{}
}
return extCtx
}
90 changes: 43 additions & 47 deletions cli/azd/extensions/azure.ai.agents/internal/cmd/files.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ type filesFlags struct {
session string // optional: explicit session ID override
}

func newFilesCommand() *cobra.Command {
func newFilesCommand(extCtx *azdext.ExtensionContext) *cobra.Command {
extCtx = ensureExtensionContext(extCtx)

cmd := &cobra.Command{
Use: "files",
Short: "Manage files in a hosted agent session.",
Expand All @@ -37,26 +39,14 @@ Agent details (name, endpoint) are automatically resolved from the
azd environment. Use --agent-name to select a specific agent when the project
has multiple azure.ai.agent services. The session ID is automatically resolved
from the last invoke session, or can be overridden with --session-id.`,
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
// Chain with root's PersistentPreRunE (root sets NoPrompt).
// Note: cmd.Parent() would return the "files" command itself when
// a subcommand runs, causing infinite recursion.
if root := cmd.Root(); root != nil && root.PersistentPreRunE != nil {
if err := root.PersistentPreRunE(cmd, args); err != nil {
return err
}
}

return nil
},
}

cmd.AddCommand(newFilesUploadCommand())
cmd.AddCommand(newFilesDownloadCommand())
cmd.AddCommand(newFilesListCommand())
cmd.AddCommand(newFilesRemoveCommand())
cmd.AddCommand(newFilesMkdirCommand())
cmd.AddCommand(newFilesStatCommand())
cmd.AddCommand(newFilesUploadCommand(extCtx))
cmd.AddCommand(newFilesDownloadCommand(extCtx))
cmd.AddCommand(newFilesListCommand(extCtx))
cmd.AddCommand(newFilesRemoveCommand(extCtx))
cmd.AddCommand(newFilesMkdirCommand(extCtx))
cmd.AddCommand(newFilesStatCommand(extCtx))

return cmd
}
Expand All @@ -74,14 +64,14 @@ type filesContext struct {
}

// resolveFilesContext resolves agent details and session from the azd environment.
func resolveFilesContext(ctx context.Context, flags *filesFlags) (*filesContext, error) {
func resolveFilesContext(ctx context.Context, flags *filesFlags, noPrompt bool) (*filesContext, error) {
azdClient, err := azdext.NewAzdClient()
if err != nil {
return nil, fmt.Errorf("failed to create azd client: %w", err)
}
defer azdClient.Close()

info, err := resolveAgentServiceFromProject(ctx, azdClient, flags.agentName, rootFlags.NoPrompt)
info, err := resolveAgentServiceFromProject(ctx, azdClient, flags.agentName, noPrompt)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -137,8 +127,9 @@ type FilesUploadAction struct {
sessionID string
}

func newFilesUploadCommand() *cobra.Command {
func newFilesUploadCommand(extCtx *azdext.ExtensionContext) *cobra.Command {
flags := &filesUploadFlags{}
extCtx = ensureExtensionContext(extCtx)

cmd := &cobra.Command{
Use: "upload [file]",
Expand All @@ -161,8 +152,6 @@ Agent details are automatically resolved from the azd environment.`,
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := azdext.WithAccessToken(cmd.Context())
logCleanup := setupDebugLogging(cmd.Flags())
defer logCleanup()

if len(args) > 0 && flags.file == "" {
flags.file = args[0]
Expand All @@ -174,7 +163,7 @@ Agent details are automatically resolved from the azd environment.`,
)
}

fc, err := resolveFilesContext(ctx, &flags.filesFlags)
fc, err := resolveFilesContext(ctx, &flags.filesFlags, extCtx.NoPrompt)
if err != nil {
return err
}
Expand Down Expand Up @@ -246,8 +235,9 @@ type FilesDownloadAction struct {
sessionID string
}

func newFilesDownloadCommand() *cobra.Command {
func newFilesDownloadCommand(extCtx *azdext.ExtensionContext) *cobra.Command {
flags := &filesDownloadFlags{}
extCtx = ensureExtensionContext(extCtx)

cmd := &cobra.Command{
Use: "download [file]",
Expand All @@ -270,8 +260,6 @@ Agent details are automatically resolved from the azd environment.`,
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := azdext.WithAccessToken(cmd.Context())
logCleanup := setupDebugLogging(cmd.Flags())
defer logCleanup()

if len(args) > 0 && flags.file == "" {
flags.file = args[0]
Expand All @@ -283,7 +271,7 @@ Agent details are automatically resolved from the azd environment.`,
)
}

fc, err := resolveFilesContext(ctx, &flags.filesFlags)
fc, err := resolveFilesContext(ctx, &flags.filesFlags, extCtx.NoPrompt)
if err != nil {
return err
}
Expand Down Expand Up @@ -359,8 +347,9 @@ type FilesListAction struct {
remotePath string
}

func newFilesListCommand() *cobra.Command {
func newFilesListCommand(extCtx *azdext.ExtensionContext) *cobra.Command {
flags := &filesListFlags{}
extCtx = ensureExtensionContext(extCtx)

cmd := &cobra.Command{
Use: "list [remote-path]",
Expand All @@ -385,11 +374,11 @@ Agent details are automatically resolved from the azd environment.`,
azd ai agent files list --session-id <session-id>`,
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
flags.output = extCtx.OutputFormat

ctx := azdext.WithAccessToken(cmd.Context())
logCleanup := setupDebugLogging(cmd.Flags())
defer logCleanup()

fc, err := resolveFilesContext(ctx, &flags.filesFlags)
fc, err := resolveFilesContext(ctx, &flags.filesFlags, extCtx.NoPrompt)
if err != nil {
return err
}
Expand All @@ -411,7 +400,11 @@ Agent details are automatically resolved from the azd environment.`,
}

addFilesFlags(cmd, &flags.filesFlags)
cmd.Flags().StringVar(&flags.output, "output", "json", "Output format (json or table)")
azdext.RegisterFlagOptions(cmd, azdext.FlagOptions{
Name: "output",
AllowedValues: []string{"json", "table"},
Default: "json",
})

return cmd
}
Expand Down Expand Up @@ -486,9 +479,10 @@ type FilesRemoveAction struct {
remotePath string
}

func newFilesRemoveCommand() *cobra.Command {
func newFilesRemoveCommand(extCtx *azdext.ExtensionContext) *cobra.Command {
flags := &filesRemoveFlags{}
var filePath string
extCtx = ensureExtensionContext(extCtx)

cmd := &cobra.Command{
Use: "delete [file]",
Expand All @@ -511,8 +505,6 @@ Agent details are automatically resolved from the azd environment.`,
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := azdext.WithAccessToken(cmd.Context())
logCleanup := setupDebugLogging(cmd.Flags())
defer logCleanup()

if len(args) > 0 && filePath == "" {
filePath = args[0]
Expand All @@ -524,7 +516,7 @@ Agent details are automatically resolved from the azd environment.`,
)
}

fc, err := resolveFilesContext(ctx, &flags.filesFlags)
fc, err := resolveFilesContext(ctx, &flags.filesFlags, extCtx.NoPrompt)
if err != nil {
return err
}
Expand Down Expand Up @@ -579,9 +571,10 @@ type FilesMkdirAction struct {
remotePath string
}

func newFilesMkdirCommand() *cobra.Command {
func newFilesMkdirCommand(extCtx *azdext.ExtensionContext) *cobra.Command {
flags := &filesFlags{}
var dirPath string
extCtx = ensureExtensionContext(extCtx)

cmd := &cobra.Command{
Use: "mkdir [dir]",
Expand All @@ -600,8 +593,6 @@ Agent details are automatically resolved from the azd environment.`,
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := azdext.WithAccessToken(cmd.Context())
logCleanup := setupDebugLogging(cmd.Flags())
defer logCleanup()

if len(args) > 0 && dirPath == "" {
dirPath = args[0]
Expand All @@ -613,7 +604,7 @@ Agent details are automatically resolved from the azd environment.`,
)
}

fc, err := resolveFilesContext(ctx, flags)
fc, err := resolveFilesContext(ctx, flags, extCtx.NoPrompt)
if err != nil {
return err
}
Expand Down Expand Up @@ -671,8 +662,9 @@ type FilesStatAction struct {
remotePath string
}

func newFilesStatCommand() *cobra.Command {
func newFilesStatCommand(extCtx *azdext.ExtensionContext) *cobra.Command {
flags := &filesStatFlags{}
extCtx = ensureExtensionContext(extCtx)

cmd := &cobra.Command{
Use: "stat <remote-path>",
Expand All @@ -692,11 +684,11 @@ Agent details are automatically resolved from the azd environment.`,
azd ai agent files stat /data/output.csv --session-id <session-id>`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
flags.output = extCtx.OutputFormat

ctx := azdext.WithAccessToken(cmd.Context())
logCleanup := setupDebugLogging(cmd.Flags())
defer logCleanup()

fc, err := resolveFilesContext(ctx, &flags.filesFlags)
fc, err := resolveFilesContext(ctx, &flags.filesFlags, extCtx.NoPrompt)
if err != nil {
return err
}
Expand All @@ -713,7 +705,11 @@ Agent details are automatically resolved from the azd environment.`,
}

addFilesFlags(cmd, &flags.filesFlags)
cmd.Flags().StringVarP(&flags.output, "output", "o", "json", "Output format (json or table)")
azdext.RegisterFlagOptions(cmd, azdext.FlagOptions{
Name: "output",
AllowedValues: []string{"json", "table"},
Default: "json",
})

return cmd
}
Expand Down
Loading
Loading