diff --git a/aspire-orchestrator/Aspire.AppHost/Aspire.AppHost.csproj b/aspire-orchestrator/Aspire.AppHost/Aspire.AppHost.csproj index 009dc84e..2f3d610b 100644 --- a/aspire-orchestrator/Aspire.AppHost/Aspire.AppHost.csproj +++ b/aspire-orchestrator/Aspire.AppHost/Aspire.AppHost.csproj @@ -20,8 +20,9 @@ - + + + diff --git a/aspire-orchestrator/Aspire.AppHost/Program.cs b/aspire-orchestrator/Aspire.AppHost/Program.cs index 155a8916..f7a8cae3 100644 --- a/aspire-orchestrator/Aspire.AppHost/Program.cs +++ b/aspire-orchestrator/Aspire.AppHost/Program.cs @@ -1,39 +1,117 @@ // Copyright (c) Microsoft. All rights reserved. -var builder = DistributedApplication.CreateBuilder(args); - -var authority = builder.AddParameterFromConfiguration("authority", "EntraId:Authority"); -var clientId = builder.AddParameterFromConfiguration("clientId", "EntraId:ClientId"); - -// Workbench backend -var workbenchService = builder.AddWorkbenchService("workbenchservice", projectDirectory: Path.Combine("..", "..", "workbench-service"), clientId: clientId); -var workbenchServiceEndpoint = workbenchService.GetSemanticWorkbenchEndpoint(builder.ExecutionContext.IsPublishMode); - -// Workbench frontend -var workbenchApp = builder.AddViteApp("workbenchapp", workingDirectory: Path.Combine("..", "..", "workbench-app"), packageManager: "pnpm") - .WithPnpmPackageInstallation() - .WithEnvironment(name: "VITE_SEMANTIC_WORKBENCH_SERVICE_URL", workbenchServiceEndpoint) - .WaitFor(workbenchService) - .PublishAsDockerFile([ - new DockerBuildArg("VITE_SEMANTIC_WORKBENCH_CLIENT_ID", clientId.Resource.Value), - new DockerBuildArg("VITE_SEMANTIC_WORKBENCH_AUTHORITY", authority.Resource.Value), - ]); - -// Sample Python agent -builder.AddAssistantApp("skill-assistant", projectDirectory: Path.Combine("..", "..", "assistants", "skill-assistant"), assistantModuleName: "skill-assistant") - .WithEnvironment(name: "assistant__workbench_service_url", workbenchServiceEndpoint); - -// Sample .NET agent -var simpleChatBot = builder.AddProject("simple-chatbot-dotnet") - .WithHttpEndpoint() - .WaitFor(workbenchService) - .WithEnvironment(name: "Workbench__WorkbenchEndpoint", workbenchServiceEndpoint); - -simpleChatBot.WithEnvironment("Workbench__ConnectorHost", $"{simpleChatBot.GetEndpoint("http")}"); - -if (!builder.ExecutionContext.IsPublishMode) +using Aspire.Hosting.Extensions; +using Projects; + +internal static class Program { - workbenchApp.WithHttpsEndpoint(env: "PORT"); -} + public static void Main(string[] args) + { + var builder = DistributedApplication.CreateBuilder(args); + + builder + .AddSemanticWorkbench(out IResourceBuilder workbenchBackend, out EndpointReference workbenchEndpoint) + .AddPythonAssistant("skill-assistant", workbenchEndpoint) + .AddDotnetExample("simple-chatbot-dotnet", workbenchBackend, workbenchEndpoint); + + // When running locally + if (!builder.ExecutionContext.IsPublishMode) + { + builder + .AddPythonExample("python-01-echo-bot", workbenchEndpoint) + .AddPythonExample("python-02-simple-chatbot", workbenchEndpoint) + .AddPythonExample("python-03-multimodel-chatbot", workbenchEndpoint) + .AddPythonAssistant("explorer-assistant", workbenchEndpoint) + .AddPythonAssistant("guided-conversation-assistant", workbenchEndpoint) + .AddPythonAssistant("prospector-assistant", workbenchEndpoint) + .AddDotnetExample("echo-bot-dotnet", workbenchBackend, workbenchEndpoint) + .AddDotnetExample("sw-tutorial-bot-dotnet", workbenchBackend, workbenchEndpoint); + } + + builder + .ShowDashboardUrl(true) + .LaunchDashboard(delay: 5000) + .Build() + .Run(); + } + + /// + /// Add the workbench frontend and backend components + /// + private static IDistributedApplicationBuilder AddSemanticWorkbench(this IDistributedApplicationBuilder builder, + out IResourceBuilder workbenchBackend, out EndpointReference workbenchServiceEndpoint) + { + var authority = builder.AddParameterFromConfiguration("authority", "EntraId:Authority"); + var clientId = builder.AddParameterFromConfiguration("clientId", "EntraId:ClientId"); + + // Workbench backend + workbenchBackend = builder.AddWorkbenchService( + name: "workbenchservice", + projectDirectory: Path.Combine("..", "..", "workbench-service"), + clientId: clientId); + + workbenchServiceEndpoint = workbenchBackend.GetSemanticWorkbenchEndpoint(builder.ExecutionContext.IsPublishMode); + + // Workbench frontend + var workbenchApp = builder.AddViteApp( + name: "workbenchapp", + workingDirectory: Path.Combine("..", "..", "workbench-app"), + packageManager: "pnpm") + .WithPnpmPackageInstallation() + .WithEnvironment(name: "VITE_SEMANTIC_WORKBENCH_SERVICE_URL", workbenchServiceEndpoint) + .WaitFor(workbenchBackend) + .PublishAsDockerFile([ + new DockerBuildArg("VITE_SEMANTIC_WORKBENCH_CLIENT_ID", clientId.Resource.Value), + new DockerBuildArg("VITE_SEMANTIC_WORKBENCH_AUTHORITY", authority.Resource.Value), + ]); -builder.Build().Run(); + // When running locally + if (!builder.ExecutionContext.IsPublishMode) + { + workbenchApp.WithHttpsEndpoint(env: "PORT"); + } + + return builder; + } + + private static IDistributedApplicationBuilder AddPythonAssistant(this IDistributedApplicationBuilder builder, + string name, EndpointReference workbenchServiceEndpoint) + { + builder + .AddAssistantUvPythonApp( + name: name, + projectDirectory: Path.Combine("..", "..", "assistants", name), + assistantModuleName: name) + .WithEnvironment(name: "assistant__workbench_service_url", workbenchServiceEndpoint); + + return builder; + } + + private static IDistributedApplicationBuilder AddPythonExample(this IDistributedApplicationBuilder builder, + string name, EndpointReference workbenchServiceEndpoint) + { + builder + .AddAssistantUvPythonApp( + name: name, + projectDirectory: Path.Combine("..", "..", "examples", "python", name), + assistantModuleName: "assistant") + .WithEnvironment(name: "assistant__workbench_service_url", workbenchServiceEndpoint); + + return builder; + } + + // .NET Agent Example 1 + private static IDistributedApplicationBuilder AddDotnetExample(this IDistributedApplicationBuilder builder, + string name, IResourceBuilder workbenchBackend, EndpointReference workbenchServiceEndpoint) where T : IProjectMetadata, new() + { + var agent = builder + .AddProject(name: name) + .WithHttpEndpoint() + .WaitFor(workbenchBackend) + .WithEnvironment(name: "Workbench__WorkbenchEndpoint", workbenchServiceEndpoint); + + agent.WithEnvironment("Workbench__ConnectorHost", $"{agent.GetEndpoint("http")}"); + + return builder; + } +} diff --git a/aspire-orchestrator/Aspire.Extensions/Dashboard.cs b/aspire-orchestrator/Aspire.Extensions/Dashboard.cs new file mode 100644 index 00000000..6c63012a --- /dev/null +++ b/aspire-orchestrator/Aspire.Extensions/Dashboard.cs @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System.Diagnostics; + +namespace Aspire.Hosting.Extensions; + +public static class Dashboard +{ + /// + /// Show Aspire dashboard URL before the logging start. + /// + public static IDistributedApplicationBuilder ShowDashboardUrl(this IDistributedApplicationBuilder builder, bool withStyle = false) + { + Console.WriteLine(withStyle + ? $"\u001b[1mAspire dashboard URL: {GetUrl(builder)}\u001b[0m\n\n" + : $"Aspire dashboard URL: {GetUrl(builder)}\n\n"); + return builder; + } + + /// + /// Wait 5 seconds and automatically open the browser (when using 'dotnet run' the browser doesn't open) + /// + public static IDistributedApplicationBuilder LaunchDashboard(this IDistributedApplicationBuilder builder, int delay = 5000) + { + Task.Run(async () => + { + await Task.Delay(delay).ConfigureAwait(false); + Process.Start(new ProcessStartInfo { FileName = GetUrl(builder), UseShellExecute = true }); + }); + + return builder; + } + + private static string GetUrl(IDistributedApplicationBuilder builder) + { + string token = builder.Configuration["AppHost:BrowserToken"] ?? string.Empty; + string url = builder.Configuration["ASPNETCORE_URLS"]?.Split(";")[0] ?? throw new ArgumentException("ASPNETCORE_URLS is empty"); + return $"{url}/login?t={token}"; + } +} diff --git a/aspire-orchestrator/Aspire.Extensions/UvAppHostingExtensions.cs b/aspire-orchestrator/Aspire.Extensions/UvAppHostingExtensions.cs index 2dcbdd32..994b3e12 100644 --- a/aspire-orchestrator/Aspire.Extensions/UvAppHostingExtensions.cs +++ b/aspire-orchestrator/Aspire.Extensions/UvAppHostingExtensions.cs @@ -22,7 +22,7 @@ public static IResourceBuilder AddUvApp( private static IResourceBuilder AddUvApp(this IDistributedApplicationBuilder builder, string name, string scriptPath, - string projectDirectory, + string? projectDirectory, string virtualEnvironmentPath, params string[] args) { @@ -32,7 +32,7 @@ private static IResourceBuilder AddUvApp(this IDistributedApplica string wd = projectDirectory ?? Path.Combine("..", name); - projectDirectory = PathNormalizer.NormalizePathForCurrentPlatform(Path.Combine(builder.AppHostDirectory, wd)); + projectDirectory = Path.Combine(builder.AppHostDirectory, wd).NormalizePathForCurrentPlatform(); var virtualEnvironment = new VirtualEnvironment(Path.IsPathRooted(virtualEnvironmentPath) ? virtualEnvironmentPath @@ -43,8 +43,8 @@ private static IResourceBuilder AddUvApp(this IDistributedApplica // var projectExecutable = instrumentationExecutable ?? pythonExecutable; string[] allArgs = args is { Length: > 0 } - ? ["run", scriptPath, .. args] - : ["run", scriptPath]; + ? ["run", "--frozen", scriptPath, .. args] + : ["run", "--frozen", scriptPath]; var projectResource = new UvAppResource(name, projectDirectory); diff --git a/aspire-orchestrator/Aspire.Extensions/WorkbenchServiceHostingExtensions.cs b/aspire-orchestrator/Aspire.Extensions/WorkbenchServiceHostingExtensions.cs index b34dca97..ff2e69be 100644 --- a/aspire-orchestrator/Aspire.Extensions/WorkbenchServiceHostingExtensions.cs +++ b/aspire-orchestrator/Aspire.Extensions/WorkbenchServiceHostingExtensions.cs @@ -13,18 +13,22 @@ public static IResourceBuilder AddWorkbenchService( { ArgumentNullException.ThrowIfNull(builder); - var workbenchService = builder.AddUvApp(name, projectDirectory, "start-semantic-workbench-service", scriptArgs) + var workbenchService = builder + .AddUvApp(name, projectDirectory, "start-semantic-workbench-service", scriptArgs) .PublishAsDockerImage(dockerContext: Path.Combine("..", ".."), dockerFilePath: Path.Combine("workbench-service", "Dockerfile"), configure: new(configure => configure .WithBuildArg("SSHD_ENABLED", "false"))) .WithEnvironment(name: "WORKBENCH__AUTH__ALLOWED_APP_ID", clientId.Resource.Value); + if (builder.ExecutionContext.IsPublishMode) { + // When running on Azure workbenchService.WithHttpsEndpoint(port: 3000); } else { + // When running locally workbenchService.WithHttpEndpoint(env: "PORT"); } @@ -40,7 +44,7 @@ public static EndpointReference GetSemanticWorkbenchEndpoint(this IResourceBuild return workbenchService.GetEndpoint(isPublishMode ? "https" : "http"); } - public static IResourceBuilder AddAssistantApp( + public static IResourceBuilder AddAssistantUvPythonApp( this IDistributedApplicationBuilder builder, string name, string projectDirectory, @@ -48,19 +52,23 @@ public static IResourceBuilder AddAssistantApp( { ArgumentNullException.ThrowIfNull(builder); - var assistant = builder.AddUvApp(name, projectDirectory, "start-assistant") + var assistant = builder + .AddUvApp(name, projectDirectory, "start-assistant") .PublishAsDockerImage(dockerContext: Path.Combine("..", ".."), dockerFilePath: Path.Combine("tools", "docker", "Dockerfile.assistant"), configure: new(configure => configure .WithBuildArg("package", assistantModuleName) .WithBuildArg("app", $"assistant.{assistantModuleName.Replace('-', '_')}:app") )); + if (builder.ExecutionContext.IsPublishMode) { + // When running on Azure assistant.WithHttpEndpoint(port: 3001, env: "ASSISTANT__PORT"); } else { + // When running locally assistant.WithHttpEndpoint(env: "ASSISTANT__PORT"); } diff --git a/aspire-orchestrator/SemanticWorkbench.Aspire.sln b/aspire-orchestrator/SemanticWorkbench.Aspire.sln index e7a8f5be..1c7a4c2c 100644 --- a/aspire-orchestrator/SemanticWorkbench.Aspire.sln +++ b/aspire-orchestrator/SemanticWorkbench.Aspire.sln @@ -10,6 +10,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dotnet-03-simple-chatbot", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aspire.Extensions", "Aspire.Extensions\Aspire.Extensions.csproj", "{773468A0-6628-4D14-8EE8-7C3F790B6192}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dotnet-01-echo-bot", "..\examples\dotnet\dotnet-01-echo-bot\dotnet-01-echo-bot.csproj", "{9AB1A55C-A7D7-45E4-9C4C-D3F5BBD78D64}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dotnet-02-message-types-demo", "..\examples\dotnet\dotnet-02-message-types-demo\dotnet-02-message-types-demo.csproj", "{B604ADC1-7FF3-449A-9BE7-BC553A810ADE}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -32,6 +36,14 @@ Global {773468A0-6628-4D14-8EE8-7C3F790B6192}.Debug|Any CPU.Build.0 = Debug|Any CPU {773468A0-6628-4D14-8EE8-7C3F790B6192}.Release|Any CPU.ActiveCfg = Release|Any CPU {773468A0-6628-4D14-8EE8-7C3F790B6192}.Release|Any CPU.Build.0 = Release|Any CPU + {9AB1A55C-A7D7-45E4-9C4C-D3F5BBD78D64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9AB1A55C-A7D7-45E4-9C4C-D3F5BBD78D64}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9AB1A55C-A7D7-45E4-9C4C-D3F5BBD78D64}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9AB1A55C-A7D7-45E4-9C4C-D3F5BBD78D64}.Release|Any CPU.Build.0 = Release|Any CPU + {B604ADC1-7FF3-449A-9BE7-BC553A810ADE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B604ADC1-7FF3-449A-9BE7-BC553A810ADE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B604ADC1-7FF3-449A-9BE7-BC553A810ADE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B604ADC1-7FF3-449A-9BE7-BC553A810ADE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/aspire-orchestrator/SemanticWorkbench.Aspire.sln.DotSettings b/aspire-orchestrator/SemanticWorkbench.Aspire.sln.DotSettings index e0cd1b82..622fbe8f 100644 --- a/aspire-orchestrator/SemanticWorkbench.Aspire.sln.DotSettings +++ b/aspire-orchestrator/SemanticWorkbench.Aspire.sln.DotSettings @@ -88,6 +88,7 @@ 512 True Copyright (c) Microsoft. All rights reserved. + ABC ACS AI AIGPT @@ -100,6 +101,7 @@ GPT GRPC HMAC + HTML HTTP IM IO @@ -267,6 +269,7 @@ public void It$SOMENAME$() True True True + True True True True diff --git a/aspire-orchestrator/run.sh b/aspire-orchestrator/run.sh index 3a178add..4c62d8e7 100755 --- a/aspire-orchestrator/run.sh +++ b/aspire-orchestrator/run.sh @@ -35,4 +35,4 @@ fi cd Aspire.AppHost -dotnet run \ No newline at end of file +dotnet run --launch-profile https diff --git a/examples/dotnet/dotnet-02-message-types-demo/Program.cs b/examples/dotnet/dotnet-02-message-types-demo/Program.cs index e8cd8ff7..5b15a696 100644 --- a/examples/dotnet/dotnet-02-message-types-demo/Program.cs +++ b/examples/dotnet/dotnet-02-message-types-demo/Program.cs @@ -54,7 +54,7 @@ private static IServiceCollection AddAzureAIContentSafety( IConfiguration config) { var authType = config.GetValue("AuthType"); - var endpoint = new Uri(config.GetValue("Endpoint")!); + var endpoint = new Uri(config.GetValue("Endpoint")!) ?? throw new ArgumentException("Failed to set Azure AI Content Safety Endpoint"); var apiKey = config.GetValue("ApiKey"); return services.AddSingleton(_ => authType == "AzureIdentity" diff --git a/examples/dotnet/dotnet-02-message-types-demo/dotnet-02-message-types-demo.csproj b/examples/dotnet/dotnet-02-message-types-demo/dotnet-02-message-types-demo.csproj index b59c5d36..3bbf2cce 100644 --- a/examples/dotnet/dotnet-02-message-types-demo/dotnet-02-message-types-demo.csproj +++ b/examples/dotnet/dotnet-02-message-types-demo/dotnet-02-message-types-demo.csproj @@ -6,7 +6,7 @@ enable AgentExample AgentExample - $(NoWarn);CA1308;CA1861;CA1515;IDE0290; + $(NoWarn);CA1308;CA1861;CA1515;IDE0290;CA1508; diff --git a/examples/dotnet/dotnet-03-simple-chatbot/Program.cs b/examples/dotnet/dotnet-03-simple-chatbot/Program.cs index 3cbc9a53..c6eb9acb 100644 --- a/examples/dotnet/dotnet-03-simple-chatbot/Program.cs +++ b/examples/dotnet/dotnet-03-simple-chatbot/Program.cs @@ -48,7 +48,7 @@ private static IServiceCollection AddAzureAIContentSafety( IConfiguration config) { var authType = config.GetValue("AuthType"); - var endpoint = new Uri(config.GetValue("Endpoint")!); + var endpoint = new Uri(config.GetValue("Endpoint")!) ?? throw new ArgumentException("Failed to set Azure AI Content Safety Endpoint"); var apiKey = config.GetValue("ApiKey"); return services.AddSingleton(_ => authType == "AzureIdentity" diff --git a/examples/dotnet/dotnet-03-simple-chatbot/dotnet-03-simple-chatbot.csproj b/examples/dotnet/dotnet-03-simple-chatbot/dotnet-03-simple-chatbot.csproj index 3d195e7a..f30ae90c 100644 --- a/examples/dotnet/dotnet-03-simple-chatbot/dotnet-03-simple-chatbot.csproj +++ b/examples/dotnet/dotnet-03-simple-chatbot/dotnet-03-simple-chatbot.csproj @@ -6,7 +6,7 @@ enable AgentExample AgentExample - $(NoWarn);SKEXP0010;CA1861;CA1515;IDE0290;CA1031;CA1812; + $(NoWarn);SKEXP0010;CA1861;CA1515;IDE0290;CA1031;CA1812;CA1508; diff --git a/libraries/dotnet/WorkbenchConnector/WorkbenchConnector.cs b/libraries/dotnet/WorkbenchConnector/WorkbenchConnector.cs index 6340ec98..c51c6027 100644 --- a/libraries/dotnet/WorkbenchConnector/WorkbenchConnector.cs +++ b/libraries/dotnet/WorkbenchConnector/WorkbenchConnector.cs @@ -439,8 +439,12 @@ public virtual void Init() return; } - // Example: http://[::]:64351 - string first = feat.Addresses.First().Replace("[::]", "host", StringComparison.OrdinalIgnoreCase); + // Example: http://[::]:64351 - Prefer non-HTTPS to avoid cert validation errors + string first = feat.Addresses.Any(x => !x.Contains("https:", StringComparison.OrdinalIgnoreCase)) + ? feat.Addresses.First(x => !x.Contains("https:", StringComparison.OrdinalIgnoreCase)).Replace("[::]", "host", StringComparison.OrdinalIgnoreCase) + : feat.Addresses.First().Replace("[::]", "host", StringComparison.OrdinalIgnoreCase); + + this.Log.LogTrace("Address: {Address}", first); Uri uri = new(first); this.ConnectorEndpoint = uri.Port > 0 ? $"{uri.Scheme}://127.0.0.1:{uri.Port}/{this.WorkbenchConfig.ConnectorApiPrefix.TrimStart('/')}" @@ -473,7 +477,7 @@ public virtual async Task PingSemanticWorkbenchBackendAsync(CancellationToken ca name = $"{this.WorkbenchConfig.ConnectorName} [{this.WorkbenchConfig.ConnectorId}]", description = this.WorkbenchConfig.ConnectorDescription, url = this.ConnectorEndpoint, - online_expires_in_seconds = 2 + (int)(PingFrequencyMsecs / 1000) + online_expires_in_seconds = 1 + (2 * (int)(PingFrequencyMsecs / 1000)) }; await this.SendAsync(HttpMethod.Put, path, data, null, "PingSWBackend", cancellationToken).ConfigureAwait(false); @@ -486,7 +490,7 @@ public virtual async Task PingSemanticWorkbenchBackendAsync(CancellationToken ca #region internals =========================================================================== - private const int PingFrequencyMsecs = 20000; + private const int PingFrequencyMsecs = 15000; public void Dispose() { diff --git a/tools/run-dotnet-example1.sh b/tools/run-dotnet-example1.sh deleted file mode 100755 index c5e45caa..00000000 --- a/tools/run-dotnet-example1.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash - -set -e -ROOT="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && cd .. && pwd)" -cd $ROOT -# ================================================================ - -cd examples/dotnet/dotnet-01-echo-bot - -dotnet build -dotnet run diff --git a/tools/run-dotnet-example2.sh b/tools/run-dotnet-example2.sh deleted file mode 100755 index bda2be45..00000000 --- a/tools/run-dotnet-example2.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash - -set -e -ROOT="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && cd .. && pwd)" -cd $ROOT -# ================================================================ - -cd examples/dotnet/dotnet-02-message-types-demo - -dotnet build -dotnet run diff --git a/tools/run-dotnet-example3.sh b/tools/run-dotnet-example3.sh deleted file mode 100755 index 0d677560..00000000 --- a/tools/run-dotnet-example3.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash - -set -e -ROOT="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && cd .. && pwd)" -cd $ROOT -# ================================================================ - -cd examples/dotnet/dotnet-03-simple-chatbot - -dotnet build -dotnet run diff --git a/tools/run-dotnet-examples-with-aspire.sh b/tools/run-dotnet-examples-with-aspire.sh new file mode 100755 index 00000000..1a85be6f --- /dev/null +++ b/tools/run-dotnet-examples-with-aspire.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +set -e +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && cd .. && pwd)" +cd $ROOT +# ================================================================ + +cd aspire-orchestrator + +echo '====================================================================' +echo '' +echo 'If the Aspire dashboard is not opened in your browser automatically ' +echo 'look in the log for the following link, including the auth token: ' +echo '' +echo ' https://localhost:17149/login?t=........ ' +echo '' +echo '====================================================================' + +. run.sh