From ce0fafefcedc1860adb38bee6dd978ed00193e34 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 27 Aug 2026 10:36:12 -0700 Subject: [PATCH] fix(tooling): avoid build and test startup stalls on shared tsx caches (#130924) * fix(tooling): avoid shared tsx cache startup stalls Use the shared preloader before tsx initializes so maintained build, check, and test commands retain memory transforms without scanning other checkouts' disk caches. Preserve changed-cwd forks and copied tooling closures. * test: align command contracts with tooling bootstrap --- .github/workflows/plugin-npm-release.yml | 1 + docs/reference/test.md | 6 + package.json | 566 +++++++++--------- scripts/ci-changed-scope.mjs | 2 +- scripts/docs-sync-publish.mjs | 4 + scripts/lib/plugin-publication-candidates.ts | 1 + scripts/lib/tsx-cli-shim.mjs | 10 +- scripts/pr | 1 + scripts/test-projects.test-support.mts | 2 +- scripts/tsx.mjs | 14 + src/infra/vitest-config.test.ts | 24 +- test/package-scripts.test.ts | 37 +- test/plugin-npm-release.test.ts | 2 + test/scripts/direct-run-entrypoints.test.ts | 280 ++++++++- test/scripts/docker-all-scheduler.test.ts | 1 + .../package-acceptance-workflow.test.ts | 7 +- test/scripts/pr-operation-lock.test.ts | 1 + test/scripts/pr-wrappers.test.ts | 15 + test/scripts/release-preflight.test.ts | 66 +- test/scripts/run-oxlint.test.ts | 10 +- test/scripts/test-projects.test.ts | 12 +- 21 files changed, 703 insertions(+), 359 deletions(-) create mode 100644 scripts/tsx.mjs diff --git a/.github/workflows/plugin-npm-release.yml b/.github/workflows/plugin-npm-release.yml index e7839c81f0bb..6bd9ee4243c3 100644 --- a/.github/workflows/plugin-npm-release.yml +++ b/.github/workflows/plugin-npm-release.yml @@ -22,6 +22,7 @@ on: - "scripts/lib/plugin-npm-package-manifest.mts" - "scripts/lib/tsx-cli-shim.mjs" - "scripts/lib/local-check-runtime.mts" + - "scripts/tsx.mjs" - "scripts/lib/plugin-npm-release.ts" - "scripts/lib/plugin-publication-candidates.ts" - "scripts/lib/plugin-publication-collector.ts" diff --git a/docs/reference/test.md b/docs/reference/test.md index 7fca57611357..1cffd2ad09ef 100644 --- a/docs/reference/test.md +++ b/docs/reference/test.md @@ -79,6 +79,12 @@ reconcile dependencies before the remote wrapper starts. ## Core commands +Maintained JavaScript tooling wrappers and root package commands use tsx's +in-process transform cache. They skip its shared disk cache before the loader +starts, and child tooling inherits that policy. This does not relocate or clean +temporary directories, Node or Vitest caches, or other global caches. Raw external +`tsx` and `node --import tsx` invocations outside these launchers are unchanged. + Test wrapper runs end with a short `[test] passed|failed|skipped ... in ...` summary; Vitest's own duration line stays the per-shard detail. | Command | What it does | diff --git a/package.json b/package.json index a58a5e0c9094..b57f82078e4b 100644 --- a/package.json +++ b/package.json @@ -1497,16 +1497,16 @@ "./cli-entry": "./openclaw.mjs" }, "scripts": { - "android:assemble": "node --import tsx scripts/run-android-gradle.mts :app:assemblePlayDebug :wear:assembleDebug :wear-shared:assembleDebug", - "android:assemble:third-party": "node --import tsx scripts/run-android-gradle.mts :app:assembleThirdPartyDebug", + "android:assemble": "node --import ./scripts/tsx.mjs scripts/run-android-gradle.mts :app:assemblePlayDebug :wear:assembleDebug :wear-shared:assembleDebug", + "android:assemble:third-party": "node --import ./scripts/tsx.mjs scripts/run-android-gradle.mts :app:assembleThirdPartyDebug", "android:bundle:release": "bash -lc 'source ./scripts/lib/android-fastlane.sh && cd apps/android && run_android_fastlane android play_store_archive'", "android:format": "cd apps/android && ./gradlew :app:ktlintFormat :benchmark:ktlintFormat :wear:ktlintFormat :wear-shared:ktlintFormat", - "android:install": "node --import tsx scripts/run-android-gradle.mts :app:installPlayDebug", - "android:install:third-party": "node --import tsx scripts/run-android-gradle.mts :app:installThirdPartyDebug", + "android:install": "node --import ./scripts/tsx.mjs scripts/run-android-gradle.mts :app:installPlayDebug", + "android:install:third-party": "node --import ./scripts/tsx.mjs scripts/run-android-gradle.mts :app:installThirdPartyDebug", "android:lint": "cd apps/android && ./gradlew :app:ktlintCheck :benchmark:ktlintCheck :wear:ktlintCheck :wear-shared:ktlintCheck", - "android:lint:android": "node --import tsx scripts/run-android-gradle.mts :app:lintPlayDebug :app:lintThirdPartyDebug :wear:lintDebug :wear-shared:lintDebug", - "android:run": "node --import tsx scripts/run-android-gradle.mts :app:runPlayDebug", - "android:run:third-party": "node --import tsx scripts/run-android-gradle.mts :app:runThirdPartyDebug", + "android:lint:android": "node --import ./scripts/tsx.mjs scripts/run-android-gradle.mts :app:lintPlayDebug :app:lintThirdPartyDebug :wear:lintDebug :wear-shared:lintDebug", + "android:run": "node --import ./scripts/tsx.mjs scripts/run-android-gradle.mts :app:runPlayDebug", + "android:run:third-party": "node --import ./scripts/tsx.mjs scripts/run-android-gradle.mts :app:runThirdPartyDebug", "android:release": "bash scripts/android-release.sh", "android:release:archive": "bash -lc 'source ./scripts/lib/android-fastlane.sh && cd apps/android && run_android_fastlane android play_store_archive'", "android:release:auth:check": "bash -lc 'source ./scripts/lib/android-fastlane.sh && cd apps/android && run_android_fastlane android auth_check'", @@ -1518,100 +1518,100 @@ "android:release:signing:sync:push": "bash -lc 'source ./scripts/lib/android-fastlane.sh && cd apps/android && run_android_fastlane android signing_sync_push'", "android:release:upload": "bash scripts/android-release-upload.sh", "android:screenshots": "bash scripts/android-screenshots.sh", - "android:test": "node --import tsx scripts/run-android-gradle.mts :app:testPlayDebugUnitTest :wear:testDebugUnitTest :wear-shared:testDebugUnitTest", - "android:test:integration": "node --import tsx scripts/run-with-env.mts OPENCLAW_LIVE_TEST=1 OPENCLAW_LIVE_ANDROID_NODE=1 -- node scripts/run-vitest.mjs run --config test/vitest/vitest.live.config.ts src/gateway/android-node.capabilities.live.test.ts", - "android:test:third-party": "node --import tsx scripts/run-android-gradle.mts :app:testThirdPartyDebugUnitTest", - "android:version": "node --import tsx scripts/android-version.ts --json", - "android:version:check": "node --import tsx scripts/android-sync-versioning.ts --check", - "android:version:pin": "node --import tsx scripts/android-pin-version.ts", - "android:version:sync": "node --import tsx scripts/android-sync-versioning.ts --write", - "audit:seams": "node --import tsx scripts/audit-seams.mts", - "build": "node --import tsx scripts/build-all.mts", - "build:ci-artifacts": "node --import tsx scripts/build-all.mts ciArtifacts", - "build:docker": "pnpm plugins:assets:build && node --import tsx scripts/tsdown-build.mts && node --import tsx scripts/check-cli-bootstrap-imports.mts && node scripts/runtime-postbuild.mjs && node --import tsx scripts/build-stamp.mts && node --import tsx scripts/runtime-postbuild-stamp.mts && pnpm plugins:assets:copy && node --import tsx scripts/write-build-info.ts && node --import tsx scripts/write-cli-startup-metadata.ts", - "build:package": "node --import tsx scripts/build-all.mts package", + "android:test": "node --import ./scripts/tsx.mjs scripts/run-android-gradle.mts :app:testPlayDebugUnitTest :wear:testDebugUnitTest :wear-shared:testDebugUnitTest", + "android:test:integration": "node --import ./scripts/tsx.mjs scripts/run-with-env.mts OPENCLAW_LIVE_TEST=1 OPENCLAW_LIVE_ANDROID_NODE=1 -- node scripts/run-vitest.mjs run --config test/vitest/vitest.live.config.ts src/gateway/android-node.capabilities.live.test.ts", + "android:test:third-party": "node --import ./scripts/tsx.mjs scripts/run-android-gradle.mts :app:testThirdPartyDebugUnitTest", + "android:version": "node --import ./scripts/tsx.mjs scripts/android-version.ts --json", + "android:version:check": "node --import ./scripts/tsx.mjs scripts/android-sync-versioning.ts --check", + "android:version:pin": "node --import ./scripts/tsx.mjs scripts/android-pin-version.ts", + "android:version:sync": "node --import ./scripts/tsx.mjs scripts/android-sync-versioning.ts --write", + "audit:seams": "node --import ./scripts/tsx.mjs scripts/audit-seams.mts", + "build": "node --import ./scripts/tsx.mjs scripts/build-all.mts", + "build:ci-artifacts": "node --import ./scripts/tsx.mjs scripts/build-all.mts ciArtifacts", + "build:docker": "pnpm plugins:assets:build && node --import ./scripts/tsx.mjs scripts/tsdown-build.mts && node --import ./scripts/tsx.mjs scripts/check-cli-bootstrap-imports.mts && node scripts/runtime-postbuild.mjs && node --import ./scripts/tsx.mjs scripts/build-stamp.mts && node --import ./scripts/tsx.mjs scripts/runtime-postbuild-stamp.mts && pnpm plugins:assets:copy && node --import ./scripts/tsx.mjs scripts/write-build-info.ts && node --import ./scripts/tsx.mjs scripts/write-cli-startup-metadata.ts", + "build:package": "node --import ./scripts/tsx.mjs scripts/build-all.mts package", "build:plugin-sdk:dts": "node scripts/run-tsgo.mjs -p tsconfig.plugin-sdk.dts.json --declaration true", - "build:plugin-sdk:strict-smoke": "node --import tsx scripts/tsdown-build.mts && node scripts/runtime-postbuild.mjs && node --import tsx scripts/run-with-env.mts OPENCLAW_PLUGIN_SDK_CANONICAL_DTS=1 -- node --import tsx scripts/write-plugin-sdk-entry-dts.ts && node --import tsx scripts/check-plugin-sdk-exports.mts", - "build:strict-smoke": "pnpm plugins:assets:build && node --import tsx scripts/tsdown-build.mts && node --import tsx scripts/check-cli-bootstrap-imports.mts && node scripts/runtime-postbuild.mjs && node --import tsx scripts/build-stamp.mts && node --import tsx scripts/runtime-postbuild-stamp.mts && node --import tsx scripts/run-with-env.mts OPENCLAW_PLUGIN_SDK_CANONICAL_DTS=1 -- node --import tsx scripts/write-plugin-sdk-entry-dts.ts && node --import tsx scripts/check-plugin-sdk-exports.mts", - "canvas:a2ui:bundle": "node --import tsx scripts/bundle-a2ui.mts", + "build:plugin-sdk:strict-smoke": "node --import ./scripts/tsx.mjs scripts/tsdown-build.mts && node scripts/runtime-postbuild.mjs && node --import ./scripts/tsx.mjs scripts/run-with-env.mts OPENCLAW_PLUGIN_SDK_CANONICAL_DTS=1 -- node --import ./scripts/tsx.mjs scripts/write-plugin-sdk-entry-dts.ts && node --import ./scripts/tsx.mjs scripts/check-plugin-sdk-exports.mts", + "build:strict-smoke": "pnpm plugins:assets:build && node --import ./scripts/tsx.mjs scripts/tsdown-build.mts && node --import ./scripts/tsx.mjs scripts/check-cli-bootstrap-imports.mts && node scripts/runtime-postbuild.mjs && node --import ./scripts/tsx.mjs scripts/build-stamp.mts && node --import ./scripts/tsx.mjs scripts/runtime-postbuild-stamp.mts && node --import ./scripts/tsx.mjs scripts/run-with-env.mts OPENCLAW_PLUGIN_SDK_CANONICAL_DTS=1 -- node --import ./scripts/tsx.mjs scripts/write-plugin-sdk-entry-dts.ts && node --import ./scripts/tsx.mjs scripts/check-plugin-sdk-exports.mts", + "canvas:a2ui:bundle": "node --import ./scripts/tsx.mjs scripts/bundle-a2ui.mts", "channels:catalog:check": "node scripts/write-official-channel-catalog.mjs --check", "channels:catalog:gen": "node scripts/write-official-channel-catalog.mjs --write", "changed:lanes": "node scripts/changed-lanes.mjs", - "check": "node --import tsx scripts/check.mts", + "check": "node --import ./scripts/tsx.mjs scripts/check.mts", "check:architecture": "pnpm check:import-cycles && pnpm check:madge-import-cycles && pnpm check:deprecated-api-usage && pnpm check:wrapper-shadowing && pnpm check:deprecated-jsdoc && pnpm db:kysely:check && pnpm lint:kysely && pnpm check:database-first-legacy-stores", - "check:assertion-safety": "node --import tsx scripts/check-assertion-safety-ratchet.mts", - "check:base-config-schema": "node --import tsx scripts/generate-base-config-schema.ts --check", - "check:bundled-channel-config-metadata": "node --import tsx scripts/generate-bundled-channel-config-metadata.ts --check", + "check:assertion-safety": "node --import ./scripts/tsx.mjs scripts/check-assertion-safety-ratchet.mts", + "check:base-config-schema": "node --import ./scripts/tsx.mjs scripts/generate-base-config-schema.ts --check", + "check:bundled-channel-config-metadata": "node --import ./scripts/tsx.mjs scripts/generate-bundled-channel-config-metadata.ts --check", "check:changed": "node scripts/check-changed.mjs", - "check:changelog-attributions": "node --import tsx scripts/check-changelog-attributions.mts", - "check:coercion-helpers": "node --import tsx scripts/check-coercion-helper-declarations.mts", - "check:database-first-legacy-stores": "node --import tsx scripts/check-database-first-legacy-stores.mts", - "check:deprecated-api-usage": "node --import tsx scripts/check-deprecated-api-usage.mts", - "check:deprecated-jsdoc": "node --import tsx scripts/check-deprecated-jsdoc.mts", - "check:doctor-deprecation-registry": "node --import tsx scripts/check-doctor-deprecation-registry.ts", + "check:changelog-attributions": "node --import ./scripts/tsx.mjs scripts/check-changelog-attributions.mts", + "check:coercion-helpers": "node --import ./scripts/tsx.mjs scripts/check-coercion-helper-declarations.mts", + "check:database-first-legacy-stores": "node --import ./scripts/tsx.mjs scripts/check-database-first-legacy-stores.mts", + "check:deprecated-api-usage": "node --import ./scripts/tsx.mjs scripts/check-deprecated-api-usage.mts", + "check:deprecated-jsdoc": "node --import ./scripts/tsx.mjs scripts/check-deprecated-jsdoc.mts", + "check:doctor-deprecation-registry": "node --import ./scripts/tsx.mjs scripts/check-doctor-deprecation-registry.ts", "check:docs": "pnpm format:docs:check && pnpm lint:docs && pnpm docs:check-mdx && pnpm docs:check-i18n-glossary && pnpm docs:check-links && pnpm docs:check-config-examples", - "check:env-var-count": "node --import tsx scripts/check-env-var-count.mts", - "check:host-env-policy:swift": "node --import tsx scripts/generate-host-env-security-policy-swift.mts --check", - "check:import-cycles": "node --import tsx scripts/check-import-cycles.ts", - "check:max-lines-ratchet": "node --import tsx scripts/check-max-lines-ratchet.mts", - "check:madge-import-cycles": "node --import tsx scripts/check-madge-import-cycles.ts", - "check:media-download-helpers": "node --import tsx scripts/check-media-download-helper-roundtrip.mts", + "check:env-var-count": "node --import ./scripts/tsx.mjs scripts/check-env-var-count.mts", + "check:host-env-policy:swift": "node --import ./scripts/tsx.mjs scripts/generate-host-env-security-policy-swift.mts --check", + "check:import-cycles": "node --import ./scripts/tsx.mjs scripts/check-import-cycles.ts", + "check:max-lines-ratchet": "node --import ./scripts/tsx.mjs scripts/check-max-lines-ratchet.mts", + "check:madge-import-cycles": "node --import ./scripts/tsx.mjs scripts/check-madge-import-cycles.ts", + "check:media-download-helpers": "node --import ./scripts/tsx.mjs scripts/check-media-download-helper-roundtrip.mts", "check:no-conflict-markers": "node scripts/check-no-conflict-markers.mjs", - "check:no-runtime-action-load-config": "node --import tsx scripts/check-no-runtime-action-load-config.mts", + "check:no-runtime-action-load-config": "node --import ./scripts/tsx.mjs scripts/check-no-runtime-action-load-config.mts", "check:opengrep-rule-metadata": "node security/opengrep/check-rule-metadata.mjs", "check:protocol-coverage": "node scripts/check-protocol-event-coverage.mjs", - "check:runtime-sidecar-loaders": "node --import tsx scripts/check-runtime-sidecar-loaders.mts", + "check:runtime-sidecar-loaders": "node --import ./scripts/tsx.mjs scripts/check-runtime-sidecar-loaders.mts", "check:script-erasability": "node --disable-warning=ExperimentalWarning scripts/check-script-erasability.mjs", "check:static-import-sccs": "pnpm check:madge-import-cycles", - "check:temp-path-guardrails": "node --import tsx scripts/check-temp-path-guardrails.ts", - "check:wrapper-shadowing": "node --import tsx scripts/check-wrapper-shadowing.mts", + "check:temp-path-guardrails": "node --import ./scripts/tsx.mjs scripts/check-temp-path-guardrails.ts", + "check:wrapper-shadowing": "node --import ./scripts/tsx.mjs scripts/check-wrapper-shadowing.mts", "check:test-types": "pnpm tsgo:test", - "check:timed": "node --import tsx scripts/check-timed.mts", - "check:timed:all-types": "node --import tsx scripts/check-timed.mts --include-test-types", - "check:timed:architecture": "node --import tsx scripts/check-timed.mts --include-architecture", - "check:workflows": "node --import tsx scripts/check-workflows.mts", + "check:timed": "node --import ./scripts/tsx.mjs scripts/check-timed.mts", + "check:timed:all-types": "node --import ./scripts/tsx.mjs scripts/check-timed.mts --include-test-types", + "check:timed:architecture": "node --import ./scripts/tsx.mjs scripts/check-timed.mts --include-architecture", + "check:workflows": "node --import ./scripts/tsx.mjs scripts/check-workflows.mts", "ci:full-release": "node scripts/full-release-validation-at-sha.mjs", "ci:timings": "node scripts/ci-run-timings.mjs --latest-main", "ci:timings:recent": "node scripts/ci-run-timings.mjs --recent 10", "ci:timings:trend": "node scripts/ci-run-timings.mjs --trend-hours 72 --compare-hours 12", "clean:dist": "node -e \"require('fs').rmSync('dist', {recursive: true, force: true})\"", - "codex-app-server:protocol:check": "node --import tsx scripts/check-codex-app-server-protocol.ts", - "codex-app-server:protocol:sync": "node --import tsx scripts/sync-codex-app-server-protocol.ts", - "codex-supervisor:mcp": "node --import tsx src/mcp/codex-supervision-tools-serve.ts", - "codex:mcp": "node --import tsx src/mcp/codex-supervision-tools-serve.ts", - "config:channels:check": "node --import tsx scripts/generate-bundled-channel-config-metadata.ts --check", - "config:channels:gen": "node --import tsx scripts/generate-bundled-channel-config-metadata.ts --write", - "config:docs:check": "node --import tsx scripts/generate-config-doc-baseline.ts --check", - "config:docs:gen": "node --import tsx scripts/generate-config-doc-baseline.ts --write", - "config:schema:check": "node --import tsx scripts/generate-base-config-schema.ts --check", - "config:schema:gen": "node --import tsx scripts/generate-base-config-schema.ts --write", + "codex-app-server:protocol:check": "node --import ./scripts/tsx.mjs scripts/check-codex-app-server-protocol.ts", + "codex-app-server:protocol:sync": "node --import ./scripts/tsx.mjs scripts/sync-codex-app-server-protocol.ts", + "codex-supervisor:mcp": "node --import ./scripts/tsx.mjs src/mcp/codex-supervision-tools-serve.ts", + "codex:mcp": "node --import ./scripts/tsx.mjs src/mcp/codex-supervision-tools-serve.ts", + "config:channels:check": "node --import ./scripts/tsx.mjs scripts/generate-bundled-channel-config-metadata.ts --check", + "config:channels:gen": "node --import ./scripts/tsx.mjs scripts/generate-bundled-channel-config-metadata.ts --write", + "config:docs:check": "node --import ./scripts/tsx.mjs scripts/generate-config-doc-baseline.ts --check", + "config:docs:gen": "node --import ./scripts/tsx.mjs scripts/generate-config-doc-baseline.ts --write", + "config:schema:check": "node --import ./scripts/tsx.mjs scripts/generate-base-config-schema.ts --check", + "config:schema:gen": "node --import ./scripts/tsx.mjs scripts/generate-base-config-schema.ts --write", "crabbox:hydrate": "node scripts/crabbox-wrapper.mjs actions hydrate", "crabbox:run": "node scripts/crabbox-wrapper.mjs run", "crabbox:stop": "node scripts/crabbox-wrapper.mjs stop", "crabbox:warmup": "node scripts/crabbox-wrapper.mjs warmup", "deadcode:dependencies": "pnpm deadcode:full", - "deadcode:exports": "node --import tsx scripts/check-deadcode-exports.mts", + "deadcode:exports": "node --import ./scripts/tsx.mjs scripts/check-deadcode-exports.mts", "deadcode:full": "pnpm --config.minimum-release-age=0 dlx --package knip@6.32.2 knip --config config/knip.config.ts --production --no-progress --reporter compact --no-config-hints --exclude duplicates && pnpm --config.minimum-release-age=0 dlx --package knip@6.32.2 knip --config config/knip.all-exports.config.ts --no-progress --reporter compact --no-config-hints --exports --exclude duplicates", "deadcode:knip": "pnpm --config.minimum-release-age=0 dlx --package knip@6.32.2 knip --config config/knip.config.ts --production --no-progress --reporter compact --files --dependencies", "deadcode:report": "pnpm deadcode:full && pnpm deadcode:exports", - "deadcode:unused-files": "node --import tsx scripts/check-deadcode-unused-files.mts", - "deps:root-ownership": "node --import tsx scripts/root-dependency-ownership-audit.mts", - "deps:root-ownership:check": "node --import tsx scripts/root-dependency-ownership-audit.mts --check", - "deps:changes:report": "node --import tsx scripts/dependency-changes-report.mts", - "deps:patches:check": "node --import tsx scripts/check-package-patches.mts", - "deps:pins:check": "node --import tsx scripts/check-dependency-pins.mts", + "deadcode:unused-files": "node --import ./scripts/tsx.mjs scripts/check-deadcode-unused-files.mts", + "deps:root-ownership": "node --import ./scripts/tsx.mjs scripts/root-dependency-ownership-audit.mts", + "deps:root-ownership:check": "node --import ./scripts/tsx.mjs scripts/root-dependency-ownership-audit.mts --check", + "deps:changes:report": "node --import ./scripts/tsx.mjs scripts/dependency-changes-report.mts", + "deps:patches:check": "node --import ./scripts/tsx.mjs scripts/check-package-patches.mts", + "deps:pins:check": "node --import ./scripts/tsx.mjs scripts/check-dependency-pins.mts", "deps:npm-lock:check": "node scripts/generate-npm-package-lock.mjs --all", "deps:npm-lock:check:changed": "node scripts/generate-npm-package-lock.mjs --changed", - "deps:ownership-surface:check": "node --import tsx scripts/dependency-ownership-surface-report.mts --check", - "deps:ownership-surface:report": "node --import tsx scripts/dependency-ownership-surface-report.mts", - "deps:transitive-risk:report": "node --import tsx scripts/transitive-manifest-risk-report.mts", - "deps:vuln:gate": "node --import tsx scripts/dependency-vulnerability-gate.mts", - "db:kysely:check": "node --import tsx scripts/generate-kysely-types.mts --verify", - "db:kysely:gen": "node --import tsx scripts/generate-kysely-types.mts", + "deps:ownership-surface:check": "node --import ./scripts/tsx.mjs scripts/dependency-ownership-surface-report.mts --check", + "deps:ownership-surface:report": "node --import ./scripts/tsx.mjs scripts/dependency-ownership-surface-report.mts", + "deps:transitive-risk:report": "node --import ./scripts/tsx.mjs scripts/transitive-manifest-risk-report.mts", + "deps:vuln:gate": "node --import ./scripts/tsx.mjs scripts/dependency-vulnerability-gate.mts", + "db:kysely:check": "node --import ./scripts/tsx.mjs scripts/generate-kysely-types.mts --verify", + "db:kysely:gen": "node --import ./scripts/tsx.mjs scripts/generate-kysely-types.mts", "dev": "node scripts/run-node.mjs", - "dev:ui:mock": "node --import tsx scripts/control-ui-mock-dev.ts", + "dev:ui:mock": "node --import ./scripts/tsx.mjs scripts/control-ui-mock-dev.ts", "docs:check-config-examples": "node scripts/check-docs-config-examples.mjs", - "docs:check-i18n-glossary": "node --import tsx scripts/check-docs-i18n-glossary.mts", + "docs:check-i18n-glossary": "node --import ./scripts/tsx.mjs scripts/check-docs-i18n-glossary.mts", "docs:check-links": "node scripts/docs-link-audit.mjs", "docs:check-links:anchors": "node scripts/docs-link-audit.mjs --anchors", "docs:check-mdx": "node scripts/check-docs-mdx.mjs docs README.md", @@ -1620,8 +1620,8 @@ "docs:map:gen": "node scripts/docs-list.js --headings", "docs:spellcheck": "bash scripts/docs-spellcheck.sh", "docs:spellcheck:fix": "bash scripts/docs-spellcheck.sh --write", - "maturity:check": "node --import tsx scripts/qa/render-maturity-docs.ts --check", - "maturity:render": "node --import tsx scripts/qa/render-maturity-docs.ts", + "maturity:check": "node --import ./scripts/tsx.mjs scripts/qa/render-maturity-docs.ts --check", + "maturity:render": "node --import ./scripts/tsx.mjs scripts/qa/render-maturity-docs.ts", "dup:check": "node scripts/check-duplicates.mjs", "dup:check:coverage": "node scripts/check-duplicates.mjs --coverage", "dup:check:json": "node scripts/check-duplicates.mjs --json", @@ -1629,16 +1629,16 @@ "format:all": "pnpm format && pnpm format:swift", "format:check": "oxfmt --check", "format:diff": "oxfmt --write --threads=1 && git --no-pager diff", - "format:docs": "node --import tsx scripts/format-docs.mts", - "format:docs:check": "node --import tsx scripts/format-docs.mts --check", + "format:docs": "node --import ./scripts/tsx.mjs scripts/format-docs.mts", + "format:docs:check": "node --import ./scripts/tsx.mjs scripts/format-docs.mts --check", "format:fix": "oxfmt --write --threads=1", "format:swift": "./scripts/format-swift.sh", - "gateway:dev": "node --import tsx scripts/run-with-env.mts OPENCLAW_SKIP_CHANNELS=1 -- node scripts/run-node.mjs --dev gateway", - "gateway:dev:reset": "node --import tsx scripts/run-with-env.mts OPENCLAW_SKIP_CHANNELS=1 -- node scripts/run-node.mjs --dev gateway --reset", - "gateway:watch": "node --import tsx scripts/gateway-watch-tmux.mts gateway --force", + "gateway:dev": "node --import ./scripts/tsx.mjs scripts/run-with-env.mts OPENCLAW_SKIP_CHANNELS=1 -- node scripts/run-node.mjs --dev gateway", + "gateway:dev:reset": "node --import ./scripts/tsx.mjs scripts/run-with-env.mts OPENCLAW_SKIP_CHANNELS=1 -- node scripts/run-node.mjs --dev gateway --reset", + "gateway:watch": "node --import ./scripts/tsx.mjs scripts/gateway-watch-tmux.mts gateway --force", "gateway:watch:raw": "node scripts/watch-node.mjs gateway --force", - "gen:host-env-policy:swift": "node --import tsx scripts/generate-host-env-security-policy-swift.mts --write", - "ghsa:patch": "node --import tsx scripts/ghsa-patch.mts", + "gen:host-env-policy:swift": "node --import ./scripts/tsx.mjs scripts/generate-host-env-security-policy-swift.mts --write", + "ghsa:patch": "node --import ./scripts/tsx.mjs scripts/ghsa-patch.mts", "ios:app-review-notes:pdf": "xcrun swift scripts/ios-app-review-notes-pdf.swift apps/ios/APP-REVIEW-NOTES.md apps/ios/build/app-review/APP-REVIEW-NOTES.pdf", "ios:build": "bash -c 'export PATH=\"$PATH:/opt/homebrew/bin:/usr/local/bin\"; ./scripts/ios-configure-signing.sh && ./scripts/ios-write-version-xcconfig.sh && node scripts/ios-write-swift-filelist.mjs && cd apps/ios && xcodegen generate && xcodebuild -project OpenClaw.xcodeproj -scheme OpenClaw -destination \"${IOS_DEST:-generic/platform=iOS Simulator}\" -configuration Debug build'", "ios:filelist:gen": "node scripts/ios-write-swift-filelist.mjs", @@ -1656,155 +1656,155 @@ "ios:release:upload": "bash scripts/ios-release-upload.sh", "ios:run": "bash scripts/ios-run.sh", "ios:screenshots": "bash scripts/ios-screenshots.sh", - "ios:version": "node --import tsx scripts/ios-version.ts --json", - "ios:version:check": "node --import tsx scripts/ios-sync-versioning.ts --check", - "ios:version:sync": "node --import tsx scripts/ios-sync-versioning.ts --write", - "leak:embedded-run": "node --import tsx --expose-gc scripts/embedded-run-abort-leak.ts", - "lint": "node --import tsx scripts/run-lint.mts", - "lint:agent:ingress-owner": "node --import tsx scripts/check-ingress-agent-owner-context.mts", + "ios:version": "node --import ./scripts/tsx.mjs scripts/ios-version.ts --json", + "ios:version:check": "node --import ./scripts/tsx.mjs scripts/ios-sync-versioning.ts --check", + "ios:version:sync": "node --import ./scripts/tsx.mjs scripts/ios-sync-versioning.ts --write", + "leak:embedded-run": "node --import ./scripts/tsx.mjs --expose-gc scripts/embedded-run-abort-leak.ts", + "lint": "node --import ./scripts/tsx.mjs scripts/run-lint.mts", + "lint:agent:ingress-owner": "node --import ./scripts/tsx.mjs scripts/check-ingress-agent-owner-context.mts", "lint:all": "node scripts/run-oxlint.mjs", "lint:apps": "pnpm lint:swift", - "lint:auth:no-pairing-store-group": "node --import tsx scripts/check-no-pairing-store-group-auth.mts", - "lint:auth:pairing-account-scope": "node --import tsx scripts/check-pairing-account-scope.mts", - "lint:core": "node --import tsx scripts/run-oxlint-shards.mts --only=core", - "lint:docker-e2e": "node --import tsx scripts/check-docker-e2e-boundaries.mts", - "lint:kysely": "node --import tsx scripts/check-kysely-guardrails.mts", + "lint:auth:no-pairing-store-group": "node --import ./scripts/tsx.mjs scripts/check-no-pairing-store-group-auth.mts", + "lint:auth:pairing-account-scope": "node --import ./scripts/tsx.mjs scripts/check-pairing-account-scope.mts", + "lint:core": "node --import ./scripts/tsx.mjs scripts/run-oxlint-shards.mts --only=core", + "lint:docker-e2e": "node --import ./scripts/tsx.mjs scripts/check-docker-e2e-boundaries.mts", + "lint:kysely": "node --import ./scripts/tsx.mjs scripts/check-kysely-guardrails.mts", "lint:docs": "pnpm dlx --config.resolution-mode=highest markdownlint-cli2 --config config/markdownlint-cli2.jsonc", "lint:docs:fix": "pnpm dlx --config.resolution-mode=highest markdownlint-cli2 --config config/markdownlint-cli2.jsonc --fix", - "lint:extensions:no-deprecated-channel-access": "node --import tsx scripts/check-no-deprecated-channel-access.ts", - "lint:extensions:telegram-grammy-types": "node --import tsx scripts/check-telegram-grammy-types-imports.mts", + "lint:extensions:no-deprecated-channel-access": "node --import ./scripts/tsx.mjs scripts/check-no-deprecated-channel-access.ts", + "lint:extensions:telegram-grammy-types": "node --import ./scripts/tsx.mjs scripts/check-telegram-grammy-types-imports.mts", "lint:extensions": "node scripts/run-oxlint.mjs --tsconfig config/tsconfig/oxlint.extensions.json extensions", - "lint:extensions:no-guarded-wildcard-reexports": "node --import tsx scripts/check-extension-wildcard-reexports.mts", - "lint:extensions:no-normalization-core-bypass": "node --import tsx scripts/check-extension-plugin-sdk-boundary.mts --mode=normalization-core-bypass", - "lint:extensions:no-plugin-sdk-wildcard-reexports": "node --import tsx scripts/check-plugin-sdk-wildcard-reexports.mts", - "lint:extensions:no-relative-outside-package": "node --import tsx scripts/check-extension-plugin-sdk-boundary.mts --mode=relative-outside-package", - "lint:extensions:no-src-outside-plugin-sdk": "node --import tsx scripts/check-extension-plugin-sdk-boundary.mts --mode=src-outside-plugin-sdk", + "lint:extensions:no-guarded-wildcard-reexports": "node --import ./scripts/tsx.mjs scripts/check-extension-wildcard-reexports.mts", + "lint:extensions:no-normalization-core-bypass": "node --import ./scripts/tsx.mjs scripts/check-extension-plugin-sdk-boundary.mts --mode=normalization-core-bypass", + "lint:extensions:no-plugin-sdk-wildcard-reexports": "node --import ./scripts/tsx.mjs scripts/check-plugin-sdk-wildcard-reexports.mts", + "lint:extensions:no-relative-outside-package": "node --import ./scripts/tsx.mjs scripts/check-extension-plugin-sdk-boundary.mts --mode=relative-outside-package", + "lint:extensions:no-src-outside-plugin-sdk": "node --import ./scripts/tsx.mjs scripts/check-extension-plugin-sdk-boundary.mts --mode=src-outside-plugin-sdk", "lint:fix": "node scripts/run-oxlint.mjs --fix && pnpm format", - "lint:plugins:no-extension-imports": "node --import tsx scripts/check-plugin-extension-import-boundary.mts", - "lint:plugins:no-extension-src-imports": "node --import tsx scripts/check-no-extension-src-imports.ts", - "lint:plugins:no-extension-test-core-imports": "node --import tsx scripts/check-no-extension-test-core-imports.ts", - "lint:plugins:no-monolithic-plugin-sdk-entry-imports": "node --import tsx scripts/check-no-monolithic-plugin-sdk-entry-imports.ts", + "lint:plugins:no-extension-imports": "node --import ./scripts/tsx.mjs scripts/check-plugin-extension-import-boundary.mts", + "lint:plugins:no-extension-src-imports": "node --import ./scripts/tsx.mjs scripts/check-no-extension-src-imports.ts", + "lint:plugins:no-extension-test-core-imports": "node --import ./scripts/tsx.mjs scripts/check-no-extension-test-core-imports.ts", + "lint:plugins:no-monolithic-plugin-sdk-entry-imports": "node --import ./scripts/tsx.mjs scripts/check-no-monolithic-plugin-sdk-entry-imports.ts", "lint:plugins:no-register-http-handler": "node scripts/run-oxlint.mjs --openclaw-focused-config --config config/oxlint/boundary-guards.json src extensions", - "lint:plugins:plugin-sdk-subpaths-exported": "node --import tsx scripts/check-plugin-sdk-subpath-exports.mts", + "lint:plugins:plugin-sdk-subpaths-exported": "node --import ./scripts/tsx.mjs scripts/check-plugin-sdk-subpath-exports.mts", "lint:no-chained-type-assertions": "node scripts/run-oxlint.mjs --openclaw-focused-config --config config/oxlint/boundary-guards.json src extensions packages ui/src", "lint:no-widen-then-assert": "node scripts/run-oxlint.mjs --openclaw-focused-config --config config/oxlint/boundary-guards.json src extensions packages ui/src", "lint:scripts": "pnpm lint:docker-e2e && pnpm lint:tmp:no-raw-http2-imports && node scripts/run-oxlint.mjs --tsconfig config/tsconfig/oxlint.scripts.json scripts", "lint:swift": "./scripts/lint-swift.sh", - "lint:tmp:channel-agnostic-boundaries": "node --import tsx scripts/check-channel-agnostic-boundaries.mts", - "lint:tmp:dynamic-import-warts": "node --import tsx scripts/check-dynamic-import-warts.mts", - "lint:tmp:export-name-collisions": "node --import tsx scripts/check-export-name-collisions.mts", - "lint:tmp:no-random-messaging": "node --import tsx scripts/check-no-random-messaging-tmp.mts", - "lint:tmp:no-raw-channel-fetch": "node --import tsx scripts/check-no-raw-channel-fetch.mts", - "lint:tmp:no-raw-http2-imports": "node --import tsx scripts/check-no-raw-http2-imports.mts", - "lint:tmp:session-accessor-boundary": "node --import tsx scripts/check-session-accessor-boundary.mts", - "lint:tmp:session-accessor-boundary:gen": "node --import tsx scripts/check-session-accessor-boundary.mts --update-debt-baseline", - "lint:tmp:session-transcript-reader-boundary": "node --import tsx scripts/check-session-transcript-reader-boundary.mts", - "lint:tmp:sqlite-transaction-boundary": "node --import tsx scripts/check-sqlite-transaction-boundary.mts", - "lint:tmp:tsgo-core-boundary": "node --import tsx scripts/check-tsgo-core-boundary.mts", + "lint:tmp:channel-agnostic-boundaries": "node --import ./scripts/tsx.mjs scripts/check-channel-agnostic-boundaries.mts", + "lint:tmp:dynamic-import-warts": "node --import ./scripts/tsx.mjs scripts/check-dynamic-import-warts.mts", + "lint:tmp:export-name-collisions": "node --import ./scripts/tsx.mjs scripts/check-export-name-collisions.mts", + "lint:tmp:no-random-messaging": "node --import ./scripts/tsx.mjs scripts/check-no-random-messaging-tmp.mts", + "lint:tmp:no-raw-channel-fetch": "node --import ./scripts/tsx.mjs scripts/check-no-raw-channel-fetch.mts", + "lint:tmp:no-raw-http2-imports": "node --import ./scripts/tsx.mjs scripts/check-no-raw-http2-imports.mts", + "lint:tmp:session-accessor-boundary": "node --import ./scripts/tsx.mjs scripts/check-session-accessor-boundary.mts", + "lint:tmp:session-accessor-boundary:gen": "node --import ./scripts/tsx.mjs scripts/check-session-accessor-boundary.mts --update-debt-baseline", + "lint:tmp:session-transcript-reader-boundary": "node --import ./scripts/tsx.mjs scripts/check-session-transcript-reader-boundary.mts", + "lint:tmp:sqlite-transaction-boundary": "node --import ./scripts/tsx.mjs scripts/check-sqlite-transaction-boundary.mts", + "lint:tmp:tsgo-core-boundary": "node --import ./scripts/tsx.mjs scripts/check-tsgo-core-boundary.mts", "lint:ui:i18n": "pnpm ui:i18n:verify", "lint:ui:lit": "lit-analyzer \"ui/src/**/*.ts\" --quiet", "lint:ui:no-raw-window-open": "node scripts/run-oxlint.mjs --openclaw-focused-config --config config/oxlint/boundary-guards.json ui/src", "lint:ui:styles": "stylelint --config config/stylelint.config.mjs \"ui/src/**/*.css\" \"ui/src/**/*.ts\" \"ui/public/themes/*.css\"", - "lint:web-fetch-provider-boundaries": "node --import tsx scripts/check-web-fetch-provider-boundaries.mts", - "lint:webhook:no-low-level-body-read": "node --import tsx scripts/check-webhook-auth-body-order.mts", + "lint:web-fetch-provider-boundaries": "node --import ./scripts/tsx.mjs scripts/check-web-fetch-provider-boundaries.mts", + "lint:webhook:no-low-level-body-read": "node --import ./scripts/tsx.mjs scripts/check-webhook-auth-body-order.mts", "mac:open": "open dist/OpenClaw.app", "mac:package": "bash scripts/package-mac-app.sh", "mac:restart": "bash scripts/restart-mac.sh", - "mobile:release:preflight": "node --import tsx scripts/mobile-release-ref.ts preflight", - "mobile:release:record": "node --import tsx scripts/mobile-release-ref.ts record", - "mobile:release:resolve": "node --import tsx scripts/mobile-release-ref.ts resolve", + "mobile:release:preflight": "node --import ./scripts/tsx.mjs scripts/mobile-release-ref.ts preflight", + "mobile:release:record": "node --import ./scripts/tsx.mjs scripts/mobile-release-ref.ts record", + "mobile:release:resolve": "node --import ./scripts/tsx.mjs scripts/mobile-release-ref.ts resolve", "openclaw": "node scripts/run-node.mjs", "openclaw:rpc": "node scripts/run-node.mjs agent --mode rpc --json", - "perf:web-fetch": "node --import tsx scripts/bench-web-fetch.ts", - "perf:kova:summary": "node --import tsx scripts/kova-ci-summary.mts", - "perf:source:summary": "node --import tsx scripts/openclaw-performance-source-summary.mts", - "plugin-sdk:api:diff": "node --max-old-space-size=8192 --import tsx scripts/plugin-sdk-api-diff.mts", - "plugin-sdk:check-exports": "node --import tsx scripts/sync-plugin-sdk-exports.mts --check", - "plugin-sdk:surface": "node --max-old-space-size=8192 --import tsx scripts/plugin-sdk-surface-report.mts", - "plugin-sdk:surface:check": "node --max-old-space-size=8192 --import tsx scripts/plugin-sdk-surface-report.mts --check", - "plugin-sdk:sync-exports": "node --import tsx scripts/sync-plugin-sdk-exports.mts", - "plugin-sdk:usage": "node --max-old-space-size=8192 --import tsx scripts/analyze-plugin-sdk-usage.ts", - "policy:config-coverage": "node --import tsx scripts/check-policy-config-coverage.ts", - "plugins:boundary-report": "node --import tsx scripts/plugin-boundary-report.ts", - "plugins:boundary-report:ci": "node --import tsx scripts/plugin-boundary-report.ts --summary --fail-on-eligible-compat", - "plugins:boundary-report:json": "node --import tsx scripts/plugin-boundary-report.ts --json", - "plugins:boundary-report:summary": "node --import tsx scripts/plugin-boundary-report.ts --summary", - "plugins:assets:build": "node --import tsx scripts/bundled-plugin-assets.mts --phase build", - "plugins:assets:check": "node --import tsx scripts/bundled-plugin-assets.mts --phase build --check", - "plugins:assets:copy": "node --import tsx scripts/bundled-plugin-assets.mts --phase copy", - "plugins:inventory:check": "node --import tsx scripts/generate-plugin-inventory-doc.mts --check", - "plugins:inventory:gen": "node --import tsx scripts/generate-plugin-inventory-doc.mts --write", - "plugins:sync": "node --import tsx scripts/sync-plugin-versions.ts", - "plugins:sync:check": "node --import tsx scripts/sync-plugin-versions.ts --check", + "perf:web-fetch": "node --import ./scripts/tsx.mjs scripts/bench-web-fetch.ts", + "perf:kova:summary": "node --import ./scripts/tsx.mjs scripts/kova-ci-summary.mts", + "perf:source:summary": "node --import ./scripts/tsx.mjs scripts/openclaw-performance-source-summary.mts", + "plugin-sdk:api:diff": "node --max-old-space-size=8192 --import ./scripts/tsx.mjs scripts/plugin-sdk-api-diff.mts", + "plugin-sdk:check-exports": "node --import ./scripts/tsx.mjs scripts/sync-plugin-sdk-exports.mts --check", + "plugin-sdk:surface": "node --max-old-space-size=8192 --import ./scripts/tsx.mjs scripts/plugin-sdk-surface-report.mts", + "plugin-sdk:surface:check": "node --max-old-space-size=8192 --import ./scripts/tsx.mjs scripts/plugin-sdk-surface-report.mts --check", + "plugin-sdk:sync-exports": "node --import ./scripts/tsx.mjs scripts/sync-plugin-sdk-exports.mts", + "plugin-sdk:usage": "node --max-old-space-size=8192 --import ./scripts/tsx.mjs scripts/analyze-plugin-sdk-usage.ts", + "policy:config-coverage": "node --import ./scripts/tsx.mjs scripts/check-policy-config-coverage.ts", + "plugins:boundary-report": "node --import ./scripts/tsx.mjs scripts/plugin-boundary-report.ts", + "plugins:boundary-report:ci": "node --import ./scripts/tsx.mjs scripts/plugin-boundary-report.ts --summary --fail-on-eligible-compat", + "plugins:boundary-report:json": "node --import ./scripts/tsx.mjs scripts/plugin-boundary-report.ts --json", + "plugins:boundary-report:summary": "node --import ./scripts/tsx.mjs scripts/plugin-boundary-report.ts --summary", + "plugins:assets:build": "node --import ./scripts/tsx.mjs scripts/bundled-plugin-assets.mts --phase build", + "plugins:assets:check": "node --import ./scripts/tsx.mjs scripts/bundled-plugin-assets.mts --phase build --check", + "plugins:assets:copy": "node --import ./scripts/tsx.mjs scripts/bundled-plugin-assets.mts --phase copy", + "plugins:inventory:check": "node --import ./scripts/tsx.mjs scripts/generate-plugin-inventory-doc.mts --check", + "plugins:inventory:gen": "node --import ./scripts/tsx.mjs scripts/generate-plugin-inventory-doc.mts --write", + "plugins:sync": "node --import ./scripts/tsx.mjs scripts/sync-plugin-versions.ts", + "plugins:sync:check": "node --import ./scripts/tsx.mjs scripts/sync-plugin-versions.ts --check", "postinstall": "node scripts/postinstall-bundled-plugins.mjs", "postpack": "node scripts/openclaw-postpack.mjs", "preinstall": "node scripts/preinstall-package-manager-warning.mjs", - "prepack": "node --import tsx scripts/openclaw-prepack.ts", + "prepack": "node --import ./scripts/tsx.mjs scripts/openclaw-prepack.ts", "prepare": "node scripts/prepare-git-hooks.mjs", "prepush:ci": "bash scripts/prepush-ci.sh", - "probe:anthropic:prompt": "node --import tsx scripts/anthropic-prompt-probe.ts", - "prompt:snapshots:check": "node --import tsx scripts/generate-prompt-snapshots.ts --check", - "prompt:snapshots:gen": "node --import tsx scripts/generate-prompt-snapshots.ts --write", - "prompt:snapshots:sync-codex-model": "node --import tsx scripts/sync-codex-model-prompt-fixture.ts", - "protocol:check": "pnpm protocol-registry:check && pnpm protocol:gen && pnpm protocol:check:swift && pnpm protocol:gen:kotlin && node --import tsx scripts/check-protocol-since.mts && git diff --exit-code -- dist/protocol.schema.json apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift apps/android/app/src/main/java/ai/openclaw/app/gateway/GatewayProtocol.kt apps/android/app/src/main/java/ai/openclaw/app/protocol/OpenClawProtocolConstants.kt", + "probe:anthropic:prompt": "node --import ./scripts/tsx.mjs scripts/anthropic-prompt-probe.ts", + "prompt:snapshots:check": "node --import ./scripts/tsx.mjs scripts/generate-prompt-snapshots.ts --check", + "prompt:snapshots:gen": "node --import ./scripts/tsx.mjs scripts/generate-prompt-snapshots.ts --write", + "prompt:snapshots:sync-codex-model": "node --import ./scripts/tsx.mjs scripts/sync-codex-model-prompt-fixture.ts", + "protocol:check": "pnpm protocol-registry:check && pnpm protocol:gen && pnpm protocol:check:swift && pnpm protocol:gen:kotlin && node --import ./scripts/tsx.mjs scripts/check-protocol-since.mts && git diff --exit-code -- dist/protocol.schema.json apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift apps/android/app/src/main/java/ai/openclaw/app/gateway/GatewayProtocol.kt apps/android/app/src/main/java/ai/openclaw/app/protocol/OpenClawProtocolConstants.kt", "protocol:check:kotlin": "pnpm protocol:gen:kotlin && git diff --exit-code -- apps/android/app/src/main/java/ai/openclaw/app/gateway/GatewayProtocol.kt apps/android/app/src/main/java/ai/openclaw/app/protocol/OpenClawProtocolConstants.kt", - "protocol:check:swift": "node --import tsx scripts/protocol-gen-swift.ts --check", - "protocol:gen": "node --import tsx scripts/protocol-gen.ts", - "protocol:gen:kotlin": "node --import tsx scripts/protocol-gen-kotlin.ts", - "protocol:gen:swift": "node --import tsx scripts/protocol-gen-swift.ts", - "protocol-registry:check": "node --import tsx scripts/check-protocol-registry.mts", + "protocol:check:swift": "node --import ./scripts/tsx.mjs scripts/protocol-gen-swift.ts --check", + "protocol:gen": "node --import ./scripts/tsx.mjs scripts/protocol-gen.ts", + "protocol:gen:kotlin": "node --import ./scripts/tsx.mjs scripts/protocol-gen-kotlin.ts", + "protocol:gen:swift": "node --import ./scripts/tsx.mjs scripts/protocol-gen-swift.ts", + "protocol-registry:check": "node --import ./scripts/tsx.mjs scripts/check-protocol-registry.mts", "proxy:coverage": "node scripts/run-node.mjs proxy coverage", "proxy:gateway": "node scripts/run-node.mjs proxy run -- node scripts/run-node.mjs gateway", - "proxy:install-ca": "node --import tsx scripts/proxy-install-ca.mts", + "proxy:install-ca": "node --import ./scripts/tsx.mjs scripts/proxy-install-ca.mts", "proxy:run": "node scripts/run-node.mjs proxy run", "proxy:start": "node scripts/run-node.mjs proxy start", - "qa:e2e": "node --import tsx scripts/qa-e2e.ts", - "qa:telegram-desktop-recorder": "node --import tsx scripts/e2e/telegram-desktop-recorder.ts", + "qa:e2e": "node --import ./scripts/tsx.mjs scripts/qa-e2e.ts", + "qa:telegram-desktop-recorder": "node --import ./scripts/tsx.mjs scripts/e2e/telegram-desktop-recorder.ts", "qa:lab:build": "vite build --config extensions/qa-lab/web/vite.config.ts", "qa:lab:ui": "pnpm openclaw qa ui", - "qa:lab:up": "node --import tsx scripts/qa-lab-up.ts", - "qa:lab:up:fast": "node --import tsx scripts/qa-lab-up.ts --use-prebuilt-image --bind-ui-dist --skip-ui-build", + "qa:lab:up": "node --import ./scripts/tsx.mjs scripts/qa-lab-up.ts", + "qa:lab:up:fast": "node --import ./scripts/tsx.mjs scripts/qa-lab-up.ts --use-prebuilt-image --bind-ui-dist --skip-ui-build", "qa:lab:watch": "vite build --watch --config extensions/qa-lab/web/vite.config.ts", "qa:observability:collector-smoke": "pnpm qa:otel:collector-smoke && pnpm qa:prometheus:smoke", "qa:observability:smoke": "pnpm qa:otel:smoke && pnpm qa:prometheus:smoke", - "qa:otel:collector-smoke": "node --import tsx test/e2e/qa-lab/runtime/qa-otel-smoke-runtime.ts --collector docker", - "qa:otel:smoke": "node --import tsx test/e2e/qa-lab/runtime/qa-otel-smoke-runtime.ts", - "qa:code-mode-models": "node --import tsx scripts/code-mode-model-matrix.ts", + "qa:otel:collector-smoke": "node --import ./scripts/tsx.mjs test/e2e/qa-lab/runtime/qa-otel-smoke-runtime.ts --collector docker", + "qa:otel:smoke": "node --import ./scripts/tsx.mjs test/e2e/qa-lab/runtime/qa-otel-smoke-runtime.ts", + "qa:code-mode-models": "node --import ./scripts/tsx.mjs scripts/code-mode-model-matrix.ts", "qa:prometheus:smoke": "node scripts/run-node.mjs qa suite --provider-mode mock-openai --scenario docker-prometheus-smoke --concurrency 1 --fast", - "release-metadata:check": "node --import tsx scripts/check-release-metadata-only.mts", - "release:beta": "node --import tsx scripts/release-candidate-checklist.mts", - "release:beta-smoke": "node --import tsx scripts/release-beta-smoke.ts", - "release:candidate": "node --import tsx scripts/release-candidate-checklist.mts", - "release:check": "pnpm build:package && pnpm release:generated:check && node --import tsx scripts/release-check.ts", + "release-metadata:check": "node --import ./scripts/tsx.mjs scripts/check-release-metadata-only.mts", + "release:beta": "node --import ./scripts/tsx.mjs scripts/release-candidate-checklist.mts", + "release:beta-smoke": "node --import ./scripts/tsx.mjs scripts/release-beta-smoke.ts", + "release:candidate": "node --import ./scripts/tsx.mjs scripts/release-candidate-checklist.mts", + "release:check": "pnpm build:package && pnpm release:generated:check && node --import ./scripts/tsx.mjs scripts/release-check.ts", "release:fast-pretag-check": "bash scripts/release-fast-pretag-check.sh", "release:generated:check": "node scripts/release-preflight.mjs --check", - "release:openclaw:npm:check": "node --import tsx scripts/openclaw-npm-release-check.ts", - "release:openclaw:npm:verify-published": "node --import tsx scripts/openclaw-npm-postpublish-verify.ts", - "release:prepare": "node --import tsx scripts/release-prepare.ts", - "release:plugins:clawhub:check": "node --import tsx scripts/plugin-clawhub-release-check.ts", - "release:plugins:clawhub:plan": "node --import tsx scripts/plugin-clawhub-release-plan.ts", - "release:plugins:npm:check": "node --import tsx scripts/plugin-npm-release-check.ts", - "release:plugins:npm:plan": "node --import tsx scripts/plugin-npm-release-plan.ts", - "release:verify-beta": "node --import tsx scripts/release-verify-beta.ts", + "release:openclaw:npm:check": "node --import ./scripts/tsx.mjs scripts/openclaw-npm-release-check.ts", + "release:openclaw:npm:verify-published": "node --import ./scripts/tsx.mjs scripts/openclaw-npm-postpublish-verify.ts", + "release:prepare": "node --import ./scripts/tsx.mjs scripts/release-prepare.ts", + "release:plugins:clawhub:check": "node --import ./scripts/tsx.mjs scripts/plugin-clawhub-release-check.ts", + "release:plugins:clawhub:plan": "node --import ./scripts/tsx.mjs scripts/plugin-clawhub-release-plan.ts", + "release:plugins:npm:check": "node --import ./scripts/tsx.mjs scripts/plugin-npm-release-check.ts", + "release:plugins:npm:plan": "node --import ./scripts/tsx.mjs scripts/plugin-npm-release-plan.ts", + "release:verify-beta": "node --import ./scripts/tsx.mjs scripts/release-verify-beta.ts", "release:prep": "node scripts/release-preflight.mjs --fix", "release:prep:version": "node scripts/release-preflight.mjs --fix --scope version", - "release:version": "node --import tsx scripts/release-version.ts", - "runtime-sidecars:check": "node --import tsx scripts/generate-runtime-sidecar-paths-baseline.ts --check", - "runtime-sidecars:gen": "node --import tsx scripts/generate-runtime-sidecar-paths-baseline.ts --write", - "sqlite:sessions-schema:check": "node --import tsx scripts/generate-sqlite-session-schema-baseline.ts --check", - "sqlite:sessions-schema:gen": "node --import tsx scripts/generate-sqlite-session-schema-baseline.ts --write", + "release:version": "node --import ./scripts/tsx.mjs scripts/release-version.ts", + "runtime-sidecars:check": "node --import ./scripts/tsx.mjs scripts/generate-runtime-sidecar-paths-baseline.ts --check", + "runtime-sidecars:gen": "node --import ./scripts/tsx.mjs scripts/generate-runtime-sidecar-paths-baseline.ts --write", + "sqlite:sessions-schema:check": "node --import ./scripts/tsx.mjs scripts/generate-sqlite-session-schema-baseline.ts --check", + "sqlite:sessions-schema:gen": "node --import ./scripts/tsx.mjs scripts/generate-sqlite-session-schema-baseline.ts --write", "start": "node openclaw.mjs", - "test": "node --import tsx scripts/test-projects.mts", + "test": "node --import ./scripts/tsx.mjs scripts/test-projects.mts", "test:all": "pnpm lint && pnpm build && pnpm test && pnpm test:e2e && pnpm test:live && pnpm test:docker:all", "test:auth:compat": "node scripts/run-vitest.mjs run --config test/vitest/vitest.gateway.config.ts src/gateway/server.auth.compat-baseline.test.ts src/gateway/client.test.ts src/gateway/reconnect-gating.test.ts && node scripts/run-vitest.mjs run packages/gateway-protocol/src/connect-error-details.test.ts", - "test:build:singleton": "node --import tsx scripts/test-built-plugin-singleton.mts", - "test:build:status-message-runtime": "node --import tsx scripts/test-built-status-message-runtime.mts", + "test:build:singleton": "node --import ./scripts/tsx.mjs scripts/test-built-plugin-singleton.mts", + "test:build:status-message-runtime": "node --import ./scripts/tsx.mjs scripts/test-built-status-message-runtime.mts", "test:bundled": "node scripts/run-vitest.mjs run --config test/vitest/vitest.bundled.config.ts", - "test:changed": "node --import tsx scripts/test-projects.mts --changed origin/main", - "test:changed:max": "node --import tsx scripts/test-projects-max.mts --changed origin/main", + "test:changed": "node --import ./scripts/tsx.mjs scripts/test-projects.mts --changed origin/main", + "test:changed:max": "node --import ./scripts/tsx.mjs scripts/test-projects-max.mts --changed origin/main", "test:channels": "node scripts/run-vitest.mjs run --config test/vitest/vitest.channels.config.ts", "test:contracts": "pnpm test:contracts:channels && pnpm test:contracts:plugins", - "test:contracts:channels": "node --import tsx scripts/test-projects.mts --maxWorkers=1 test/vitest/vitest.contracts-channel-surface.config.ts test/vitest/vitest.contracts-channel-config.config.ts test/vitest/vitest.contracts-channel-registry.config.ts test/vitest/vitest.contracts-channel-session.config.ts", + "test:contracts:channels": "node --import ./scripts/tsx.mjs scripts/test-projects.mts --maxWorkers=1 test/vitest/vitest.contracts-channel-surface.config.ts test/vitest/vitest.contracts-channel-config.config.ts test/vitest/vitest.contracts-channel-registry.config.ts test/vitest/vitest.contracts-channel-session.config.ts", "test:contracts:plugins": "node scripts/run-vitest.mjs run --config test/vitest/vitest.contracts-plugin.config.ts --maxWorkers=1", "test:coverage": "node scripts/run-vitest.mjs run --config test/vitest/vitest.unit.config.ts --coverage", "test:coverage:changed": "node scripts/run-vitest.mjs run --config test/vitest/vitest.unit.config.ts --coverage --changed origin/main", @@ -1856,8 +1856,8 @@ "test:docker:live-models": "bash scripts/test-live-models-docker.sh", "test:docker:live-models:claude": "OPENCLAW_LIVE_PROVIDERS=claude-cli OPENCLAW_LIVE_MODELS=claude-cli/claude-sonnet-4-6 bash scripts/test-live-models-docker.sh", "test:docker:live-models:gemini": "OPENCLAW_LIVE_PROVIDERS=google-gemini-cli OPENCLAW_LIVE_MODELS=google-gemini-cli/gemini-3.1-pro-preview bash scripts/test-live-models-docker.sh", - "test:docker:live:all": "node --import tsx scripts/run-with-env.mts OPENCLAW_DOCKER_ALL_LIVE_MODE=only -- node scripts/test-docker-all.mjs", - "test:docker:local:all": "node --import tsx scripts/run-with-env.mts OPENCLAW_DOCKER_ALL_LIVE_MODE=skip -- node scripts/test-docker-all.mjs", + "test:docker:live:all": "node --import ./scripts/tsx.mjs scripts/run-with-env.mts OPENCLAW_DOCKER_ALL_LIVE_MODE=only -- node scripts/test-docker-all.mjs", + "test:docker:local:all": "node --import ./scripts/tsx.mjs scripts/run-with-env.mts OPENCLAW_DOCKER_ALL_LIVE_MODE=skip -- node scripts/test-docker-all.mjs", "test:docker:cli-installer-distribution": "bash scripts/e2e/cli-installer-distribution-docker.sh", "test:docker:mcp-channels": "bash scripts/e2e/mcp-channels-docker.sh", "test:docker:mcp-code-mode-gateway": "bash scripts/e2e/mcp-code-mode-gateway-docker.sh", @@ -1882,109 +1882,109 @@ "test:docker:plugin-lifecycle-matrix": "bash scripts/e2e/plugin-lifecycle-matrix-docker.sh", "test:docker:plugin-update": "bash scripts/e2e/plugin-update-unchanged-docker.sh", "test:docker:plugins": "bash scripts/e2e/plugins-docker.sh", - "test:docker:published-upgrade-survivor": "node --import tsx scripts/run-with-env.mts OPENCLAW_DOCKER_ALL_LANES=published-upgrade-survivor -- node scripts/test-docker-all.mjs", + "test:docker:published-upgrade-survivor": "node --import ./scripts/tsx.mjs scripts/run-with-env.mts OPENCLAW_DOCKER_ALL_LANES=published-upgrade-survivor -- node scripts/test-docker-all.mjs", "test:docker:qr": "bash scripts/e2e/qr-import-docker.sh", - "test:docker:rerun": "node --import tsx scripts/docker-e2e-rerun.mts", + "test:docker:rerun": "node --import ./scripts/tsx.mjs scripts/docker-e2e-rerun.mts", "test:docker:root-managed-vps-upgrade": "env OPENCLAW_UPGRADE_SURVIVOR_PUBLISHED_BASELINE=1 OPENCLAW_UPGRADE_SURVIVOR_ROOT_MANAGED_VPS=1 OPENCLAW_UPGRADE_SURVIVOR_BASELINE_SPEC=${OPENCLAW_UPGRADE_SURVIVOR_BASELINE_SPEC:-openclaw@2026.5.7} OPENCLAW_UPGRADE_SURVIVOR_DOCKER_RUN_TIMEOUT=${OPENCLAW_UPGRADE_SURVIVOR_DOCKER_RUN_TIMEOUT:-1500s} bash scripts/e2e/upgrade-survivor-docker.sh", "test:docker:selected-plugins": "bash scripts/e2e/docker-selected-plugins.sh", "test:docker:session-runtime-context": "bash scripts/e2e/session-runtime-context-docker.sh", "test:docker:skill-install": "bash scripts/e2e/skill-install-docker.sh", - "test:docker:timings": "node --import tsx scripts/docker-e2e-timings.mts", + "test:docker:timings": "node --import ./scripts/tsx.mjs scripts/docker-e2e-timings.mts", "test:docker:update-channel-switch": "bash scripts/e2e/update-channel-switch-docker.sh", "test:docker:update-corrupt-plugin": "bash scripts/e2e/update-corrupt-plugin-docker.sh", "test:docker:update-migration": "env OPENCLAW_UPGRADE_SURVIVOR_PUBLISHED_BASELINE=1 OPENCLAW_UPGRADE_SURVIVOR_BASELINE_SPEC=${OPENCLAW_UPGRADE_SURVIVOR_BASELINE_SPEC:-openclaw@2026.4.23} OPENCLAW_UPGRADE_SURVIVOR_SCENARIO=${OPENCLAW_UPGRADE_SURVIVOR_SCENARIO:-plugin-deps-cleanup} bash scripts/e2e/upgrade-survivor-docker.sh", "test:docker:update-restart-auth": "env OPENCLAW_UPGRADE_SURVIVOR_UPDATE_RESTART_MODE=auto-auth OPENCLAW_UPGRADE_SURVIVOR_DOCKER_RUN_TIMEOUT=${OPENCLAW_UPGRADE_SURVIVOR_DOCKER_RUN_TIMEOUT:-1500s} bash scripts/e2e/upgrade-survivor-docker.sh", "test:docker:update-run-package-self-upgrade": "bash scripts/e2e/update-run-package-self-upgrade-docker.sh", "test:docker:upgrade-survivor": "bash scripts/e2e/upgrade-survivor-docker.sh", - "test:env-mutations:report": "node --import tsx scripts/test-env-mutation-report.ts", - "test:skip-inventory:report": "node --import tsx scripts/test-skip-inventory.ts", - "test:type-suppression-inventory:report": "node --import tsx scripts/type-suppression-inventory.ts", + "test:env-mutations:report": "node --import ./scripts/tsx.mjs scripts/test-env-mutation-report.ts", + "test:skip-inventory:report": "node --import ./scripts/tsx.mjs scripts/test-skip-inventory.ts", + "test:type-suppression-inventory:report": "node --import ./scripts/tsx.mjs scripts/type-suppression-inventory.ts", "test:e2e": "pnpm test:e2e:gateway && pnpm test:e2e:agent-plugin-gateway && pnpm test:ui:e2e", - "test:e2e:agent-plugin-gateway": "node --import tsx scripts/agent-plugin-gateway-e2e.ts", - "test:e2e:browser-extension": "node --import tsx scripts/run-with-env.mts PLAYWRIGHT_BROWSERS_PATH=.artifacts/playwright-browsers -- node --import tsx scripts/ensure-playwright-chromium.mts --require-playwright-chromium && node --import tsx scripts/run-with-env.mts PLAYWRIGHT_BROWSERS_PATH=.artifacts/playwright-browsers OPENCLAW_BROWSER_EXTENSION_E2E=1 OPENCLAW_E2E_WORKERS=1 -- node scripts/run-vitest.mjs extensions/browser/chrome-extension/bootstrap.chromium.test.ts", + "test:e2e:agent-plugin-gateway": "node --import ./scripts/tsx.mjs scripts/agent-plugin-gateway-e2e.ts", + "test:e2e:browser-extension": "node --import ./scripts/tsx.mjs scripts/run-with-env.mts PLAYWRIGHT_BROWSERS_PATH=.artifacts/playwright-browsers -- node --import ./scripts/tsx.mjs scripts/ensure-playwright-chromium.mts --require-playwright-chromium && node --import ./scripts/tsx.mjs scripts/run-with-env.mts PLAYWRIGHT_BROWSERS_PATH=.artifacts/playwright-browsers OPENCLAW_BROWSER_EXTENSION_E2E=1 OPENCLAW_E2E_WORKERS=1 -- node scripts/run-vitest.mjs extensions/browser/chrome-extension/bootstrap.chromium.test.ts", "test:e2e:gateway": "node scripts/run-vitest.mjs run --config test/vitest/vitest.e2e.config.ts", - "test:e2e:openshell": "node --import tsx scripts/run-with-env.mts OPENCLAW_E2E_OPENSHELL=1 -- node scripts/run-vitest.mjs run --config test/vitest/vitest.e2e.config.ts extensions/openshell/src/backend.e2e.test.ts", + "test:e2e:openshell": "node --import ./scripts/tsx.mjs scripts/run-with-env.mts OPENCLAW_E2E_OPENSHELL=1 -- node scripts/run-vitest.mjs run --config test/vitest/vitest.e2e.config.ts extensions/openshell/src/backend.e2e.test.ts", "test:e2e:status-corrupt-plugin-deps": "bash scripts/e2e/status-corrupt-plugin-deps.sh", - "test:extension": "node --import tsx scripts/test-extension.mts", - "test:extensions": "node --import tsx scripts/test-projects.mts extensions", - "test:extensions:batch": "node --import tsx scripts/test-extension-batch.mts", - "test:extensions:memory": "node --import tsx scripts/profile-extension-memory.mts", - "test:extensions:package-boundary": "node --import tsx scripts/check-extension-package-tsc-boundary.mts", - "test:extensions:package-boundary:canary": "node --import tsx scripts/check-extension-package-tsc-boundary.mts --mode=canary", - "test:extensions:package-boundary:compile": "node --import tsx scripts/check-extension-package-tsc-boundary.mts --mode=compile", + "test:extension": "node --import ./scripts/tsx.mjs scripts/test-extension.mts", + "test:extensions": "node --import ./scripts/tsx.mjs scripts/test-projects.mts extensions", + "test:extensions:batch": "node --import ./scripts/tsx.mjs scripts/test-extension-batch.mts", + "test:extensions:memory": "node --import ./scripts/tsx.mjs scripts/profile-extension-memory.mts", + "test:extensions:package-boundary": "node --import ./scripts/tsx.mjs scripts/check-extension-package-tsc-boundary.mts", + "test:extensions:package-boundary:canary": "node --import ./scripts/tsx.mjs scripts/check-extension-package-tsc-boundary.mts --mode=canary", + "test:extensions:package-boundary:compile": "node --import ./scripts/tsx.mjs scripts/check-extension-package-tsc-boundary.mts --mode=compile", "test:fast": "node scripts/run-vitest.mjs run --config test/vitest/vitest.unit.config.ts", - "test:force": "node --import tsx scripts/test-force.ts", - "test:gateway": "node --import tsx scripts/run-with-env.mts OPENCLAW_GATEWAY_PROJECT_SHARDS=1 -- node scripts/run-vitest.mjs run --config test/vitest/vitest.gateway.config.ts", - "test:gateway:cpu-scenarios": "node --import tsx scripts/check-gateway-cpu-scenarios.mts", + "test:force": "node --import ./scripts/tsx.mjs scripts/test-force.ts", + "test:gateway": "node --import ./scripts/tsx.mjs scripts/run-with-env.mts OPENCLAW_GATEWAY_PROJECT_SHARDS=1 -- node scripts/run-vitest.mjs run --config test/vitest/vitest.gateway.config.ts", + "test:gateway:cpu-scenarios": "node --import ./scripts/tsx.mjs scripts/check-gateway-cpu-scenarios.mts", "test:gateway:concurrency": "node scripts/bench-gateway-concurrency.ts", - "test:gateway:memory-fd-repro": "node --import tsx scripts/check-memory-fd-repro.mts", - "test:gateway:watch-regression": "node --import tsx scripts/check-gateway-watch-regression.mts", + "test:gateway:memory-fd-repro": "node --import ./scripts/tsx.mjs scripts/check-memory-fd-repro.mts", + "test:gateway:watch-regression": "node --import ./scripts/tsx.mjs scripts/check-gateway-watch-regression.mts", "test:install:e2e": "bash scripts/test-install-sh-e2e-docker.sh", "test:install:e2e:anthropic": "OPENCLAW_E2E_MODELS=anthropic bash scripts/test-install-sh-e2e-docker.sh", "test:install:e2e:openai": "OPENCLAW_E2E_MODELS=openai bash scripts/test-install-sh-e2e-docker.sh", "test:install:smoke": "bash scripts/test-install-sh-docker.sh", - "test:live": "node --import tsx scripts/test-live.mts", - "test:live:cache": "node --import tsx scripts/run-with-env.mts OPENCLAW_LIVE_TEST=1 OPENCLAW_LIVE_CACHE_TEST=1 -- node --import tsx scripts/check-live-cache.ts", - "test:live:codex-harness": "node --import tsx scripts/test-live.mts --codex-harness -- src/gateway/gateway-codex-harness.live.test.ts", - "test:live:system-agent-rescue-channel": "node --import tsx scripts/run-with-env.mts OPENCLAW_LIVE_SYSTEM_AGENT_RESCUE_CHANNEL=1 -- node --import tsx scripts/test-live.mts -- src/system-agent/rescue-channel.live.test.ts", - "test:live:gateway-profiles": "node --import tsx scripts/test-live.mts -- src/gateway/gateway-models.profiles.live.test.ts", - "test:live:media": "node --import tsx test/e2e/qa-lab/media/hosted-media-provider-live.ts", - "test:live:media:image": "node --import tsx test/e2e/qa-lab/media/hosted-media-provider-live.ts image", - "test:live:media:music": "node --import tsx test/e2e/qa-lab/media/hosted-media-provider-live.ts music", - "test:live:media:video": "node --import tsx test/e2e/qa-lab/media/hosted-media-provider-live.ts video", - "test:live:models-profiles": "node --import tsx scripts/test-live.mts -- src/agents/models.profiles.live.test.ts", - "test:macos:ci": "node --import tsx scripts/test-projects.mts src/daemon/launchd.test.ts src/daemon/runtime-paths.test.ts src/daemon/runtime-binary.test.ts src/gateway/worker-environments/workspace-rsync-path.test.ts src/infra/brew.test.ts src/infra/stable-node-path.test.ts src/process/supervisor/adapters/child.service-lifecycle.test.ts test/scripts/vitest-process-group.test.ts test/scripts/package-mac-app.test.ts test/scripts/package-mac-dist.test.ts test/scripts/create-dmg.test.ts test/scripts/codesign-mac-app.test.ts test/scripts/notarize-mac-artifact.test.ts test/scripts/mac-elevation-host.test.ts", - "test:max": "node --import tsx scripts/test-projects-max.mts", + "test:live": "node --import ./scripts/tsx.mjs scripts/test-live.mts", + "test:live:cache": "node --import ./scripts/tsx.mjs scripts/run-with-env.mts OPENCLAW_LIVE_TEST=1 OPENCLAW_LIVE_CACHE_TEST=1 -- node --import ./scripts/tsx.mjs scripts/check-live-cache.ts", + "test:live:codex-harness": "node --import ./scripts/tsx.mjs scripts/test-live.mts --codex-harness -- src/gateway/gateway-codex-harness.live.test.ts", + "test:live:system-agent-rescue-channel": "node --import ./scripts/tsx.mjs scripts/run-with-env.mts OPENCLAW_LIVE_SYSTEM_AGENT_RESCUE_CHANNEL=1 -- node --import ./scripts/tsx.mjs scripts/test-live.mts -- src/system-agent/rescue-channel.live.test.ts", + "test:live:gateway-profiles": "node --import ./scripts/tsx.mjs scripts/test-live.mts -- src/gateway/gateway-models.profiles.live.test.ts", + "test:live:media": "node --import ./scripts/tsx.mjs test/e2e/qa-lab/media/hosted-media-provider-live.ts", + "test:live:media:image": "node --import ./scripts/tsx.mjs test/e2e/qa-lab/media/hosted-media-provider-live.ts image", + "test:live:media:music": "node --import ./scripts/tsx.mjs test/e2e/qa-lab/media/hosted-media-provider-live.ts music", + "test:live:media:video": "node --import ./scripts/tsx.mjs test/e2e/qa-lab/media/hosted-media-provider-live.ts video", + "test:live:models-profiles": "node --import ./scripts/tsx.mjs scripts/test-live.mts -- src/agents/models.profiles.live.test.ts", + "test:macos:ci": "node --import ./scripts/tsx.mjs scripts/test-projects.mts src/daemon/launchd.test.ts src/daemon/runtime-paths.test.ts src/daemon/runtime-binary.test.ts src/gateway/worker-environments/workspace-rsync-path.test.ts src/infra/brew.test.ts src/infra/stable-node-path.test.ts src/process/supervisor/adapters/child.service-lifecycle.test.ts test/scripts/vitest-process-group.test.ts test/scripts/package-mac-app.test.ts test/scripts/package-mac-dist.test.ts test/scripts/create-dmg.test.ts test/scripts/codesign-mac-app.test.ts test/scripts/notarize-mac-artifact.test.ts test/scripts/mac-elevation-host.test.ts", + "test:max": "node --import ./scripts/tsx.mjs scripts/test-projects-max.mts", "test:parallels:linux": "bash scripts/e2e/parallels-linux-smoke.sh", "test:parallels:macos": "bash scripts/e2e/parallels-macos-smoke.sh", "test:parallels:npm-update": "bash scripts/e2e/parallels-npm-update-smoke.sh", "test:parallels:windows:prepare": "bash scripts/e2e/parallels-windows-prepare.sh", "test:parallels:windows": "bash scripts/e2e/parallels-windows-smoke.sh", - "test:agents:concurrency": "node --import tsx scripts/bench-agent-concurrency.ts", - "test:tasks:sqlite-churn": "node --import tsx scripts/bench-task-registry-sqlite.ts", - "test:perf:budget": "node --import tsx scripts/test-perf-budget.mts", - "test:perf:changed:bench": "node --import tsx scripts/bench-test-changed.mts", - "test:perf:groups": "node --import tsx scripts/test-group-report.mts", - "test:perf:groups:compare": "node --import tsx scripts/test-group-report.mts --compare", - "test:perf:hotspots": "node --import tsx scripts/test-hotspots.mts", - "test:perf:imports": "node --import tsx scripts/test-projects-imports.mts", - "test:perf:imports:changed": "node --import tsx scripts/test-projects-imports.mts --changed origin/main", - "test:perf:profile:main": "node --import tsx scripts/run-vitest-profile.mts main", - "test:perf:profile:runner": "node --import tsx scripts/run-vitest-profile.mts runner", - "test:sqlite:perf": "node --import tsx scripts/bench-sqlite-state.ts --profile default --output .artifacts/sqlite-perf/default.json", - "test:sqlite:perf:large": "node --import tsx scripts/bench-sqlite-state.ts --profile large --output .artifacts/sqlite-perf/large.json", - "test:sqlite:perf:smoke": "node --import tsx scripts/bench-sqlite-state.ts --profile smoke --output .artifacts/sqlite-perf/smoke.json", - "test:plugins:gateway-gauntlet": "node --import tsx scripts/check-plugin-gateway-gauntlet.mts", - "test:plugins:init-provider-scaffold": "node --import tsx scripts/validate-plugin-init-provider-scaffold.ts", + "test:agents:concurrency": "node --import ./scripts/tsx.mjs scripts/bench-agent-concurrency.ts", + "test:tasks:sqlite-churn": "node --import ./scripts/tsx.mjs scripts/bench-task-registry-sqlite.ts", + "test:perf:budget": "node --import ./scripts/tsx.mjs scripts/test-perf-budget.mts", + "test:perf:changed:bench": "node --import ./scripts/tsx.mjs scripts/bench-test-changed.mts", + "test:perf:groups": "node --import ./scripts/tsx.mjs scripts/test-group-report.mts", + "test:perf:groups:compare": "node --import ./scripts/tsx.mjs scripts/test-group-report.mts --compare", + "test:perf:hotspots": "node --import ./scripts/tsx.mjs scripts/test-hotspots.mts", + "test:perf:imports": "node --import ./scripts/tsx.mjs scripts/test-projects-imports.mts", + "test:perf:imports:changed": "node --import ./scripts/tsx.mjs scripts/test-projects-imports.mts --changed origin/main", + "test:perf:profile:main": "node --import ./scripts/tsx.mjs scripts/run-vitest-profile.mts main", + "test:perf:profile:runner": "node --import ./scripts/tsx.mjs scripts/run-vitest-profile.mts runner", + "test:sqlite:perf": "node --import ./scripts/tsx.mjs scripts/bench-sqlite-state.ts --profile default --output .artifacts/sqlite-perf/default.json", + "test:sqlite:perf:large": "node --import ./scripts/tsx.mjs scripts/bench-sqlite-state.ts --profile large --output .artifacts/sqlite-perf/large.json", + "test:sqlite:perf:smoke": "node --import ./scripts/tsx.mjs scripts/bench-sqlite-state.ts --profile smoke --output .artifacts/sqlite-perf/smoke.json", + "test:plugins:gateway-gauntlet": "node --import ./scripts/tsx.mjs scripts/check-plugin-gateway-gauntlet.mts", + "test:plugins:init-provider-scaffold": "node --import ./scripts/tsx.mjs scripts/validate-plugin-init-provider-scaffold.ts", "test:plugins:kitchen-sink-live": "bash -lc 'if [ -x \"$HOME/.local/bin/openclaw-testbox-env\" ]; then exec \"$HOME/.local/bin/openclaw-testbox-env\" pnpm openclaw qa suite --provider-mode live-frontier --scenario kitchen-sink-live-openai; fi; exec pnpm openclaw qa suite --provider-mode live-frontier --scenario kitchen-sink-live-openai'", - "test:plugins:kitchen-sink-rpc": "node --import tsx scripts/e2e/kitchen-sink-rpc-walk.mts", - "test:sectriage": "node --import tsx scripts/run-with-env.mts OPENCLAW_GATEWAY_PROJECT_SHARDS=1 -- node scripts/run-vitest.mjs run --config test/vitest/vitest.gateway.config.ts && node scripts/run-vitest.mjs run --config test/vitest/vitest.unit.config.ts --exclude src/process/exec.test.ts", - "test:serial": "node --import tsx scripts/test-projects-serial.mts", - "test:stability:gateway": "node --import tsx scripts/run-with-env.mts OPENCLAW_VITEST_MAX_WORKERS=1 -- node scripts/run-vitest.mjs run --config test/vitest/vitest.gateway.config.ts src/gateway/gateway-stability.test.ts && node --import tsx scripts/run-with-env.mts OPENCLAW_VITEST_MAX_WORKERS=1 -- node scripts/run-vitest.mjs run --config test/vitest/vitest.logging.config.ts src/logging/diagnostic-stability-bundle.test.ts && node --import tsx scripts/run-with-env.mts OPENCLAW_VITEST_MAX_WORKERS=1 -- node scripts/run-vitest.mjs run --config test/vitest/vitest.infra.config.ts src/infra/fatal-error-hooks.test.ts", - "test:cli-response:contract": "node --import tsx scripts/build-all.mts cliStartup && node --import tsx scripts/test-cli-startup-bench-budget.mts --preset response --runs 1 --warmup 0 --timeout-ms 10000 --skip-baseline", - "test:startup:bench": "node --import tsx scripts/bench-cli-startup.ts", - "test:startup:bench:check": "node --import tsx scripts/test-cli-startup-bench-budget.mts", - "test:startup:bench:save": "node --import tsx scripts/bench-cli-startup.ts --preset all --runs 5 --warmup 1 --output .artifacts/cli-startup-bench-all.json", - "test:startup:bench:smoke": "node --import tsx scripts/ensure-cli-startup-build.mts && node --import tsx scripts/bench-cli-startup.ts --preset real --case gatewayStatusJson --runs 1 --warmup 0 --output .artifacts/cli-startup-bench-smoke.json", - "test:startup:bench:update": "node --import tsx scripts/test-update-cli-startup-bench.mts", - "test:startup:gateway": "node --import tsx scripts/bench-gateway-startup.ts", - "test:restart:gateway": "node --import tsx scripts/bench-gateway-restart.ts", - "test:sessions:hydration-memory": "node --import tsx scripts/bench-session-manager-hydration-memory.ts", - "test:startup:memory": "node --import tsx scripts/ensure-cli-startup-build.mts && node scripts/check-cli-startup-memory.mjs", - "test:ui": "pnpm lint:ui:no-raw-window-open && node --import tsx scripts/ensure-playwright-chromium.mts && pnpm --dir ui test", - "test:ui:e2e": "node --import tsx scripts/ensure-playwright-chromium.mts && node scripts/run-vitest.mjs run --config test/vitest/vitest.ui-e2e.config.ts --configLoader runner", + "test:plugins:kitchen-sink-rpc": "node --import ./scripts/tsx.mjs scripts/e2e/kitchen-sink-rpc-walk.mts", + "test:sectriage": "node --import ./scripts/tsx.mjs scripts/run-with-env.mts OPENCLAW_GATEWAY_PROJECT_SHARDS=1 -- node scripts/run-vitest.mjs run --config test/vitest/vitest.gateway.config.ts && node scripts/run-vitest.mjs run --config test/vitest/vitest.unit.config.ts --exclude src/process/exec.test.ts", + "test:serial": "node --import ./scripts/tsx.mjs scripts/test-projects-serial.mts", + "test:stability:gateway": "node --import ./scripts/tsx.mjs scripts/run-with-env.mts OPENCLAW_VITEST_MAX_WORKERS=1 -- node scripts/run-vitest.mjs run --config test/vitest/vitest.gateway.config.ts src/gateway/gateway-stability.test.ts && node --import ./scripts/tsx.mjs scripts/run-with-env.mts OPENCLAW_VITEST_MAX_WORKERS=1 -- node scripts/run-vitest.mjs run --config test/vitest/vitest.logging.config.ts src/logging/diagnostic-stability-bundle.test.ts && node --import ./scripts/tsx.mjs scripts/run-with-env.mts OPENCLAW_VITEST_MAX_WORKERS=1 -- node scripts/run-vitest.mjs run --config test/vitest/vitest.infra.config.ts src/infra/fatal-error-hooks.test.ts", + "test:cli-response:contract": "node --import ./scripts/tsx.mjs scripts/build-all.mts cliStartup && node --import ./scripts/tsx.mjs scripts/test-cli-startup-bench-budget.mts --preset response --runs 1 --warmup 0 --timeout-ms 10000 --skip-baseline", + "test:startup:bench": "node --import ./scripts/tsx.mjs scripts/bench-cli-startup.ts", + "test:startup:bench:check": "node --import ./scripts/tsx.mjs scripts/test-cli-startup-bench-budget.mts", + "test:startup:bench:save": "node --import ./scripts/tsx.mjs scripts/bench-cli-startup.ts --preset all --runs 5 --warmup 1 --output .artifacts/cli-startup-bench-all.json", + "test:startup:bench:smoke": "node --import ./scripts/tsx.mjs scripts/ensure-cli-startup-build.mts && node --import ./scripts/tsx.mjs scripts/bench-cli-startup.ts --preset real --case gatewayStatusJson --runs 1 --warmup 0 --output .artifacts/cli-startup-bench-smoke.json", + "test:startup:bench:update": "node --import ./scripts/tsx.mjs scripts/test-update-cli-startup-bench.mts", + "test:startup:gateway": "node --import ./scripts/tsx.mjs scripts/bench-gateway-startup.ts", + "test:restart:gateway": "node --import ./scripts/tsx.mjs scripts/bench-gateway-restart.ts", + "test:sessions:hydration-memory": "node --import ./scripts/tsx.mjs scripts/bench-session-manager-hydration-memory.ts", + "test:startup:memory": "node --import ./scripts/tsx.mjs scripts/ensure-cli-startup-build.mts && node scripts/check-cli-startup-memory.mjs", + "test:ui": "pnpm lint:ui:no-raw-window-open && node --import ./scripts/tsx.mjs scripts/ensure-playwright-chromium.mts && pnpm --dir ui test", + "test:ui:e2e": "node --import ./scripts/tsx.mjs scripts/ensure-playwright-chromium.mts && node scripts/run-vitest.mjs run --config test/vitest/vitest.ui-e2e.config.ts --configLoader runner", "test:unit": "pnpm test:unit:fast && node scripts/run-vitest.mjs run --config test/vitest/vitest.unit.config.ts", "test:unit:fast": "node scripts/run-vitest.mjs run --config test/vitest/vitest.unit-fast.config.ts", - "test:unit:fast:audit": "node --import tsx scripts/test-unit-fast-audit.mts", - "test:voicecall:closedloop": "node --import tsx scripts/test-voicecall-closedloop.mts", - "test:watch": "node --import tsx scripts/test-projects.mts --watch", + "test:unit:fast:audit": "node --import ./scripts/tsx.mjs scripts/test-unit-fast-audit.mts", + "test:voicecall:closedloop": "node --import ./scripts/tsx.mjs scripts/test-voicecall-closedloop.mts", + "test:watch": "node --import ./scripts/tsx.mjs scripts/test-projects.mts --watch", "test:windows:ci": "pnpm test:windows:ci:1 && pnpm test:windows:ci:2", - "test:windows:ci:1": "node --import tsx scripts/test-projects.mts src/state/openclaw-state-ownership.test.ts src/snapshot/local-repository.windows.test.ts src/state/openclaw-database-paths.windows.test.ts src/node-host/invoke-system-run-allowlist.test.ts src/node-host/node-worker-transfer-client.test.ts packages/terminal-core/src/display-string.test.ts src/config/sessions/session-accessor.sqlite-archive.worker.test.ts src/commands/doctor-gateway-auth-token.windows.test.ts src/infra/state-migrations.audit-logs.windows.test.ts src/commands/backup-verify.test.ts src/commands/agents.commands.list.test.ts src/shared/runtime-import.test.ts src/shared/worker-bundle-archive.test.ts src/node-host/node-worker-bundle-installer.test.ts test/scripts/direct-run-entrypoints.test.ts test/scripts/format-generated-module.test.ts test/scripts/openclaw-cross-os-installer.windows.test.ts test/scripts/openclaw-cross-os-release-workflow.test.ts test/scripts/pnpm-runner.test.ts test/scripts/run-with-env.test.ts test/scripts/ui.test.ts test/scripts/vitest-process-group.test.ts src/agents/tools/media-tool-file-url.windows.test.ts extensions/memory-core/src/memory-extra-file-path.windows.test.ts src/daemon/schtasks.startup-fallback.test.ts src/plugin-sdk/node-host.test.ts test/e2e/qa-lab/runtime/package-openclaw-for-docker.e2e.test.ts extensions/lobster/src/lobster-runner.test.ts src/gateway/worker-environments/workspace-quiescence.windows.test.ts", - "test:windows:ci:2": "node --import tsx scripts/test-projects.mts src/infra/sqlite-snapshot.test.ts src/infra/ports.test.ts src/infra/advertised-lan-host.windows.test.ts src/infra/update-managed-service-handoff-command.test.ts src/infra/update-managed-service-handoff-lifecycle.test.ts src/infra/executable-path.test.ts src/infra/fs-safe-remove.test.ts src/infra/state-migrations.legacy-session-store.test.ts src/infra/windows-install-roots.test.ts src/infra/ssh-client.windows.test.ts src/infra/exec-allowlist-pattern.test.ts src/infra/process-env.test.ts src/agents/sessions/windows-git-bash-path.test.ts test/scripts/npm-runner.test.ts test/scripts/ts-topology.test.ts src/test-utils/openclaw-test-state.test.ts src/utils.test.ts src/media/local-media-path.windows.test.ts src/media/web-media.file-url.windows.test.ts src/process/exec.windows.test.ts src/process/exec.windows.integration.test.ts src/agents/sessions/exec.real.test.ts src/process/windows-command.test.ts src/process/terminal-pty.test.ts src/process/supervisor/supervisor.anchored-shell.real.test.ts src/tui/tui.resolve-codex-bin.test.ts src/media-understanding/attachments.file-url.windows.test.ts src/cli/daemon-cli/status.print.test.ts src/cli/mcp-cli.path-case.windows.test.ts src/auto-reply/usage-bar/template.windows.test.ts src/auto-reply/reply.triggers.trigger-handling.stages-inbound-media-into-sandbox-workspace.test.ts src/agents/provider-local-service.env-case.test.ts src/agents/bash-tools.exec.script-preflight.test.ts src/agents/agent-tools.read.windows.test.ts src/agents/agent-tools.read.host-operations.test.ts src/agents/sandbox/fs-paths.test.ts src/agents/sessions/tools/render-utils.test.ts src/agents/sessions/tools/path-utils.test.ts extensions/msteams/src/media-helpers.test.ts extensions/msteams/src/messenger.test.ts extensions/mxc/test/mxc-backend.test.ts extensions/mxc/test/sandbox-policy-loader.test.ts", - "test:windows:schtasks:integration": "node --import tsx scripts/run-with-env.mts CI_WINDOWS_SCHTASKS_INTEGRATION=1 OPENCLAW_E2E_SKIP_BUILD=1 OPENCLAW_E2E_VERBOSE=1 OPENCLAW_VITEST_MAX_WORKERS=1 -- node scripts/run-vitest.mjs src/daemon/schtasks.integration.e2e.test.ts", - "tool-display:check": "node --import tsx scripts/tool-display.ts --check", - "tool-display:write": "node --import tsx scripts/tool-display.ts --write", - "ts-topology": "node --import tsx scripts/ts-topology.ts", + "test:windows:ci:1": "node --import ./scripts/tsx.mjs scripts/test-projects.mts src/state/openclaw-state-ownership.test.ts src/snapshot/local-repository.windows.test.ts src/state/openclaw-database-paths.windows.test.ts src/node-host/invoke-system-run-allowlist.test.ts src/node-host/node-worker-transfer-client.test.ts packages/terminal-core/src/display-string.test.ts src/config/sessions/session-accessor.sqlite-archive.worker.test.ts src/commands/doctor-gateway-auth-token.windows.test.ts src/infra/state-migrations.audit-logs.windows.test.ts src/commands/backup-verify.test.ts src/commands/agents.commands.list.test.ts src/shared/runtime-import.test.ts src/shared/worker-bundle-archive.test.ts src/node-host/node-worker-bundle-installer.test.ts test/scripts/direct-run-entrypoints.test.ts test/scripts/format-generated-module.test.ts test/scripts/openclaw-cross-os-installer.windows.test.ts test/scripts/openclaw-cross-os-release-workflow.test.ts test/scripts/pnpm-runner.test.ts test/scripts/run-with-env.test.ts test/scripts/ui.test.ts test/scripts/vitest-process-group.test.ts src/agents/tools/media-tool-file-url.windows.test.ts extensions/memory-core/src/memory-extra-file-path.windows.test.ts src/daemon/schtasks.startup-fallback.test.ts src/plugin-sdk/node-host.test.ts test/e2e/qa-lab/runtime/package-openclaw-for-docker.e2e.test.ts extensions/lobster/src/lobster-runner.test.ts src/gateway/worker-environments/workspace-quiescence.windows.test.ts", + "test:windows:ci:2": "node --import ./scripts/tsx.mjs scripts/test-projects.mts src/infra/sqlite-snapshot.test.ts src/infra/ports.test.ts src/infra/advertised-lan-host.windows.test.ts src/infra/update-managed-service-handoff-command.test.ts src/infra/update-managed-service-handoff-lifecycle.test.ts src/infra/executable-path.test.ts src/infra/fs-safe-remove.test.ts src/infra/state-migrations.legacy-session-store.test.ts src/infra/windows-install-roots.test.ts src/infra/ssh-client.windows.test.ts src/infra/exec-allowlist-pattern.test.ts src/infra/process-env.test.ts src/agents/sessions/windows-git-bash-path.test.ts test/scripts/npm-runner.test.ts test/scripts/ts-topology.test.ts src/test-utils/openclaw-test-state.test.ts src/utils.test.ts src/media/local-media-path.windows.test.ts src/media/web-media.file-url.windows.test.ts src/process/exec.windows.test.ts src/process/exec.windows.integration.test.ts src/agents/sessions/exec.real.test.ts src/process/windows-command.test.ts src/process/terminal-pty.test.ts src/process/supervisor/supervisor.anchored-shell.real.test.ts src/tui/tui.resolve-codex-bin.test.ts src/media-understanding/attachments.file-url.windows.test.ts src/cli/daemon-cli/status.print.test.ts src/cli/mcp-cli.path-case.windows.test.ts src/auto-reply/usage-bar/template.windows.test.ts src/auto-reply/reply.triggers.trigger-handling.stages-inbound-media-into-sandbox-workspace.test.ts src/agents/provider-local-service.env-case.test.ts src/agents/bash-tools.exec.script-preflight.test.ts src/agents/agent-tools.read.windows.test.ts src/agents/agent-tools.read.host-operations.test.ts src/agents/sandbox/fs-paths.test.ts src/agents/sessions/tools/render-utils.test.ts src/agents/sessions/tools/path-utils.test.ts extensions/msteams/src/media-helpers.test.ts extensions/msteams/src/messenger.test.ts extensions/mxc/test/mxc-backend.test.ts extensions/mxc/test/sandbox-policy-loader.test.ts", + "test:windows:schtasks:integration": "node --import ./scripts/tsx.mjs scripts/run-with-env.mts CI_WINDOWS_SCHTASKS_INTEGRATION=1 OPENCLAW_E2E_SKIP_BUILD=1 OPENCLAW_E2E_VERBOSE=1 OPENCLAW_VITEST_MAX_WORKERS=1 -- node scripts/run-vitest.mjs src/daemon/schtasks.integration.e2e.test.ts", + "tool-display:check": "node --import ./scripts/tsx.mjs scripts/tool-display.ts --check", + "tool-display:write": "node --import ./scripts/tsx.mjs scripts/tool-display.ts --write", + "ts-topology": "node --import ./scripts/tsx.mjs scripts/ts-topology.ts", "tsgo": "pnpm tsgo:core", "tsgo:all": "pnpm tsgo:core:all && pnpm tsgo:extensions:all && pnpm tsgo:scripts && pnpm tsgo:test:root", "tsgo:core": "node scripts/run-tsgo.mjs -p tsconfig.core.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/core.tsbuildinfo", @@ -1994,7 +1994,7 @@ "tsgo:extensions:all": "node scripts/run-tsgo.mjs -b tsconfig.extensions.projects.json", "tsgo:extensions:test": "node scripts/run-tsgo.mjs -p test/tsconfig/tsconfig.extensions.test.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/extensions-test.tsbuildinfo", "tsgo:prod": "pnpm tsgo:core && pnpm tsgo:ui && pnpm tsgo:extensions", - "tsgo:profile": "node --import tsx scripts/profile-tsgo.mts", + "tsgo:profile": "node --import ./scripts/tsx.mjs scripts/profile-tsgo.mts", "tsgo:scripts": "node scripts/run-tsgo.mjs -p tsconfig.scripts.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/scripts.tsbuildinfo", "tsgo:test": "pnpm tsgo:core:test && pnpm tsgo:extensions:test && pnpm tsgo:test:root", "tsgo:test:extensions": "pnpm tsgo:extensions:test", @@ -2004,32 +2004,32 @@ "tsgo:test:ui": "node scripts/run-tsgo-core-test-shards.mjs ui", "tsgo:ui": "node scripts/run-tsgo.mjs -p tsconfig.ui.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/ui.tsbuildinfo", "tui": "node scripts/run-node.mjs tui", - "tui:dev": "node --import tsx scripts/run-with-env.mts OPENCLAW_PROFILE=dev -- node scripts/run-node.mjs --dev tui", - "tui:pty:test:watch": "node --import tsx scripts/dev/tui-pty-test-watch.ts", - "tui:pty:test:watch:all": "node --import tsx scripts/dev/tui-pty-test-watch.ts --mode all", - "tui:pty:test:watch:fake": "node --import tsx scripts/dev/tui-pty-test-watch.ts --mode fake", - "tui:pty:test:watch:local": "node --import tsx scripts/dev/tui-pty-test-watch.ts --mode local", - "ui:boot-manifest:gen": "node --import tsx scripts/control-ui-boot-manifest.mts", + "tui:dev": "node --import ./scripts/tsx.mjs scripts/run-with-env.mts OPENCLAW_PROFILE=dev -- node scripts/run-node.mjs --dev tui", + "tui:pty:test:watch": "node --import ./scripts/tsx.mjs scripts/dev/tui-pty-test-watch.ts", + "tui:pty:test:watch:all": "node --import ./scripts/tsx.mjs scripts/dev/tui-pty-test-watch.ts --mode all", + "tui:pty:test:watch:fake": "node --import ./scripts/tsx.mjs scripts/dev/tui-pty-test-watch.ts --mode fake", + "tui:pty:test:watch:local": "node --import ./scripts/tsx.mjs scripts/dev/tui-pty-test-watch.ts --mode local", + "ui:boot-manifest:gen": "node --import ./scripts/tsx.mjs scripts/control-ui-boot-manifest.mts", "ui:build": "node scripts/ui.js build", "ui:dev": "node scripts/ui.js dev", - "ui:i18n:baseline": "node --import tsx scripts/control-ui-i18n-verify.ts baseline", - "ui:i18n:check": "node --import tsx scripts/control-ui-i18n.ts check", - "ui:i18n:report": "node --import tsx scripts/control-ui-i18n-report.ts", - "ui:i18n:sync": "node --import tsx scripts/control-ui-i18n.ts sync --write", - "ui:i18n:verify": "node --import tsx scripts/control-ui-i18n-verify.ts verify", - "ui:proof:composer-mic-hover": "node --import tsx scripts/capture-composer-mic-hover-proof.mts", - "ui:proof:injected-turn-notice": "node --import tsx scripts/capture-injected-turn-notice-proof.mts", - "ui:proof:model-picker": "node --import tsx scripts/capture-model-picker-proof.mts", - "ui:proof:session-toolbar": "node --import tsx scripts/capture-session-toolbar-proof.mts", - "ui:proof:workboard": "node --import tsx scripts/capture-workboard-ui-proof.mts", - "native:i18n:baseline": "node --import tsx scripts/native-app-i18n.ts baseline --write", - "native:i18n:check": "node --import tsx scripts/native-app-i18n.ts check", - "native:i18n:sync": "node --import tsx scripts/native-app-i18n.ts sync --write", - "native:i18n:verify": "node --import tsx scripts/native-app-i18n.ts verify", - "android:i18n:check": "node --import tsx scripts/android-app-i18n.ts check", - "apple:i18n:check": "node --import tsx scripts/apple-app-i18n.ts check", + "ui:i18n:baseline": "node --import ./scripts/tsx.mjs scripts/control-ui-i18n-verify.ts baseline", + "ui:i18n:check": "node --import ./scripts/tsx.mjs scripts/control-ui-i18n.ts check", + "ui:i18n:report": "node --import ./scripts/tsx.mjs scripts/control-ui-i18n-report.ts", + "ui:i18n:sync": "node --import ./scripts/tsx.mjs scripts/control-ui-i18n.ts sync --write", + "ui:i18n:verify": "node --import ./scripts/tsx.mjs scripts/control-ui-i18n-verify.ts verify", + "ui:proof:composer-mic-hover": "node --import ./scripts/tsx.mjs scripts/capture-composer-mic-hover-proof.mts", + "ui:proof:injected-turn-notice": "node --import ./scripts/tsx.mjs scripts/capture-injected-turn-notice-proof.mts", + "ui:proof:model-picker": "node --import ./scripts/tsx.mjs scripts/capture-model-picker-proof.mts", + "ui:proof:session-toolbar": "node --import ./scripts/tsx.mjs scripts/capture-session-toolbar-proof.mts", + "ui:proof:workboard": "node --import ./scripts/tsx.mjs scripts/capture-workboard-ui-proof.mts", + "native:i18n:baseline": "node --import ./scripts/tsx.mjs scripts/native-app-i18n.ts baseline --write", + "native:i18n:check": "node --import ./scripts/tsx.mjs scripts/native-app-i18n.ts check", + "native:i18n:sync": "node --import ./scripts/tsx.mjs scripts/native-app-i18n.ts sync --write", + "native:i18n:verify": "node --import ./scripts/tsx.mjs scripts/native-app-i18n.ts verify", + "android:i18n:check": "node --import ./scripts/tsx.mjs scripts/android-app-i18n.ts check", + "apple:i18n:check": "node --import ./scripts/tsx.mjs scripts/apple-app-i18n.ts check", "ui:install": "node scripts/ui.js install", - "verify": "node --import tsx scripts/verify.mts" + "verify": "node --import ./scripts/tsx.mjs scripts/verify.mts" }, "dependencies": { "@agentclientprotocol/sdk": "1.3.0", diff --git a/scripts/ci-changed-scope.mjs b/scripts/ci-changed-scope.mjs index 9fdbebab8876..f8dd637d34c1 100644 --- a/scripts/ci-changed-scope.mjs +++ b/scripts/ci-changed-scope.mjs @@ -62,7 +62,7 @@ const WINDOWS_SQLITE_SCOPE_RE = /^src\/(?:state\/|.*sqlite.*\.ts$)/; const WINDOWS_FILE_URL_SCOPE_RE = /^(?:src\/agents\/tools\/(?:media-tool-file-url\.windows\.test|media-tool-shared(?:\.test)?|pdf-tool(?:\.test)?)|src\/auto-reply\/(?:reply\/stage-sandbox-media|reply\.triggers\.trigger-handling\.stages-inbound-media-into-sandbox-workspace\.test)|src\/media\/(?:local-media-path(?:\.windows\.test)?|local-roots(?:\.test)?|web-media(?:\.file-url\.windows\.test)?)|src\/channels\/inbound-event\/media(?:\.test)?|src\/gateway\/managed-image-attachments(?:\.test)?|extensions\/msteams\/src\/(?:media-helpers|messenger)(?:\.test)?)\.ts$/; const WINDOWS_SCOPE_RE = - /^(extensions\/mxc\/|src\/agents\/(?:bash-tools\.exec-script-(?:preflight|target)|bash-tools\.exec\.script-preflight\.test)\.ts$|src\/config\/sessions\/(?:session-accessor\.sqlite-archive(?:\.worker(?:\.test)?)?|store\.session-lifecycle-mutation\.test)\.ts$|src\/process\/|src\/infra\/(?:(?:advertised-lan-host|exec-allowlist-pattern|fs-safe-remove)(?:\.windows)?(?:\.test)?|ports(?:-inspect|\.test)|ssh-client(?:\.windows\.test)?|update-managed-service-handoff(?:-(?:command|lifecycle)\.test)?|windows-install-roots)\.ts$|src\/shared\/(?:import-specifier|runtime-import)(?:\.test)?\.ts$|src\/test-utils\/openclaw-test-state(?:\.test)?\.ts$|scripts\/(?:android-(?:app-i18n|pin-version)\.ts|ci-run-timings\.mjs|e2e\/lib\/package-compat\.mjs|generate-bundled-channel-config-metadata\.ts|install\.ps1|openclaw-cross-os-release-checks\.ts|plan-release-workflow-matrix\.mjs|run-additional-boundary-checks\.mts|verify-docker-attestations\.mjs|github\/run-openclaw-cross-os-release-checks\.sh|(?:npm-runner|pnpm-runner|ui|vitest-process-group)\.(?:mjs|mts|js)|lib\/(?:direct-run\.(?:mjs|mts)|format-generated-module\.mts|tsx-cli-shim\.mjs|cross-os-release-checks\/[^/]+\.ts))$|test\/scripts\/(?:direct-run-entrypoints|format-generated-module|install-ps1|npm-runner|openclaw-cross-os-release-workflow|pnpm-runner|ui|vitest-process-group)\.test\.ts$|package\.json$|pnpm-lock\.yaml$|pnpm-workspace\.yaml$|\.github\/workflows\/(?:ci|openclaw-cross-os-release-checks-reusable)\.yml$|\.github\/actions\/setup-node-env\/action\.yml$|\.github\/actions\/setup-pnpm-store-cache\/action\.yml$)/; + /^(extensions\/mxc\/|src\/agents\/(?:bash-tools\.exec-script-(?:preflight|target)|bash-tools\.exec\.script-preflight\.test)\.ts$|src\/config\/sessions\/(?:session-accessor\.sqlite-archive(?:\.worker(?:\.test)?)?|store\.session-lifecycle-mutation\.test)\.ts$|src\/process\/|src\/infra\/(?:(?:advertised-lan-host|exec-allowlist-pattern|fs-safe-remove)(?:\.windows)?(?:\.test)?|ports(?:-inspect|\.test)|ssh-client(?:\.windows\.test)?|update-managed-service-handoff(?:-(?:command|lifecycle)\.test)?|windows-install-roots)\.ts$|src\/shared\/(?:import-specifier|runtime-import)(?:\.test)?\.ts$|src\/test-utils\/openclaw-test-state(?:\.test)?\.ts$|scripts\/(?:android-(?:app-i18n|pin-version)\.ts|ci-run-timings\.mjs|e2e\/lib\/package-compat\.mjs|generate-bundled-channel-config-metadata\.ts|install\.ps1|openclaw-cross-os-release-checks\.ts|plan-release-workflow-matrix\.mjs|run-additional-boundary-checks\.mts|verify-docker-attestations\.mjs|github\/run-openclaw-cross-os-release-checks\.sh|tsx\.mjs|(?:npm-runner|pnpm-runner|ui|vitest-process-group)\.(?:mjs|mts|js)|lib\/(?:direct-run\.(?:mjs|mts)|format-generated-module\.mts|tsx-cli-shim\.mjs|cross-os-release-checks\/[^/]+\.ts))$|test\/scripts\/(?:direct-run-entrypoints|format-generated-module|install-ps1|npm-runner|openclaw-cross-os-release-workflow|pnpm-runner|ui|vitest-process-group)\.test\.ts$|package\.json$|pnpm-lock\.yaml$|pnpm-workspace\.yaml$|\.github\/workflows\/(?:ci|openclaw-cross-os-release-checks-reusable)\.yml$|\.github\/actions\/setup-node-env\/action\.yml$|\.github\/actions\/setup-pnpm-store-cache\/action\.yml$)/; const WINDOWS_TEST_SCOPE_RE = /^(extensions\/mxc\/test\/(?:mxc-backend|sandbox-policy-loader)\.test\.ts$|src\/agents\/bash-tools\.exec\.script-preflight\.test\.ts$|src\/config\/sessions\/(?:session-accessor\.sqlite-archive\.worker|store\.session-lifecycle-mutation)\.test\.ts$|src\/process\/(?:exec\.windows|supervisor\/supervisor\.anchored-shell\.real|terminal-pty|windows-command)\.test\.ts$|src\/infra\/(?:advertised-lan-host(?:\.windows)?|exec-allowlist-pattern|fs-safe-remove|ports|ssh-client\.windows|update-managed-service-handoff-(?:command|lifecycle)|windows-install-roots)\.test\.ts$|src\/shared\/runtime-import\.test\.ts$|src\/state\/openclaw-database-paths\.windows\.test\.ts$|src\/test-utils\/openclaw-test-state\.test\.ts$|test\/scripts\/(?:direct-run-entrypoints|format-generated-module|npm-runner|openclaw-cross-os-release-workflow|pnpm-runner|ui|vitest-process-group)\.test\.ts$)/; const WINDOWS_SECRETREF_SCOPE_RE = diff --git a/scripts/docs-sync-publish.mjs b/scripts/docs-sync-publish.mjs index 896677bbea3c..28bf93a79ef1 100644 --- a/scripts/docs-sync-publish.mjs +++ b/scripts/docs-sync-publish.mjs @@ -37,6 +37,10 @@ const SYNC_SUPPORT_FILES = [ source: path.join(ROOT, "scripts", "lib", "local-check-runtime.mts"), target: path.join(".openclaw-sync", "lib", "local-check-runtime.mts"), }, + { + source: path.join(ROOT, "scripts", "tsx.mjs"), + target: path.join(".openclaw-sync", "tsx.mjs"), + }, { source: path.join(ROOT, "scripts", "lib", "mintlify-accordion.mjs"), target: path.join(".openclaw-sync", "lib", "mintlify-accordion.mjs"), diff --git a/scripts/lib/plugin-publication-candidates.ts b/scripts/lib/plugin-publication-candidates.ts index 7c6bc462f6ea..4c1bfbace6e9 100644 --- a/scripts/lib/plugin-publication-candidates.ts +++ b/scripts/lib/plugin-publication-candidates.ts @@ -31,6 +31,7 @@ export const PLUGIN_NPM_RELEASE_AUTHORITY_PATHS = [ "scripts/lib/plugin-npm-package-manifest.mts", "scripts/lib/plugin-npm-release.ts", "scripts/lib/tsx-cli-shim.mjs", + "scripts/tsx.mjs", "scripts/plugin-npm-publish.sh", "scripts/plugin-npm-release-check.ts", "scripts/plugin-npm-release-plan.ts", diff --git a/scripts/lib/tsx-cli-shim.mjs b/scripts/lib/tsx-cli-shim.mjs index c1a9a5fc5ef0..59456a5cab51 100644 --- a/scripts/lib/tsx-cli-shim.mjs +++ b/scripts/lib/tsx-cli-shim.mjs @@ -55,6 +55,14 @@ function resolveTsxImport(checkoutRoot) { throw resolutionError; } +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"; + await import(resolveTsxImport(SHIM_CHECKOUT_ROOT)); +} + function signalExitCode(signal) { const signalNumber = osConstants.signals[signal]; return typeof signalNumber === "number" ? 128 + signalNumber : 1; @@ -118,7 +126,7 @@ async function runTsxCliShimInner(moduleUrl, options) { try { const implementationUrl = new URL(options.implementation, moduleUrl); const implementationPath = fileURLToPath(implementationUrl); - const tsxImport = resolveTsxImport(SHIM_CHECKOUT_ROOT); + const tsxImport = new URL("../tsx.mjs", import.meta.url).href; const nodeExecutable = process.versions.bun ? "node" : process.execPath; child = spawn( nodeExecutable, diff --git a/scripts/pr b/scripts/pr index ac3076f3c8fd..e1aed87afec0 100755 --- a/scripts/pr +++ b/scripts/pr @@ -52,6 +52,7 @@ pr_wrapper_components=( scripts/lib/direct-run.mjs scripts/lib/tsx-cli-shim.mjs scripts/lib/local-check-runtime.mts + scripts/tsx.mjs scripts/verify-pr-hosted-gates.mjs scripts/verify-pr-hosted-gates.mts scripts/watch-pr-ci.mjs diff --git a/scripts/test-projects.test-support.mts b/scripts/test-projects.test-support.mts index dbfde136680f..a8edcc920571 100644 --- a/scripts/test-projects.test-support.mts +++ b/scripts/test-projects.test-support.mts @@ -2362,7 +2362,7 @@ const SEMANTIC_TOOLING_TARGET_PATTERNS: Array<[RegExp, string[]]> = [ [/^scripts\/ci-changed-scope\.mjs$/u, [...changedScopeTests, "control-ui-i18n"]], [/^scripts\/check-changed\.(?:mjs|mts)$/u, ["changed-lanes"]], [/^scripts\/changed-lanes\.(?:mjs|mts)$/u, ["changed-lanes"]], - [/^scripts\/lib\/tsx-cli-shim\.mjs$/u, ["direct-run-entrypoints"]], + [/^scripts\/(?:lib\/tsx-cli-shim|tsx)\.mjs$/u, ["direct-run-entrypoints"]], [ new RegExp( [ diff --git a/scripts/tsx.mjs b/scripts/tsx.mjs new file mode 100644 index 000000000000..5a77d5f15a20 --- /dev/null +++ b/scripts/tsx.mjs @@ -0,0 +1,14 @@ +import { registerToolingTsx } from "./lib/tsx-cli-shim.mjs"; + +// fork() inherits execArgv but can change cwd. Keep our root-script preload +// resolvable there without rewriting unrelated flags or other preloads. +const preload = "./scripts/tsx.mjs"; +for (const [index, arg] of process.execArgv.entries()) { + if (arg === preload && process.execArgv[index - 1] === "--import") { + process.execArgv[index] = import.meta.url; + } else if (arg === `--import=${preload}`) { + process.execArgv[index] = `--import=${import.meta.url}`; + } +} + +await registerToolingTsx(); diff --git a/src/infra/vitest-config.test.ts b/src/infra/vitest-config.test.ts index 8a96f2c28415..9e001c7548a9 100644 --- a/src/infra/vitest-config.test.ts +++ b/src/infra/vitest-config.test.ts @@ -253,16 +253,20 @@ describe("test scripts", () => { scripts?: Record; }; - expect(pkg.scripts?.["test:serial"]).toBe("node --import tsx scripts/test-projects-serial.mts"); - expect(pkg.scripts?.["test:max"]).toBe("node --import tsx scripts/test-projects-max.mts"); + expect(pkg.scripts?.["test:serial"]).toBe( + "node --import ./scripts/tsx.mjs scripts/test-projects-serial.mts", + ); + expect(pkg.scripts?.["test:max"]).toBe( + "node --import ./scripts/tsx.mjs scripts/test-projects-max.mts", + ); expect(pkg.scripts?.["test:changed:max"]).toBe( - "node --import tsx scripts/test-projects-max.mts --changed origin/main", + "node --import ./scripts/tsx.mjs scripts/test-projects-max.mts --changed origin/main", ); expect(pkg.scripts?.["test:perf:imports"]).toBe( - "node --import tsx scripts/test-projects-imports.mts", + "node --import ./scripts/tsx.mjs scripts/test-projects-imports.mts", ); expect(pkg.scripts?.["test:perf:imports:changed"]).toBe( - "node --import tsx scripts/test-projects-imports.mts --changed origin/main", + "node --import ./scripts/tsx.mjs scripts/test-projects-imports.mts --changed origin/main", ); expect(pkg.scripts?.["test:fast"]).toBe( "node scripts/run-vitest.mjs run --config test/vitest/vitest.unit.config.ts", @@ -274,12 +278,14 @@ describe("test scripts", () => { "node scripts/run-vitest.mjs run --config test/vitest/vitest.unit-fast.config.ts", ); expect(pkg.scripts?.["test:unit:fast:audit"]).toBe( - "node --import tsx scripts/test-unit-fast-audit.mts", + "node --import ./scripts/tsx.mjs scripts/test-unit-fast-audit.mts", + ); + expect(pkg.scripts?.["test"]).toBe("node --import ./scripts/tsx.mjs scripts/test-projects.mts"); + expect(pkg.scripts?.["test:force"]).toBe( + "node --import ./scripts/tsx.mjs scripts/test-force.ts", ); - expect(pkg.scripts?.["test"]).toBe("node --import tsx scripts/test-projects.mts"); - expect(pkg.scripts?.["test:force"]).toBe("node --import tsx scripts/test-force.ts"); expect(pkg.scripts?.["test:gateway"]).toBe( - "node --import tsx scripts/run-with-env.mts OPENCLAW_GATEWAY_PROJECT_SHARDS=1 -- node scripts/run-vitest.mjs run --config test/vitest/vitest.gateway.config.ts", + "node --import ./scripts/tsx.mjs scripts/run-with-env.mts OPENCLAW_GATEWAY_PROJECT_SHARDS=1 -- node scripts/run-vitest.mjs run --config test/vitest/vitest.gateway.config.ts", ); expect(pkg.scripts?.["test:single"]).toBeUndefined(); }); diff --git a/test/package-scripts.test.ts b/test/package-scripts.test.ts index 326c05338334..10a3ed41a0ac 100644 --- a/test/package-scripts.test.ts +++ b/test/package-scripts.test.ts @@ -98,7 +98,7 @@ describe("package scripts", () => { it("finds node script targets after env assignments and valued node options", () => { expect( extractNodeScriptTargets( - "FOO=1 node --import tsx scripts/release-check.ts && node --max-old-space-size=8192 --import tsx scripts/plugin-sdk-surface-report.mts && env BAR=1 node -r tsx scripts/check.ts", + "FOO=1 node --import ./scripts/tsx.mjs scripts/release-check.ts && node --max-old-space-size=8192 --import ./scripts/tsx.mjs scripts/plugin-sdk-surface-report.mts && env BAR=1 node -r ./preload.cjs scripts/check.ts", ), ).toEqual([ "scripts/release-check.ts", @@ -142,31 +142,36 @@ describe("package scripts", () => { { scriptName: "build:docker", expectedCount: 2 }, { scriptName: "build:plugin-sdk:strict-smoke", expectedCount: 1 }, { scriptName: "build:strict-smoke", expectedCount: 1 }, - ])("runs TypeScript steps in $scriptName through tsx", ({ scriptName, expectedCount }) => { - const script = expectDefined( - readPackageJson().scripts[scriptName], - `package script ${scriptName}`, - ); + ])( + "runs TypeScript steps in $scriptName through the tooling bootstrap", + ({ scriptName, expectedCount }) => { + const script = expectDefined( + readPackageJson().scripts[scriptName], + `package script ${scriptName}`, + ); - expect(script).not.toContain("--experimental-strip-types"); - expect(script.match(/node --import tsx scripts\/[^\s]+\.ts/gu)).toHaveLength(expectedCount); - }); + expect(script).not.toContain("--experimental-strip-types"); + expect( + script.match(/node --import \.\/scripts\/tsx\.mjs scripts\/[^\s]+\.ts(?=\s|$)/gu), + ).toHaveLength(expectedCount); + }, + ); it("enables live cache validation in the package script", () => { expect(readPackageJson().scripts["test:live:cache"]).toBe( - "node --import tsx scripts/run-with-env.mts OPENCLAW_LIVE_TEST=1 OPENCLAW_LIVE_CACHE_TEST=1 -- node --import tsx scripts/check-live-cache.ts", + "node --import ./scripts/tsx.mjs scripts/run-with-env.mts OPENCLAW_LIVE_TEST=1 OPENCLAW_LIVE_CACHE_TEST=1 -- node --import ./scripts/tsx.mjs scripts/check-live-cache.ts", ); }); it("runs browser extension bootstrap E2E against real Chromium", () => { expect(readPackageJson().scripts["test:e2e:browser-extension"]).toBe( - "node --import tsx scripts/run-with-env.mts PLAYWRIGHT_BROWSERS_PATH=.artifacts/playwright-browsers -- node --import tsx scripts/ensure-playwright-chromium.mts --require-playwright-chromium && node --import tsx scripts/run-with-env.mts PLAYWRIGHT_BROWSERS_PATH=.artifacts/playwright-browsers OPENCLAW_BROWSER_EXTENSION_E2E=1 OPENCLAW_E2E_WORKERS=1 -- node scripts/run-vitest.mjs extensions/browser/chrome-extension/bootstrap.chromium.test.ts", + "node --import ./scripts/tsx.mjs scripts/run-with-env.mts PLAYWRIGHT_BROWSERS_PATH=.artifacts/playwright-browsers -- node --import ./scripts/tsx.mjs scripts/ensure-playwright-chromium.mts --require-playwright-chromium && node --import ./scripts/tsx.mjs scripts/run-with-env.mts PLAYWRIGHT_BROWSERS_PATH=.artifacts/playwright-browsers OPENCLAW_BROWSER_EXTENSION_E2E=1 OPENCLAW_E2E_WORKERS=1 -- node scripts/run-vitest.mjs extensions/browser/chrome-extension/bootstrap.chromium.test.ts", ); }); it("gives the plugin SDK usage scan enough heap for repository-wide analysis", () => { expect(readPackageJson().scripts["plugin-sdk:usage"]).toBe( - "node --max-old-space-size=8192 --import tsx scripts/analyze-plugin-sdk-usage.ts", + "node --max-old-space-size=8192 --import ./scripts/tsx.mjs scripts/analyze-plugin-sdk-usage.ts", ); }); @@ -178,7 +183,7 @@ describe("package scripts", () => { it("runs runtime postbuild before plugin SDK strict export checks", () => { expect(readPackageJson().scripts["build:plugin-sdk:strict-smoke"]).toBe( - "node --import tsx scripts/tsdown-build.mts && node scripts/runtime-postbuild.mjs && node --import tsx scripts/run-with-env.mts OPENCLAW_PLUGIN_SDK_CANONICAL_DTS=1 -- node --import tsx scripts/write-plugin-sdk-entry-dts.ts && node --import tsx scripts/check-plugin-sdk-exports.mts", + "node --import ./scripts/tsx.mjs scripts/tsdown-build.mts && node scripts/runtime-postbuild.mjs && node --import ./scripts/tsx.mjs scripts/run-with-env.mts OPENCLAW_PLUGIN_SDK_CANONICAL_DTS=1 -- node --import ./scripts/tsx.mjs scripts/write-plugin-sdk-entry-dts.ts && node --import ./scripts/tsx.mjs scripts/check-plugin-sdk-exports.mts", ); }); @@ -196,9 +201,11 @@ describe("package scripts", () => { it("cleans package builds before validating release contents", () => { const scripts = readPackageJson().scripts; - expect(scripts["build:package"]).toBe("node --import tsx scripts/build-all.mts package"); + expect(scripts["build:package"]).toBe( + "node --import ./scripts/tsx.mjs scripts/build-all.mts package", + ); expect(scripts["release:check"]).toBe( - "pnpm build:package && pnpm release:generated:check && node --import tsx scripts/release-check.ts", + "pnpm build:package && pnpm release:generated:check && node --import ./scripts/tsx.mjs scripts/release-check.ts", ); }); diff --git a/test/plugin-npm-release.test.ts b/test/plugin-npm-release.test.ts index f3887082299c..4c9798e715be 100644 --- a/test/plugin-npm-release.test.ts +++ b/test/plugin-npm-release.test.ts @@ -833,6 +833,8 @@ describe("collectPluginNpmGitRangeSelection", () => { "packages/plugin-package-contract/src/schema.ts", "scripts/lib/plugin-publication-candidates.ts", "scripts/lib/plugin-publication-collector.ts", + "scripts/lib/tsx-cli-shim.mjs", + "scripts/tsx.mjs", "scripts/plugin-npm-release-plan.ts", ])("selects all publishable plugins for an authority-only %s change", (changedPath) => { const repoDir = makeTempRepoRoot(tempDirs, "openclaw-plugin-npm-range-"); diff --git a/test/scripts/direct-run-entrypoints.test.ts b/test/scripts/direct-run-entrypoints.test.ts index 63f00bd1fc74..9f562bb1e8b9 100644 --- a/test/scripts/direct-run-entrypoints.test.ts +++ b/test/scripts/direct-run-entrypoints.test.ts @@ -1,15 +1,19 @@ import { spawnSync } from "node:child_process"; import { copyFileSync, + existsSync, mkdirSync, mkdtempSync, readFileSync, + readdirSync, realpathSync, rmSync, writeFileSync, } from "node:fs"; -import { tmpdir } from "node:os"; +import { createRequire } from "node:module"; +import { tmpdir, userInfo } from "node:os"; import path from "node:path"; +import { pathToFileURL } from "node:url"; import { describe, expect, it } from "vitest"; import { detectChangedScope } from "../../scripts/ci-changed-scope.mjs"; import { isDirectRunPath } from "../../scripts/lib/direct-run.mjs"; @@ -109,12 +113,14 @@ function writeTsxFixture(modulesDir: string, marker: string) { writeFileSync(path.join(dependencyDir, "index.js"), 'export const value = "loaded";\n'); } -function runShimFixture( +function withShimFixture( wrapper: (typeof TSX_SHIM_WRAPPERS)[number], - configureModules: (paths: { + run: (paths: { checkoutRoot: string; fixtureRoot: string; - }) => ModulesEnv = () => ({}), + implementationPath: string; + wrapperPath: string; + }) => T, ) { const fixtureRoot = realpathSync(mkdtempSync(path.join(tmpdir(), "openclaw-tsx-cli-shim-"))); const checkoutRoot = path.join(fixtureRoot, "checkout"); @@ -124,6 +130,7 @@ function runShimFixture( mkdirSync(path.dirname(wrapperPath), { recursive: true }); mkdirSync(path.join(checkoutRoot, "scripts", "lib"), { recursive: true }); copyFileSync(wrapper, wrapperPath); + copyFileSync("scripts/tsx.mjs", path.join(checkoutRoot, "scripts", "tsx.mjs")); copyFileSync( "scripts/lib/tsx-cli-shim.mjs", path.join(checkoutRoot, "scripts", "lib", "tsx-cli-shim.mjs"), @@ -133,30 +140,45 @@ function runShimFixture( path.join(checkoutRoot, "scripts", "lib", "local-check-runtime.mts"), ); writeFileSync(path.join(checkoutRoot, "pnpm-lock.yaml"), "lockfileVersion: '9.0'\n"); - writeFileSync( - implementationPath, - 'import { value } from "shim-dependency";\nprocess.stdout.write(JSON.stringify({ loader: process.env.OPENCLAW_TSX_FIXTURE_LOADER, dependency: value, args: process.argv.slice(2) }));\n', - ); - writeTsxFixture(path.join(checkoutRoot, "node_modules"), "checkout"); - const modulesEnv = configureModules({ checkoutRoot, fixtureRoot }); - - const env = { ...process.env }; - delete env.NODE_OPTIONS; - delete env.NODE_PATH; - delete env.PNPM_CONFIG_MODULES_DIR; - delete env.npm_config_modules_dir; - Object.assign(env, modulesEnv); - return spawnSync(process.execPath, [wrapperPath, "--hydrated-proof"], { - cwd: fixtureRoot, - encoding: "utf8", - env, - timeout: 10_000, - }); + return run({ checkoutRoot, fixtureRoot, implementationPath, wrapperPath }); } finally { rmSync(fixtureRoot, { recursive: true, force: true }); } } +function runShimFixture( + wrapper: (typeof TSX_SHIM_WRAPPERS)[number], + configureModules: (paths: { + checkoutRoot: string; + fixtureRoot: string; + }) => ModulesEnv = () => ({}), +) { + return withShimFixture( + wrapper, + ({ checkoutRoot, fixtureRoot, implementationPath, wrapperPath }) => { + writeFileSync( + implementationPath, + 'import { value } from "shim-dependency";\nprocess.stdout.write(JSON.stringify({ loader: process.env.OPENCLAW_TSX_FIXTURE_LOADER, dependency: value, args: process.argv.slice(2) }));\n', + ); + writeTsxFixture(path.join(checkoutRoot, "node_modules"), "checkout"); + const modulesEnv = configureModules({ checkoutRoot, fixtureRoot }); + + const env = { ...process.env }; + delete env.NODE_OPTIONS; + delete env.NODE_PATH; + delete env.PNPM_CONFIG_MODULES_DIR; + delete env.npm_config_modules_dir; + Object.assign(env, modulesEnv); + return spawnSync(process.execPath, [wrapperPath, "--hydrated-proof"], { + cwd: fixtureRoot, + encoding: "utf8", + env, + timeout: 10_000, + }); + }, + ); +} + function expectShimLoader(result: ReturnType, loader: string) { expect(result.error).toBeUndefined(); expect(result.status, result.stderr).toBe(0); @@ -168,6 +190,217 @@ function expectShimLoader(result: ReturnType, loader: str } describe("script direct-run entrypoints", () => { + it.each([false, true])( + "preserves preloads when forking into another cwd (equals=%s)", + (equals) => { + withShimFixture(TSX_SHIM_WRAPPERS[0], ({ checkoutRoot, fixtureRoot, implementationPath }) => { + const forkCwd = path.join(fixtureRoot, "child cwd"); + mkdirSync(forkCwd); + const childPath = path.join(fixtureRoot, "fork-child.mts"); + const extraPreloadPath = path.join(fixtureRoot, "extra-preload.mjs"); + writeFileSync(extraPreloadPath, 'globalThis.fixturePreload = "preserved";\n'); + const snapshotSource = ` +enum Transformed { Value = "transformed" } +console.log(JSON.stringify({ transformed: Transformed.Value, preload: globalThis.fixturePreload, + args: process.argv.slice(2), cwd: process.cwd(), execArgv: process.execArgv })); +`; + writeFileSync(childPath, `${snapshotSource}\nprocess.exitCode = 17;\n`); + writeFileSync( + implementationPath, + `${snapshotSource} +import { fork } from "node:child_process"; +const child = fork(${JSON.stringify(childPath)}, process.argv.slice(2), { + cwd: ${JSON.stringify(forkCwd)}, stdio: "inherit", +}); +process.exitCode = await new Promise((resolve, reject) => { + child.once("error", reject); + child.once("exit", code => resolve(code ?? 1)); +}); +`, + ); + const nodeFlags = ["--no-warnings", "--import", pathToFileURL(extraPreloadPath).href]; + const trailingFlags = ["--title", "./scripts/tsx.mjs", "--import=node:fs"]; + const preload = equals ? ["--import=./scripts/tsx.mjs"] : ["--import", "./scripts/tsx.mjs"]; + const bootstrapUrl = pathToFileURL(path.join(checkoutRoot, "scripts/tsx.mjs")).href; + const expectedPreload = equals ? [`--import=${bootstrapUrl}`] : ["--import", bootstrapUrl]; + const env: NodeJS.ProcessEnv = { + ...process.env, + TMPDIR: fixtureRoot, + TMP: fixtureRoot, + TEMP: fixtureRoot, + PNPM_CONFIG_MODULES_DIR: path.dirname( + path.dirname(createRequire(import.meta.url).resolve("tsx/package.json")), + ), + }; + delete env.TSX_DISABLE_CACHE; + delete env.NODE_OPTIONS; + const result = spawnSync( + process.execPath, + [ + ...nodeFlags, + ...preload, + ...trailingFlags, + implementationPath, + "argument with spaces", + "--fork-proof", + ], + { + cwd: checkoutRoot, + encoding: "utf8", + env, + timeout: 10_000, + }, + ); + expect(result.error).toBeUndefined(); + expect(result.status, result.stderr).toBe(17); + expect( + result.stdout + .trim() + .split("\n") + .map((line) => JSON.parse(line)), + ).toEqual( + [checkoutRoot, forkCwd].map((cwd) => ({ + transformed: "transformed", + preload: "preserved", + args: ["argument with spaces", "--fork-proof"], + cwd, + execArgv: [...nodeFlags, ...expectedPreload, ...trailingFlags], + })), + ); + }); + }, + ); + + it.each(["wrapper", "root package preloads"])( + "keeps %s and raw tsx children off disk caches without changing other cache settings", + (entrypoint) => { + withShimFixture(TSX_SHIM_WRAPPERS[0], ({ fixtureRoot, implementationPath, wrapperPath }) => { + const require = createRequire(import.meta.url); + const modulesDir = path.dirname(path.dirname(require.resolve("tsx/package.json"))); + const tempRoot = path.join(fixtureRoot, "temp"); + const cacheRoots = ["tsx", `tsx-${process.geteuid?.() ?? userInfo().username}`].map( + (name) => path.join(tempRoot, name), + ); + for (const cacheRoot of cacheRoots) { + mkdirSync(cacheRoot, { recursive: true }); + writeFileSync(path.join(cacheRoot, "0-sentinel"), "keep"); + } + const accessLog = path.join(fixtureRoot, "cache-access.log"); + const guard = path.join(fixtureRoot, "cache-guard.cjs"); + writeFileSync( + guard, + ` +const fs = require("node:fs"); +const path = require("node:path"); +const readdirSync = fs.readdirSync; +fs.readdirSync = function (directory, ...args) { + if (/^tsx(?:-|$)/.test(path.basename(String(directory)))) { + fs.appendFileSync(${JSON.stringify(accessLog)}, "cache scan\\n"); + throw new Error("Unexpected tsx disk cache access"); + } + return readdirSync.call(this, directory, ...args); +}; +`, + ); + const preservedEnv = Object.fromEntries( + [ + "TMPDIR", + "TMP", + "TEMP", + "XDG_CACHE_HOME", + "NODE_COMPILE_CACHE", + "OPENCLAW_VITEST_FS_MODULE_CACHE_PATH", + ].map((key) => [ + key, + key === "TMPDIR" || key === "TEMP" ? tempRoot : path.join(fixtureRoot, key), + ]), + ); + const childPath = path.join(fixtureRoot, "child.mts"); + const snapshotSource = ` +enum Transformed { Value = "transformed" } +console.log(JSON.stringify({ + transformed: Transformed.Value, + args: process.argv.slice(2), + cwd: process.cwd(), + env: Object.fromEntries(${JSON.stringify(Object.keys(preservedEnv))}.map(key => [key, process.env[key]])), +})); +`; + writeFileSync(childPath, `${snapshotSource}\nprocess.exitCode = 17;\n`); + writeFileSync( + implementationPath, + `${snapshotSource} +import { spawnSync } from "node:child_process"; +const child = spawnSync(process.execPath, ["--import", "tsx", ${JSON.stringify(childPath)}, ...process.argv.slice(2)], { stdio: "inherit" }); +if (child.error) throw child.error; +process.exitCode = child.status ?? 1; +`, + ); + const { scripts } = JSON.parse(readFileSync("package.json", "utf8")) as { + scripts: Record; + }; + const preloads = [ + ...new Set( + Object.values(scripts).flatMap((command) => + [...command.matchAll(/--import(?:=|\s+)(\S+)/gu)].map((match) => match[1]!), + ), + ), + ]; + expect(preloads.length).toBeGreaterThan(0); + const launches = + entrypoint === "wrapper" + ? [[wrapperPath]] + : preloads.map((preload) => ["--import", preload, implementationPath]); + for (const cacheFlag of [undefined, ""]) { + const env: NodeJS.ProcessEnv = { + ...process.env, + ...preservedEnv, + PNPM_CONFIG_MODULES_DIR: modulesDir, + NODE_OPTIONS: `--require ${JSON.stringify(guard)}`, + }; + delete env.TSX_DISABLE_CACHE; + if (cacheFlag !== undefined) { + env.TSX_DISABLE_CACHE = cacheFlag; + } + for (const launch of launches) { + const result = spawnSync( + process.execPath, + [...launch, "argument with spaces", "--proof"], + { + cwd: process.cwd(), + encoding: "utf8", + env, + timeout: 10_000, + }, + ); + expect(result.error).toBeUndefined(); + expect(result.status, result.stderr).toBe(17); + expect( + result.stdout + .trim() + .split("\n") + .map((line) => JSON.parse(line)), + ).toEqual( + Array.from({ length: 2 }, () => ({ + transformed: "transformed", + args: ["argument with spaces", "--proof"], + cwd: process.cwd(), + env: preservedEnv, + })), + ); + if (entrypoint === "wrapper") { + expect(result.stderr.trim().split("\n").at(-1)).toBe("[test] FAILED (exit 17)"); + } + } + } + expect(existsSync(accessLog)).toBe(false); + for (const cacheRoot of cacheRoots) { + expect(readdirSync(cacheRoot)).toEqual(["0-sentinel"]); + expect(readFileSync(path.join(cacheRoot, "0-sentinel"), "utf8")).toBe("keep"); + } + }); + }, + ); + it.each(EXECUTABLE_ENTRYPOINTS)("runs $script through its guarded CLI", (entrypoint) => { const result = runEntrypoint(entrypoint); const output = `${result.stdout}${result.stderr}`; @@ -257,6 +490,7 @@ describe("script direct-run entrypoints", () => { "scripts/lib/direct-run.mjs", "scripts/lib/tsx-cli-shim.mjs", "test/scripts/direct-run-entrypoints.test.ts", + "scripts/tsx.mjs", ])("routes %s through Windows CI", (changedPath) => { expect(detectChangedScope([changedPath]).runWindows).toBe(true); }); diff --git a/test/scripts/docker-all-scheduler.test.ts b/test/scripts/docker-all-scheduler.test.ts index 7d184d85ca55..d1164d766b21 100644 --- a/test/scripts/docker-all-scheduler.test.ts +++ b/test/scripts/docker-all-scheduler.test.ts @@ -616,6 +616,7 @@ describe("scripts/test-docker-all scheduler", () => { copyFileSync("scripts/test-docker-all.mjs", path.join(scriptsDir, "test-docker-all.mjs")); copyFileSync("scripts/test-docker-all.mts", path.join(scriptsDir, "test-docker-all.mts")); copyFileSync("scripts/lib/tsx-cli-shim.mjs", path.join(libDir, "tsx-cli-shim.mjs")); + copyFileSync("scripts/tsx.mjs", path.join(scriptsDir, "tsx.mjs")); copyFileSync( "scripts/prepublish-plugin-registry-artifact.mjs", path.join(scriptsDir, "prepublish-plugin-registry-artifact.mjs"), diff --git a/test/scripts/package-acceptance-workflow.test.ts b/test/scripts/package-acceptance-workflow.test.ts index 16df52af1a70..788c26cfdcbe 100644 --- a/test/scripts/package-acceptance-workflow.test.ts +++ b/test/scripts/package-acceptance-workflow.test.ts @@ -7258,9 +7258,10 @@ describe("package artifact reuse", () => { const postpublishEvidence = workflowStep(releasePublishJob, "Upload postpublish evidence"); expect(packageJson.scripts).toMatchObject({ - "release:verify-beta": "node --import tsx scripts/release-verify-beta.ts", - "release:candidate": "node --import tsx scripts/release-candidate-checklist.mts", - "release:beta": "node --import tsx scripts/release-candidate-checklist.mts", + "release:verify-beta": "node --import ./scripts/tsx.mjs scripts/release-verify-beta.ts", + "release:candidate": + "node --import ./scripts/tsx.mjs scripts/release-candidate-checklist.mts", + "release:beta": "node --import ./scripts/tsx.mjs scripts/release-candidate-checklist.mts", "release:fast-pretag-check": "bash scripts/release-fast-pretag-check.sh", }); expect(workflowStep(releasePublishJob, "Setup Node environment").with).toMatchObject({ diff --git a/test/scripts/pr-operation-lock.test.ts b/test/scripts/pr-operation-lock.test.ts index 9e12cd250f78..358d300c184a 100644 --- a/test/scripts/pr-operation-lock.test.ts +++ b/test/scripts/pr-operation-lock.test.ts @@ -214,6 +214,7 @@ function installPrCliFixture(repoDir: string) { "scripts/lib/direct-run.mjs", "scripts/lib/tsx-cli-shim.mjs", "scripts/lib/local-check-runtime.mts", + "scripts/tsx.mjs", "scripts/pr-lib/worktree.sh", "scripts/pr-lib/operation-lock.sh", "scripts/pr-lib/process-group-runner.mjs", diff --git a/test/scripts/pr-wrappers.test.ts b/test/scripts/pr-wrappers.test.ts index 800d422a6e46..0fa64a89fd75 100644 --- a/test/scripts/pr-wrappers.test.ts +++ b/test/scripts/pr-wrappers.test.ts @@ -91,6 +91,7 @@ function makeMismatchedWrapperRepo() { "scripts/lib/local-check-runtime.mts", join(canonical, "scripts", "lib", "local-check-runtime.mts"), ); + cpSync("scripts/tsx.mjs", join(canonical, "scripts", "tsx.mjs")); writeFileSync( join(canonical, "scripts", "lib", "plain-gh.sh"), "resolve_plain_gh_bin() { printf '/usr/bin/true\\n'; }\ngh_plain() { :; }\n", @@ -205,6 +206,7 @@ describe("scripts/pr wrappers", () => { expect(script).toContain("scripts/verify-pr-hosted-gates.mjs"); expect(script).toContain("scripts/verify-pr-hosted-gates.mts"); expect(script).toContain("scripts/lib/tsx-cli-shim.mjs"); + expect(script).toContain("scripts/tsx.mjs"); expect(script).toContain("scripts/lib/plain-gh.mjs"); expect(script).toContain("scripts/lib/direct-run.mjs"); expect(script).toContain("scripts/pr review-init "); @@ -443,6 +445,7 @@ describe("scripts/pr wrappers", () => { writeFileSync(join(repo, "scripts", "lib", "direct-run.mjs"), "// canonical\n"); writeFileSync(join(repo, "scripts", "lib", "tsx-cli-shim.mjs"), "// canonical\n"); writeFileSync(join(repo, "scripts", "lib", "local-check-runtime.mts"), "// canonical\n"); + writeFileSync(join(repo, "scripts", "tsx.mjs"), "// canonical\n"); writeFileSync(join(repo, "scripts", "watch-pr-ci.mjs"), "// canonical\n"); writeFileSync(join(repo, "scripts", "watch-pr-ci.mts"), "// canonical\n"); writeFileSync(join(repo, "scripts", "verify-pr-hosted-gates.mjs"), "// canonical\n"); @@ -477,6 +480,17 @@ describe("scripts/pr wrappers", () => { expect(git(linked, ["restore", component]).status).toBe(0); } + writeFileSync(join(linked, "scripts", "tsx.mjs"), "// dirty preloader\n"); + const dirtyPreloaderResult = spawnSync(join(linked, "scripts", "pr"), ["ls"], { + cwd: linked, + encoding: "utf8", + }); + expect(dirtyPreloaderResult.status).toBe(1); + expect(dirtyPreloaderResult.stderr).toContain( + "scripts/pr wrapper files have uncommitted changes", + ); + expect(git(linked, ["restore", "scripts/tsx.mjs"]).status).toBe(0); + // A dirty canonical checkout no longer blocks a linked worktree whose // committed wrapper matches the origin/main trust anchor; without that // anchor it must still refuse. @@ -520,6 +534,7 @@ describe("scripts/pr wrappers", () => { writeFileSync(join(repo, "scripts", "lib", "direct-run.mjs"), "// canonical\n"); writeFileSync(join(repo, "scripts", "lib", "tsx-cli-shim.mjs"), "// canonical\n"); writeFileSync(join(repo, "scripts", "lib", "local-check-runtime.mts"), "// canonical\n"); + writeFileSync(join(repo, "scripts", "tsx.mjs"), "// canonical\n"); writeFileSync(join(repo, "scripts", "watch-pr-ci.mjs"), "// canonical\n"); writeFileSync(join(repo, "scripts", "watch-pr-ci.mts"), "// canonical\n"); writeFileSync(join(repo, "scripts", "verify-pr-hosted-gates.mjs"), "// canonical\n"); diff --git a/test/scripts/release-preflight.test.ts b/test/scripts/release-preflight.test.ts index 37015d654669..b2ec29896166 100644 --- a/test/scripts/release-preflight.test.ts +++ b/test/scripts/release-preflight.test.ts @@ -129,6 +129,7 @@ function makeIsolatedPreflightFixture(params: Parameters { ); }); - it("uses bounded parallelism for independent checks", () => { + it.each([1, 2, 3])("uses bounded parallelism for independent checks with jobs=%i", (jobs) => { const fakePnpm = makeFakePnpm(); const root = makeReleaseFixture(); - const env = { OPENCLAW_RELEASE_PREFLIGHT_DELAY_MS: "120" }; + const commands = [ + "pnpm config:schema:check", + "pnpm config:channels:check", + "pnpm config:docs:check", + ]; + const observerPath = join(root, "observe-concurrency.cjs"); + const resultPath = join(root, "concurrency.json"); + writeFileSync( + observerPath, + `const childProcess = require("node:child_process"); +const { writeFileSync } = require("node:fs"); +const { syncBuiltinESMExports } = require("node:module"); +const commands = ${JSON.stringify(commands)}; +const originalSpawn = childProcess.spawn; +let active = 0; +let maxActive = 0; +let total = 0; +childProcess.spawn = function (...args) { + const child = originalSpawn.apply(this, args); + if (commands.includes([args[0], ...(args[1] ?? [])].join(" "))) { + active += 1; + total += 1; + maxActive = Math.max(maxActive, active); + child.once("close", () => { active -= 1; }); + } + return child; +}; +syncBuiltinESMExports(); +process.once("exit", () => { + if (total > 0) { + writeFileSync(${JSON.stringify(resultPath)}, JSON.stringify({ maxActive, total, active })); + } +}); +`, + ); - const serialStartedAt = performance.now(); - const serial = runPreflight(["--scope", "config", "--jobs", "1"], fakePnpm, env, root); - const serialMs = performance.now() - serialStartedAt; + // Count real managed commands from synchronous admission through close, excluding + // loader startup time. Inherited observers with no matching commands write nothing. + const result = runPreflight( + ["--scope", "config", "--jobs", String(jobs)], + fakePnpm, + { + NODE_OPTIONS: `${process.env.NODE_OPTIONS ?? ""} --require ${JSON.stringify(observerPath)}`, + }, + root, + ); - const parallelStartedAt = performance.now(); - const parallel = runPreflight(["--scope", "config", "--jobs", "3"], fakePnpm, env, root); - const parallelMs = performance.now() - parallelStartedAt; - - expect(serial.status).toBe(0); - expect(parallel.status).toBe(0); - expect(parallelMs).toBeLessThan(serialMs * 0.75); + expect(result.status, result.stderr).toBe(0); + expect(JSON.parse(readFileSync(resultPath, "utf8"))).toEqual({ + maxActive: Math.min(jobs, commands.length), + total: commands.length, + active: 0, + }); + expect(readPnpmLog(fakePnpm.logPath).toSorted()).toEqual(commands.toSorted()); }); it("accepts base macOS metadata for a beta package version", () => { diff --git a/test/scripts/run-oxlint.test.ts b/test/scripts/run-oxlint.test.ts index 7a8cc56e4330..60e42e0dd94b 100644 --- a/test/scripts/run-oxlint.test.ts +++ b/test/scripts/run-oxlint.test.ts @@ -239,13 +239,13 @@ describe("run-oxlint", () => { }; const shardedLintRunner = readFileSync("scripts/run-oxlint-shards.mts", "utf8"); - expect(packageJson.scripts.check).toBe("node --import tsx scripts/check.mts"); - expect(packageJson.scripts.lint).toBe("node --import tsx scripts/run-lint.mts"); + expect(packageJson.scripts.check).toBe("node --import ./scripts/tsx.mjs scripts/check.mts"); + expect(packageJson.scripts.lint).toBe("node --import ./scripts/tsx.mjs scripts/run-lint.mts"); expect(packageJson.scripts["lint:core"]).toBe( - "node --import tsx scripts/run-oxlint-shards.mts --only=core", + "node --import ./scripts/tsx.mjs scripts/run-oxlint-shards.mts --only=core", ); expect(packageJson.scripts.check).not.toContain( - "node --import tsx scripts/prepare-extension-package-boundary-artifacts.mts", + "node --import ./scripts/tsx.mjs scripts/prepare-extension-package-boundary-artifacts.mts", ); expect(shardedLintRunner).toContain("prepare-extension-package-boundary-artifacts.mts"); expect(shardedLintRunner).toContain('OPENCLAW_OXLINT_SKIP_PREPARE: "1"'); @@ -257,7 +257,7 @@ describe("run-oxlint", () => { }; const lintRunner = readFileSync("scripts/run-lint.mts", "utf8"); - expect(packageJson.scripts.lint).toBe("node --import tsx scripts/run-lint.mts"); + expect(packageJson.scripts.lint).toBe("node --import ./scripts/tsx.mjs scripts/run-lint.mts"); expect(lintRunner.indexOf("ensureRepoToolNodeModulesLink(")).toBeGreaterThan(-1); expect( lintRunner.indexOf('path.resolve("scripts", "control-ui-i18n-verify.ts")'), diff --git a/test/scripts/test-projects.test.ts b/test/scripts/test-projects.test.ts index 2d9a2076d6f6..46caef269104 100644 --- a/test/scripts/test-projects.test.ts +++ b/test/scripts/test-projects.test.ts @@ -232,12 +232,12 @@ describe("scripts/test-projects changed-target routing", () => { } }); - it("routes shared TypeScript CLI shim changes through wrapper tests", () => { - expectChangedTargets( - ["scripts/lib/tsx-cli-shim.mjs"], - ["test/scripts/direct-run-entrypoints.test.ts"], - ); - }); + it.each(["scripts/lib/tsx-cli-shim.mjs", "scripts/tsx.mjs"])( + "routes shared TypeScript tooling changes through wrapper tests for %s", + (scriptPath) => { + expectChangedTargets([scriptPath], ["test/scripts/direct-run-entrypoints.test.ts"]); + }, + ); it("routes Docker pull retry helper changes through its regression test", () => { expectChangedTargets(