Skip to content

Commit 78aa686

Browse files
committed
fix(studio-bridge): use child_process.spawn for process launch to prevent Studio from closing
execa's internal Job Object on Windows kills child processes when the parent exits. Switch to Node's built-in spawn with detached + unref so Studio survives after the CLI exits.
1 parent f44528e commit 78aa686

1 file changed

Lines changed: 10 additions & 9 deletions

File tree

tools/studio-bridge/src/process/studio-process-manager.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55

66
import * as fs from 'fs/promises';
77
import * as path from 'path';
8-
import { execa, type ResultPromise } from 'execa';
8+
import { spawn, type ChildProcess } from 'child_process';
9+
import { execa } from 'execa';
910
import { OutputHelper } from '@quenty/cli-output-helpers';
1011

1112
// ---------------------------------------------------------------------------
@@ -93,31 +94,31 @@ export function findPluginsFolder(): string {
9394

9495
export interface StudioProcess {
9596
/** The underlying child process handle */
96-
process: ResultPromise;
97+
process: ChildProcess;
9798
/** Kill the Studio process (idempotent, best-effort) */
9899
killAsync: () => Promise<void>;
99100
}
100101

101102
/**
102103
* Launch Roblox Studio with the given place file.
104+
*
105+
* Uses Node's built-in `spawn` with `detached: true` + `unref()` so that
106+
* Studio survives after the CLI process exits. execa's internal Job Object
107+
* on Windows kills children on parent exit, so we avoid it here.
103108
*/
104109
export async function launchStudioAsync(
105110
placePath: string
106111
): Promise<StudioProcess> {
107112
const studioExe = await findStudioPathAsync();
108113
OutputHelper.verbose(`[StudioBridge] ${studioExe} "${placePath}"`);
109114

110-
const proc = execa(studioExe, [placePath], {
111-
// Don't tie Studio's lifetime to our process
115+
const proc = spawn(studioExe, placePath ? [placePath] : [], {
112116
detached: true,
113-
// Don't wait for stdio
114117
stdio: 'ignore',
115-
// Don't reject on non-zero exit
116-
reject: false,
117118
});
118119

119-
// Allow our Node process to exit even if Studio is still running
120-
proc.unref?.();
120+
// Allow our Node process to exit without waiting for Studio
121+
proc.unref();
121122

122123
let killed = false;
123124
const killAsync = async () => {

0 commit comments

Comments
 (0)