mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-03 17:53:39 +00:00
* fix(build): refuse dist rebuild under a live managed Gateway Stop pnpm build and run-node auto-build from deleting hashed dist modules while a managed Gateway ExecStart still points at this checkout. * chore(build): drop unused fence message field * fix(build): also fence direct tsdown and run-node rebuilds Cover the cleanTsdownOutputRoots path and refuse early in run-node so live managed Gateway dist cannot be wiped outside build-all. * fix(build): keep live Gateway stop off the rebuild path Dispatch gateway stop/restart from existing dist, apply --profile before service inspection, and fence only physically overlapping checkouts. * fix(build): keep live dist fence off tsdown declaration graph Load daemon inspection lazily so tsdown fixtures and plugin-sdk dts generation do not import service-layout. Move run-node recovery tests to a sibling file so the line-cap ratchet does not grow. * fix(build): use import type for SpawnOptions in live-dist tests * fix(build): dispatch source-only QA reports before the live dist fence qa parity-report and qa coverage already run from source without rebuilding private QA dist. Check that path before refusing a live managed Gateway rebuild. * fix(daemon): discover managed Gateway bindings across profiles The live-dist fence needs every installed managed selector, not only the current OPENCLAW_PROFILE. Reuse includeManagedOpenClaw scans and leave findExtraGatewayServices semantics unchanged. * fix(build): refuse live dist rebuild for every overlapping Gateway profile A default-env build could still replace dist under a sibling profile Gateway. Inspect all managed bindings, name offenders, and point operators at stop or openclaw update. * fix(daemon): keep managed Gateway binding discovery under lint limits Move profile binding mapping out of inspect.ts, list managed services via listManagedOpenClawGatewayServices, and use toSorted for profile naming. * fix(build): keep live-dist refusal out of updater restore and system census Propagate admissionRefused so update-gateway-build does not rm live output roots after a pre-mutation fence refuse. Enumerate system-scope managed units with explicit systemdReadTarget so user and system siblings both reach the fence. * test(build): pass a compare function to toSorted in the fence fixture * fix(build): satisfy live-dist fence CI type, knip, and assertion gates Treat toSorted comparators as possibly undefined, stop re-exporting the unused binding type, and read launchd profile env through the record owner instead of a type assertion. * fix(daemon): inspect systemd template instances in the live-dist census Discovered `openclaw@.service` files were forwarded as unit names, so the fence queried a non-runnable template and fail-opened past a live instance when a separate user Gateway was installed. Reuse the existing instance resolution owner and cover user+system template inspection through the service reader. * fix(tooling): pin foreign-cwd tsconfig and clear CI gates - pin TSX_TSCONFIG_PATH in the CLI shim only when the cwd lacks one, so fixture-spawned run-node.mts resolves workspace profile imports - pass --import scripts/tsx.mjs to live-updater spawns and pin the tsconfig in run-node-lifecycle fixture envs (production loader parity) - drop the unused systemd re-export for knip and move the template- instance test to service.systemd-scope.test.ts for the line cap * fix(daemon): inspect registered Gateway services accurately Read strict Windows service commands from registered actions and preserve supported launcher encodings without treating dynamic CMD expansion as a verified command. Retain exact task identity and bound systemd selection. Use one platform inventory collector for the existing full and diagnostic projections. Report incomplete inspection through Doctor while preserving status JSON and the existing managed-service filters. Keep load-state inspection behind a leaf capability instead of reverse facade imports. Refs #151608, #151463. * fix(daemon): classify service commands by position Share runtime and root-option parsing so Node display names and profile values cannot classify a Node service as a Gateway. Preserve literal shell, env and generated launchd wrappers, and honor the executable selected by launchd. Read systemd's single-quoted arguments through the existing parser and preserve literal apostrophes when rendering. Keep strict Windows command rejection separate from lenient diagnostics, and align the native-boundary fixtures with that contract without weakening ownership or source-preservation assertions. * fix(daemon): inspect direct registered task actions Read literal executable actions from their registered Scheduled Task and revalidate the action before returning command facts. Strict runtime inspection uses the same registered command instead of a default launcher. Keep executable paths out of managed launcher provenance. Direct actions remain outside automatic update service management when no restorable CMD/VBS launcher exists, preserving the prior stop and definition ownership boundary. * test(windows): prove installed service upgrade paths Extend the existing native Scheduled Task proof with verified immutable package handoff and fixed fresh, published 2026.9.3, and published 2026.9.4 CLI cells. Require live version/build identity, selected PID replacement, peer continuity, strict registered-action inspection, and settled cleanup before evidence publication and disposable installation retirement. Keep source-only and repair modes, permissions, deadlines, and native lifecycle owners intact. Direct executable fixtures remain disabled and preserve the updater's unsupported-mutation boundary. Native execution remains pending. * test(doctor): retain managed Windows launcher provenance Keep the generated CMD path on the existing managed-service fixture before and after reinstall so Doctor admission sees the installation it models. Preserve all stop, install, restart, rollback, and direct-action refusal expectations without changing production behavior. * test(windows): exercise native autostart ownership boundaries Extend the existing published-updater cell with its stopped, task-owned Scheduled Task. Capture real admission, exercise native enable/disable, and preserve the definition and files after foreign-owner refusal. Test retained admission separately from restoration so legitimate same-root refresh remains supported. Restore the original XML through the existing fixture lifetime; do not broaden product control or workflow permissions. Modeled owner tests, selected source checks, and independent review pass. Actual Windows execution remains required before a native proof claim. * test(windows): retain sanitized installed command failures Keep unexpected command stdout and stderr in bounded failure diagnostics so JSON-mode CLI errors survive native proof failures. Reuse the existing terminal and support redaction owners before clipping; withhold incomplete captures while preserving the existing truncation failure. Move the existing command runner into its own test-support module and update both consumers without changing timeout, exit, signal, or cleanup semantics. Real-child regressions reproduce both privacy defects and pass with the correction. The original installed Gateway failure remains undiagnosed. * fix(daemon): preserve Windows probe budgets and Doctor cleanup eligibility Use the existing cold PowerShell startup budget for Task Scheduler queries without an explicit deadline. Keep periodic activation checks bounded and preserve unknown results rather than treating timeouts as task absence. The native probe test now exercises the production default. Share Doctor's existing legacy cleanup classification with its registered preview. Keep unsupported platforms, scopes and unrecognized Linux unit names as findings without advertising removal or invoking unrelated cleanup. Extract the classifier and complete cleanup test group without dropping assertions or changing native mutation ownership. Retain the failed installed Windows runs and their source identities; new package and native upgrade qualification remain separate requirements. * chore(daemon): retire stale Doctor size baseline The split Doctor service module no longer needs a max-lines suppression. Remove only its stale baseline entry so the shrink-only ratchet matches actual source. Product, dependency, workflow and fixture bytes are unchanged. * fix(build): keep native fixture import closures complete Keep the legacy source-update transaction at its existing direct CLI call site so native declaration-library consumers do not load its CLI-only source resolver closure. Copy the exact PID helper inputs into the runtime fixture. Use the real legacy-loader file URL in its existing subprocess invocation, so execution and unused-file analysis share the same dependency reference. Preserve all assertions, lifecycle guards and package runtime behavior. Co-authored-by: Donnie Fiander <44792682+DonnieFi@users.noreply.github.com> * test(windows): retain sanitized service proof observations Record bounded install and status facts before semantic assertions so a successful CLI exit cannot hide the native inspection reason. Exclude free-form stderr and private response fields, and preserve existing execution, deadline, and cleanup assertions. * test(windows): retain safe installed-service observations Retain allowlisted install/status JSON before strict semantic assertions, without copying private response fields or opaque successful stderr. Compose service observation and exact sibling-refusal checks through one internal options record and migrate every test/support caller together. Preserve native process cleanup, readiness assertions and command budgets. Co-authored-by: Donnie Fiander <44792682+DonnieFi@users.noreply.github.com> * test(update): isolate source updater fixture inputs Give the synthetic compiler declared memory capacity through the existing memory owner instead of inheriting competing CI workers. Keep real build heap admission and all lifecycle assertions unchanged. Use the established TypeScript loader for the Linux-only live-updater CLI case so it reaches the platform refusal rather than failing during import. Refs #152875. Retains the exact failing Linux CI evidence and contributor work. Co-authored-by: Donnie Fiander <44792682+DonnieFi@users.noreply.github.com> * fix(windows): preserve native status inspection defaults Keep the CLI RPC default distinct from an explicit timeout so Windows service inspection can use its existing cold-start budget. Forward explicit load-query deadlines through the Task Scheduler owner while preserving lifecycle defaults and timeout diagnostics. * fix(models): retain discovered models after refresh failures Record successful legacy catalog results at the producer boundary so unavailable refreshes retain the accepted inventory. Preserve explicit outcomes, advisory SDK fallback behavior, and first-discovery starter policy. * fix(models): preserve skipped catalog outcome semantics Mark bundled static, configured, and advisory catalog projections with explicit empty outcomes so legacy success inference cannot promote them to observed account inventory. Preserve live outcomes and helper types. Keep exact auth provenance histories and move existing fixture/policy code into focused owners where required by the line-cap ratchet. Validation: 447 producer and sibling cases, 56 shared self-hosted cases, 95 auth/policy cases, causal missing-outcome failures, maintained checks, and independent review. * test(plugin-sdk): keep discovery loader types acyclic Move the shared loader type into a leaf consumed by both discovery contract helpers. Preserve its public provider-test-contracts export without a child-to-parent type import cycle. Validation: maintained Madge check reports zero cycles; core, all core test graphs, extension test types, lint, formatting and independent review pass. Runtime behavior and previous catalog proof are unchanged. * fix(plugin-sdk): mark generated static catalogs explicitly Keep the generated non-live, non-strict catalog adapter from claiming successful acquisition for manifest or configured rows. Preserve null, errors, strict and custom callbacks, static catalogs, and public types. Validation: three existing controls fail before the correction; all49 owner and sibling cases pass afterward, with types, lint, line caps and fresh independent review clean. * test(windows): honor omitted task XML defaults Use the existing task XML setter for disabled installed fixtures and normalize only the task-level Enabled setting in the enable roundtrip. Keep all other definition, native state, cleanup, and sibling build refusal assertions intact. Co-authored-by: Donnie Fiander <44792682+DonnieFi@users.noreply.github.com> * test: preserve native and npm fixture environment Use the canonical native service environment projection for installed Windows fixtures, retaining private application and npm paths with case-aware overrides. Keep credentials and Node options outside the projection. Apply the exact isolated npm global-config fixture correction from #158197 so version-selection tests reach their existing assertions after config discovery. Validation: 16 focused cases, all selected changed checks, and independent P2 review passed. Product and dependency inputs are unchanged; Windows installed upgrade and sibling-build qualification remains outstanding. * test(windows): await installed readiness before status checks Use the existing HTTP startup-readiness owner after install and update, while retaining strict one-shot status, build identity, and sibling continuity checks. Stop registered fixture services through the guarded owner before deleting Task definitions. Proven absence skips only stop; unknown inspection or stop failure retains the error and cleanup authority instead of bypassing the guard. Validation: source-identical readiness/stop owner proof (71 cases), affected services types, lint, formatting and ratchets passed. Fresh P2 review's request to delete after failed stop was rejected against the retained-authority contract. Native Windows completion remains unqualified; no deadline or assertion changed. * fix(tests): wait for queued RPC admission before restart Await the exact terminal chat event after deferred registration before releasing the held first response. Preserve the actual queue assertion, all delivery and restart checks, and the overall test deadline. Refs #150153 Refs #152875 Source-equivalent carry of #158347 (471321034e). * test(windows): stop installed fixtures in their own profile Invoke the guarded installed CLI through the existing managed command owner, using the admitted Task entry, profile and environment. Vitest's synthetic ambient state must not decide whether that Task can be stopped. Keep the existing 180-second command lifetime and child join, without inheriting an already-aborted test-body signal during cleanup. Unknown registration and failed stop still retain authority; proven absence skips only stop. Validation: affected services types, lint, formatting and independent P2 review passed. Shared command-owner proof remains retained. Native completion is still pending; no service guard, process assertion or test deadline was weakened. * fix(update): retain native custody during partial-stop recovery Compensate a failed native stop through the existing guarded service restart, revalidating the original binding inside its operation lock. Require completed recovery and preserve the original failure without starting a build or custom shell command. Successful and failed-build custom restart contracts stay intact. Four published-shell regressions fail on the former path and pass after this repair; 21 unchanged controls, selected checks, and independent review pass. Co-authored-by: Donnie Fiander <44792682+DonnieFi@users.noreply.github.com> * test: preserve threaded SQLite cleanup fixture metadata Carry the exact qualified d103e239/#158407 fixture correction for incoming compile-cache initialization. The non-isolated harness runs in threads; retain its false/null metadata and forbidden Worker constructor without changing assertions, order, or timeouts. * test: align SQLite lifecycle proof with canonical host contract Replace the provisional physical-thread metadata with the logical-host fixture contract from merged #158209. Preserve every cleanup assertion and the forbidden Worker constructor. Carry merged #158409's literal dependency resolvers, caller update and selection regression. Exact canonical postimages, the ordered cleanup case, dependency-selection case, selected checks and independent review pass. * test(windows): preserve installed command budgets and progress The aggregate 240-second fixture deadline cut the published updater short before its existing 180-second command budget. Allocate installed cells 360/900/900 seconds and matching 10/19/19-minute Actions envelopes, including the existing teardown and runner margin. Source-native and command limits, assertions, and the quiet watchdog stay unchanged. Reuse the existing durable recorder for real phase progress, preserving original failures and emitting only fixed labels. The Task-only 9.4 cell needs one sibling fence, with no Startup fingerprint allowance. The 32 focused cases, selected types and export scans, workflow validation, owned lint, and independent review pass. Retain the unchanged main replay lint-cap failure separately. Production and dependency inputs are unchanged. * test(windows): retain installed updater failure progress Read the isolated selected service update ledger before native fixture cleanup and retain only phase and step metadata. Preserve the original updater error and all existing execution budgets.\n\nValidation: 15 focused tests, incremental changed-file checks, and independent review passed. The moving-main ratchet failure on three untouched older files is retained separately. * test(daemon): complete system template runtime fixture Provide the loaded-runtime reader's required ControlGroup metadata. The missing native response correctly made inspection unavailable and broke the template regression after main integration; production behavior is unchanged. Validation: the original fixture fails locally for the CI assertion, then all 76 service-scope and loaded-runtime cases pass after correction. Independent P2 review is clean. No assertion, timeout, or package source changed. * fix(daemon): ignore unrelated registered task profiles Read-only Scheduled Task discovery can inspect a static unrelated command without requiring an OpenClaw profile. Keep selected-service profile checks strict, and preserve launcher, registration and deadline revalidation. The installed Windows fixture exposed a false Doctor finding for a Helper that only runs node.exe --version. Add its causal collector regression and retain the selected-service rejection assertion. Receive the canonical2b2ae319SDK fixture fix so mutation occurs after real declaration emission. Receiving tests and core/services/SDK types pass; focused lint and P2 review are clean. The full changed gate still encounters the inherited OpenRouter suppression defect already repaired on main. The production change requires new package acceptance and native Windows proof before landing. * fix(build): give external recovery steps for stale dist The live-build refusal now names an external stop, successful rebuild, and start sequence for every matching service. Explain that an already-current update does not rebuild stale output, and document the default-profile commands with failure-short-circuiting in the source update guide. This changes guidance only, without altering admission or lifecycle policy. The existing fence suite passes 37 cases with one platform skip; focused lint, formatting and independent P2 review are clean. * test(daemon): observe published updater settlement Record launcher, command, and output lifecycle timestamps for the published update command. After its current ledger run becomes terminal, retain at most two bounded and sanitized process snapshots with creation and CPU/I/O facts. Fence progress and snapshots to that same run; observations neither refresh progress nor change command, cleanup, or watchdog budgets. This diagnoses a published 9.3 command that timed out after its candidate reported successful activation and verification. Preserve the failure and avoid inferring early process exit from post-timeout cleanup observations. 34 focused cases, services types, scoped lint, formatting, and independent P2 review pass. Native diagnostic execution remains required. * fix(daemon): recognize the released waiting task launcher Accept the exact waiting VBS form generated by the published 2026.9.3 installer during an owned service-definition audit. Keep custom launcher behavior classified as an unknown edit and preserve all selector, command, root, and native authority checks. Receive the three reviewed postimages fromec742584. The actual audit regression fails on the original implementation; 55 receiving cases pass. The shared owner also verified 140 backup/rewrite cases, core/services types, scoped lint and P2 review. This repairs the observed reconciliation warning; it does not claim to explain the independent updater settlement timeout. * test(daemon): sample unsettled updates before the command deadline Keep the same two-snapshot cap, current-run binding and progress behavior. Capture an unfinished run at 300 seconds so a slower installation cannot prevent all process observations. Rename the diagnostic field to reflect both active and terminal observations. The original observer regression fails; 25 cases, scoped lint/types and independent P2 review pass. Command and cleanup budgets are unchanged. * test(daemon): preserve CRCRLF task export comparisons * fix(windows): enable a verified disabled task on explicit start Receive the shared selected-service start repair, preserving published command fingerprints and optional Enabled metadata behavior. Keep the finalization process fixture's real entrypoint exports available. Source-build recovery ownership remains unchanged. * docs(windows): clarify explicit start partial effects Document that successful enablement remains an audited effect when the later launch fails. Preserve the existing explicit-start contract and require inspection before retrying; do not imply rollback after authority loss. * docs: clarify source CLI recovery uses the existing build --------- Co-authored-by: Peter Steinberger <steipete@gmail.com> Co-authored-by: Donnie Fiander <44792682+DonnieFi@users.noreply.github.com>
207 lines
7.3 KiB
JavaScript
207 lines
7.3 KiB
JavaScript
// Bootstraps documented JavaScript entrypoints before the TypeScript loader is active.
|
|
import { spawn } from "node:child_process";
|
|
import { existsSync } from "node:fs";
|
|
import { createRequire } from "node:module";
|
|
import { constants as osConstants } from "node:os";
|
|
import path from "node:path";
|
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
import { ensureRepoNodeModulesLink } from "./local-check-runtime.mts";
|
|
|
|
const FORWARDED_SIGNALS = ["SIGINT", "SIGTERM", "SIGHUP"];
|
|
const DEFAULT_FORCE_KILL_DELAY_MS = 5_000;
|
|
const FORWARDED_COMPILER_FLAGS = new Set([
|
|
"--maglev",
|
|
"--no-maglev",
|
|
"--concurrent-sparkplug",
|
|
"--no-concurrent-sparkplug",
|
|
]);
|
|
const SHIM_CHECKOUT_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
|
|
// Forward compiler policy without replaying parent loaders, evals, or debuggers.
|
|
export function resolveForwardedNodeCompilerArgs(execArgv = process.execArgv) {
|
|
return execArgv.filter((arg) => FORWARDED_COMPILER_FLAGS.has(arg));
|
|
}
|
|
|
|
export function resolveConfiguredModulesDir(checkoutRoot) {
|
|
const modulesDir =
|
|
(process.env.PNPM_CONFIG_MODULES_DIR ?? process.env.pnpm_config_modules_dir) ||
|
|
process.env.npm_config_modules_dir;
|
|
return modulesDir ? path.resolve(checkoutRoot, modulesDir) : undefined;
|
|
}
|
|
|
|
export function resolveTsxImport(checkoutRoot) {
|
|
const localModulesDir = path.resolve(checkoutRoot, "node_modules");
|
|
const configuredModulesDir = resolveConfiguredModulesDir(checkoutRoot);
|
|
const candidates = configuredModulesDir
|
|
? [configuredModulesDir, localModulesDir]
|
|
: [localModulesDir];
|
|
for (const selectedModulesDir of candidates) {
|
|
const tsxManifest = path.join(selectedModulesDir, "tsx", "package.json");
|
|
if (!existsSync(tsxManifest)) {
|
|
continue;
|
|
}
|
|
const require = createRequire(tsxManifest);
|
|
// Keep compiled ESM native: tsx's CJS hook rewrites its import-only
|
|
// dependency edges into require() calls with incompatible export conditions.
|
|
const importUrl = pathToFileURL(require.resolve("tsx/esm")).href;
|
|
if (selectedModulesDir === configuredModulesDir) {
|
|
ensureRepoNodeModulesLink(selectedModulesDir, { cwd: checkoutRoot });
|
|
}
|
|
return importUrl;
|
|
}
|
|
throw new Error(
|
|
`Repository dependencies are missing from ${localModulesDir}. Run pnpm install --frozen-lockfile in an independently owned checkout.`,
|
|
);
|
|
}
|
|
|
|
export async function registerToolingTsx() {
|
|
// tsx indexes the entire shared disk cache before expiration, coupling startup
|
|
// to other checkouts' cache size. This flag retains its in-process Map and
|
|
// reaches descendant tooling before their loaders initialize.
|
|
process.env.TSX_DISABLE_CACHE = "1";
|
|
// Fixtures run this checkout's tooling from a cwd without a tsconfig, which
|
|
// disables tsx paths mapping for workspace imports. Pin this checkout's
|
|
// tsconfig only for that foreign cwd; explicit env and cwd tsconfigs win.
|
|
const checkoutTsconfig = path.join(SHIM_CHECKOUT_ROOT, "tsconfig.json");
|
|
if (
|
|
process.env.TSX_TSCONFIG_PATH === undefined &&
|
|
!existsSync(path.join(process.cwd(), "tsconfig.json")) &&
|
|
existsSync(checkoutTsconfig)
|
|
) {
|
|
process.env.TSX_TSCONFIG_PATH = checkoutTsconfig;
|
|
}
|
|
await import(resolveTsxImport(SHIM_CHECKOUT_ROOT));
|
|
}
|
|
|
|
function signalExitCode(signal) {
|
|
const signalNumber = osConstants.signals[signal];
|
|
return typeof signalNumber === "number" ? 128 + signalNumber : 1;
|
|
}
|
|
|
|
function writeFailureTrailer(tool, exitCode) {
|
|
if (tool && exitCode !== 0) {
|
|
console.error(`[${tool}] FAILED (exit ${exitCode})`);
|
|
}
|
|
}
|
|
|
|
function signalChild(child, signal, detached) {
|
|
if (!child?.pid) {
|
|
return;
|
|
}
|
|
try {
|
|
if (detached && process.platform !== "win32") {
|
|
process.kill(-child.pid, signal);
|
|
} else {
|
|
child.kill(signal);
|
|
}
|
|
} catch (error) {
|
|
if (!error || typeof error !== "object" || !("code" in error) || error.code !== "ESRCH") {
|
|
console.error(error);
|
|
}
|
|
}
|
|
}
|
|
|
|
async function runCliShimInner(moduleUrl, options, nodeArgs) {
|
|
const detached = options.detached ?? (process.platform !== "win32" && !process.stdin.isTTY);
|
|
const forceKillDelayMs = options.forceKillDelayMs ?? DEFAULT_FORCE_KILL_DELAY_MS;
|
|
let child = null;
|
|
let forceKillTimer = null;
|
|
const signalHandlers = new Map();
|
|
const cleanup = () => {
|
|
if (forceKillTimer) {
|
|
clearTimeout(forceKillTimer);
|
|
forceKillTimer = null;
|
|
}
|
|
for (const [signal, handler] of signalHandlers) {
|
|
process.off(signal, handler);
|
|
}
|
|
process.off("exit", exitHandler);
|
|
};
|
|
const exitHandler = () => signalChild(child, "SIGTERM", detached);
|
|
|
|
for (const signal of FORWARDED_SIGNALS) {
|
|
const handler = () => {
|
|
signalChild(child, signal, detached);
|
|
// A lifecycle-owning implementation must finish killing its own child groups.
|
|
// A competing shim deadline can kill that owner and orphan those children.
|
|
if (options.terminationOwner !== "implementation") {
|
|
forceKillTimer ??= setTimeout(
|
|
() => signalChild(child, "SIGKILL", detached),
|
|
forceKillDelayMs,
|
|
);
|
|
forceKillTimer.unref();
|
|
}
|
|
};
|
|
signalHandlers.set(signal, handler);
|
|
process.on(signal, handler);
|
|
}
|
|
process.on("exit", exitHandler);
|
|
|
|
try {
|
|
// Native entrypoints need the explicit dependency link without loading TSX.
|
|
if (nodeArgs.length === 0) {
|
|
const modulesDir = resolveConfiguredModulesDir(SHIM_CHECKOUT_ROOT);
|
|
if (modulesDir) {
|
|
ensureRepoNodeModulesLink(modulesDir, { cwd: SHIM_CHECKOUT_ROOT });
|
|
}
|
|
}
|
|
const implementationUrl = new URL(options.implementation, moduleUrl);
|
|
const implementationPath = fileURLToPath(implementationUrl);
|
|
const nodeExecutable = options.executable ?? (process.versions.bun ? "node" : process.execPath);
|
|
// Preserve explicit compiler policy without copying parent loaders, evals, or debuggers.
|
|
const compilerArgs = resolveForwardedNodeCompilerArgs();
|
|
child = spawn(
|
|
nodeExecutable,
|
|
[
|
|
...nodeArgs,
|
|
...compilerArgs,
|
|
...(options.execArgv ?? []),
|
|
implementationPath,
|
|
...process.argv.slice(2),
|
|
],
|
|
{
|
|
cwd: process.cwd(),
|
|
detached,
|
|
env: process.env,
|
|
stdio: options.stdio ?? "inherit",
|
|
},
|
|
);
|
|
const result = await new Promise((resolve, reject) => {
|
|
child.once("error", reject);
|
|
child.once("close", (code, signal) => resolve({ code, signal }));
|
|
});
|
|
cleanup();
|
|
|
|
if (result.signal) {
|
|
writeFailureTrailer(options.failureTool, signalExitCode(result.signal));
|
|
process.kill(process.pid, result.signal);
|
|
return;
|
|
}
|
|
const exitCode = result.code ?? 1;
|
|
writeFailureTrailer(options.failureTool, exitCode);
|
|
process.exitCode = exitCode;
|
|
} catch (error) {
|
|
cleanup();
|
|
console.error(error);
|
|
writeFailureTrailer(options.failureTool, 1);
|
|
process.exitCode = 1;
|
|
}
|
|
}
|
|
|
|
async function runCliShim(moduleUrl, options, nodeArgs) {
|
|
try {
|
|
await runCliShimInner(moduleUrl, options, nodeArgs);
|
|
} catch (error) {
|
|
console.error(error);
|
|
writeFailureTrailer(options.failureTool, 1);
|
|
process.exitCode = 1;
|
|
}
|
|
}
|
|
|
|
export function runNodeCliShim(moduleUrl, options = {}) {
|
|
return runCliShim(moduleUrl, options, []);
|
|
}
|
|
|
|
export function runTsxCliShim(moduleUrl, options = {}) {
|
|
return runCliShim(moduleUrl, options, ["--import", new URL("../tsx.mjs", import.meta.url).href]);
|
|
}
|