fix(runner): load sealed Node entrypoints as ESM
This commit is contained in:
parent
8d36deaa72
commit
96fcadeb51
|
|
@ -24,8 +24,8 @@ use crate::durable::{
|
|||
DurableRunnerError, EventPriority, PolledEvent,
|
||||
};
|
||||
use crate::process_supervisor::{
|
||||
is_node_interpreter, VerifiedProcessArgument, VerifiedProcessLaunch,
|
||||
VERIFIED_NODE_ESM_DEFAULT_TYPE_ARG,
|
||||
is_node_interpreter, verified_node_esm_loader_arguments, VerifiedProcessArgument,
|
||||
VerifiedProcessLaunch, VERIFIED_NODE_ESM_LOADER, VERIFIED_NODE_EVAL_ARG,
|
||||
};
|
||||
use crate::provider_bridge::{
|
||||
authorized_tool_catalog_digest, AuthorizedToolSet, ToolResult, TOOL_SET_SCHEMA,
|
||||
|
|
@ -234,14 +234,10 @@ impl AcpxProviderDescriptor {
|
|||
.collect::<Result<Vec<_>, _>>()?;
|
||||
// Verified scripts are exposed to Node through an immutable descriptor
|
||||
// path (for example /proc/self/fd/12), which intentionally has no .js
|
||||
// suffix. Pin ESM interpretation so Node does not misclassify the
|
||||
// bundled sidecar as CommonJS merely because its verified path is
|
||||
// extensionless.
|
||||
// suffix. A runner-owned eval loader reads that exact sealed descriptor
|
||||
// and imports it as ESM without mutating the qualified launch artifact.
|
||||
if is_node_interpreter(&launch_profile.command) {
|
||||
verified_args.insert(
|
||||
0,
|
||||
VerifiedProcessArgument::Literal(VERIFIED_NODE_ESM_DEFAULT_TYPE_ARG.to_owned()),
|
||||
);
|
||||
verified_args.splice(0..0, verified_node_esm_loader_arguments());
|
||||
}
|
||||
Ok(AcpxSidecarTransportConfig {
|
||||
command: launch_profile.command.clone(),
|
||||
|
|
@ -1434,10 +1430,15 @@ mod tests {
|
|||
assert!(matches!(
|
||||
verified_launch.arguments().first(),
|
||||
Some(VerifiedProcessArgument::Literal(argument))
|
||||
if argument == VERIFIED_NODE_ESM_DEFAULT_TYPE_ARG
|
||||
if argument == VERIFIED_NODE_EVAL_ARG
|
||||
));
|
||||
assert!(matches!(
|
||||
verified_launch.arguments().get(1),
|
||||
Some(VerifiedProcessArgument::Literal(argument))
|
||||
if argument == VERIFIED_NODE_ESM_LOADER
|
||||
));
|
||||
assert!(matches!(
|
||||
verified_launch.arguments().get(2),
|
||||
Some(VerifiedProcessArgument::Artifact(_))
|
||||
));
|
||||
|
||||
|
|
|
|||
|
|
@ -15,8 +15,9 @@ use crate::durable::QualifiedLaunchArtifact;
|
|||
use crate::durable::{redact_text, OpenCodeLaunchProfile};
|
||||
use crate::local_runner::LocalRunnerError;
|
||||
use crate::process_supervisor::{
|
||||
is_node_interpreter, BoundedLogBuffer, ProcessOutput, SupervisedProcess,
|
||||
VerifiedProcessArgument, VerifiedProcessLaunch, VERIFIED_NODE_ESM_DEFAULT_TYPE_ARG,
|
||||
is_node_interpreter, verified_node_esm_loader_arguments, BoundedLogBuffer, ProcessOutput,
|
||||
SupervisedProcess, VerifiedProcessArgument, VerifiedProcessLaunch, VERIFIED_NODE_ESM_LOADER,
|
||||
VERIFIED_NODE_EVAL_ARG,
|
||||
};
|
||||
use crate::provider_bridge::{AuthorizedTool, DurableReplayFilter, ToolResult};
|
||||
use crate::provider_events::normalized_codex_terminal_event_type;
|
||||
|
|
@ -1954,14 +1955,11 @@ fn verified_opencode_launch(
|
|||
VerifiedProcessArgument::ExecutableArtifact(executable),
|
||||
];
|
||||
if is_node_interpreter(&profile.command.path) {
|
||||
// The immutable verified proxy path is extensionless, so explicitly
|
||||
// retain the ESM semantics of the bundled .js artifact instead of
|
||||
// letting Node infer CommonJS. Qualified non-Node test/provider
|
||||
// commands retain their original argument contract.
|
||||
args.insert(
|
||||
0,
|
||||
VerifiedProcessArgument::Literal(VERIFIED_NODE_ESM_DEFAULT_TYPE_ARG.to_owned()),
|
||||
);
|
||||
// The immutable verified proxy path is extensionless. Load its exact
|
||||
// sealed bytes as an ESM data URL while leaving process.argv[1] bound
|
||||
// to that descriptor, so the proxy retains its ordinary CLI contract.
|
||||
// Qualified non-Node test/provider commands retain their arguments.
|
||||
args.splice(0..0, verified_node_esm_loader_arguments());
|
||||
}
|
||||
Ok(VerifiedProcessLaunch::new(command, args))
|
||||
}
|
||||
|
|
@ -2711,19 +2709,24 @@ mod tests {
|
|||
assert!(matches!(
|
||||
launch.arguments().first(),
|
||||
Some(VerifiedProcessArgument::Literal(argument))
|
||||
if argument == VERIFIED_NODE_ESM_DEFAULT_TYPE_ARG
|
||||
if argument == VERIFIED_NODE_EVAL_ARG
|
||||
));
|
||||
assert!(matches!(
|
||||
launch.arguments().get(1),
|
||||
Some(VerifiedProcessArgument::Artifact(_))
|
||||
Some(VerifiedProcessArgument::Literal(argument))
|
||||
if argument == VERIFIED_NODE_ESM_LOADER
|
||||
));
|
||||
assert!(matches!(
|
||||
launch.arguments().get(2),
|
||||
Some(VerifiedProcessArgument::Artifact(_))
|
||||
));
|
||||
assert!(matches!(
|
||||
launch.arguments().get(3),
|
||||
Some(VerifiedProcessArgument::Literal(argument))
|
||||
if argument == TRUSTED_OPENCODE_EXECUTABLE_ARG
|
||||
));
|
||||
assert!(matches!(
|
||||
launch.arguments().get(3),
|
||||
launch.arguments().get(4),
|
||||
Some(VerifiedProcessArgument::ExecutableArtifact(_))
|
||||
));
|
||||
fs::remove_dir_all(directory).unwrap();
|
||||
|
|
|
|||
|
|
@ -26,7 +26,8 @@ use sha2::{Digest, Sha256};
|
|||
use crate::local_runner::LocalRunnerError;
|
||||
|
||||
const PROCESS_OUTPUT_QUEUE_CAPACITY: usize = 256;
|
||||
pub(crate) const VERIFIED_NODE_ESM_DEFAULT_TYPE_ARG: &str = "--experimental-default-type=module";
|
||||
pub(crate) const VERIFIED_NODE_EVAL_ARG: &str = "--eval";
|
||||
pub(crate) const VERIFIED_NODE_ESM_LOADER: &str = r#"const fs=require("node:fs");const source=fs.readFileSync(process.argv[1]);import("data:text/javascript;base64,"+source.toString("base64")).catch((error)=>{console.error(error instanceof Error?error.message:String(error));process.exitCode=1;});"#;
|
||||
|
||||
pub(crate) fn is_node_interpreter(path: &Path) -> bool {
|
||||
path.file_name()
|
||||
|
|
@ -195,6 +196,13 @@ pub enum VerifiedProcessArgument {
|
|||
ExecutableArtifact(VerifiedProcessArtifact),
|
||||
}
|
||||
|
||||
pub(crate) fn verified_node_esm_loader_arguments() -> [VerifiedProcessArgument; 2] {
|
||||
[
|
||||
VerifiedProcessArgument::Literal(VERIFIED_NODE_EVAL_ARG.to_owned()),
|
||||
VerifiedProcessArgument::Literal(VERIFIED_NODE_ESM_LOADER.to_owned()),
|
||||
]
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct VerifiedProcessLaunch {
|
||||
program: VerifiedProcessArtifact,
|
||||
|
|
|
|||
Loading…
Reference in New Issue