Repository navigation
[C#DK v2 Aspire integration] Expose apphost query API and aspire resource management API from aspire extension #17705
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Adam Ratzman (adamint)
merged 26 commits into
microsoft:main
from
LittleLittleCloud:u/xiaoyun/aspireC#DK
Jun 19, 2026
Merged
Changes from 15 commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
2704831
Implement public API for Aspire extension and add fetchAppHostsOnce m…
LittleLittleCloud 91dc92d
Merge remote-tracking branch 'upstream/main' into pr-17705-csdk
adamint a030a21
Add Aspire test run DCP leases
adamint 33dcde3
Clean lease notifications after release
adamint 0e68f5b
Address Aspire extension API review feedback
adamint 8d8861b
Harden Aspire test run lease integration
adamint 12f2768
Refactor Aspire API to use DCP server for test run session management
LittleLittleCloud 45487b5
Merge branch 'main' into u/xiaoyun/aspireC#DK
LittleLittleCloud 2129f65
Refactor AspireDcpServer constructor to remove unused app parameter a…
LittleLittleCloud fdbba30
Update API function signatures to use inline arrow functions for DCP …
LittleLittleCloud c8739a3
Merge branch 'main' into u/xiaoyun/aspireC#DK
LittleLittleCloud b918206
Fix extension test failures by returning full E2E API from activate
LittleLittleCloud 5ca3d02
Refactor Aspire extension API: consolidate resource command functions…
LittleLittleCloud b9a3118
Fix stopRunSession cleanup and remove redundant closeLeaseNotificatio…
LittleLittleCloud d6666bc
Merge branch 'main' into u/xiaoyun/aspireC#DK
LittleLittleCloud 1321ec5
Enhance resource command handling and error logging in DCP server and…
LittleLittleCloud 9939510
Remove terminalProvider parameter from createExtensionApi function ca…
LittleLittleCloud 9fb1d95
Refactor process termination logic in AppHostDataRepository for clarity
LittleLittleCloud 79b9dbe
Refactor DCP server and test run session manager for improved session…
LittleLittleCloud bbdefca
Refactor AspireDcpServer and TestRunSessionManager for improved clari…
LittleLittleCloud 4d1ce6e
Fix test run session DCP credentials
adamint 03f2ff5
Merge remote-tracking branch 'upstream/main' into copilot/pr-17705-fix
adamint 8dbae32
Fix VS Code extension API lifecycle issues
adamint 0131c76
Handle selected test comment permission failures
adamint 778aea1
Merge upstream/main into PR 17705 fixes
adamint 08fabc5
Grant issue comment permission to CI tests workflow
adamint File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,143 @@ | ||
| import { randomBytes, timingSafeEqual } from 'crypto'; | ||
| import { getRunSessionInfo, getSupportedCapabilities } from '../capabilities'; | ||
| import { generateToken } from '../utils/security'; | ||
| import { DcpServerConnectionInfo, RunSessionInfo } from './types'; | ||
|
|
||
| export interface TestRunSessionAcquireOptions { | ||
| debug: boolean; | ||
| } | ||
|
|
||
| export interface AcquiredTestRunSession { | ||
| id: string; | ||
| sessionId: string; | ||
| env: Record<string, string>; | ||
| } | ||
|
|
||
| export interface TestRunSessionLease { | ||
| id: string; | ||
| sessionId: string; | ||
| token: string; | ||
| expiresAt: number; | ||
| } | ||
|
|
||
| export interface TestRunSessionManagerOptions { | ||
| leaseLifetimeMs?: number; | ||
| now?: () => number; | ||
| } | ||
|
|
||
| export class TestRunSessionManager { | ||
| private readonly leases = new Map<string, TestRunSessionLease>(); | ||
| private readonly leaseLifetimeMs?: number; | ||
| private readonly now: () => number; | ||
|
|
||
| constructor( | ||
| private readonly connectionInfo: Pick<DcpServerConnectionInfo, 'address' | 'certificate'>, | ||
| private readonly getSupportedLaunchConfigurations: () => string[] = getSupportedCapabilities, | ||
| options: TestRunSessionManagerOptions = {}) { | ||
| this.leaseLifetimeMs = options.leaseLifetimeMs; | ||
| this.now = options.now ?? Date.now; | ||
| } | ||
|
|
||
| acquire(options: TestRunSessionAcquireOptions): AcquiredTestRunSession { | ||
| this.removeExpiredLeases(); | ||
|
|
||
| const id = generateToken(); | ||
| const sessionId = `aspire-extension-test-run-${randomBytes(16).toString('hex')}`; | ||
| const token = generateToken(); | ||
| const runSessionInfo: RunSessionInfo = { | ||
| ...getRunSessionInfo(), | ||
| supported_launch_configurations: this.getSupportedLaunchConfigurations() | ||
| }; | ||
|
|
||
| this.leases.set(id, { id, sessionId, token, expiresAt: this.getExpiresAt() }); | ||
|
|
||
| return { | ||
| id, | ||
| sessionId, | ||
| env: { | ||
| DEBUG_SESSION_PORT: this.connectionInfo.address, | ||
| DEBUG_SESSION_TOKEN: token, | ||
| DEBUG_SESSION_SERVER_CERTIFICATE: this.connectionInfo.certificate, | ||
| DCP_INSTANCE_ID_PREFIX: `${sessionId}-`, | ||
| DEBUG_SESSION_RUN_MODE: options.debug ? 'Debug' : 'NoDebug', | ||
| DEBUG_SESSION_INFO: JSON.stringify(runSessionInfo) | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| release(id: string): TestRunSessionLease | undefined { | ||
| const lease = this.leases.get(id); | ||
| this.leases.delete(id); | ||
| return lease; | ||
| } | ||
|
|
||
| isActive(id: string): boolean { | ||
| const lease = this.leases.get(id); | ||
| if (!lease) { | ||
| return false; | ||
| } | ||
|
|
||
| if (this.isExpired(lease)) { | ||
| this.leases.delete(id); | ||
| return false; | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| tryAuthorizeDcpRequest(dcpId: string, token: string): TestRunSessionLease | undefined { | ||
| const lease = this.tryGetLeaseForDcpId(dcpId); | ||
| if (!lease) { | ||
| return undefined; | ||
| } | ||
|
|
||
| const bearerTokenBuffer = Buffer.from(token); | ||
| const expectedTokenBuffer = Buffer.from(lease.token); | ||
| if (bearerTokenBuffer.length !== expectedTokenBuffer.length) { | ||
| return undefined; | ||
| } | ||
|
|
||
| if (timingSafeEqual(bearerTokenBuffer, expectedTokenBuffer) === false) { | ||
| return undefined; | ||
| } | ||
|
|
||
| return lease; | ||
| } | ||
|
|
||
| tryGetLeaseForDcpId(dcpId: string): TestRunSessionLease | undefined { | ||
| for (const lease of this.leases.values()) { | ||
| if (dcpId.startsWith(`${lease.sessionId}-`)) { | ||
| if (this.isExpired(lease)) { | ||
| this.leases.delete(lease.id); | ||
| return undefined; | ||
| } | ||
|
|
||
| return lease; | ||
| } | ||
| } | ||
|
|
||
| return undefined; | ||
| } | ||
|
|
||
| private removeExpiredLeases(): void { | ||
| for (const lease of this.leases.values()) { | ||
| if (this.isExpired(lease)) { | ||
| this.leases.delete(lease.id); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private isExpired(lease: TestRunSessionLease): boolean { | ||
| if (this.leaseLifetimeMs === undefined) { | ||
| return false; | ||
| } | ||
|
|
||
| return this.now() >= lease.expiresAt; | ||
| } | ||
|
|
||
| private getExpiresAt(): number { | ||
| return this.leaseLifetimeMs === undefined | ||
| ? Number.POSITIVE_INFINITY | ||
| : this.now() + this.leaseLifetimeMs; | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.