Skip to content

Commit e295690

Browse files
committed
feat(core): add nx cloud connect flow to the tui performance report
## Current Behavior The TUI performance report shows run stats but no way to act on the remote-cache recommendation. Connecting means quitting the run and running `nx connect`. ## Expected Behavior - While the workspace is not connected, the performance report footer offers `enable remote cache: <shift>+c` (clickable). Pressing it runs `nx connect` logic headlessly and shows the onboarding URL inline under the stats, so the whole flow stays in the TUI. - The browser is never opened automatically: `o` (or a click on the link) opens the URL; the report stays open. `<shift>+c` retries after a failure. - A missing VCS remote does not block: the URL is generated anyway and the report nudges to https://github.com/new (Nx Cloud covers VCS in the browser). - The connect experience is a reusable `ConnectFlow` component that owns its own lifecycle state and rendering; the report embeds it and forwards keys via a small intent enum. Other popups can embed it later without touching the report. - Its state lives in `TuiState` so it survives F11 mode switches. The URL is a registry-backed clickable link, not OSC 8 (which breaks ratatui layout). - Connection crosses napi as the constructor's existing optional bool: undefined = cloud off, true = connected, false = not connected. ## Related Issue(s) NXC-4701
1 parent f31588f commit e295690

12 files changed

Lines changed: 1054 additions & 53 deletions

File tree

packages/nx/src/command-line/nx-cloud/connect/connect-to-nx-cloud.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,69 @@ function sleep(ms: number) {
242242
return new Promise((resolve) => setTimeout(resolve, ms));
243243
}
244244

245+
export interface TuiConnectResult {
246+
/** The onboarding URL to open in the browser. */
247+
url: string;
248+
/**
249+
* True when the workspace has no VCS remote yet. The browser onboarding
250+
* still works (and covers VCS itself), so this only lets the TUI nudge the
251+
* user rather than block them.
252+
*/
253+
needsVcsPush: boolean;
254+
}
255+
256+
/**
257+
* Runs the same logic as `nx connect` headlessly for the TUI: no prompts, no
258+
* spinner, no browser open (the TUI displays the returned onboarding URL in a
259+
* popup instead). Rejects when the workspace cannot be created (e.g. the
260+
* create-org API is unreachable) so the TUI can surface the error; note the
261+
* already-connected branch resolves with a fallback URL even offline. A missing
262+
* VCS remote is not a failure - it comes back as `needsVcsPush`.
263+
*/
264+
export async function connectToNxCloudFromTui(): Promise<TuiConnectResult> {
265+
return withConnectStats(
266+
connectStatMeta({ source: 'nx-tui' }),
267+
() => true,
268+
() => generateConnectUrlForTui()
269+
);
270+
}
271+
272+
async function generateConnectUrlForTui(): Promise<TuiConnectResult> {
273+
const nxJson = readNxJson();
274+
// A missing remote does not block the TUI: the browser flow walks the user
275+
// through pushing to a VCS provider, so we generate the URL regardless and
276+
// let the popup surface the nudge.
277+
const needsVcsPush = !getVcsRemoteInfo();
278+
279+
// The connect shortcut is only offered while not connected, but guard anyway
280+
// (e.g. the workspace was connected from another terminal mid-run).
281+
if (isNxCloudUsed(nxJson)) {
282+
const token =
283+
process.env.NX_CLOUD_AUTH_TOKEN ||
284+
process.env.NX_CLOUD_ACCESS_TOKEN ||
285+
nxJson.nxCloudAccessToken ||
286+
nxJson.nxCloudId;
287+
const url = await createNxCloudOnboardingURL(
288+
'nx-tui',
289+
token,
290+
undefined,
291+
false
292+
);
293+
return { url, needsVcsPush };
294+
}
295+
296+
const token = await connectWorkspaceToCloud({
297+
installationSource: 'nx-tui',
298+
});
299+
const url = await createNxCloudOnboardingURL(
300+
'nx-tui',
301+
token,
302+
undefined,
303+
false
304+
);
305+
return { url, needsVcsPush };
306+
}
307+
245308
export async function connectExistingRepoToNxCloudPrompt(
246309
command = 'init',
247310
key: MessageKey = 'setupNxCloud',

packages/nx/src/native/index.d.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export declare class ExternalObject<T> {
3232
}
3333
}
3434
export declare class AppLifeCycle {
35-
constructor(tasks: Array<Task>, initiatingTasks: Array<string>, runMode: RunMode, pinnedTasks: Array<string>, tuiCliArgs: TuiCliArgs, tuiConfig: TuiConfig, titleText: string, workspaceRoot: string, taskGraph: TaskGraph, isCloudEnabled?: boolean | undefined | null)
35+
constructor(tasks: Array<Task>, initiatingTasks: Array<string>, runMode: RunMode, pinnedTasks: Array<string>, tuiCliArgs: TuiCliArgs, tuiConfig: TuiConfig, titleText: string, workspaceRoot: string, taskGraph: TaskGraph, isConnectedToCloud?: boolean | undefined | null)
3636
startCommand(threadCount?: number | undefined | null): void
3737
scheduleTask(task: Task): void
3838
startTasks(tasks: Array<Task>, metadata: object): void
@@ -57,6 +57,25 @@ export declare class AppLifeCycle {
5757
* Cloud client can call it via the lifecycle it already receives.
5858
*/
5959
setCloudLink(label: string, url: string): void
60+
/**
61+
* Register the callback fired when the user presses the connect-to-cloud
62+
* shortcut. JS runs the `nx connect` logic and pushes the resulting URL
63+
* back via `setConnectUrl` / `setConnectError`.
64+
*/
65+
registerConnectToCloudCallback(connectCallback: (() => unknown)): void
66+
/**
67+
* Deliver the Nx Cloud onboarding URL to the connect flow. `needsVcsPush`
68+
* is true when the workspace has no VCS remote yet, so the popup can nudge
69+
* the user to create one (the browser flow still works either way).
70+
*/
71+
setConnectUrl(url: string, needsVcsPush: boolean): void
72+
/** Surface a connect failure in the connect flow. */
73+
setConnectError(message: string): void
74+
/**
75+
* Mark the workspace as connected, once a TUI-initiated connect has
76+
* written an nxCloudId to nx.json.
77+
*/
78+
setConnectedToCloud(): void
6079
}
6180

6281
export declare class ChildProcess {

0 commit comments

Comments
 (0)