22
This commit is contained in:
@@ -41,6 +41,17 @@ static RE_REQUIRE: Lazy<Regex> =
|
||||
|
||||
const INLINE_ENTRY: &str = "__inline_entry__.js";
|
||||
|
||||
/// Windows:子进程不创建可见控制台窗口(避免运行 node.exe 时闪黑框)。
|
||||
#[cfg(windows)]
|
||||
fn hide_console_window(cmd: &mut Command) {
|
||||
use std::os::windows::process::CommandExt;
|
||||
const CREATE_NO_WINDOW: u32 = 0x0800_0000;
|
||||
cmd.as_std_mut().creation_flags(CREATE_NO_WINDOW);
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn hide_console_window(_cmd: &mut Command) {}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum NodeError {
|
||||
#[error("node.exe 未找到(请检查 resources/nodejs/ 是否存在)")]
|
||||
@@ -125,6 +136,13 @@ fn build_file_runner(entry_script: &str) -> String {
|
||||
}};
|
||||
}})();
|
||||
|
||||
/** Windows 管道子进程:write 后立刻 process.exit 会触发 libuv UV_HANDLE_CLOSING 断言 */
|
||||
function writeAsync(stream, chunk) {{
|
||||
return new Promise((resolve, reject) => {{
|
||||
stream.write(chunk, (err) => (err ? reject(err) : resolve()));
|
||||
}});
|
||||
}}
|
||||
|
||||
(async () => {{
|
||||
let raw = "";
|
||||
process.stdin.setEncoding("utf8");
|
||||
@@ -134,29 +152,45 @@ fn build_file_runner(entry_script: &str) -> String {
|
||||
require("./{entry_script}");
|
||||
|
||||
if (typeof globalThis.__nodejsMain !== "function") {{
|
||||
process.stderr.write(
|
||||
"__EVT__" + JSON.stringify({{
|
||||
type: "log",
|
||||
level: "error",
|
||||
msg: "脚本未注册 globalThis.__nodejsMain",
|
||||
}}) + "\n"
|
||||
await writeAsync(
|
||||
process.stderr,
|
||||
"__EVT__" +
|
||||
JSON.stringify({{
|
||||
type: "log",
|
||||
level: "error",
|
||||
msg: "脚本未注册 globalThis.__nodejsMain",
|
||||
}}) +
|
||||
"\n"
|
||||
);
|
||||
process.exit(1);
|
||||
process.exitCode = 1;
|
||||
return;
|
||||
}}
|
||||
try {{
|
||||
const result = await globalThis.__nodejsMain(params);
|
||||
process.stdout.write(
|
||||
"__RESULT__" + JSON.stringify(result === undefined ? null : result) + "\n"
|
||||
await writeAsync(
|
||||
process.stdout,
|
||||
"__RESULT__" +
|
||||
JSON.stringify(result === undefined ? null : result) +
|
||||
"\n"
|
||||
);
|
||||
process.exit(0);
|
||||
}} catch (e) {{
|
||||
const msg = (e && (e.stack || e.message)) || String(e);
|
||||
process.stderr.write(
|
||||
await writeAsync(
|
||||
process.stderr,
|
||||
"__EVT__" + JSON.stringify({{ type: "log", level: "error", msg }}) + "\n"
|
||||
);
|
||||
process.exit(2);
|
||||
process.exitCode = 2;
|
||||
}}
|
||||
}})();
|
||||
}})().catch(async (e) => {{
|
||||
const msg = (e && (e.stack || e.message)) || String(e);
|
||||
try {{
|
||||
await writeAsync(
|
||||
process.stderr,
|
||||
"__EVT__" + JSON.stringify({{ type: "log", level: "error", msg }}) + "\n"
|
||||
);
|
||||
}} catch (_) {{}}
|
||||
process.exitCode = 1;
|
||||
}});
|
||||
"#
|
||||
)
|
||||
}
|
||||
@@ -369,6 +403,7 @@ async fn run_script_bundle(
|
||||
if let Some(dir) = crate::cover_python::locate_cover_scripts_dir() {
|
||||
cmd.env("AICLIENT_COVER_SCRIPTS_DIR", dir);
|
||||
}
|
||||
hide_console_window(&mut cmd);
|
||||
let mut child = cmd.spawn()?;
|
||||
|
||||
// 把 params JSON 灌进 stdin
|
||||
@@ -456,9 +491,15 @@ async fn run_script_bundle(
|
||||
result
|
||||
});
|
||||
|
||||
let status = child.wait().await?;
|
||||
let stderr_tail = stderr_task.await.unwrap_or_default();
|
||||
let result_line = stdout_task.await.unwrap_or(None);
|
||||
// 与 stdout/stderr 读取并行等待,避免子进程在管道未排空时退出(Windows libuv)
|
||||
let (status, stderr_tail, result_line) = tokio::join!(
|
||||
child.wait(),
|
||||
stderr_task,
|
||||
stdout_task,
|
||||
);
|
||||
let status = status?;
|
||||
let stderr_tail = stderr_tail.unwrap_or_default();
|
||||
let result_line = result_line.unwrap_or(None);
|
||||
|
||||
let _ = tokio::fs::remove_dir_all(&bundle_dir).await;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user