-
Notifications
You must be signed in to change notification settings - Fork 707
[rush-daemon][WS2] Add daemon host bootstrap #5928
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
Mo Jazayeri (mojaza)
merged 4 commits into
microsoft:main
from
mojaza:mojazayeri-microsoft-rush-daemon-host-bootstrap
Aug 18, 2026
+677
−17
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
4581fcf
[rush-daemon] Add daemon host bootstrap
mojazayeri 795bde6
[rush-daemon] Refresh injected dependency metadata
mojazayeri 0379b52
[rush-daemon-protocol] Preserve pong compatibility
mojazayeri 387a196
[rush-daemon] Move rushd entrypoint into daemon package
mojazayeri 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
11 changes: 11 additions & 0 deletions
11
...ushstack/rush-daemon-protocol/mojazayeri-rush-daemon-host-bootstrap_2026-08-17-23-30.json
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,11 @@ | ||
| { | ||
| "changes": [ | ||
| { | ||
| "packageName": "@rushstack/rush-daemon-protocol", | ||
| "comment": "Include daemon and protocol version metadata in pong control messages.", | ||
| "type": "minor" | ||
| } | ||
| ], | ||
| "packageName": "@rushstack/rush-daemon-protocol", | ||
| "email": "mojazayeri@users.noreply.github.com" | ||
| } |
11 changes: 11 additions & 0 deletions
11
...hanges/@rushstack/rush-daemon/mojazayeri-rush-daemon-host-bootstrap_2026-08-17-23-30.json
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,11 @@ | ||
| { | ||
| "changes": [ | ||
| { | ||
| "packageName": "@rushstack/rush-daemon", | ||
| "comment": "Add the rushd executable and workspace-keyed daemon host bootstrap with handshake, liveness, readiness, and clean shutdown lifecycle.", | ||
| "type": "minor" | ||
| } | ||
| ], | ||
| "packageName": "@rushstack/rush-daemon", | ||
| "email": "mojazayeri@users.noreply.github.com" | ||
| } |
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
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
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
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
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,16 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. | ||
| // See LICENSE in the project root for license information. | ||
|
|
||
| import type { IDaemonProtocolVersion } from './DaemonProtocolVersion'; | ||
|
|
||
| /** The liveness reply. @beta */ | ||
| export interface IDaemonPongMessage { | ||
| readonly kind: 'pong'; | ||
| readonly payload: { | ||
| /** The daemon implementation version, when reported by protocol 0.2 or newer. */ | ||
| readonly daemonVersion?: string; | ||
| /** The daemon wire protocol version, when reported by protocol 0.2 or newer. */ | ||
| readonly protocolVersion?: IDaemonProtocolVersion; | ||
| readonly uptimeMs: number; | ||
| }; | ||
| } |
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
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 |
|---|---|---|
| @@ -1,5 +1,8 @@ | ||
| # @rushstack/rush-daemon | ||
|
|
||
| The long-lived Rush workspace daemon host. | ||
| The long-lived Rush workspace daemon host, including workspace-keyed listener bootstrap, | ||
| protocol handshake and liveness control, and explicit serve/shutdown lifecycle APIs. | ||
|
|
||
| This package is under active development and is not yet integrated with the Rush command line. | ||
| The package provides an opt-in `rushd` executable. Run it from a Rush workspace to start the host | ||
| for the nearest `rush.json`; it does not change the default behavior of `rush`, `rushx`, or | ||
| `rush-pnpm`. |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,117 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. | ||
| // See LICENSE in the project root for license information. | ||
|
|
||
| import { randomUUID } from 'node:crypto'; | ||
|
|
||
| import { | ||
| DAEMON_PROTOCOL_VERSION, | ||
| DaemonFrameType, | ||
| DaemonProtocolError, | ||
| decodeDaemonControlMessage, | ||
| encodeDaemonControlMessage, | ||
| negotiateDaemonHello | ||
| } from '@rushstack/rush-daemon-protocol'; | ||
| import type { | ||
| DaemonControlMessage, | ||
| IDaemonErrorMessage, | ||
| IDaemonFrame, | ||
| IDaemonPongMessage | ||
| } from '@rushstack/rush-daemon-protocol'; | ||
| import type { DaemonFrameConnection } from '@rushstack/rush-daemon-transport'; | ||
|
|
||
| export interface IDaemonControlSessionOptions { | ||
| readonly daemonVersion: string; | ||
| readonly startedAtMs: number; | ||
| readonly onClosed: (session: DaemonControlSession, error: Error | undefined) => void; | ||
| readonly onError: (error: Error) => void; | ||
| } | ||
|
|
||
| export class DaemonControlSession { | ||
| private readonly _connection: DaemonFrameConnection; | ||
| private readonly _options: IDaemonControlSessionOptions; | ||
| private _handshakeComplete: boolean = false; | ||
| private _sendQueue: Promise<void> = Promise.resolve(); | ||
|
|
||
| public constructor(connection: DaemonFrameConnection, options: IDaemonControlSessionOptions) { | ||
| this._connection = connection; | ||
| this._options = options; | ||
| connection.onFrame((frame: IDaemonFrame) => this._onFrame(frame)); | ||
| connection.onClosed((error: Error | undefined) => options.onClosed(this, error)); | ||
| } | ||
|
|
||
| public closeAsync(): Promise<void> { | ||
| return this._connection.closeAsync(); | ||
| } | ||
|
|
||
| private _onFrame(frame: IDaemonFrame): void { | ||
| if (frame.kind !== DaemonFrameType.controlJson) { | ||
| throw new DaemonProtocolError( | ||
| 'malformedControlMessage', | ||
| 'A daemon control connection only accepts control frames.' | ||
| ); | ||
| } | ||
| const message: DaemonControlMessage = decodeDaemonControlMessage(frame.payload); | ||
| if (!this._handshakeComplete) { | ||
| this._handleHello(message); | ||
| } else if (message.kind === 'ping') { | ||
| this._send(this._createPong()); | ||
| } else { | ||
| throw new DaemonProtocolError( | ||
| 'malformedControlMessage', | ||
| `Control message "${message.kind}" is not valid in this daemon host state.` | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| private _handleHello(message: DaemonControlMessage): void { | ||
| if (message.kind !== 'hello') { | ||
| throw new DaemonProtocolError( | ||
| 'malformedControlMessage', | ||
| 'The first control message on a connection must be hello.' | ||
| ); | ||
| } | ||
| const outcome: ReturnType<typeof negotiateDaemonHello> = negotiateDaemonHello( | ||
| message, | ||
| DAEMON_PROTOCOL_VERSION, | ||
| randomUUID() | ||
| ); | ||
| if (outcome.accepted) { | ||
| this._handshakeComplete = true; | ||
| this._send(outcome.ack); | ||
| } else { | ||
| const errorMessage: IDaemonErrorMessage = { | ||
| kind: 'error', | ||
| payload: { code: outcome.error.code, message: outcome.error.message } | ||
| }; | ||
| this._send(errorMessage, true); | ||
| } | ||
| } | ||
|
|
||
| private _createPong(): IDaemonPongMessage { | ||
| return { | ||
| kind: 'pong', | ||
| payload: { | ||
| daemonVersion: this._options.daemonVersion, | ||
| protocolVersion: DAEMON_PROTOCOL_VERSION, | ||
| uptimeMs: Date.now() - this._options.startedAtMs | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| private _send(message: DaemonControlMessage, closeAfterSend: boolean = false): void { | ||
| const frame: IDaemonFrame = { | ||
| kind: DaemonFrameType.controlJson, | ||
| payload: encodeDaemonControlMessage(message) | ||
| }; | ||
| this._sendQueue = this._sendQueue | ||
| .then(() => this._connection.sendFrameAsync(frame)) | ||
| .then(() => (closeAfterSend ? this._connection.closeAsync() : undefined)) | ||
| .catch((error: unknown) => this._handleSendErrorAsync(error)); | ||
| } | ||
|
|
||
| private async _handleSendErrorAsync(error: unknown): Promise<void> { | ||
| const normalizedError: Error = error instanceof Error ? error : new Error(String(error)); | ||
| this._options.onError(normalizedError); | ||
| await this._connection.closeAsync(); | ||
| } | ||
| } |
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.