Files
leon-ai--leon/scripts/setup/run-setup.js
T
2026-07-13 12:44:08 +08:00

99 lines
2.3 KiB
JavaScript

import fs from 'node:fs'
import path from 'node:path'
import { spawn } from 'node:child_process'
const SETUP_ENTRY_PATH = path.join('scripts', 'setup', 'setup.js')
const TSX_ENTRY_PATH = path.join(
process.cwd(),
'node_modules',
'tsx',
'dist',
'cli.mjs'
)
const TTY_PATH = '/dev/tty'
const WINDOWS_CONSOLE_IN_PATH = 'CONIN$'
const WINDOWS_CONSOLE_OUT_PATH = 'CONOUT$'
// pnpm 11 reads project config from pnpm-workspace.yaml. That also makes root
// lifecycle scripts run through pnpm's workspace runner, which does not expose
// a TTY to the child process. Leon setup needs a TTY to ask setup questions, so
// this wrapper reattaches the real setup process to the user's terminal.
function openWindowsConsoleStdio() {
try {
const stdin = fs.openSync(WINDOWS_CONSOLE_IN_PATH, 'r')
const stdout = fs.openSync(WINDOWS_CONSOLE_OUT_PATH, 'w')
return [stdin, stdout, stdout]
} catch {
return null
}
}
// Use the controlling terminal when available. In CI, Docker, or other
// non-interactive contexts there may be no terminal, so setup falls back to the
// inherited stdio and keeps its existing non-interactive behavior.
function getTTYStdio() {
if (process.platform === 'win32') {
return openWindowsConsoleStdio()
}
if (!fs.existsSync(TTY_PATH)) {
return null
}
try {
const tty = fs.openSync(TTY_PATH, 'r+')
return [tty, tty, tty]
} catch {
return null
}
}
function closeTTYStdio(stdio) {
if (stdio === null) {
return
}
for (const descriptor of new Set(stdio)) {
fs.closeSync(descriptor)
}
}
async function runSetup() {
const ttyStdio = getTTYStdio()
try {
await new Promise((resolve, reject) => {
const child = spawn(process.execPath, [TSX_ENTRY_PATH, SETUP_ENTRY_PATH], {
cwd: process.cwd(),
env: process.env,
stdio: ttyStdio || 'inherit',
windowsHide: false
})
child.once('error', reject)
child.once('exit', (code, signal) => {
if (signal) {
process.kill(process.pid, signal)
return
}
if ((code ?? 0) !== 0) {
reject(new Error(`Setup exited with code ${code ?? 0}`))
return
}
resolve()
})
})
} finally {
closeTTYStdio(ttyStdio)
}
}
runSetup().catch((error) => {
console.error(error)
process.exit(1)
})