diff --git a/cli/azd/extensions/azure.ai.agents/README.md b/cli/azd/extensions/azure.ai.agents/README.md index 6f91594e886..5ac8686de99 100644 --- a/cli/azd/extensions/azure.ai.agents/README.md +++ b/cli/azd/extensions/azure.ai.agents/README.md @@ -1,5 +1,31 @@ # Azure Developer CLI (azd) Agents Extension +## Extension telemetry API + +Extension code reports best-effort usage events through the shared +`pkg/foundry/telemetry` reporter. Extension-owned event builders remain in +`internal/telemetry`: + +```go +reporter := foundryTelemetry.NewReporter(azdClient.Telemetry(), nil) +reporter.Report(ctx, extensionTelemetry.LocalClientRouteSelected(route)) +``` + +`Report` has no return value and never changes the command result. It applies a +one-second timeout, never retries, and writes only the event name and gRPC status +code to the debug log when reporting fails. Attribute values and transport error +details are not logged. + +Define event names, attribute keys, and bounded values in +`internal/telemetry/events.go`. Do not call `ReportUsage` directly from command +or provider code. Events must contain low-cardinality product metadata only; +never include prompts, responses, resource or service names, IDs, paths, URLs, +connection values, or other customer content. The azd host records events only +for extensions installed from the official registry. + +The event currently emitted by this extension is documented under +[Local client route telemetry](#local-client-route-telemetry). + ## Non-interactive automation See the shared [AI extension non-interactive input reference](../ai-non-interactive.md) diff --git a/cli/azd/extensions/azure.ai.agents/go.mod b/cli/azd/extensions/azure.ai.agents/go.mod index 758aed55ef3..48f58491684 100644 --- a/cli/azd/extensions/azure.ai.agents/go.mod +++ b/cli/azd/extensions/azure.ai.agents/go.mod @@ -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.32.0 + github.com/azure/azure-dev/cli/azd v1.34.0 github.com/braydonk/yaml v0.9.0 github.com/distribution/reference v0.6.0 github.com/drone/envsubst v1.0.3 // indirect diff --git a/cli/azd/extensions/azure.ai.agents/go.sum b/cli/azd/extensions/azure.ai.agents/go.sum index bb84d7c86a0..7b8fabb44f5 100644 --- a/cli/azd/extensions/azure.ai.agents/go.sum +++ b/cli/azd/extensions/azure.ai.agents/go.sum @@ -61,8 +61,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.32.0 h1:uhe7yvCHYEqd4qPs4PXwzXsjWb395oljFY87uzCGj+c= -github.com/azure/azure-dev/cli/azd v1.32.0/go.mod h1:4iHvNefTWxdFoIJWPc52E00EDWtgU0j7MfFyjqwNKaw= +github.com/azure/azure-dev/cli/azd v1.34.0 h1:5/udV0Iy76KeLNX9DN4U2k4m3NZdbZh1gwkPyE0BbjA= +github.com/azure/azure-dev/cli/azd v1.34.0/go.mod h1:f0Irys8I1qIKAIjYae1VkUnSVQB8qaSKlrDwOeYQQDA= 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= diff --git a/cli/azd/extensions/azure.ai.agents/internal/cmd/run.go b/cli/azd/extensions/azure.ai.agents/internal/cmd/run.go index 5f44783d3b9..37f7c36ae5c 100644 --- a/cli/azd/extensions/azure.ai.agents/internal/cmd/run.go +++ b/cli/azd/extensions/azure.ai.agents/internal/cmd/run.go @@ -28,8 +28,10 @@ import ( "azureaiagent/internal/exterrors" "azureaiagent/internal/pkg/agents/agent_yaml" "azureaiagent/internal/project" + "azureaiagent/internal/telemetry" "github.com/azure/azure-dev/cli/azd/pkg/azdext" + foundryTelemetry "github.com/azure/azure-dev/cli/azd/pkg/foundry/telemetry" "github.com/spf13/cobra" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -39,11 +41,6 @@ const ( agentInspectorExtensionID = "azure.ai.inspector" agentInspectorReadyPollPeriod = 250 * time.Millisecond windowsControlCExitCode = 0xC000013A - localClientRouteSelectedEvent = "local_client.route.selected" - localClientRouteAttribute = "route" - localClientRouteInspector = "inspector" - localClientRoutePlayground = "playground" - localClientRouteSuppressed = "suppressed" // defaultInspectorUIPort mirrors the default UI port of the // azure.ai.inspector extension. The inspector extension remains the source // of truth for the actual default: when --inspector-port is unset we do not @@ -159,7 +156,12 @@ func runRun(ctx context.Context, flags *runFlags, noPrompt bool) error { // not clear a session belonging to an already-running agent. activityProfile := resolveActivityRunProfile(runCtx.Definition) suppressClient := flags.noInspector || flags.noClient - reportLocalClientRouteSelected(ctx, azdClient.Telemetry(), activityProfile, suppressClient) + reportLocalClientRouteSelected( + ctx, + foundryTelemetry.NewReporter(azdClient.Telemetry(), nil), + activityProfile, + suppressClient, + ) if err := validateInspectorPortForProfile(flags, activityProfile.IsActivity); err != nil { return err } @@ -405,25 +407,18 @@ func isAgentProcessCanceled(err error) bool { func reportLocalClientRouteSelected( ctx context.Context, - telemetry azdext.TelemetryServiceClient, + reporter foundryTelemetry.Reporter, activityProfile activityRunProfile, suppressClient bool, ) { - route := localClientRouteInspector + route := telemetry.LocalClientRouteInspector if suppressClient { - route = localClientRouteSuppressed + route = telemetry.LocalClientRouteSuppressed } else if activityProfile.IsActivity { - route = localClientRoutePlayground + route = telemetry.LocalClientRoutePlayground } - if _, err := telemetry.ReportUsage(ctx, &azdext.ReportUsageRequest{ - EventName: localClientRouteSelectedEvent, - Attributes: map[string]string{ - localClientRouteAttribute: route, - }, - }); err != nil { - log.Printf("run: failed to report local client route selection: %v", err) - } + reporter.Report(ctx, telemetry.LocalClientRouteSelected(route)) } func handleInspectorAutoLaunch( diff --git a/cli/azd/extensions/azure.ai.agents/internal/cmd/run_test.go b/cli/azd/extensions/azure.ai.agents/internal/cmd/run_test.go index 66b88ccb61c..f80048bc10d 100644 --- a/cli/azd/extensions/azure.ai.agents/internal/cmd/run_test.go +++ b/cli/azd/extensions/azure.ai.agents/internal/cmd/run_test.go @@ -24,8 +24,10 @@ import ( "time" "azureaiagent/internal/pkg/agents/agent_yaml" + "azureaiagent/internal/telemetry" "github.com/azure/azure-dev/cli/azd/pkg/azdext" + foundryTelemetry "github.com/azure/azure-dev/cli/azd/pkg/foundry/telemetry" goyaml "go.yaml.in/yaml/v3" "google.golang.org/grpc" "google.golang.org/grpc/codes" @@ -246,33 +248,27 @@ func TestReportLocalClientRouteSelected(t *testing.T) { name string activityProfile activityRunProfile suppressClient bool - reportErr error - wantRoute string + wantRoute telemetry.LocalClientRoute }{ { name: "selects Inspector for non-activity agent", - wantRoute: localClientRouteInspector, + wantRoute: telemetry.LocalClientRouteInspector, }, { name: "selects Playground for activity agent", activityProfile: activityRunProfile{IsActivity: true}, - wantRoute: localClientRoutePlayground, + wantRoute: telemetry.LocalClientRoutePlayground, }, { name: "selects suppressed for non-activity agent", suppressClient: true, - wantRoute: localClientRouteSuppressed, + wantRoute: telemetry.LocalClientRouteSuppressed, }, { name: "suppression overrides activity route", activityProfile: activityRunProfile{IsActivity: true}, suppressClient: true, - wantRoute: localClientRouteSuppressed, - }, - { - name: "reporting failure is best effort", - reportErr: errors.New("telemetry unavailable"), - wantRoute: localClientRouteInspector, + wantRoute: telemetry.LocalClientRouteSuppressed, }, } @@ -280,27 +276,17 @@ func TestReportLocalClientRouteSelected(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() - telemetry := &recordingTelemetryClient{err: tt.reportErr} + reporter := &recordingUsageReporter{} reportLocalClientRouteSelected( t.Context(), - telemetry, + reporter, tt.activityProfile, tt.suppressClient, ) - if telemetry.request == nil { - t.Fatal("expected telemetry request") - } - if telemetry.request.EventName != localClientRouteSelectedEvent { - t.Fatalf( - "event name = %q, want %q", - telemetry.request.EventName, - localClientRouteSelectedEvent, - ) - } - wantAttributes := map[string]string{localClientRouteAttribute: tt.wantRoute} - if !maps.Equal(telemetry.request.Attributes, wantAttributes) { - t.Fatalf("attributes = %v, want %v", telemetry.request.Attributes, wantAttributes) + wantEvent := telemetry.LocalClientRouteSelected(tt.wantRoute) + if reporter.event.Name != wantEvent.Name || !maps.Equal(reporter.event.Attributes, wantEvent.Attributes) { + t.Fatalf("event = %#v, want %#v", reporter.event, wantEvent) } }) } @@ -914,9 +900,9 @@ type recordingWorkflowClient struct { called chan struct{} } -type recordingTelemetryClient struct { - request *azdext.ReportUsageRequest - err error +type recordingUsageReporter struct { + ctx context.Context + event foundryTelemetry.Event } type lockedBuffer struct { @@ -948,16 +934,9 @@ func (c *recordingWorkflowClient) Run( return &azdext.EmptyResponse{}, c.err } -func (c *recordingTelemetryClient) ReportUsage( - _ context.Context, - request *azdext.ReportUsageRequest, - _ ...grpc.CallOption, -) (*azdext.ReportUsageResponse, error) { - c.request = request - if c.err != nil { - return nil, c.err - } - return &azdext.ReportUsageResponse{Accepted: true}, nil +func (r *recordingUsageReporter) Report(ctx context.Context, event foundryTelemetry.Event) { + r.ctx = ctx + r.event = event } // createVenv sets up a minimal .venv directory structure for testing. diff --git a/cli/azd/extensions/azure.ai.agents/internal/telemetry/events.go b/cli/azd/extensions/azure.ai.agents/internal/telemetry/events.go new file mode 100644 index 00000000000..f9a321062d7 --- /dev/null +++ b/cli/azd/extensions/azure.ai.agents/internal/telemetry/events.go @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package telemetry + +import foundryTelemetry "github.com/azure/azure-dev/cli/azd/pkg/foundry/telemetry" + +const ( + localClientRouteSelectedEvent = "local_client.route.selected" + localClientRouteAttribute = "route" +) + +// LocalClientRoute identifies the client selected for a local agent run. +type LocalClientRoute string + +const ( + LocalClientRouteInspector LocalClientRoute = "inspector" + LocalClientRoutePlayground LocalClientRoute = "playground" + LocalClientRouteSuppressed LocalClientRoute = "suppressed" +) + +// LocalClientRouteSelected creates the event emitted after resolving the local client route. +func LocalClientRouteSelected(route LocalClientRoute) foundryTelemetry.Event { + return foundryTelemetry.Event{ + Name: localClientRouteSelectedEvent, + Attributes: map[string]string{ + localClientRouteAttribute: string(route), + }, + } +} diff --git a/cli/azd/extensions/azure.ai.agents/internal/telemetry/events_test.go b/cli/azd/extensions/azure.ai.agents/internal/telemetry/events_test.go new file mode 100644 index 00000000000..8d3f6f5abd7 --- /dev/null +++ b/cli/azd/extensions/azure.ai.agents/internal/telemetry/events_test.go @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package telemetry + +import ( + "maps" + "testing" +) + +func TestLocalClientRouteSelectedWireContract(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + route LocalClientRoute + wantValue string + }{ + { + name: "inspector", + route: LocalClientRouteInspector, + wantValue: "inspector", + }, + { + name: "playground", + route: LocalClientRoutePlayground, + wantValue: "playground", + }, + { + name: "suppressed", + route: LocalClientRouteSuppressed, + wantValue: "suppressed", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + event := LocalClientRouteSelected(tt.route) + if event.Name != "local_client.route.selected" { + t.Fatalf("event name = %q, want %q", event.Name, "local_client.route.selected") + } + + wantAttributes := map[string]string{"route": tt.wantValue} + if !maps.Equal(event.Attributes, wantAttributes) { + t.Fatalf("event attributes = %#v, want %#v", event.Attributes, wantAttributes) + } + }) + } +} diff --git a/cli/azd/extensions/azure.ai.connections/README.md b/cli/azd/extensions/azure.ai.connections/README.md index 4e9b850ad9f..914c52e6624 100644 --- a/cli/azd/extensions/azure.ai.connections/README.md +++ b/cli/azd/extensions/azure.ai.connections/README.md @@ -1,3 +1,30 @@ # Foundry Connections Manage Microsoft Foundry Connections from your terminal. (Preview) + +## Extension telemetry API + +Extension code can report best-effort usage events through the shared +`pkg/foundry/telemetry` package: + +```go +reporter := telemetry.NewReporter(azdClient.Telemetry(), nil) +reporter.Report(ctx, telemetry.Event{ + Name: "connection.operation.completed", + Attributes: map[string]string{ + "operation": "create", + }, +}) +``` + +The example is illustrative; this extension does not currently emit a product +usage event. Add an event only after its product question, bounded values, +documentation, and privacy review are agreed. + +`Report` has no return value and never changes command behavior. It uses a +one-second timeout, does not retry, and does not log attribute values or +transport error details. Put approved event builders and finite-value types in +`internal/telemetry/events.go`; do not call `ReportUsage` directly from command +or service-target code. Never include connection names, endpoints, credentials, +IDs, resource names, paths, URLs, or other customer content. The azd host records +events only for extensions installed from the official registry. diff --git a/cli/azd/extensions/azure.ai.connections/go.mod b/cli/azd/extensions/azure.ai.connections/go.mod index f0b08bcad80..b4d25ef97c5 100644 --- a/cli/azd/extensions/azure.ai.connections/go.mod +++ b/cli/azd/extensions/azure.ai.connections/go.mod @@ -6,7 +6,7 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/cognitiveservices/armcognitiveservices/v2 v2.0.0 - github.com/azure/azure-dev/cli/azd v1.28.0 + github.com/azure/azure-dev/cli/azd v1.34.0 github.com/fatih/color v1.18.0 github.com/spf13/cobra v1.10.1 github.com/stretchr/testify v1.11.1 diff --git a/cli/azd/extensions/azure.ai.connections/go.sum b/cli/azd/extensions/azure.ai.connections/go.sum index c94cfe49dd6..ad7831595ec 100644 --- a/cli/azd/extensions/azure.ai.connections/go.sum +++ b/cli/azd/extensions/azure.ai.connections/go.sum @@ -51,8 +51,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.28.0 h1:mqqyV85m7A1XfWJFjV/Ut0QoIEImFeF++1Ruq/cRp0s= -github.com/azure/azure-dev/cli/azd v1.28.0/go.mod h1:Ge7QaU9PoJM7i6J0xArDoQCf2tUn6O7OIKkoItxFTA8= +github.com/azure/azure-dev/cli/azd v1.34.0 h1:5/udV0Iy76KeLNX9DN4U2k4m3NZdbZh1gwkPyE0BbjA= +github.com/azure/azure-dev/cli/azd v1.34.0/go.mod h1:f0Irys8I1qIKAIjYae1VkUnSVQB8qaSKlrDwOeYQQDA= 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= diff --git a/cli/azd/extensions/azure.ai.inspector/README.md b/cli/azd/extensions/azure.ai.inspector/README.md index e1303d7e083..0c6fe7e787e 100644 --- a/cli/azd/extensions/azure.ai.inspector/README.md +++ b/cli/azd/extensions/azure.ai.inspector/README.md @@ -3,6 +3,28 @@ Browser-based inspector UI for locally running Foundry agents, packaged as a standalone azd extension. +## Extension telemetry API + +Extension code reports best-effort usage events through the shared +`pkg/foundry/telemetry` reporter. Extension-owned event builders remain in +`internal/telemetry`: + +```go +reporter := foundryTelemetry.NewReporter(azdClient.Telemetry(), nil) +reporter.Report(ctx, extensionTelemetry.InspectorUIReady()) +``` + +`Report` has no return value and never changes Inspector behavior. It applies a +one-second timeout, never retries, and does not log attribute values or transport +error details. Event names, attribute keys, and bounded values belong in +`internal/telemetry/events.go`; the server and RPC packages pass typed events and +must not call `ReportUsage` directly. + +Never include ports, URLs, session or conversation IDs, prompts, responses, or +other customer content. The azd host records events only for extensions installed +from the official registry. The event currently emitted by this extension is +documented under [Telemetry](#telemetry). + ## What it does `azd ai inspector launch` starts a small local HTTP server that: diff --git a/cli/azd/extensions/azure.ai.inspector/go.mod b/cli/azd/extensions/azure.ai.inspector/go.mod index 177a12ff2aa..d6d1b10fffb 100644 --- a/cli/azd/extensions/azure.ai.inspector/go.mod +++ b/cli/azd/extensions/azure.ai.inspector/go.mod @@ -3,7 +3,7 @@ module azureaiinspector go 1.26.4 require ( - github.com/azure/azure-dev/cli/azd v1.31.0 + github.com/azure/azure-dev/cli/azd v1.34.0 github.com/cli/browser v1.3.0 github.com/gorilla/websocket v1.5.3 github.com/spf13/cobra v1.10.2 diff --git a/cli/azd/extensions/azure.ai.inspector/go.sum b/cli/azd/extensions/azure.ai.inspector/go.sum index 81d4ec242f9..5d977b6c76f 100644 --- a/cli/azd/extensions/azure.ai.inspector/go.sum +++ b/cli/azd/extensions/azure.ai.inspector/go.sum @@ -47,8 +47,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.31.0 h1:p0U4F6w2bPrdzmzavksqfJCnlXoQu9GTQogy+6KXMmM= -github.com/azure/azure-dev/cli/azd v1.31.0/go.mod h1:HFBGeWRWhNsOoYaUcyToqaowibqcbSCfkfJfnIfI4nU= +github.com/azure/azure-dev/cli/azd v1.34.0 h1:5/udV0Iy76KeLNX9DN4U2k4m3NZdbZh1gwkPyE0BbjA= +github.com/azure/azure-dev/cli/azd v1.34.0/go.mod h1:f0Irys8I1qIKAIjYae1VkUnSVQB8qaSKlrDwOeYQQDA= 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= diff --git a/cli/azd/extensions/azure.ai.inspector/internal/cmd/inspector.go b/cli/azd/extensions/azure.ai.inspector/internal/cmd/inspector.go index b243c5a7674..ab7c2ef0236 100644 --- a/cli/azd/extensions/azure.ai.inspector/internal/cmd/inspector.go +++ b/cli/azd/extensions/azure.ai.inspector/internal/cmd/inspector.go @@ -16,6 +16,7 @@ import ( "azureaiinspector/internal/inspector" "github.com/azure/azure-dev/cli/azd/pkg/azdext" + foundryTelemetry "github.com/azure/azure-dev/cli/azd/pkg/foundry/telemetry" "github.com/cli/browser" "github.com/spf13/cobra" ) @@ -149,19 +150,14 @@ func newUsageReporter(ctx context.Context) (inspector.ReportUsageFunc, func()) { return nil, func() {} } - return usageReporter(ctx, azdClient.Telemetry()), azdClient.Close + return usageReporter(ctx, foundryTelemetry.NewReporter(azdClient.Telemetry(), nil)), azdClient.Close } -func usageReporter(ctx context.Context, telemetry azdext.TelemetryServiceClient) inspector.ReportUsageFunc { +func usageReporter(ctx context.Context, reporter foundryTelemetry.Reporter) inspector.ReportUsageFunc { // Capture the command context because WebSocket request contexts do not carry // the azd access token or parent trace metadata needed by ReportUsage. - reportUsage := func(eventName string, attributes map[string]string) { - if _, err := telemetry.ReportUsage(ctx, &azdext.ReportUsageRequest{ - EventName: eventName, - Attributes: attributes, - }); err != nil { - log.Printf("inspector: failed to report %s: %v", eventName, err) - } + reportUsage := func(event foundryTelemetry.Event) { + reporter.Report(ctx, event) } return reportUsage diff --git a/cli/azd/extensions/azure.ai.inspector/internal/cmd/inspector_test.go b/cli/azd/extensions/azure.ai.inspector/internal/cmd/inspector_test.go index 56908fb93aa..136a619997e 100644 --- a/cli/azd/extensions/azure.ai.inspector/internal/cmd/inspector_test.go +++ b/cli/azd/extensions/azure.ai.inspector/internal/cmd/inspector_test.go @@ -12,9 +12,8 @@ import ( "testing" "time" - "github.com/azure/azure-dev/cli/azd/pkg/azdext" + foundryTelemetry "github.com/azure/azure-dev/cli/azd/pkg/foundry/telemetry" "github.com/stretchr/testify/require" - "google.golang.org/grpc" "google.golang.org/grpc/metadata" ) @@ -23,21 +22,21 @@ func TestUsageReporterForwardsEventWithCommandContext(t *testing.T) { "authorization", "token", "traceparent", "00-11111111111111111111111111111111-2222222222222222-01", )) - telemetry := &recordingTelemetryClient{} + reporter := &recordingUsageReporter{} - usageReporter(ctx, telemetry)("inspector.funnel.stage", map[string]string{ + usageReporter(ctx, reporter)(foundryTelemetry.Event{Name: "inspector.funnel.stage", Attributes: map[string]string{ "stage": "ui_ready", "outcome": "succeeded", - }) + }}) - require.Equal(t, &azdext.ReportUsageRequest{ - EventName: "inspector.funnel.stage", + require.Equal(t, foundryTelemetry.Event{ + Name: "inspector.funnel.stage", Attributes: map[string]string{ "stage": "ui_ready", "outcome": "succeeded", }, - }, telemetry.request) - md, ok := metadata.FromOutgoingContext(telemetry.ctx) + }, reporter.event) + md, ok := metadata.FromOutgoingContext(reporter.ctx) require.True(t, ok) require.Equal(t, []string{"token"}, md.Get("authorization")) require.Equal(t, []string{ @@ -126,17 +125,12 @@ func (r errorReader) Read([]byte) (int, error) { return 0, r.err } -type recordingTelemetryClient struct { - ctx context.Context - request *azdext.ReportUsageRequest +type recordingUsageReporter struct { + ctx context.Context + event foundryTelemetry.Event } -func (c *recordingTelemetryClient) ReportUsage( - ctx context.Context, - request *azdext.ReportUsageRequest, - _ ...grpc.CallOption, -) (*azdext.ReportUsageResponse, error) { - c.ctx = ctx - c.request = request - return &azdext.ReportUsageResponse{Accepted: true}, nil +func (r *recordingUsageReporter) Report(ctx context.Context, event foundryTelemetry.Event) { + r.ctx = ctx + r.event = event } diff --git a/cli/azd/extensions/azure.ai.inspector/internal/inspector/telemetry.go b/cli/azd/extensions/azure.ai.inspector/internal/inspector/telemetry.go index 3dbad797c5c..2cf1c231b8f 100644 --- a/cli/azd/extensions/azure.ai.inspector/internal/inspector/telemetry.go +++ b/cli/azd/extensions/azure.ai.inspector/internal/inspector/telemetry.go @@ -3,19 +3,16 @@ package inspector -import "sync" +import ( + "sync" -const ( - inspectorFunnelStageEvent = "inspector.funnel.stage" - inspectorFunnelStageAttribute = "stage" - inspectorFunnelOutcomeAttribute = "outcome" + "azureaiinspector/internal/telemetry" - inspectorFunnelStageUIReady = "ui_ready" - inspectorFunnelSucceeded = "succeeded" + foundryTelemetry "github.com/azure/azure-dev/cli/azd/pkg/foundry/telemetry" ) // ReportUsageFunc records one extension-owned usage event. -type ReportUsageFunc func(eventName string, attributes map[string]string) +type ReportUsageFunc func(event foundryTelemetry.Event) func newUIReadyReporter(reportUsage ReportUsageFunc) func() { return sync.OnceFunc(func() { @@ -23,9 +20,6 @@ func newUIReadyReporter(reportUsage ReportUsageFunc) func() { return } - reportUsage(inspectorFunnelStageEvent, map[string]string{ - inspectorFunnelStageAttribute: inspectorFunnelStageUIReady, - inspectorFunnelOutcomeAttribute: inspectorFunnelSucceeded, - }) + reportUsage(telemetry.InspectorUIReady()) }) } diff --git a/cli/azd/extensions/azure.ai.inspector/internal/inspector/telemetry_test.go b/cli/azd/extensions/azure.ai.inspector/internal/inspector/telemetry_test.go index d68a96bf0b1..56db2dbdef6 100644 --- a/cli/azd/extensions/azure.ai.inspector/internal/inspector/telemetry_test.go +++ b/cli/azd/extensions/azure.ai.inspector/internal/inspector/telemetry_test.go @@ -7,21 +7,14 @@ import ( "sync" "testing" + foundryTelemetry "github.com/azure/azure-dev/cli/azd/pkg/foundry/telemetry" "github.com/stretchr/testify/require" ) func TestUIReadyReporterReportsFunnelStageOnce(t *testing.T) { - type usageEvent struct { - name string - attributes map[string]string - } - - var events []usageEvent - reportUIReady := newUIReadyReporter(func(eventName string, attributes map[string]string) { - events = append(events, usageEvent{ - name: eventName, - attributes: attributes, - }) + var events []foundryTelemetry.Event + reportUIReady := newUIReadyReporter(func(event foundryTelemetry.Event) { + events = append(events, event) }) var wg sync.WaitGroup @@ -30,11 +23,11 @@ func TestUIReadyReporterReportsFunnelStageOnce(t *testing.T) { } wg.Wait() - require.Equal(t, []usageEvent{{ - name: inspectorFunnelStageEvent, - attributes: map[string]string{ - inspectorFunnelStageAttribute: inspectorFunnelStageUIReady, - inspectorFunnelOutcomeAttribute: inspectorFunnelSucceeded, + require.Equal(t, []foundryTelemetry.Event{{ + Name: "inspector.funnel.stage", + Attributes: map[string]string{ + "stage": "ui_ready", + "outcome": "succeeded", }, }}, events) } diff --git a/cli/azd/extensions/azure.ai.inspector/internal/telemetry/events.go b/cli/azd/extensions/azure.ai.inspector/internal/telemetry/events.go new file mode 100644 index 00000000000..ec4d9964e9e --- /dev/null +++ b/cli/azd/extensions/azure.ai.inspector/internal/telemetry/events.go @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package telemetry + +import foundryTelemetry "github.com/azure/azure-dev/cli/azd/pkg/foundry/telemetry" + +const ( + inspectorFunnelStageEvent = "inspector.funnel.stage" + inspectorFunnelStageAttribute = "stage" + inspectorFunnelOutcomeAttribute = "outcome" + + inspectorFunnelStageUIReady = "ui_ready" + inspectorFunnelSucceeded = "succeeded" +) + +// InspectorUIReady creates the event emitted after the Inspector UI mounts. +func InspectorUIReady() foundryTelemetry.Event { + return foundryTelemetry.Event{ + Name: inspectorFunnelStageEvent, + Attributes: map[string]string{ + inspectorFunnelStageAttribute: inspectorFunnelStageUIReady, + inspectorFunnelOutcomeAttribute: inspectorFunnelSucceeded, + }, + } +} diff --git a/cli/azd/extensions/azure.ai.projects/README.md b/cli/azd/extensions/azure.ai.projects/README.md index 629f6d65727..2f32125fea2 100644 --- a/cli/azd/extensions/azure.ai.projects/README.md +++ b/cli/azd/extensions/azure.ai.projects/README.md @@ -2,6 +2,36 @@ Manage Microsoft Foundry Project resources from your terminal. (Preview) +## Extension telemetry API + +Extension code can report best-effort usage events through the shared +`pkg/foundry/telemetry` package: + +```go +reporter := telemetry.NewReporter(azdClient.Telemetry(), nil) +reporter.Report(ctx, telemetry.Event{ + Name: "project.operation.completed", + Attributes: map[string]string{ + "operation": "provision", + }, +}) +``` + +The example is illustrative; this extension does not currently emit a product +usage event through this interface. Existing provisioning span attributes are +unchanged by this adapter. Add an event only after its product question, bounded +values, documentation, and privacy review are agreed. + +`Report` has no return value and never changes command or provider behavior. It +uses a one-second timeout, does not retry, and does not log attribute values or +transport error details. Put approved event builders and finite-value types in +`internal/telemetry/events.go`; do not call `ReportUsage` directly from command, +service-target, or provisioning code. Never include project endpoints, ARM IDs, +deployment names, resource names, paths, URLs, or other customer content. The azd +host records events only for extensions installed from the official registry. + +## Non-interactive automation + See the shared [AI extension non-interactive input reference](../ai-non-interactive.md) for every prompt's flag, environment/configuration input, or deterministic no-prompt behavior. diff --git a/cli/azd/extensions/azure.ai.projects/go.mod b/cli/azd/extensions/azure.ai.projects/go.mod index 954a15a03e0..99629f12631 100644 --- a/cli/azd/extensions/azure.ai.projects/go.mod +++ b/cli/azd/extensions/azure.ai.projects/go.mod @@ -7,7 +7,7 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.14.0-beta.3 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/cognitiveservices/armcognitiveservices/v2 v2.0.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0 - github.com/azure/azure-dev/cli/azd v1.32.0 + github.com/azure/azure-dev/cli/azd v1.34.0 github.com/drone/envsubst v1.0.3 github.com/fatih/color v1.18.0 github.com/spf13/cobra v1.10.1 diff --git a/cli/azd/extensions/azure.ai.projects/go.sum b/cli/azd/extensions/azure.ai.projects/go.sum index e3b205aa0c7..fec69c5c779 100644 --- a/cli/azd/extensions/azure.ai.projects/go.sum +++ b/cli/azd/extensions/azure.ai.projects/go.sum @@ -53,8 +53,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.32.0 h1:uhe7yvCHYEqd4qPs4PXwzXsjWb395oljFY87uzCGj+c= -github.com/azure/azure-dev/cli/azd v1.32.0/go.mod h1:4iHvNefTWxdFoIJWPc52E00EDWtgU0j7MfFyjqwNKaw= +github.com/azure/azure-dev/cli/azd v1.34.0 h1:5/udV0Iy76KeLNX9DN4U2k4m3NZdbZh1gwkPyE0BbjA= +github.com/azure/azure-dev/cli/azd v1.34.0/go.mod h1:f0Irys8I1qIKAIjYae1VkUnSVQB8qaSKlrDwOeYQQDA= 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= diff --git a/cli/azd/extensions/azure.ai.routines/README.md b/cli/azd/extensions/azure.ai.routines/README.md index 24af6a31e1b..cf2c69eb20d 100644 --- a/cli/azd/extensions/azure.ai.routines/README.md +++ b/cli/azd/extensions/azure.ai.routines/README.md @@ -2,6 +2,33 @@ Manage Microsoft Foundry Routines from your terminal. (Preview) +## Extension telemetry API + +Extension code can report best-effort usage events through the shared +`pkg/foundry/telemetry` package: + +```go +reporter := telemetry.NewReporter(azdClient.Telemetry(), nil) +reporter.Report(ctx, telemetry.Event{ + Name: "routine.operation.completed", + Attributes: map[string]string{ + "operation": "dispatch", + }, +}) +``` + +The example is illustrative; this extension does not currently emit a product +usage event. Add an event only after its product question, bounded values, +documentation, and privacy review are agreed. + +`Report` has no return value and never changes command or service-target +behavior. It uses a one-second timeout, does not retry, and does not log +attribute values or transport error details. Put approved event builders and +finite-value types in `internal/telemetry/events.go`; do not call `ReportUsage` +directly. Never include routine names, definitions, schedules, inputs, outputs, +IDs, endpoints, paths, URLs, or other customer content. The azd host records +events only for extensions installed from the official registry. + ## Add a routine to azure.yaml Use `routine add` to declare a local YAML or JSON routine manifest in the diff --git a/cli/azd/extensions/azure.ai.routines/go.mod b/cli/azd/extensions/azure.ai.routines/go.mod index a99b9d15bc9..5bdf6589155 100644 --- a/cli/azd/extensions/azure.ai.routines/go.mod +++ b/cli/azd/extensions/azure.ai.routines/go.mod @@ -5,7 +5,7 @@ go 1.26.4 require ( github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 - github.com/azure/azure-dev/cli/azd v1.28.0 + github.com/azure/azure-dev/cli/azd v1.34.0 github.com/fatih/color v1.18.0 github.com/spf13/cobra v1.10.1 github.com/stretchr/testify v1.11.1 diff --git a/cli/azd/extensions/azure.ai.routines/go.sum b/cli/azd/extensions/azure.ai.routines/go.sum index 485e1614daf..0f25d494c55 100644 --- a/cli/azd/extensions/azure.ai.routines/go.sum +++ b/cli/azd/extensions/azure.ai.routines/go.sum @@ -49,8 +49,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.28.0 h1:mqqyV85m7A1XfWJFjV/Ut0QoIEImFeF++1Ruq/cRp0s= -github.com/azure/azure-dev/cli/azd v1.28.0/go.mod h1:Ge7QaU9PoJM7i6J0xArDoQCf2tUn6O7OIKkoItxFTA8= +github.com/azure/azure-dev/cli/azd v1.34.0 h1:5/udV0Iy76KeLNX9DN4U2k4m3NZdbZh1gwkPyE0BbjA= +github.com/azure/azure-dev/cli/azd v1.34.0/go.mod h1:f0Irys8I1qIKAIjYae1VkUnSVQB8qaSKlrDwOeYQQDA= 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= diff --git a/cli/azd/extensions/azure.ai.skills/README.md b/cli/azd/extensions/azure.ai.skills/README.md index c9be2cbabdf..26ad95ccd40 100644 --- a/cli/azd/extensions/azure.ai.skills/README.md +++ b/cli/azd/extensions/azure.ai.skills/README.md @@ -4,6 +4,33 @@ Manage [Microsoft Foundry](https://learn.microsoft.com/azure/ai-services/) **ski (reusable behavioral guidelines an agent can attach at runtime) directly from your terminal. +## Extension telemetry API + +Extension code can report best-effort usage events through the shared +`pkg/foundry/telemetry` package: + +```go +reporter := telemetry.NewReporter(azdClient.Telemetry(), nil) +reporter.Report(ctx, telemetry.Event{ + Name: "skill.operation.completed", + Attributes: map[string]string{ + "content.mode": "archive", + }, +}) +``` + +The example is illustrative; this extension does not currently emit a product +usage event. Add an event only after its product question, bounded values, +documentation, and privacy review are agreed. + +`Report` has no return value and never changes command or service-target +behavior. It uses a one-second timeout, does not retry, and does not log +attribute values or transport error details. Put approved event builders and +finite-value types in `internal/telemetry/events.go`; do not call `ReportUsage` +directly. Never include skill names, descriptions, instructions, file contents, +IDs, endpoints, paths, URLs, or other customer content. The azd host records +events only for extensions installed from the official registry. + ## Commands ```bash diff --git a/cli/azd/extensions/azure.ai.skills/go.mod b/cli/azd/extensions/azure.ai.skills/go.mod index a079b8cebb4..480f8b8a55c 100644 --- a/cli/azd/extensions/azure.ai.skills/go.mod +++ b/cli/azd/extensions/azure.ai.skills/go.mod @@ -5,7 +5,7 @@ go 1.26.4 require ( github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.14.0-beta.3 - github.com/azure/azure-dev/cli/azd v1.28.0 + github.com/azure/azure-dev/cli/azd v1.34.0 github.com/fatih/color v1.18.0 github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 diff --git a/cli/azd/extensions/azure.ai.skills/go.sum b/cli/azd/extensions/azure.ai.skills/go.sum index 4b69f1448cd..bfa03ea4113 100644 --- a/cli/azd/extensions/azure.ai.skills/go.sum +++ b/cli/azd/extensions/azure.ai.skills/go.sum @@ -49,8 +49,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.28.0 h1:mqqyV85m7A1XfWJFjV/Ut0QoIEImFeF++1Ruq/cRp0s= -github.com/azure/azure-dev/cli/azd v1.28.0/go.mod h1:Ge7QaU9PoJM7i6J0xArDoQCf2tUn6O7OIKkoItxFTA8= +github.com/azure/azure-dev/cli/azd v1.34.0 h1:5/udV0Iy76KeLNX9DN4U2k4m3NZdbZh1gwkPyE0BbjA= +github.com/azure/azure-dev/cli/azd v1.34.0/go.mod h1:f0Irys8I1qIKAIjYae1VkUnSVQB8qaSKlrDwOeYQQDA= 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= diff --git a/cli/azd/extensions/azure.ai.toolboxes/README.md b/cli/azd/extensions/azure.ai.toolboxes/README.md index dd3acc39ab0..e3722afce41 100644 --- a/cli/azd/extensions/azure.ai.toolboxes/README.md +++ b/cli/azd/extensions/azure.ai.toolboxes/README.md @@ -2,6 +2,33 @@ Manage Microsoft Foundry Toolboxes from your terminal. (Preview) +## Extension telemetry API + +Extension code can report best-effort usage events through the shared +`pkg/foundry/telemetry` package: + +```go +reporter := telemetry.NewReporter(azdClient.Telemetry(), nil) +reporter.Report(ctx, telemetry.Event{ + Name: "toolbox.operation.completed", + Attributes: map[string]string{ + "operation": "publish", + }, +}) +``` + +The example is illustrative; this extension does not currently emit a product +usage event. Add an event only after its product question, bounded values, +documentation, and privacy review are agreed. + +`Report` has no return value and never changes command or service-target +behavior. It uses a one-second timeout, does not retry, and does not log +attribute values or transport error details. Put approved event builders and +finite-value types in `internal/telemetry/events.go`; do not call `ReportUsage` +directly. Never include toolbox, connection, or skill names, tool definitions, +IDs, endpoints, paths, URLs, or other customer content. The azd host records +events only for extensions installed from the official registry. + ## Reuse an existing toolbox in `azure.yaml` A `host: azure.ai.toolbox` service normally creates a new toolbox version from diff --git a/cli/azd/extensions/azure.ai.toolboxes/go.mod b/cli/azd/extensions/azure.ai.toolboxes/go.mod index e7c829be0b4..0827a0cc510 100644 --- a/cli/azd/extensions/azure.ai.toolboxes/go.mod +++ b/cli/azd/extensions/azure.ai.toolboxes/go.mod @@ -5,7 +5,7 @@ go 1.26.4 require ( github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 - github.com/azure/azure-dev/cli/azd v1.28.0 + github.com/azure/azure-dev/cli/azd v1.34.0 github.com/spf13/cobra v1.10.1 github.com/stretchr/testify v1.11.1 google.golang.org/grpc v1.83.1 diff --git a/cli/azd/extensions/azure.ai.toolboxes/go.sum b/cli/azd/extensions/azure.ai.toolboxes/go.sum index 485e1614daf..0f25d494c55 100644 --- a/cli/azd/extensions/azure.ai.toolboxes/go.sum +++ b/cli/azd/extensions/azure.ai.toolboxes/go.sum @@ -49,8 +49,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.28.0 h1:mqqyV85m7A1XfWJFjV/Ut0QoIEImFeF++1Ruq/cRp0s= -github.com/azure/azure-dev/cli/azd v1.28.0/go.mod h1:Ge7QaU9PoJM7i6J0xArDoQCf2tUn6O7OIKkoItxFTA8= +github.com/azure/azure-dev/cli/azd v1.34.0 h1:5/udV0Iy76KeLNX9DN4U2k4m3NZdbZh1gwkPyE0BbjA= +github.com/azure/azure-dev/cli/azd v1.34.0/go.mod h1:f0Irys8I1qIKAIjYae1VkUnSVQB8qaSKlrDwOeYQQDA= 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=