fix(runner): scope verified ESM mode to Node

This commit is contained in:
Dotta 2026-09-02 18:00:27 -05:00
parent 03bab72ee8
commit 8d36deaa72
3 changed files with 31 additions and 18 deletions

View File

@ -24,7 +24,8 @@ use crate::durable::{
DurableRunnerError, EventPriority, PolledEvent,
};
use crate::process_supervisor::{
VerifiedProcessArgument, VerifiedProcessLaunch, VERIFIED_NODE_ESM_DEFAULT_TYPE_ARG,
is_node_interpreter, VerifiedProcessArgument, VerifiedProcessLaunch,
VERIFIED_NODE_ESM_DEFAULT_TYPE_ARG,
};
use crate::provider_bridge::{
authorized_tool_catalog_digest, AuthorizedToolSet, ToolResult, TOOL_SET_SCHEMA,
@ -236,10 +237,12 @@ impl AcpxProviderDescriptor {
// suffix. Pin ESM interpretation so Node does not misclassify the
// bundled sidecar as CommonJS merely because its verified path is
// extensionless.
verified_args.insert(
0,
VerifiedProcessArgument::Literal(VERIFIED_NODE_ESM_DEFAULT_TYPE_ARG.to_owned()),
);
if is_node_interpreter(&launch_profile.command) {
verified_args.insert(
0,
VerifiedProcessArgument::Literal(VERIFIED_NODE_ESM_DEFAULT_TYPE_ARG.to_owned()),
);
}
Ok(AcpxSidecarTransportConfig {
command: launch_profile.command.clone(),
args: launch_profile.args.clone(),

View File

@ -15,8 +15,8 @@ use crate::durable::QualifiedLaunchArtifact;
use crate::durable::{redact_text, OpenCodeLaunchProfile};
use crate::local_runner::LocalRunnerError;
use crate::process_supervisor::{
BoundedLogBuffer, ProcessOutput, SupervisedProcess, VerifiedProcessArgument,
VerifiedProcessLaunch, VERIFIED_NODE_ESM_DEFAULT_TYPE_ARG,
is_node_interpreter, BoundedLogBuffer, ProcessOutput, SupervisedProcess,
VerifiedProcessArgument, VerifiedProcessLaunch, VERIFIED_NODE_ESM_DEFAULT_TYPE_ARG,
};
use crate::provider_bridge::{AuthorizedTool, DurableReplayFilter, ToolResult};
use crate::provider_events::normalized_codex_terminal_event_type;
@ -1948,18 +1948,22 @@ fn verified_opencode_launch(
.map_err(|error| LocalRunnerError::invalid(error.to_string()))?;
let executable = verify_launch_artifact(&profile.executable, "OpenCode provider executable")
.map_err(|error| LocalRunnerError::invalid(error.to_string()))?;
Ok(VerifiedProcessLaunch::new(
command,
vec![
// The immutable verified proxy path is extensionless, so
// explicitly retain the ESM semantics of the bundled .js
// artifact instead of letting Node infer CommonJS.
let mut args = vec![
VerifiedProcessArgument::Artifact(proxy),
VerifiedProcessArgument::Literal(TRUSTED_OPENCODE_EXECUTABLE_ARG.to_owned()),
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()),
VerifiedProcessArgument::Artifact(proxy),
VerifiedProcessArgument::Literal(TRUSTED_OPENCODE_EXECUTABLE_ARG.to_owned()),
VerifiedProcessArgument::ExecutableArtifact(executable),
],
))
);
}
Ok(VerifiedProcessLaunch::new(command, args))
}
fn json_size(value: &Value, label: &str) -> Result<usize, LocalRunnerError> {

View File

@ -28,6 +28,12 @@ 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) fn is_node_interpreter(path: &Path) -> bool {
path.file_name()
.and_then(|name| name.to_str())
.is_some_and(|name| matches!(name, "node" | "nodejs" | "node.exe"))
}
#[derive(Clone, Debug)]
pub struct VerifiedProcessArtifact {
display_path: PathBuf,