Skip to content

WithTerminal(): per-replica interactive terminal sessions (Aspire side, draft)#17866

Open
mitchdenny wants to merge 89 commits into
mainfrom
feature/with-terminal
Open

WithTerminal(): per-replica interactive terminal sessions (Aspire side, draft)#17866
mitchdenny wants to merge 89 commits into
mainfrom
feature/with-terminal

Conversation

@mitchdenny

Copy link
Copy Markdown
Member

Migration note: This PR continues #16760, which had to be closed because GitHub does not allow changing a PR's head repo. All review feedback has been re-posted below as inline comments, with resolutions noted for the four already-fixed Critical/High items.

Description

Adds the Aspire-side implementation of WithTerminal(...) — letting an
AppHost author opt a resource into a real interactive terminal session
that the dashboard, the aspire CLI, or any other HMP v1 viewer can
attach to and detach from at will.

End-to-end pipeline (Windows executables today; container/Linux/macOS
follow-ups tracked separately):

your process (cmd.exe / dotnet run / repl)
      ↓ stdout/stdin via ConPTY
DCP process_executable_runner_terminal       ← microsoft/dcp#133
      ↓ HMP1 server on dcp/r{i}.sock
Aspire.TerminalHost (Hex1bTerminal per replica)
   - WithHmp1UdsClient(producer)             ← consumes from DCP
   - WithHmp1UdsServer(consumer)             ← serves to viewers
      ↓ HMP1 server on host/r{i}.sock
viewers: dashboard /api/terminal proxy, `aspire terminal <name>`, …

This is a draft because:

  • DCP-side support is in microsoft/dcp#133 and not yet merged or version-bumped here.
  • IDE/debug execution is not terminal-attached yet (see "Known limitations").
  • Containers and Linux/macOS executables are follow-ups.

Tracks #16317.

What's in this PR

Public API (src/Aspire.Hosting/ApplicationModel)

  • WithTerminal() extension method + TerminalAnnotation / TerminalOptions /
    TerminalHostResource. Per-replica producer + consumer UDS layout owned by
    the TerminalHostResource; lifecycle is wired through
    TerminalHostEventingSubscriber.

DCP wire-up (src/Aspire.Hosting/Dcp/Model/TerminalSpec.cs,
src/Aspire.Hosting/Dcp/ExecutableCreator.cs)

  • New TerminalSpec mirrors the DCP API (Enabled / UdsPath / Cols / Rows).
  • ExecutableCreator populates spec.Terminal per replica from the layout
    on Windows when a TerminalAnnotation is present.
  • Bug fix in PreparePlainExecutables: plain executables now get
    ResourceReplicaCount=1 / ResourceReplicaIndex=0 annotations so the
    per-replica UDS lookup succeeds (without this, WithTerminal() on
    AddExecutable(...) silently no-op'd).

Out-of-process Terminal Host (src/Aspire.TerminalHost)

  • One Hex1bTerminal per replica acting as an HMP v1 client to DCP and an
    HMP v1 server to viewers. Native AOT-compatible.

Backchannel (src/Aspire.Hosting.Cli/... + src/Aspire.Cli/...)

  • AppHost exposes per-replica terminal info (resource name, replica index,
    consumer UDS path) over the existing CLI backchannel.

CLI (src/Aspire.Cli/Commands/TerminalCommand.cs)

  • aspire terminal <resource> [--replica N] connects to the consumer UDS
    via Hex1b's HMP v1 client and bridges the local terminal.

Dashboard (src/Aspire.Dashboard/...)

  • New TerminalView Blazor component (xterm.js + a thin JS module).
  • New /api/terminal WebSocket proxy (Terminal/TerminalWebSocketProxy.cs)
    bridging the browser WebSocket to the per-replica consumer UDS.
  • ConsoleLogs page swaps the log viewer for the terminal view when the
    selected resource has a terminal session, and now correctly rebinds the
    view when the user switches between terminal-enabled resources/replicas.

Playground (playground/Terminals/...)

  • Terminals.Repl — interactive ANSI REPL (help, whoami, time,
    size, echo, rainbow, clear, exit).
  • Terminals.AppHost — hosts the REPL with WithReplicas(2) +
    WithTerminal(120x32) and a Windows-gated shell resource
    (AddExecutable("cmd.exe") + WithTerminal()).

Spec

  • docs/specs/with-terminal.md documents the architecture, the per-replica
    UDS layout, and the lifecycle.

Validation

End-to-end requires the matching DCP build from
microsoft/dcp#133 and the
freshly-built Aspire.TerminalHost, both pointed at via env vars:

# 1. Build DCP from the PR branch (Go 1.22+)
git clone https://github.com/microsoft/dcp.git
cd dcp
git fetch origin mitchdenny/with-terminal-pty
git checkout mitchdenny/with-terminal-pty
go build -o bin/dcp.exe ./cmd/dcp

# 2. Build Aspire from this PR
git clone https://github.com/microsoft/aspire.git aspire-with-terminal
cd aspire-with-terminal
git fetch origin feature/with-terminal
git checkout feature/with-terminal
.\restore.cmd
.\build.cmd

# 3. Point Aspire at the local DCP and TerminalHost binaries
$env:ASPIRE_DCP_PATH           = "<path-to-dcp>\bin"
$env:ASPIRE_TERMINAL_HOST_PATH = "<path-to-aspire>\artifacts\bin\Aspire.TerminalHost\Debug\net8.0\Aspire.TerminalHost.exe"

# 4. Run the playground
cd playground\Terminals\Terminals.AppHost
dotnet run

Expected:

  • The dashboard shows three terminal-enabled resources: shell,
    repl-r0, repl-r1. Each entry's "Console Logs" tab renders a live
    xterm.js terminal instead of the log viewer.
  • Switching between resources/replicas in the resource selector swaps the
    attached terminal in place (state replay courtesy of Hex1bTerminal).
  • aspire terminal repl --replica 1 from a real conhost session attaches
    to replica 1 of the REPL with full interactivity.
  • Per-resource DCP logs at %LocalAppData%\Temp\aspire-dcp*\resource-executable-{guid}.log
    show Starting process under PTY... and Terminal session listening.

Unit / integration tests:

  • dotnet test tests\Aspire.Hosting.Tests\Aspire.Hosting.Tests.csproj -- --filter-class "*.WithTerminalTests" --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true" (15 pass)
  • --filter-method "*.Project_WithTerminal_PopulatesPerReplicaTerminalSpecOnWindows" --filter-method "*.Project_WithoutTerminal_HasNullTerminalSpec" --filter-method "*.PlainExecutable_WithTerminal_PopulatesTerminalSpecOnWindows" (3 pass)

Known limitations / non-goals (deferred)

  • Debugger attach: when ExecutionType.IDE is in effect (project
    resources running under VS / VS Code), DCP's IDE runner ignores
    spec.Terminal and forwards the launch to the IDE, which wires
    stdin/stdout to its own debug console. The terminal view in the
    dashboard will be empty in that case. The Process runner fallback
    (no-debug, CLI scenarios) honors spec.Terminal and works as designed.
    Proper fix is cross-component (Aspire + DCP + VS / VSCode extension).
  • Linux/macOS executables: tracked as a follow-up against DCP
    (creack/pty).
  • Containers: tracked as a follow-up against DCP (docker/podman --tty).
  • Hello-frame dimensions: consumer UDS reports width:80,height:24
    even when WithTerminal(Cols=120,Rows=32) is set — needs
    investigation of whether DCP's PTY allocation or Hex1b's headless
    presentation is overriding the configured dims. Doesn't break
    rendering (xterm.js auto-fits), but should be fixed before shipping.
  • Persistent resources were called out in the issue — not in scope
    here, follow-up.

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected (DCP PR merge + version bump,
      Linux/macOS, containers, debugger-attach support).
  • Are you including unit tests for the changes and scenario tests if relevant?
    • Yes
    • No
  • Did you add public API?
    • Yes
      • If yes, did you have an API Review for it?
        • Yes
        • No (deferred until non-draft)
      • Did you add <remarks /> and <code /> elements on your triple slash comments?
        • Yes
        • No
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
      • If yes, have you done a threat model and had a security review?
        • Yes
        • No
    • No (UDS in user-owned temp dir; no network surface)
  • Does the change require an update in our Aspire docs?
    • Yes (after non-draft)
      • Link to aspire.dev issue: TBD
    • No

Companion PR

DCP-side: microsoft/dcp#133 — Add Windows PTY support for executables (HMP v1 over UDS)

Copilot AI review requested due to automatic review settings June 3, 2026 07:55

@mitchdenny mitchdenny left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-posting the 20 code-review findings from #16760 (closed because GitHub doesn't let us change the head repo). The 4 Critical/High items already fixed in this branch have resolution details inline with the original concern.

Summary by severity:

  • 🔴 Critical: 1 (✅ fixed)
  • 🟠 High: 3 (✅ fixed)
  • 🟡 Medium: 8 (open)
  • 🔵 Low: 8 (open)

Comment thread src/Aspire.Dashboard/Terminal/TerminalWebSocketProxy.cs
Comment thread src/Aspire.TerminalHost/TerminalHostControlListener.cs Outdated
Comment thread src/Aspire.Hosting/TerminalResourceBuilderExtensions.cs Outdated
Comment thread src/Aspire.TerminalHost/TerminalReplica.cs
Comment thread src/Aspire.Hosting/TerminalResourceBuilderExtensions.cs
Comment thread src/Aspire.Cli/Tui/TerminalViewerApp.cs Outdated
Comment thread tests/Aspire.Hosting.Tests/WithTerminalTests.cs
Comment thread tests/Aspire.Hosting.Tests/WithTerminalTests.cs
{
return ReadOnlyMemory<byte>.Empty;
}
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Low — Uncertain] ReadOutputAsync collapses three distinct end-states into ReadOnlyMemory<byte>.Empty

Empty is returned for: (a) already-disposed, (b) connect failure, (c) WaitToReadAsync returning false (channel completed = EOF), (d) TryRead race miss, and (e) ChannelClosedException. If IHex1bTerminalWorkloadAdapter's contract is that Empty means "EOF / shut down the workload", case (d) — a benign race after a wakeup with no successful TryRead — will cause Hex1b to tear down the workload spuriously. If Empty means "spurious, call me again", cases (b)/(c)/(e) will turn into a tight busy-loop. Couldn't verify the Hex1b contract from this repo.

Fix: confirm IHex1bTerminalWorkloadAdapter.ReadOutputAsync semantics; for (d) loop on TryRead until success or exit the wait; for (c)/(e) ensure the return value matches Hex1b's "workload terminated" sentinel.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parked as a follow-up - tracking in #17894 because the fix shape depends on confirming Hex1b's contract for the workload adapter's read-output sentinels. Will revisit in a dedicated PR.

Comment thread src/Aspire.TerminalHost/Aspire.TerminalHost.csproj
@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 17866

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 17866"

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a draft, end-to-end “interactive terminals” feature (WithTerminal()) across Aspire.Hosting, DCP spec wiring, a new out-of-process Aspire.TerminalHost, and viewer integrations (Dashboard + CLI), using per-replica Unix domain sockets and Hex1b/HMP v1 as the transport.

Changes:

  • Adds WithTerminal() app-model surface area and lifecycle wiring to materialize per-replica terminal host resources and resolve the terminal-host executable path at BeforeStartEvent.
  • Extends DCP resource specs (executables/containers) with a terminal block and populates it from per-replica terminal layouts.
  • Adds Dashboard and CLI plumbing for discovering/attaching to terminal sessions, plus build/packaging work to ship a per-RID TerminalHost payload.
Show a summary per file
File Description
tests/Shared/Aspire.Templates.Testing.targets Excludes TerminalHost SDK packs from unexpected-package assertions.
tests/Aspire.TerminalHost.Tests/TerminalHostArgsTests.cs Unit tests for terminal host argument parsing.
tests/Aspire.TerminalHost.Tests/Aspire.TerminalHost.Tests.csproj New test project for Aspire.TerminalHost.
tests/Aspire.Templates.Tests/README.md Documents TerminalHost SDK pack as part of workload inputs.
tests/Aspire.Hosting.Tests/DistributedApplicationBuilderTests.cs Ensures terminal host subscriber is registered by default.
tests/Aspire.Hosting.Tests/Dcp/DcpExecutorTests.cs Adds tests for per-replica terminal spec population and executable replica annotations.
tests/Aspire.Hosting.Tests/Dcp/ConfigureDefaultDcpOptionsTests.cs Tests new dashboard→terminalhost bundle fallback behavior in DCP options.
tests/Aspire.Hosting.Tests/Backchannel/BackchannelContractTests.cs Adds terminal backchannel types to contract test coverage.
tests/Aspire.Dashboard.Tests/Terminal/TerminalWebSocketProxyOriginTests.cs Tests origin-validation logic for terminal WebSocket proxy.
tests/Aspire.Dashboard.Tests/Terminal/DefaultTerminalConnectionResolverTests.cs Tests resolver behavior for locating/connecting to per-replica sockets.
tests/Aspire.Dashboard.Tests/Model/ResourceViewModelExtensionsTerminalTests.cs Tests terminal-related snapshot property parsing helpers.
tests/Aspire.Cli.Tests/Utils/CliTestHelper.cs Registers new terminal commands in CLI test DI.
tests/Aspire.Cli.Tests/TestServices/TestAppHostAuxiliaryBackchannel.cs Adds terminal-related capability flags + RPC stubs to the test backchannel.
tests/Aspire.Cli.Tests/Commands/TerminalCommandViewerOptionTests.cs Tests CLI parsing/help text for --viewer behavior.
tests/Aspire.Cli.Tests/Backchannel/BackchannelJsonSerializerContextTests.cs Back-compat + roundtrip tests for new terminal backchannel payloads.
src/Shared/TerminalHost/TerminalHostControlProtocol.cs Shared JSON-RPC wire types for AppHost↔TerminalHost control channel.
src/Shared/Model/KnownProperties.cs Adds terminal.* snapshot property keys used by the dashboard.
src/Aspire.TerminalHost/TerminalHostControlRpcTarget.cs Implements control-plane RPC target methods for terminal host process.
src/Aspire.TerminalHost/TerminalHostControlListener.cs Hosts a StreamJsonRpc server over a control UDS with permission tightening.
src/Aspire.TerminalHost/TerminalHostArgs.cs Defines terminal host argument parsing and validation.
src/Aspire.TerminalHost/TerminalHostApp.cs Implements terminal host app lifecycle, control listener, and shutdown flow.
src/Aspire.TerminalHost/StderrLoggerProvider.cs Minimal stderr logger provider to avoid extra logging deps.
src/Aspire.TerminalHost/Program.cs Console entry point with Ctrl+C cancellation wiring.
src/Aspire.TerminalHost/Aspire.TerminalHost.csproj New per-RID terminal host executable project.
src/Aspire.Managed/Program.cs Adds terminalhost subcommand dispatch in the multi-mode managed binary.
src/Aspire.Managed/Aspire.Managed.csproj References Aspire.TerminalHost for bundle dispatch support.
src/Aspire.Hosting/Lifecycle/TerminalHostEventingSubscriber.cs Resolves terminal host binary + invocation args during BeforeStartEvent.
src/Aspire.Hosting/DistributedApplicationBuilder.cs Registers the terminal host eventing subscriber.
src/Aspire.Hosting/Dcp/Model/TerminalSpec.cs Adds DCP terminal spec model (udsPath/cols/rows).
src/Aspire.Hosting/Dcp/Model/Executable.cs Adds terminal block to executable spec model.
src/Aspire.Hosting/Dcp/Model/Container.cs Adds terminal block to container spec model.
src/Aspire.Hosting/Dcp/ExecutableCreator.cs Populates per-replica spec.Terminal and fixes replica annotations for plain executables.
src/Aspire.Hosting/Dcp/DcpOptions.cs Adds TerminalHostPath + invocation args, includes bundle fallback from dashboard path.
src/Aspire.Hosting/Dcp/ContainerCreator.cs Populates container spec.Terminal from terminal layout.
src/Aspire.Hosting/Dashboard/DashboardServiceData.cs Stamps terminal markers/replica info and (sensitive) consumer UDS path onto snapshots.
src/Aspire.Hosting/Backchannel/TerminalHostControlClient.cs Adds AppHost-side control RPC client over UDS with bounded retry.
src/Aspire.Hosting/Backchannel/BackchannelDataTypes.cs Adds terminal capability strings + request/response DTOs.
src/Aspire.Hosting/Backchannel/AuxiliaryBackchannelRpcTarget.cs Adds GetTerminalInfoAsync + ListTerminalsAsync RPCs and terminal capabilities.
src/Aspire.Hosting/Aspire.Hosting.csproj Links shared terminal control protocol types into hosting assembly.
src/Aspire.Hosting/ApplicationModel/TerminalHostResource.cs Defines hidden per-replica terminal host executable resource.
src/Aspire.Hosting/ApplicationModel/TerminalHostLayout.cs Defines per-replica socket layout (producer/consumer/control).
src/Aspire.Hosting/ApplicationModel/TerminalAnnotation.cs Adds terminal annotation + deferred per-replica host materialization.
src/Aspire.Hosting.Tasks/ResolveAspireCliBundle.cs Extends bundle resolution outputs for terminal host path/args.
src/Aspire.Hosting.AppHost/build/Aspire.Hosting.AppHost.in.targets Emits assembly metadata for terminal host discovery + bundle wiring.
src/Aspire.Dashboard/wwwroot/js/xterm/xterm.min.css Adds xterm.js CSS asset.
src/Aspire.Dashboard/wwwroot/js/xterm/addon-fit.min.js Adds xterm.js fit addon asset.
src/Aspire.Dashboard/wwwroot/fonts/cascadia-mono-nf/README.md Documents bundled terminal font choice and provenance.
src/Aspire.Dashboard/wwwroot/fonts/cascadia-mono-nf/LICENSE.txt Adds font license text for redistribution compliance.
src/Aspire.Dashboard/Terminal/NullTerminalConnectionResolver.cs Adds no-op resolver for non-AppHost dashboard scenarios.
src/Aspire.Dashboard/Terminal/ITerminalConnectionResolver.cs Defines dashboard abstraction for resolving terminal connections.
src/Aspire.Dashboard/Terminal/DefaultTerminalConnectionResolver.cs Implements resolver via snapshot properties + UDS connect.
src/Aspire.Dashboard/Program.cs Adds dashboard crash/heartbeat stderr diagnostics.
src/Aspire.Dashboard/Model/ResourceViewModelExtensions.cs Adds terminal-related helpers for snapshot properties.
src/Aspire.Dashboard/DashboardWebApplication.cs Registers terminal resolver and maps terminal WebSocket endpoint.
src/Aspire.Dashboard/Components/Pages/ConsoleLogs.razor.cs Switches console logs page to terminal view when terminal-enabled resource selected.
src/Aspire.Dashboard/Components/Pages/ConsoleLogs.razor UI changes to host terminal view + terminal toolbar controls.
src/Aspire.Dashboard/Components/Controls/TerminalView.razor Adds Blazor host element for xterm.js terminal.
src/Aspire.Dashboard/Aspire.Dashboard.csproj Adds Hex1b dependency + adjusts content/none items for font README.
src/Aspire.Cli/Program.cs Registers terminal commands in CLI host.
src/Aspire.Cli/Commands/TerminalCommand.cs Adds terminal parent command.
src/Aspire.Cli/Commands/RootCommand.cs Adds terminal command to root.
src/Aspire.Cli/Backchannel/IAppHostAuxiliaryBackchannel.cs Extends backchannel interface for terminal capability + RPC methods.
src/Aspire.Cli/Backchannel/BackchannelJsonSerializerContext.cs Adds JSON source-gen entries for new terminal DTOs.
src/Aspire.Cli/Backchannel/AppHostAuxiliaryBackchannel.cs Implements terminal RPC calls + capability gating in the CLI backchannel.
src/Aspire.Cli/Aspire.Cli.csproj Adds Hex1b dependency for CLI terminal client functionality.
src/Aspire.AppHost.Sdk/SDK/Sdk.in.targets Adds implicit TerminalHost SDK pack reference alongside dashboard/DCP packs.
playground/Terminals/Terminals.Repl/Terminals.Repl.csproj Adds demo interactive REPL project.
playground/Terminals/Terminals.Repl/Program.cs Implements demo ANSI REPL commands for terminal testing.
playground/Terminals/Terminals.AppHost/Terminals.AppHost.csproj Adds demo AppHost project and optional inner-loop terminal host discovery.
playground/Terminals/Terminals.AppHost/Properties/launchSettings.json Adds launch profiles for playground AppHost.
playground/Terminals/Terminals.AppHost/appsettings.json Adds logging configuration for playground.
playground/Terminals/Terminals.AppHost/appsettings.Development.json Adds dev logging configuration for playground.
playground/Terminals/Terminals.AppHost/AppHost.cs Demonstrates terminal-enabled resources and replicas.
playground/Terminals/aspire.config.json Points aspire tooling to the playground AppHost.
eng/terminalhostpack/UnixFilePermissions.xml Defines Unix executable permissions for packed terminal host tools.
eng/terminalhostpack/Sdk.targets Adds transitive SDK targets for terminal host discovery properties.
eng/terminalhostpack/Sdk.props Adds placeholder SDK props.
eng/terminalhostpack/Common.projitems Adds RID-packaging project items to publish/pack terminal host.
eng/terminalhostpack/buildTransitive/Aspire.TerminalHost.Sdk.in.targets BuildTransitive target import for TerminalHost SDK pack.
eng/terminalhostpack/buildTransitive/Aspire.TerminalHost.Sdk.in.props BuildTransitive props import for TerminalHost SDK pack.
eng/terminalhostpack/buildMultiTargeting/Aspire.TerminalHost.Sdk.in.targets BuildMultiTargeting target import for TerminalHost SDK pack.
eng/terminalhostpack/buildMultiTargeting/Aspire.TerminalHost.Sdk.in.props BuildMultiTargeting props import for TerminalHost SDK pack.
eng/terminalhostpack/AutoImport.props Placeholder auto-import props for workload infra.
eng/terminalhostpack/Aspire.TerminalHost.Sdk.win-x64.csproj New RID-specific packaging project (win-x64).
eng/terminalhostpack/Aspire.TerminalHost.Sdk.win-arm64.csproj New RID-specific packaging project (win-arm64).
eng/terminalhostpack/Aspire.TerminalHost.Sdk.osx-x64.csproj New RID-specific packaging project (osx-x64).
eng/terminalhostpack/Aspire.TerminalHost.Sdk.osx-arm64.csproj New RID-specific packaging project (osx-arm64).
eng/terminalhostpack/Aspire.TerminalHost.Sdk.linux-x64.csproj New RID-specific packaging project (linux-x64).
eng/terminalhostpack/Aspire.TerminalHost.Sdk.linux-musl-x64.csproj New RID-specific packaging project (linux-musl-x64).
eng/terminalhostpack/Aspire.TerminalHost.Sdk.linux-arm64.csproj New RID-specific packaging project (linux-arm64).
eng/Publishing.props Publishes terminal host artifacts ZIPs alongside dashboard artifacts.
eng/Build.props Includes terminalhostpack in bundle-deps build logic and skip switches.
docs/specs/with-terminal.md Adds architecture/spec documentation for WithTerminal feature.
Directory.Packages.props Updates Hex1b package versions repo-wide.
Directory.Build.props Adds terminal host artifacts directory + inner-loop path property.
Aspire.slnx Adds TerminalHost projects and new playground/tests to the solution.
.github/workflows/build-cli-native-archives.yml Includes TerminalHost SDK nupkgs in native archive build artifacts.

Copilot's findings

  • Files reviewed: 110/113 changed files
  • Comments generated: 13

Comment thread src/Aspire.Hosting/Backchannel/AuxiliaryBackchannelRpcTarget.cs Outdated
Comment thread src/Aspire.Hosting/Backchannel/AuxiliaryBackchannelRpcTarget.cs
Comment thread src/Aspire.Cli/Backchannel/AppHostAuxiliaryBackchannel.cs Outdated
Comment thread src/Aspire.Hosting/Dashboard/DashboardServiceData.cs Outdated
Comment on lines +86 to +88
case "--control-uds":
control = ParseString(args, ref i, "--control-uds");
break;
Comment thread docs/specs/with-terminal.md
Comment thread src/Aspire.Dashboard/Program.cs Outdated
Comment thread src/Aspire.Dashboard/Terminal/ITerminalConnectionResolver.cs
Comment thread docs/specs/with-terminal.md Outdated
Comment thread docs/specs/with-terminal.md Outdated
mitchdenny added a commit that referenced this pull request Jun 3, 2026
… ~/.aspire/trmnl/

Previously, each AppHost run created a Directory.CreateTempSubdirectory("aspire-term-")
in $TMPDIR and dropped per-replica subdirectories with the UDS triple inside. That:

  - did not match the repo convention for per-user runtime state (~/.aspire/cli/bch,
    ~/.aspire/dev-certs, ~/.aspire/deployments)
  - dropped sockets in the global /tmp on Linux where distros vary on /tmp perms
  - on macOS could push absolute paths close to sockaddr_un.sun_path (104 bytes)
  - made it impossible for external tools to enumerate live terminals on disk

This change introduces a flat layout where every per-replica file is named
{replicaId}.{purpose} under ~/.aspire/trmnl/, with replicaId = base64url(xxHash3(
NormalizePath(appHostPath) ++ NUL ++ resourceName ++ NUL ++ replicaIndex)). The
four files for a replica are:

  {id}.dcp.sock      — producer UDS (host listens, DCP dials)
  {id}.host.sock     — consumer UDS (host listens, viewers dial)
  {id}.control.sock  — control UDS  (host listens, AppHost dials)
  {id}.metadata.json — descriptor sidecar (schema, replica id, resource name,
                       replica index, AppHost path, AppHost PID, columns, rows,
                       socket paths, createdAtUtc)

Security and defense-in-depth:
  - ~/.aspire/trmnl/ is created 0700 on Unix
  - metadata sidecar is written 0600
  - producer / consumer sockets get a best-effort post-bind chmod 0600 from
    TerminalReplica.ApplyRestrictiveSocketPermissionsAsync (the control socket
    already had its own explicit 0600 chmod in TerminalHostControlListener)
  - cleanup deletes by {replicaId}.* glob on ApplicationStopped, not rmdir, so
    multiple AppHosts can safely share the trmnl directory

Resolves Medium finding #4 from PR #17866 (sun_path length not validated): the
new layout produces ~52-byte absolute paths on macOS, well under the 104-byte
sockaddr_un.sun_path limit, and TerminalHostPathsTests.GetSocketPathFitsInsideMacOsSunPathLimit
guards against future regressions.

New tests:
  - TerminalHostPathsTests (10 cases): replica id determinism, distinctness across
    {index, resource, AppHost path}, tuple-boundary collision guard, case-sensitivity
    on Unix, path layout shape, sun_path size guard
  - WithTerminalWritesMetadataSidecarWithExpectedShape: verifies on-disk JSON
    fields and 0600 perms
  - WithTerminalCleansUpPerReplicaFilesOnApplicationStopped: now asserts the
    production metadata write, no longer touches sentinels
  - ProducerAndConsumerSocketsAreRestrictedToOwningUser: verifies post-bind 0600

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread docs/specs/with-terminal.md Outdated
Comment thread src/Aspire.AppHost.Sdk/SDK/Sdk.in.targets Outdated
mitchdenny and others added 12 commits June 4, 2026 14:07
…xtension method

Implements Phase 1 of the live terminal support feature (#16317).

- TerminalAnnotation: IResourceAnnotation with TerminalOptions (Columns, Rows, Shell)
  and a SocketPath property for the UDS path set by the orchestrator.

- TerminalHostResource: Internal hidden resource (IResourceWithParent) that will
  manage the Hex1b-based terminal host process for a parent resource.

- WithTerminal<T>(): Extension method that adds TerminalAnnotation to a resource,
  creates a hidden TerminalHostResource, and adds a WaitAnnotation so the parent
  waits for the terminal host to be started.

- Tests: 8 unit tests covering annotation creation, custom options, hidden resource
  creation, wait annotation wiring, chaining, container support, manifest exclusion,
  and null argument handling.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- docs/specs/terminal-protocol.md: Full protocol specification defining
  the binary framing format for terminal I/O over Unix domain sockets.
  Covers HELLO, DATA, RESIZE, EXIT, and CLOSE message types with wire
  examples and implementation notes for DCP (Go) and Aspire (C#).

- src/Shared/Terminal/: Shared protocol types (TerminalProtocol constants,
  TerminalFrameReader, TerminalFrameWriter, TerminalFrame) designed to be
  linked into Aspire.Hosting, Dashboard, and CLI projects.

- playground/Terminals/: Two-project playground demonstrating WithTerminal
  without DCP:
  - Terminals.TerminalHost: .NET console app using Hex1b with a custom
    IHex1bTerminalPresentationAdapter that implements the Aspire Terminal
    Protocol over UDS. Receives socket path via TERMINAL_SOCKET_PATH env var.
  - Terminals.AppHost: Aspire AppHost that launches the terminal host as
    a child process with a custom resource lifecycle (OnInitializeResource),
    demonstrating the full WithTerminal flow before DCP PTY support lands.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Terminals.Client: Standalone console app that connects to an Aspire
Terminal Protocol UDS server, performs the HELLO handshake, puts the
local console in raw mode, and bridges stdin/stdout bidirectionally.
Supports Ctrl+] to detach.

Verified end-to-end: TerminalHost starts pwsh with Hex1b PTY, listens
on UDS. Client connects, receives HELLO(v1, 80x24, Pty), and gets a
fully interactive PowerShell session with prompt rendering and command
execution working correctly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
DCP Model:
- TerminalSpec: New model type with enabled, socketPath, columns, rows
- Added Terminal property to ExecutableSpec and ContainerSpec
- ExecutableCreator populates TerminalSpec from TerminalAnnotation

Backchannel:
- GetTerminalInfoRequest/Response in BackchannelDataTypes
- GetTerminalInfoAsync on AuxiliaryBackchannelRpcTarget (server)
- GetTerminalInfoAsync on AppHostAuxiliaryBackchannel (client)
- Added to IAppHostAuxiliaryBackchannel interface

CLI:
- New 'aspire terminal <resource>' command (TerminalCommand.cs)
- Connects to AppHost backchannel, gets terminal UDS path
- Connects to UDS, performs HELLO handshake
- Puts console in raw mode, bridges stdin/stdout bidirectionally
- Ctrl+] to detach, handles EXIT/CLOSE frames
- Registered in RootCommand and DI container

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds a second WithTerminal overload that accepts a
Func<CancellationToken, Task<string>> socketPathProvider for resources
that manage their own terminal server (e.g., remote SSH, cloud resources).

Unlike the standard overload, this does NOT create a hidden
TerminalHostResource — the caller is responsible for running a server
that speaks the Aspire Terminal Protocol on the provided socket path.

Also adds SocketPathProvider property to TerminalAnnotation and
two new tests (10 total, all passing).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…s integration

Dashboard terminal view that replaces Console Logs for terminal-enabled resources:

- TerminalView.razor: Blazor component wrapping xterm.js via JS interop
- TerminalView.razor.js: xterm.js initialization, WebSocket connection, resize handling
- TerminalWebSocketProxy.cs: ASP.NET Core middleware at /api/terminal that bridges
  browser WebSocket to UDS using the Aspire Terminal Protocol (HELLO/DATA/RESIZE/EXIT/CLOSE)
- Vendored xterm.js 5.5.0 + fit addon in wwwroot/js/xterm/

ConsoleLogs integration:
- ConsoleLogs.razor: Conditionally renders TerminalView instead of LogViewer
  when the selected resource has terminal.enabled property
- ConsoleLogs.razor.cs: Detects terminal resources in SubscribeAsync,
  skips console log subscription for terminal resources

Infrastructure:
- KnownProperties.Terminal.Enabled/SocketPath constants in shared model
- DashboardServiceData: Injects terminal.enabled and terminal.socketPath
  properties into resource snapshots when TerminalAnnotation is present
- ResourceViewModelExtensions: HasTerminal() and TryGetTerminalSocketPath()

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…JS interop

Two fixes for the Blazor unhandled error:
1. xterm.min.js is UMD format, not ES module — cannot use dynamic import().
   Changed to load via script tags into window.Terminal / window.FitAddon.
2. initTerminal returned a plain JS object which can't be marshaled as
   IJSObjectReference. Changed to return an int ID and use a Map-based
   registry on the JS side.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
New project: src/Aspire.TerminalHost/
- Console app using Hex1b with UnixDomainSocketPresentationAdapter
- Speaks Aspire Terminal Protocol over UDS
- Receives config via TERMINAL_SOCKET_PATH, TERMINAL_COLUMNS/ROWS/SHELL env vars
- Bridges PTY shell ↔ UDS clients

AppHost discovery (following Dashboard pattern):
- DcpOptions.TerminalHostPath for path resolution
- Three-tier discovery: env var (ASPIRE_TERMINAL_HOST_PATH) → config → assembly metadata
- Assembly metadata key: 'aspireterminalhostpath'
- MSBuild target SetTerminalHostDiscoveryAttributes in AppHost.in.targets
- Development path: artifacts/bin/Aspire.TerminalHost/{Config}/net8.0/

WithTerminal lifecycle:
- AddTerminalHostResource now generates UDS path and sets it on TerminalAnnotation
- OnInitializeResource resolves terminal host binary via DcpOptions
- Launches terminal host as child process with env var configuration
- Forwards stderr to resource logs
- Manages process lifecycle with clean shutdown

DCP flow:
- TerminalAnnotation.SocketPath → TerminalSpec on ExecutableSpec/ContainerSpec
- DCP receives terminal.enabled + terminal.socketPath in the CRD spec
- DCP can use socketPath to forward PTY I/O (Go implementation separate)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…l host

The playground's TerminalDemoResource manages its own terminal host
process lifecycle, so it should use WithTerminal(socketPathProvider)
instead of the bare WithTerminal() which now also launches a terminal
host. Using the custom overload avoids creating a conflicting hidden
TerminalHostResource.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replaces the presentation adapter approach with a presentation filter
architecture (modeled after Hex1b's DiagnosticsSocketListener):

- Terminal runs headless (Hex1b manages internal screen state)
- TerminalSocketServer is an IHex1bTerminalPresentationFilter that
  intercepts all output via OnOutputAsync and broadcasts to clients
- On client connect: CreateSnapshot().ToAnsi() captures current screen
  state and sends it as the first DATA frame after HELLO (with REPLAY flag)
- On client disconnect: terminal keeps running, accepts new connections
- On reconnect: fresh snapshot replayed, then live streaming resumes

This enables navigating away from the terminal in the Dashboard and
returning to find the same terminal state preserved.

Key changes:
- New TerminalSocketServer.cs (filter-based, session management)
- Program.cs: WithHeadless() + AddPresentationFilter(server) instead of
  WithPresentation(adapter)
- Old UnixDomainSocketPresentationAdapter kept for playground compatibility

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Refactor the WithTerminal API to match the new architecture decided for the
13.4 end-to-end work:

- TerminalAnnotation: drop SocketPath / SocketPathProvider; carry a strong
  reference to the hidden TerminalHostResource and the user-supplied
  TerminalOptions instead. The host owns its own UDS layout.

- TerminalHostResource: now public and derives from ExecutableResource so
  DCP launches it as a regular hidden executable. Carries a Parent
  reference plus the per-resource TerminalHostLayout. Constructed with a
  placeholder command (UnresolvedCommand) so we can rewrite it later.

- TerminalHostLayout (new): per-resource, per-run UDS layout — N producer
  paths under {tmp}/aspire-term-{guid}/dcp/, N consumer paths under
  host/, and one control.sock. Built via Directory.CreateTempSubdirectory
  per the repo temp-directory convention.

- TerminalHostEventingSubscriber (new): subscribes to BeforeStartEvent and
  resolves the real terminal host binary from DcpOptions.TerminalHostPath
  before DCP launches the resource. Emits a warning if the path is unset
  or if the parent's replica count drifted between WithTerminal() and
  start. Registered via TryAddEventingSubscriber in the builder.

- TerminalResourceBuilderExtensions: single overload, eager UDS layout,
  hidden host as ExecutableResource, args wired via a callback
  (--replica-count, --producer-uds xN, --consumer-uds xN, --control-uds,
  --columns, --rows, --shell). Adds a WaitAnnotation on the host
  (WaitUntilStarted for now; Phase 2 will upgrade to WaitUntilHealthy
  once the host exposes a health probe). Throws on double WithTerminal
  call.

- Stub leftovers in ExecutableCreator, AuxiliaryBackchannelRpcTarget, and
  DashboardServiceData for proper wire-up in Phases 4/5/7. Each stub is
  marked with a comment.

- Update WithTerminalTests to cover the new design (15 tests, all green).

- Drop playground/Terminals/Terminals.AppHost/TerminalDemoResource.cs and
  stub Terminals.AppHost itself; full playground rebuild lands in Phase 8.

- Add a GetTerminalInfoAsync stub on TestAppHostAuxiliaryBackchannel so
  the CLI test fake satisfies the interface.

Build is green with /p:SkipNativeBuild=true. WithTerminalTests pass 15/15.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replaces the custom binary frame protocol with Hex1b 0.137 HMP v1 and
adopts the per-replica UDS pair design from Phase 1.

The host now creates one independent Hex1bTerminal per replica using
WithHmp1UdsClient(producerUds[i]).WithHmp1UdsServer(consumerUds[i]).
DCP runs the HMP v1 producer side; viewers (CLI / Dashboard) connect
to the consumer side. State replay on reconnect is handled by Hex1b.

A small StreamJsonRpc control listener on a separate UDS exposes
GetReplicas() and Shutdown() so the AppHost backchannel can populate
GetTerminalInfoAsync() without sharing the data plane.

The host no longer auto-exits when all replicas exit -- DCP owns the
host lifetime via cancellation or the control protocol.

Removed: src/Shared/Terminal/* (custom protocol), TerminalSocketServer,
UnixDomainSocketPresentationAdapter, playground/Terminals.TerminalHost.

Added: tests/Aspire.TerminalHost.Tests with 17 tests (12 args, 5 app)
covering arg parsing edge cases plus end-to-end control-listener +
replica startup over real UDS sockets. All 17 pass on Windows.

Phase 1 WithTerminalTests (15/15) still pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
mitchdenny and others added 12 commits June 4, 2026 14:08
Per davidfowl review: drop the per-RID NuGet shipping vehicle for the terminal
host. WithTerminal() now relies on the aspire-managed multi-mode bundle the
CLI already ships, dispatching via 'terminalhost' as a subcommand arg.

- Delete eng/terminalhostpack/ (Sdk.props/targets, 7 RID csprojs, AutoImport,
  UnixFilePermissions).
- Drop the implicit Aspire.TerminalHost.Sdk.{RID} PackageReference from
  Aspire.AppHost.Sdk's targets.
- Drop terminalhostpack build/publish wiring (eng/Build.props,
  eng/Publishing.props).
- Drop the TerminalHost.Sdk artifact glob from build-cli-native-archives.yml.

Kept: inner-loop wiring (Directory.Build.props AspireTerminalHostDir,
SetTerminalHostDiscoveryAttributes target, aspireterminalhostpath assembly
metadata) so local repo dev continues to point at artifacts/bin. CLI bundle
users hit the same metadata via ResolveAspireCliBundle's managed-binary
resolution.

Trade-off: end users who consume the AppHost without the CLI bundle (IDE-only
flows that pull standalone per-RID Aspire.Dashboard.Sdk packages) get an
unresolved TerminalHostPath, so WithTerminal() becomes a silent no-op for
them. Acceptable: WithTerminal() adopters are almost always CLI users.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Karol's dev/karolz/with-terminal got native TerminalSpec.socketMode support;
  send socketMode="connect" so DCP dials our listener (drops our DCP fork).
- Switch Aspire.TerminalHost from bare ServiceCollection.BuildServiceProvider
  to Host.CreateApplicationBuilder + StartAsync. The OpenTelemetry tracer/meter
  providers are registered as DI singletons but only instantiated by
  TelemetryHostedService.StartAsync, which never runs without an IHost. Result:
  metrics and traces now actually export instead of being silently dropped.
- ASPIRE_TERMINAL_HOST_LOG_LEVEL env var (Trace..None) for verbosity control;
  propagated from AppHost to each terminal-host child.
- Playground launchSettings.json: set ASPIRE_TERMINAL_HOST_LOG_LEVEL=Debug.
- Additional lifecycle logs in TerminalReplica: "awaiting DCP", cycle index
  on DCP-connected/disconnected events, consumer endpoint path on cycle start.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Addresses 9 review threads:
- AppHostAuxiliaryBackchannel: gate GetTerminalInfoAsync on
  SupportsTerminalsV1 (not SupportsV2); an AppHost can speak aux.v2
  without terminal support compiled in.
- AuxiliaryBackchannelRpcTarget: advertise Terminals_PsV1 in
  GetCapabilitiesAsync (the CLI gates 'aspire terminal ps' on it);
  collapse the duplicate <summary> block.
- DashboardServiceData: fix the comment that claimed the consumer UDS
  was 'intentionally not surfaced' - it IS surfaced, just sensitive-
  marked; the security boundary is ITerminalConnectionResolver, not
  the snapshot.
- ITerminalConnectionResolver: docs referenced NullTerminalConnection-
  Resolver as the default registration, but the actual default is
  DefaultTerminalConnectionResolver. Removed the dead NullTerminal-
  ConnectionResolver class (never DI-registered, no references).
- Aspire.TerminalHost.csproj: rewrite the CA2007 justification - we
  do not 'control the synchronization context', we just have none in
  a console exe and all awaits use ConfigureAwait(false).
- Dcp/Model/Executable.cs + Container.cs: rename 'Aspire Terminal
  Protocol' (no such thing) to Hex1b HMP v1 with link.
- Dcp/ExecutableCreator.cs: expand the PTY platform comment to spell
  out ConPTY vs Unix98 paths and clarify what 'not supported' means.
- docs/specs/with-terminal.md: per-replica process count (not per-
  resource); controlUdsPath lifetime is per-replica; rewrite DCP
  integration section to match the actual TerminalSpec wire shape
  (udsPath/socketMode/cols/rows), not the fictional terminal.enabled
  shape.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replaces the hand-rolled switch-based parser with the same
System.CommandLine 2.x API used everywhere else in the Aspire CLI.

Benefits called out by the review:
- Consistent error messages with the rest of the CLI.
- Uniform duplicate-detection across ALL flags (the previous parser
  only rejected duplicate --producer-uds / --consumer-uds; the other
  four flags were silently last-write-wins). Implemented as a single
  validator on a SingleValueOption<T> helper.
- Conversion errors (e.g. '--columns abc') now flow through the
  System.CommandLine pipeline and surface with the standard
  'Cannot parse argument' message.
- TreatUnmatchedTokensAsErrors=true makes unknown flags fail loudly
  rather than being silently dropped.

The user-facing TerminalHostArgs surface (properties + Parse +
TerminalHostArgsException) is unchanged so callers and the existing
12 unit tests still hold.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
#13 (UX) TerminalViewerApp: track user-initiated Detach (Ctrl+B D) and
suppress the post-finally rethrow of _embeddedFault when the embedded
RunAsync faults purely as a side effect of the outer-app teardown.
Without this, a clean detach commonly surfaces as a misleading
'Could not connect to terminal session' error because the embedded
SocketException from the torn consumer UDS gets captured and rethrown.

#14 (DoS) TerminalHostControlListener: cap active control RPC sessions
to 1. The control protocol is documented as a single AppHost client;
the old loop accepted unbounded concurrent connections, each allocating
a NetworkStream + HeaderDelimitedMessageHandler and three RPC method
registrations. Defence in depth on top of the existing 0600 socket perm.

#15 (silent failure) TerminalHostApp: observe TerminalReplica.RunTask in
the shutdown wait and exit non-zero if the recycle loop faults or
completes unexpectedly. The old code blocked on Task.Delay(Infinite)
forever, so a permanent setup failure (bad UDS path, EADDRINUSE on a
stale .sock, permission denied) left the host 'ready' with no producer
and the only signal to the AppHost was RestartCount climbing while
IsAlive stayed false.

#16 (DoS) hmp1-client.js: validate frame length is in [0, 16MiB) before
slicing the buffer or allocating. Negative length silently desynced the
buffer for the lifetime of the connection; multi-GiB length OOM'd the
tab. Protocol errors now close the WS with code 1002 so the existing
reconnect path takes over.

#17 (leak) TerminalHostControlListener.StartAsync: wrap Bind/Listen/
SetUnixFileMode in try/catch that disposes the Socket and deletes any
partially-bound socket file before rethrowing.

#18 (robustness) TerminalViewerApp.Render: guard Console.WindowWidth/
Height with try/catch (IOException) to match the existing
TryGetLocalDimensions pattern. Hex1b doesn't expose terminal dims on
RootContext or Hex1bApp, so falling through to the BCL is unavoidable -
but the BCL raises IOException when there is no controlling TTY.

#19 (robustness) TerminalHostEventingSubscriber.ParseInvocationArgs:
support POSIX-ish single/double quoting so users can pass arguments
that contain spaces. System.CommandLine.CommandLineStringSplitter is
not public in S.CL 2.0.x, so this is a focused reimplementation of the
common subset we need (single-quote literal; double-quote with \" and
\\ escape).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
#21 TerminalCommandViewerOptionTests.cs:
  - Replace Console.SetOut with System.CommandLine InvocationConfiguration.Output.
    Console.SetOut mutates process-wide state and this project runs tests in
    parallel across classes.

#22 WithTerminalTests.TerminalHostResourcesAreExcludedFromManifest:
  - Already addressed in a prior batch (asserts Same(...Ignore, ...)). No change
    needed here; thread is being resolved on GitHub.

#23 WithTerminalTests.BuildAndPublishBeforeStartAsync:
  - Change helper to dispose the built DistributedApplication internally and
    return only the model. Callers no longer discard the app via tuple, which
    leaked the app's background services until finalization. Updated all six
    call sites.

#24 WithTerminalTests.WithTerminalAcceptsCustomOptions:
  - Rename to WithTerminalOptionsCallbackUpdatesAnnotation to match scope and
    add an XML comment pointing at the propagation tests
    (TerminalHostHasCommandLineArgsForLayoutPaths).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The option toggles visibility of terminal host resources on a single parent
resource at a time (the resource the WithTerminal() call is chained off). The
singular form reads naturally on the call site:

  resource.WithTerminal(options => options.ShowTerminalHost = true);

Touches TerminalAnnotation.ShowTerminalHost, the WithTerminal/IsHidden wiring
in TerminalResourceBuilderExtensions, the test
ShowTerminalHostOptionMakesTerminalHostsVisible, and the four call sites in
the Terminals playground AppHost.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The only functional change to AddReferenceToDashboardAndDCP in this PR is
the new `and '$(AspireUseCliBundle)' != 'true'` Condition on the Target
element - the surrounding comment did not need to be reflowed and the
reflow created review noise.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The two capability flags (terminals.v1 covering GetTerminalInfoAsync's
per-replica info, and terminals.ps.v1 covering ListTerminalsAsync +
TerminalReplicaInfo's per-replica current grid size / attached peers /
peer details) ship together as a single feature and were always advertised
in lockstep by Aspire.Hosting. The split added noise on the call sites
without any practical version-skew benefit.

Changes:
- BackchannelDataTypes: delete Terminals_PsV1, fold its description into
  the Terminals_V1 summary (with a historical note about the rename) and
  retarget all four <see cref> doc references on TerminalReplicaInfo and
  ListTerminalsRequest.
- IAppHostAuxiliaryBackchannel: delete SupportsTerminalsPsV1, expand
  SupportsTerminalsV1's doc to cover both surfaces, retarget the
  ListTerminalsAsync remarks cref.
- AppHostAuxiliaryBackchannel: delete the SupportsTerminalsPsV1 property
  and switch ListTerminalsAsync's gate to SupportsTerminalsV1.
- TerminalPsCommand: gate on SupportsTerminalsV1 and update the error
  message + class remarks to refer to terminals.v1.
- Tests: TestAppHostAuxiliaryBackchannel, TerminalCommandTests
  (incl. CapturingTerminalAppHostBackchannel + the renamed
  WhenAppHostLacksTerminalsV1Capability_ReturnsAppHostIncompatible test),
  and the BackchannelJsonSerializerContextTests comment all switched.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Reviewer flagged 'Phase 17 + UX transplant:' on TerminalAttachCommand.cs as
making no sense to future readers. Did a sweep across the PR-scope files
and rewrote every other 'Phase N' / 'Phase Nx hardening' comment to convey
the actual constraint or follow-up without referencing dev-time milestones
nobody outside this branch can decode.

Touched comments only (no behavior change) in:
- src/Aspire.Cli/Commands/TerminalAttachCommand.cs (the cited site)
- src/Aspire.Dashboard/Terminal/TerminalWebSocketProxy.cs (2x)
- src/Aspire.Hosting/Dcp/ExecutableCreator.cs
- src/Aspire.Hosting/TerminalResourceBuilderExtensions.cs
- tests/Aspire.Hosting.Tests/Dcp/DcpExecutorTests.cs
- tests/Aspire.Cli.Tests/Commands/TerminalCommandViewerOptionTests.cs
- tests/Aspire.TerminalHost.Tests/TerminalHostAppTests.cs

Also caught + fixed a build break that surfaced from the prior
'consolidate Terminals_PsV1 into Terminals_V1' commit:
AuxiliaryBackchannelRpcTarget.cs:55 still listed Terminals_PsV1 in its
advertised capabilities array. Replaced with the single Terminals_V1
constant. This means the AppHost now actually advertises the capability
its CLI gate checks for - which the earlier commit's reply on the
consolidation thread incorrectly described as 'never advertised'.

Phase-numbered comments left alone in unrelated files (Kubernetes
HelmDeploymentEngine, AgentInitCommand, PackageJsonMerger, Kubernetes
deploy E2E tests) - those describe legitimate phased pipelines within
their own scope and are not session narration.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Track the BaseCommand ctor consolidation from #17652 (merged to main
2026-06-03), which replaced the individual IFeatures + ICliUpdateNotifier +
CliExecutionContext + IInteractionService + AspireCliTelemetry parameters
with a single CommonCommandServices bag.

TerminalCommand, TerminalAttachCommand, and TerminalPsCommand now take
CommonCommandServices and forward it to base(...). Per-command-specific
deps (IAuxiliaryBackchannelMonitor, IProjectLocator, ILogger<T>) are still
direct parameters resolved by DI. _interactionService field and
AppHostConnectionResolver construction now pull from services.* instead of
the deleted parameters.

Also drop now-unused usings (Configuration, Telemetry, Utils on the parent
TerminalCommand; Configuration + Telemetry on the two leaves; the
TerminalPsCommand keeps Utils for the AddBoldColumn extension method).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
WithTerminal<T>(this IResourceBuilder<T>, ...) is a new public extension
on the Aspire.Hosting builder surface. The polyglot codegen tests reflect
over that surface and emit a withTerminal capability + per-resource
WithTerminal() method into every Go/Java/Python/Rust/TypeScript snapshot.

Updates the 5 TwoPassScanningGeneratedAspire.verified.* snapshots and
the TypeScript HostingContainerResourceCapabilities.verified.txt
capability list. No production code changes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mitchdenny mitchdenny force-pushed the feature/with-terminal branch from 7cd67f9 to 35cae1d Compare June 4, 2026 04:15
if (!connectionResult.Success)
{
_interactionService.DisplayMessage(KnownEmojis.Information, connectionResult.ErrorMessage);
return CommandResult.Success();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This returns success for project-resolution failures too. I can repro with aspire terminal attach web --apphost /tmp/does-not-exist.csproj: it prints the invalid apphost message but exits 0. Other action commands use AppHostConnectionResultHandler.DisplayFailureAsInformation, which preserves the non-zero exit code for invalid/ambiguous AppHost resolution. Could we route this through the shared handler?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in aa1f31140 — both terminal attach and terminal ps now route failed AppHostConnectionResult values through AppHostConnectionResultHandler.DisplayFailureAsInformation. Project-resolution errors (bad --apphost path, missing SDK, ambiguous project) now propagate their real exit code instead of returning 0.

For terminal ps --format json I kept the []-on-no-apphost behavior (so JSON consumers don't have to special-case "no AppHost running"), but project-resolution errors short-circuit out through the shared handler with the correct non-zero exit code.

@adamint

adamint commented Jun 4, 2026

Copy link
Copy Markdown
Member

Tested with the PR CLI artifact 13.5.0-pr.17866.g35cae1d9 (matches 35cae1d9). aspire new aspire-starter against the PR hive completed successfully; TerminalCommandTests/TerminalCommandViewerOptionTests passed (19 tests), and the dashboard terminal resolver/proxy tests passed (20 tests). I left one inline comment for the invalid --apphost exit code on terminal attach.

@adamint adamint self-assigned this Jun 4, 2026

@adamint adamint left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High-confidence finding from review:

The new terminal toolbar UI in ConsoleLogs.razor / ConsoleLogs.razor.cs introduces hard-coded user-visible strings such as Decrease font size, Terminal font size, Increase font size, Terminal grid size, Current terminal grid, and the primary-button labels/titles (Take control, Release control, Connecting to the terminal session…). Dashboard UI strings need to come from resources, with the matching .Designer.cs/xlf update, so these can be localized like the rest of the page.

CLI (TerminalAttachCommand, TerminalPsCommand): route failed connection
results through AppHostConnectionResultHandler so bad --apphost paths,
missing SDKs, and other project-resolution errors propagate the proper
non-zero exit code instead of silently returning 0. For
'terminal ps --format json', JSON consumers still get '[]' on
'no running AppHost' (a normal state), but project-resolution errors
now error out with their real exit code.

Dashboard (ConsoleLogs terminal toolbar): pull the previously hard-coded
toolbar strings (Decrease/Increase font size, Terminal font size,
Terminal grid size, Current terminal grid, Take control, Primary,
Connecting…, primary/no-primary/viewer/connecting titles) into
ConsoleLogs.resx so they can be localized like the rest of the page.
Includes regenerated .Designer.cs and UpdateXlf-refreshed xlf files
for all 13 locales.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mitchdenny

Copy link
Copy Markdown
Member Author

Fixed in aa1f31140 — moved all the new terminal toolbar strings (Decrease font size, Terminal font size, Increase font size, Terminal grid size, Current terminal grid, Take control, Primary, Connecting…, and the four primary/no-primary/viewer/connecting button titles) into ConsoleLogs.resx with TerminalToolbar* keys.

Updated ConsoleLogs.razor and ConsoleLogs.razor.cs to consume them via the existing IStringLocalizer<ConsoleLogs> Loc (made the two TerminalPrimaryButton* helpers instance methods since they now need Loc). UpdateXlf refreshed the .Designer.cs and all 13 locale xlfs.

cc @adamint

- Remove the purple drop-shadow halo on the terminal frame (drop
  --aspire-term-glow and the box-shadow). Shrink .terminal-pane
  padding from 36px to 8px now that the shadow no longer needs blur
  clearance.
- Top-align the terminal frame (#terminal align-items: flex-start)
  so the prompt starts at the natural reading position instead of
  floating in the middle of the available space.
- Recover the terminal toolbar after viewport / layout transitions
  that drop the cached snapshot: add refreshToolbarState() on the JS
  side (bypasses the change-detection cache and re-flushes) and
  TerminalView.RefreshToolbarStateAsync(); ConsoleLogs.OnAfterRenderAsync
  triggers it when _terminalToolbarState is null but the JS terminal
  is still live.
- When a terminal-enabled resource is selected, render the page
  header and browser tab title as 'Terminal' / '{app} terminal'
  instead of 'Console logs' / '{app} console logs'. Adds
  TerminalHeader and TerminalPageTitle to ConsoleLogs.resx (and
  regenerates all 13 xlf locales via /t:UpdateXlf).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

@davidfowl davidfowl left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found 14 regression/test-coverage issues across terminal hosting, dashboard, and CLI surfaces.

Comment thread src/Aspire.TerminalHost/TerminalHostControlListener.cs
Comment thread src/Aspire.Hosting/Dcp/ExecutableCreator.cs
Comment thread src/Aspire.Hosting/Dcp/ContainerCreator.cs
Comment thread src/Aspire.Hosting/Backchannel/AuxiliaryBackchannelRpcTarget.cs Outdated
Comment thread tests/Aspire.Hosting.Tests/Backchannel/BackchannelContractTests.cs
Comment thread src/Aspire.Dashboard/Components/Pages/ConsoleLogs.razor
Comment thread src/Aspire.Dashboard/Components/Controls/TerminalView.razor.js
Comment thread tests/Aspire.Cli.Tests/Commands/TerminalCommandTests.cs
Comment thread src/Aspire.Cli/Backchannel/AppHostAuxiliaryBackchannel.cs
Comment thread src/Aspire.Cli/Commands/TerminalAttachCommand.cs
Comment thread src/Aspire.Hosting/Dcp/DcpOptions.cs
midenn and others added 4 commits June 7, 2026 12:20
- Rewrite DcpOptions.cs:238 discovery-order comment to drop misleading
  'bundle fallback' framing; aspire-managed is now the only shipping
  terminal-host form, so describe primary (build-time metadata) vs
  runtime-inference paths accurately.
- Add TerminalHostFailureDiagnosticService (IHostedService) that watches
  ResourceNotificationService for TerminalHostResource failure states,
  unhides the failed host, and injects an actionable diagnostic into the
  host's resource log. When the resolved binary was the bundled
  aspire-managed via the terminalhost dispatcher, also surfaces the most
  common cause and recovery (run 'aspire update --self').
- Wire it up next to TerminalHostEventingSubscriber in
  DistributedApplicationBuilder.
- Tests: 5 new in TerminalHostFailureDiagnosticServiceTests covering
  FailedToStart, Exited+non-zero, clean shutdown (Exited+0 → no
  diagnostic), non-bundle path, and dedupe of repeated events.

Also tests for #14 + #15 (TerminalPsCommand --format json round-trip,
real-backchannel capability-gate coverage).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
AppHosts generated by `aspire new` default to per-RID NuGets
(AspireUseCliBundle != true). No per-RID NuGet stamps the terminal
host metadata path today, so those AppHosts hit <unresolved-aspire-terminalhost>
when WithTerminal() resources start. ConfigureCliBundleEnvironmentAsync
was gated on IsUsingCliBundleAsync, so the env-var path didn't fire
for them either.

Drop the gate for terminal host env vars only. aspire-managed in the
bundle exposes the `terminalhost` subcommand regardless of how the
AppHost was built, and injecting ASPIRE_TERMINAL_HOST_PATH is
additive (no per-RID NuGet to clobber). DCP and Dashboard env vars
stay gated to avoid clobbering per-RID NuGet metadata.

Treat ASPIRE_TERMINAL_HOST_PATH and ASPIRE_TERMINAL_HOST_INVOCATION_ARGS
as a pair: if a caller pre-populates the path env var (e.g. side-loading
a custom build), don't overwrite the args — a custom binary may not
understand the "terminalhost" dispatcher arg.

Add BundleDiscovery constants for both env var names and use them in
DcpOptions to avoid drift.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The control listener's accept loop bailed on any SocketException, which
caused ConcurrentControlConnectsAreRefusedDownToOne to fail intermittently
on Ubuntu CI: under load, AcceptAsync can surface EAGAIN even though it is
awaiting, which killed the control channel for the rest of the process.

Bump the listen backlog from 5 to 16 to absorb reconnect bursts (the
single-client contract still holds — extra accepts are immediately closed
by the existing 'one client wins' logic), and catch transient socket
errors (EAGAIN/EWOULDBLOCK/EINTR) so the loop retries instead of dying.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… runs

When running `dotnet run --project src/Aspire.Cli` inside the Aspire repo
checkout, the bundle layout discovered for terminal-host injection resolves
to the user's installed CLI cache (e.g. ~/.aspire/bundle/) whose
aspire-managed predates the `terminalhost` subcommand. That caused
WithTerminal() resources launched via the repo CLI to fail with a
confusing "older CLI" diagnostic, even though the repo had just built a
working aspire-managed in artifacts/bin/Aspire.Managed/Debug/net10.0/.

Prefer the repo-local artifact over the bundle layout when
AspireRepositoryDetector locates an Aspire repo root. The detector is
DEBUG-gated (walks for Aspire.slnx) and release-only honors
ASPIRE_REPO_ROOT, so installed CLIs are unaffected.

Also fall through to the terminal-host injection when no bundle layout
exists at all, so a clean dev machine (no `aspire` install) still gets
terminal host wired up from the repo build.

Adds a test seam (RepoLocalManagedPathProviderOverride) so existing tests
that build fake bundle layouts under temp paths don't get shadowed by the
real in-repo build artifact, plus a new test covering the repo-local
override path.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

CLI E2E Tests unknown — 113 passed, 0 failed, 2 unknown (commit 41d9fe5)

View all recordings
- Test Detail
AddPackageInteractiveWhileAppHostRunningDetached Recording · Job · CLI logs
AddPackageWhileAppHostRunningDetached Recording · Job · CLI logs
AgentCommands_AllHelpOutputs_AreCorrect Recording · Job · CLI logs
AgentInitCommand_DefaultSelection_InstallsDefaultSkills Recording · Job · CLI logs
AgentInitCommand_MigratesDeprecatedConfig Recording · Job · CLI logs
AgentInit_NonInteractive_BundleOnlySkillsNotInCatalog Recording · Job · CLI logs
AgentMcpListStructuredLogsReturnsLogsFromStarterApp Recording · Job · CLI logs
AgentMcpListStructuredLogsReturnsLogsFromStarterApp_DevLocalhost Recording · Job · CLI logs
AgentMcpListStructuredLogsReturnsLogsFromStarterApp_Isolated Recording · Job · CLI logs
AllPublishMethodsBuildDockerImages Recording · Job · CLI logs
AspireAddAndStartWorkAgainstLegacyAppHostTs Recording · Job · CLI logs
AspireAddPackageVersionToDirectoryPackagesProps Recording · Job · CLI logs
AspireInitSingleFileAppHostRunsViaDotnetRunAppHost Recording · Job · CLI logs
AspireInit_ExistingAppHostDir_RecreatesNuGetConfigKeepsFiles Recording · Job · CLI logs
AspireInit_SolutionFile_BuildsAgainstChannelHive Recording · Job · CLI logs
AspireStartUpdatesStaleTypeScriptAppHostPath Recording · Job · CLI logs
AspireUpdateRemovesAppHostPackageVersionFromDirectoryPackagesProps Recording · Job · CLI logs
AspireUpdateRemovesOrphanAppHostPackageVersionWhenSdkAlreadyCurrent Recording · Job · CLI logs
Banner_DisplayedOnFirstRun Recording · Job · CLI logs
Banner_DisplayedWithExplicitFlag Recording · Job · CLI logs
Banner_NotDisplayedWithNoLogoFlag Recording · Job · CLI logs
CertificatesClean_RemovesCertificates Recording · Job · CLI logs
CertificatesTrust_WithNoCert_CreatesAndTrustsCertificate Recording · Job · CLI logs
CertificatesTrust_WithUntrustedCert_TrustsCertificate Recording · Job · CLI logs
ConfigSetGet_CreatesNestedJsonFormat Recording · Job · CLI logs
CreateAndRunAspireStarterProject Recording · Job · CLI logs
CreateAndRunAspireStarterProjectWithBundle Recording · Job · CLI logs
CreateAndRunEmptyAppHostProject Recording · Job · CLI logs
CreateAndRunJavaEmptyAppHostProject Recording · Job · CLI logs
CreateAndRunJsReactProject Recording · Job · CLI logs
CreateAndRunPolyglotAppHostWithDevLocalhostUrls Recording · Job · CLI logs
CreateAndRunPythonReactProject Recording · Job · CLI logs
CreateAndRunTypeScriptEmptyAppHostProject Recording · Job · CLI logs
CreateAndRunTypeScriptStarterProject Recording · Job · CLI logs
CreateJavaAppHostWithViteApp Recording · Job · CLI logs
CreateTypeScriptAppHostWithViteApp_UsesConfiguredToolchain Recording · Job · CLI logs
DashboardRunWithAgentMcpListTracesReturnsNoTraces Recording · Job · CLI logs
DashboardRunWithAgentMcpListTracesReturnsNoTraces_DevLocalhost Recording · Job · CLI logs
DashboardRunWithOtelTracesReturnsNoTraces Recording · Job · CLI logs
DashboardRunWithOtelTracesReturnsNoTraces_DevLocalhost Recording · Job · CLI logs
DeployK8sBasicApiService Recording · Job · CLI logs
DeployK8sWithExternalHelmChart Recording · Job · CLI logs
DeployK8sWithGarnet Recording · Job · CLI logs
DeployK8sWithMongoDB Recording · Job · CLI logs
DeployK8sWithMySql Recording · Job · CLI logs
DeployK8sWithPostgres Recording · Job · CLI logs
DeployK8sWithRabbitMQ Recording · Job · CLI logs
DeployK8sWithRedis Recording · Job · CLI logs
DeployK8sWithSqlServer Recording · Job · CLI logs
DeployK8sWithValkey Recording · Job · CLI logs
DeployTypeScriptAppToKubernetes Recording · Job · CLI logs
DescribeCommandResolvesReplicaNames Recording · Job · CLI logs
DescribeCommandShowsRunningResources Recording · Job · CLI logs
DetachFormatJsonProducesValidJson Recording · Job · CLI logs
DetachFormatJsonProducesValidJsonWhenRestartingExistingInstance Recording · Job · CLI logs
DoPublishAndDeployListStepsWork Recording · Job · CLI logs
DocsCommand_RendersInteractiveMarkdownFromLocalSource Recording · Job · CLI logs
DoctorCommand_DetectsDeprecatedAgentConfig Recording · Job · CLI logs
DoctorCommand_TypeScriptAppHostReportsMissingConfiguredToolchain Recording · Job · CLI logs
DoctorCommand_WithSslCertDir_ShowsTrusted Recording · Job · CLI logs
DoctorCommand_WithoutSslCertDir_ShowsPartiallyTrusted Recording · Job · CLI logs
DotNetRunFileBasedAppHostUsesAspireCliBundle Recording · Job · CLI logs
DotNetRunProjectAppHostUsesAspireCliBundle Recording · Job · CLI logs
GatewayWithoutExternalEndpoint_FailsPublishWithGuidance Recording · Job · CLI logs
GeneratedAspireDevScript_StartsWatchMode_WithConfiguredToolchain Recording · Job · CLI logs
GlobalMigration_HandlesCommentsAndTrailingCommas Recording · Job · CLI logs
GlobalMigration_HandlesMalformedLegacyJson Recording · Job · CLI logs
GlobalMigration_PreservesAllValueTypes Recording · Job · CLI logs
GlobalMigration_SkipsWhenNewConfigExists Recording · Job · CLI logs
GlobalSettings_MigratedFromLegacyFormat Recording · Job · CLI logs
IngressWithoutExternalEndpoint_FailsPublishWithGuidance Recording · Job · CLI logs
InitTypeScriptAppHost_AugmentsExistingViteRepoInWorkspaceSubdirectory Recording · Job · CLI logs
InteractiveCSharpInitCreatesExpectedFiles Recording · Job · CLI logs
InvalidAppHostPathWithComments_IsHealedOnRun Recording · Job · CLI logs
JavaScriptHostingApisRunFromTypeScriptAppHost Recording · Job · CLI logs
LatestCliCanStartStableChannelAppHost Recording · Job · CLI logs
LatestCliCanStartStableChannelTypeScriptAppHost Recording · Job · CLI logs
LegacySettingsMigration_AdjustsRelativeAppHostPath Recording · Job · CLI logs
LogsCommandShowsResourceLogs Recording · Job · CLI logs
OtelLogsReturnsStructuredLogsFromStarterApp Recording · Job · CLI logs
OtelLogsReturnsStructuredLogsFromStarterAppIsolated Recording · Job · CLI logs
ProcessCommandCallbackReceivesCliArguments Recording · Job · CLI logs
PsCommandListsRunningAppHost Recording · Job · CLI logs
PsFormatJsonOutputsOnlyJsonToStdout Recording · Job · CLI logs
PublishJavaScriptPatternsGeneratesExpectedDockerComposeArtifacts Recording · Job · CLI logs
PublishWithConfigureEnvFileUpdatesEnvOutput Recording · Job · CLI logs
PublishWithDockerComposeServiceCallbackSucceeds Recording · Job · CLI logs
PublishWithoutOutputPathUsesAppHostDirectoryDefault Recording · Job · CLI logs
ResourceCommand_FailedExec_ShowsLogPathAndLogHasEntries Recording · Job · CLI logs
ResourceCommand_SetAndDeleteParameterUpdatesDescribeOutput Recording · Job · CLI logs
RestoreGeneratesSdkFiles Recording · Job · CLI logs
RestoreGeneratesSdkFiles_WithConfiguredToolchain Recording · Job · CLI logs
RestoreRefreshesGeneratedSdkAfterAddingIntegration Recording · Job · CLI logs
RestoreSupportsConfigOnlyHelperPackageAndCrossPackageTypes Recording · Job · CLI logs
RunFromParentDirectory_UsesExistingConfigNearAppHost Recording · Job · CLI logs
RunReportsSyntaxErrorsForDotNetAppHost Recording · Job · CLI logs
RunReportsSyntaxErrorsForTypeScriptAppHost Recording · Job · CLI logs
SecretCrudOnDotNetAppHost Recording · Job · CLI logs
SecretCrudOnTypeScriptAppHost Recording · Job · CLI logs
StagingChannel_ConfigureAndVerifySettings_ThenSwitchChannels Recording · Job · CLI logs
StartAndWaitForTypeScriptSqlServerAppHostWithNativeAssets Recording · Job · CLI logs
StartReportsSyntaxErrorsForDotNetAppHost Recording · Job · CLI logs
StartReportsSyntaxErrorsForTypeScriptAppHost Recording · Job · CLI logs
StopAllAppHostsFromAppHostDirectory Recording · Job · CLI logs
StopJavaPolyglotAppHostUsingApphostDirectory Recording · Job · CLI logs
StopNonInteractiveSingleAppHost Recording · Job · CLI logs
StopTypeScriptPolyglotAppHostUsingApphostDirectory Recording · Job · CLI logs
StopWithNoRunningAppHostExitsSuccessfully Recording · Job · CLI logs
TypeScriptAppHostRunDoesNotDeadlockWhenLazyOptionsInvokeAsyncCallback Recording · Job · CLI logs
TypeScriptAppHostWithVite_AllowsDifferentGuestPkgManager Recording · Job · CLI logs
UnAwaitedChainsCompileWithAutoResolvePromises Recording · Job · CLI logs
UpdateToStable_CSharpEmptyAppHost_KeepsConfigChannel Recording · Job · CLI logs
UpdateToStable_CSharpSingleFileInit_KeepsConfigChannel Recording · Job · CLI logs
UpdateToStable_TypeScriptSingleFileInit_KeepsConfigChannel Recording · Job · CLI logs
UpdateToStable_TypeScript_PreviewsStablePkgsAndKeepsChannel Recording · Job · CLI logs

📹 Recordings uploaded automatically from CI run #27181931389

mitchdenny added a commit to mitchdenny/aspire that referenced this pull request Jun 9, 2026
CLI (TerminalAttachCommand, TerminalPsCommand): route failed connection
results through AppHostConnectionResultHandler so bad --apphost paths,
missing SDKs, and other project-resolution errors propagate the proper
non-zero exit code instead of silently returning 0. For
'terminal ps --format json', JSON consumers still get '[]' on
'no running AppHost' (a normal state), but project-resolution errors
now error out with their real exit code.

Dashboard (ConsoleLogs terminal toolbar): pull the previously hard-coded
toolbar strings (Decrease/Increase font size, Terminal font size,
Terminal grid size, Current terminal grid, Take control, Primary,
Connecting…, primary/no-primary/viewer/connecting titles) into
ConsoleLogs.resx so they can be localized like the rest of the page.
Includes regenerated .Designer.cs and UpdateXlf-refreshed xlf files
for all 13 locales.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants