mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 10:40:43 +00:00
fix(e2e): escape Windows stale update import regex (#75315) thanks @steipete
Fix the Windows stale-import guard regex used by the Parallels npm update smoke path, with related maintainer-flow and regression-test cleanup preserved on a verified branch.
This commit is contained in:
@@ -61,6 +61,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Plugins/Codex: allow the official npm Codex plugin to install without the unsafe-install override, keep `/codex` command ownership, and cover the real npm Docker live path through managed `.openclaw/npm` dependencies plus uninstall failure proof.
|
||||
- Gateway/status: add concrete service, config, listener-owner, and log collection next steps when gateway probes fail and Bonjour finds no local gateway, so frozen or port-conflict reports include the data needed for root-cause triage. Refs #49012. Thanks @vincentkoc.
|
||||
- Codex harness: forward OpenClaw workspace bootstrap files such as `SOUL.md` through native Codex config instructions while leaving `AGENTS.md` to Codex project-doc discovery. Fixes #76273. Thanks @zknicker.
|
||||
- Parallels/Windows update smoke: escape the stale post-swap import regex in the generated PowerShell script so expected `ERR_MODULE_NOT_FOUND` update handoffs continue to post-update health checks. (#75315)
|
||||
|
||||
## 2026.5.2
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ export interface NpmUpdateScriptInput {
|
||||
updateTarget: string;
|
||||
}
|
||||
|
||||
const windowsStalePostSwapImportRegex = String.raw`node_modules\\openclaw\\dist\\[^\\]+-[A-Za-z0-9_-]+\.js`;
|
||||
|
||||
function posixModelProviderConfigCommands(
|
||||
command: string,
|
||||
modelId: string,
|
||||
@@ -180,7 +182,7 @@ $updateExit = $LASTEXITCODE
|
||||
$updateOutput
|
||||
if ($updateExit -ne 0) {
|
||||
$updateText = $updateOutput | Out-String
|
||||
$stalePostSwapImport = $updateText -match 'ERR_MODULE_NOT_FOUND' -and $updateText -match 'node_modules\\openclaw\\dist\\[^\\]+-[A-Za-z0-9_-]+\\.js'
|
||||
$stalePostSwapImport = $updateText -match 'ERR_MODULE_NOT_FOUND' -and $updateText -match ${psSingleQuote(windowsStalePostSwapImportRegex)}
|
||||
if (-not $stalePostSwapImport) { throw "openclaw update failed with exit code $updateExit" }
|
||||
Write-Host "openclaw update returned a stale post-swap module import; continuing to post-update health checks"
|
||||
}
|
||||
|
||||
@@ -159,6 +159,20 @@ validate_changelog_attribution_policy() {
|
||||
node scripts/check-changelog-attributions.mjs CHANGELOG.md
|
||||
}
|
||||
|
||||
changelog_thanks_required_for_contributor() {
|
||||
local contrib="${1:-}"
|
||||
local normalized
|
||||
normalized=$(printf '%s' "$contrib" | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
case "$normalized" in
|
||||
""|"null"|"app/"*|"codex"|"openclaw"|"clawsweeper"|"openclaw-clawsweeper"|"clawsweeper[bot]"|"openclaw-clawsweeper[bot]"|"steipete")
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
validate_changelog_entry_for_pr() {
|
||||
local pr="$1"
|
||||
local contrib="$2"
|
||||
@@ -314,7 +328,7 @@ END {
|
||||
rm -f "$diff_file"
|
||||
echo "changelog placement validated: PR-linked entries are appended at section tail"
|
||||
|
||||
if [ -n "$contrib" ] && [ "$contrib" != "null" ]; then
|
||||
if changelog_thanks_required_for_contributor "$contrib"; then
|
||||
local with_pr_and_thanks
|
||||
with_pr_and_thanks=$(printf '%s\n' "$added_lines" | rg -in "$pr_pattern" | rg -i "thanks @$contrib" || true)
|
||||
if [ -z "$with_pr_and_thanks" ]; then
|
||||
@@ -325,7 +339,7 @@ END {
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "changelog validated: found PR #$pr (contributor handle unavailable, skipping thanks check)"
|
||||
echo "changelog validated: found PR #$pr (no eligible human contributor handle, skipping thanks check)"
|
||||
}
|
||||
|
||||
validate_changelog_merge_hygiene() {
|
||||
|
||||
@@ -191,6 +191,32 @@ merge_author_email_candidates() {
|
||||
"${reviewer}@users.noreply.github.com" | awk 'NF && !seen[$0]++'
|
||||
}
|
||||
|
||||
pr_contributor_allows_human_trailers() {
|
||||
local contrib="${1:-}"
|
||||
local normalized
|
||||
normalized=$(printf '%s' "$contrib" | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
case "$normalized" in
|
||||
""|"null"|"app/"*|"codex"|"openclaw"|"clawsweeper"|"openclaw-clawsweeper"|"clawsweeper[bot]"|"openclaw-clawsweeper[bot]"|"steipete")
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
resolve_contributor_coauthor_email() {
|
||||
local contrib="${1:-}"
|
||||
|
||||
if ! pr_contributor_allows_human_trailers "$contrib"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
local contrib_id
|
||||
contrib_id=$(gh api "users/$contrib" --jq .id) || return 1
|
||||
printf '%s+%s@users.noreply.github.com\n' "$contrib_id" "$contrib"
|
||||
}
|
||||
|
||||
common_repo_root() {
|
||||
if command -v repo_root >/dev/null 2>&1; then
|
||||
repo_root
|
||||
|
||||
@@ -199,9 +199,11 @@ merge_run() {
|
||||
|
||||
local contrib_coauthor_email="${COAUTHOR_EMAIL:-}"
|
||||
if [ -z "$contrib_coauthor_email" ] || [ "$contrib_coauthor_email" = "null" ]; then
|
||||
local contrib_id
|
||||
contrib_id=$(gh api "users/$contrib" --jq .id)
|
||||
contrib_coauthor_email="${contrib_id}+${contrib}@users.noreply.github.com"
|
||||
if contrib_coauthor_email=$(resolve_contributor_coauthor_email "$contrib"); then
|
||||
:
|
||||
else
|
||||
contrib_coauthor_email=""
|
||||
fi
|
||||
fi
|
||||
|
||||
local reviewer_email_candidates=()
|
||||
@@ -218,14 +220,16 @@ merge_run() {
|
||||
local reviewer_email="${reviewer_email_candidates[0]}"
|
||||
local reviewer_coauthor_email="${reviewer_id}+${reviewer}@users.noreply.github.com"
|
||||
|
||||
cat > .local/merge-body.txt <<EOF_BODY
|
||||
Merged via squash.
|
||||
|
||||
Prepared head SHA: $PREP_HEAD_SHA
|
||||
Co-authored-by: $contrib <$contrib_coauthor_email>
|
||||
Co-authored-by: $reviewer <$reviewer_coauthor_email>
|
||||
Reviewed-by: @$reviewer
|
||||
EOF_BODY
|
||||
{
|
||||
echo "Merged via squash."
|
||||
echo
|
||||
echo "Prepared head SHA: $PREP_HEAD_SHA"
|
||||
if [ -n "$contrib_coauthor_email" ]; then
|
||||
echo "Co-authored-by: $contrib <$contrib_coauthor_email>"
|
||||
fi
|
||||
echo "Co-authored-by: $reviewer <$reviewer_coauthor_email>"
|
||||
echo "Reviewed-by: @$reviewer"
|
||||
} > .local/merge-body.txt
|
||||
|
||||
delete_remote_pr_head_branch_after_merge() {
|
||||
local head_json
|
||||
@@ -347,22 +351,29 @@ EOF_BODY
|
||||
|
||||
local commit_body
|
||||
commit_body=$(gh api repos/:owner/:repo/commits/"$merge_sha" --jq .commit.message)
|
||||
printf '%s\n' "$commit_body" | rg -q "^Co-authored-by: $contrib <" || { echo "Missing PR author co-author trailer"; exit 1; }
|
||||
if [ -n "$contrib_coauthor_email" ]; then
|
||||
printf '%s\n' "$commit_body" | rg -q "^Co-authored-by: $contrib <" || { echo "Missing PR author co-author trailer"; exit 1; }
|
||||
else
|
||||
echo "Skipping PR author co-author trailer check for bot/app author $contrib."
|
||||
fi
|
||||
printf '%s\n' "$commit_body" | rg -q "^Co-authored-by: $reviewer <" || { echo "Missing reviewer co-author trailer"; exit 1; }
|
||||
|
||||
local ok=0
|
||||
local comment_output=""
|
||||
local attempt
|
||||
for attempt in 1 2 3; do
|
||||
if comment_output=$(gh pr comment "$pr" -F - 2>&1 <<EOF_COMMENT
|
||||
Merged via squash.
|
||||
|
||||
- Prepared head SHA: [$PREP_HEAD_SHA]($prep_sha_url)
|
||||
- Merge commit: [$merge_sha]($merge_sha_url)
|
||||
|
||||
Thanks @$contrib!
|
||||
EOF_COMMENT
|
||||
); then
|
||||
if comment_output=$(
|
||||
{
|
||||
echo "Merged via squash."
|
||||
echo
|
||||
echo "- Prepared head SHA: [$PREP_HEAD_SHA]($prep_sha_url)"
|
||||
echo "- Merge commit: [$merge_sha]($merge_sha_url)"
|
||||
if pr_contributor_allows_human_trailers "$contrib"; then
|
||||
echo
|
||||
echo "Thanks @$contrib!"
|
||||
fi
|
||||
} | gh pr comment "$pr" -F - 2>&1
|
||||
); then
|
||||
ok=1
|
||||
break
|
||||
fi
|
||||
|
||||
@@ -163,9 +163,12 @@ prepare_push() {
|
||||
if [ -z "$contrib" ]; then
|
||||
contrib=$(gh pr view "$pr" --json author --jq .author.login)
|
||||
fi
|
||||
local contrib_id
|
||||
contrib_id=$(gh api "users/$contrib" --jq .id)
|
||||
local coauthor_email="${contrib_id}+${contrib}@users.noreply.github.com"
|
||||
local coauthor_email=""
|
||||
if coauthor_email=$(resolve_contributor_coauthor_email "$contrib"); then
|
||||
:
|
||||
else
|
||||
coauthor_email=""
|
||||
fi
|
||||
|
||||
cat >> .local/prep.md <<EOF_PREP
|
||||
- Gates passed and push succeeded to branch $PR_HEAD.
|
||||
@@ -237,9 +240,12 @@ prepare_sync_head() {
|
||||
if [ -z "$contrib" ]; then
|
||||
contrib=$(gh pr view "$pr" --json author --jq .author.login)
|
||||
fi
|
||||
local contrib_id
|
||||
contrib_id=$(gh api "users/$contrib" --jq .id)
|
||||
local coauthor_email="${contrib_id}+${contrib}@users.noreply.github.com"
|
||||
local coauthor_email=""
|
||||
if coauthor_email=$(resolve_contributor_coauthor_email "$contrib"); then
|
||||
:
|
||||
else
|
||||
coauthor_email=""
|
||||
fi
|
||||
|
||||
cat >> .local/prep.md <<EOF_PREP
|
||||
- Prep head sync completed to branch $PR_HEAD.
|
||||
|
||||
@@ -112,14 +112,20 @@ fi
|
||||
# Default scan paths match CI. Override by passing `-- <paths...>`.
|
||||
if (( PATHS_PASSED == 0 )); then
|
||||
if (( CHANGED_ONLY )); then
|
||||
mapfile -t SCAN_PATHS < <(
|
||||
SCAN_PATHS=()
|
||||
while IFS= read -r path; do
|
||||
SCAN_PATHS+=( "$path" )
|
||||
done < <(
|
||||
{
|
||||
git diff --name-only --diff-filter=ACMRTUXB "${OPENCLAW_OPENGREP_BASE_REF:-origin/main...HEAD}" 2>/dev/null || true
|
||||
git diff --name-only --diff-filter=ACMRTUXB -- 2>/dev/null || true
|
||||
git ls-files --others --exclude-standard
|
||||
} | awk '/^(src|extensions|apps|packages|scripts)\// { print }' | sort -u
|
||||
)
|
||||
mapfile -t RULEPACK_CHANGED_PATHS < <(
|
||||
RULEPACK_CHANGED_PATHS=()
|
||||
while IFS= read -r path; do
|
||||
RULEPACK_CHANGED_PATHS+=( "$path" )
|
||||
done < <(
|
||||
{
|
||||
git diff --name-only --diff-filter=ACMRTUXB "${OPENCLAW_OPENGREP_BASE_REF:-origin/main...HEAD}" 2>/dev/null || true
|
||||
git diff --name-only --diff-filter=ACMRTUXB -- 2>/dev/null || true
|
||||
@@ -148,9 +154,11 @@ fi
|
||||
|
||||
echo "→ Running opengrep ($BUCKET) against $(IFS=' '; echo "${SCAN_PATHS[*]:-overridden}")" >&2
|
||||
echo " Using exclusions from .semgrepignore" >&2
|
||||
exec opengrep scan \
|
||||
--no-strict \
|
||||
--config "$CONFIG" \
|
||||
--no-git-ignore \
|
||||
"${EXTRA_ARGS[@]}" \
|
||||
"${SCAN_PATHS[@]}"
|
||||
OPENGREP_ARGS=( scan --no-strict --config "$CONFIG" --no-git-ignore )
|
||||
if (( ${#EXTRA_ARGS[@]} > 0 )); then
|
||||
OPENGREP_ARGS+=( "${EXTRA_ARGS[@]}" )
|
||||
fi
|
||||
if (( ${#SCAN_PATHS[@]} > 0 )); then
|
||||
OPENGREP_ARGS+=( "${SCAN_PATHS[@]}" )
|
||||
fi
|
||||
exec opengrep "${OPENGREP_ARGS[@]}"
|
||||
|
||||
@@ -8,31 +8,39 @@ let webMedia: typeof import("../../media/web-media.js");
|
||||
let createImageGenerateTool: typeof import("./image-generate-tool.js").createImageGenerateTool;
|
||||
let resolveImageGenerationModelConfigForTool: typeof import("./image-generate-tool.js").resolveImageGenerationModelConfigForTool;
|
||||
|
||||
const IMAGE_GENERATION_PROVIDER_AUTH_ENV_VARS = [
|
||||
"OPENAI_API_KEY",
|
||||
"OPENAI_API_KEYS",
|
||||
const GENERATION_PROVIDER_ENV_VARS = [
|
||||
"BYTEPLUS_API_KEY",
|
||||
"COMFY_API_KEY",
|
||||
"COMFY_CLOUD_API_KEY",
|
||||
"DASHSCOPE_API_KEY",
|
||||
"DEEPINFRA_API_KEY",
|
||||
"FAL_API_KEY",
|
||||
"FAL_KEY",
|
||||
"GCLOUD_PROJECT",
|
||||
"GEMINI_API_KEY",
|
||||
"GEMINI_API_KEYS",
|
||||
"GOOGLE_API_KEY",
|
||||
"GOOGLE_API_KEYS",
|
||||
"DEEPINFRA_API_KEY",
|
||||
"FAL_KEY",
|
||||
"FAL_API_KEY",
|
||||
"GOOGLE_APPLICATION_CREDENTIALS",
|
||||
"GOOGLE_CLOUD_API_KEY",
|
||||
"GOOGLE_CLOUD_LOCATION",
|
||||
"GOOGLE_CLOUD_PROJECT",
|
||||
"LITELLM_API_KEY",
|
||||
"MINIMAX_API_KEY",
|
||||
"MINIMAX_CODE_PLAN_KEY",
|
||||
"MINIMAX_CODING_API_KEY",
|
||||
"MINIMAX_API_KEY",
|
||||
"MINIMAX_OAUTH_TOKEN",
|
||||
"MODELSTUDIO_API_KEY",
|
||||
"OPENAI_API_KEY",
|
||||
"OPENAI_API_KEYS",
|
||||
"OPENROUTER_API_KEY",
|
||||
"XAI_API_KEY",
|
||||
"QWEN_API_KEY",
|
||||
"RUNWAY_API_KEY",
|
||||
"RUNWAYML_API_SECRET",
|
||||
"TOGETHER_API_KEY",
|
||||
"VYDRA_API_KEY",
|
||||
] as const;
|
||||
|
||||
function clearImageGenerationProviderAuthEnv() {
|
||||
for (const key of IMAGE_GENERATION_PROVIDER_AUTH_ENV_VARS) {
|
||||
vi.stubEnv(key, "");
|
||||
}
|
||||
}
|
||||
"XAI_API_KEY",
|
||||
];
|
||||
|
||||
function hasStubbedImageProviderAuth(providerId: string): boolean {
|
||||
if (providerId === "openai") {
|
||||
@@ -243,7 +251,9 @@ describe("createImageGenerateTool", () => {
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
clearImageGenerationProviderAuthEnv();
|
||||
for (const envVar of GENERATION_PROVIDER_ENV_VARS) {
|
||||
vi.stubEnv(envVar, "");
|
||||
}
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
@@ -52,6 +52,40 @@ const VIDEO_GENERATION_PROVIDER_AUTH_ENV_VARS = [
|
||||
vi.mock("../../tasks/runtime-internal.js", () => taskRuntimeInternalMocks);
|
||||
vi.mock("../../tasks/detached-task-runtime.js", () => taskExecutorMocks);
|
||||
|
||||
const GENERATION_PROVIDER_ENV_VARS = [
|
||||
"BYTEPLUS_API_KEY",
|
||||
"COMFY_API_KEY",
|
||||
"COMFY_CLOUD_API_KEY",
|
||||
"DASHSCOPE_API_KEY",
|
||||
"DEEPINFRA_API_KEY",
|
||||
"FAL_API_KEY",
|
||||
"FAL_KEY",
|
||||
"GCLOUD_PROJECT",
|
||||
"GEMINI_API_KEY",
|
||||
"GEMINI_API_KEYS",
|
||||
"GOOGLE_API_KEY",
|
||||
"GOOGLE_API_KEYS",
|
||||
"GOOGLE_APPLICATION_CREDENTIALS",
|
||||
"GOOGLE_CLOUD_API_KEY",
|
||||
"GOOGLE_CLOUD_LOCATION",
|
||||
"GOOGLE_CLOUD_PROJECT",
|
||||
"LITELLM_API_KEY",
|
||||
"MINIMAX_API_KEY",
|
||||
"MINIMAX_CODE_PLAN_KEY",
|
||||
"MINIMAX_CODING_API_KEY",
|
||||
"MINIMAX_OAUTH_TOKEN",
|
||||
"MODELSTUDIO_API_KEY",
|
||||
"OPENAI_API_KEY",
|
||||
"OPENAI_API_KEYS",
|
||||
"OPENROUTER_API_KEY",
|
||||
"QWEN_API_KEY",
|
||||
"RUNWAY_API_KEY",
|
||||
"RUNWAYML_API_SECRET",
|
||||
"TOGETHER_API_KEY",
|
||||
"VYDRA_API_KEY",
|
||||
"XAI_API_KEY",
|
||||
];
|
||||
|
||||
function asConfig(value: unknown): OpenClawConfig {
|
||||
return value as OpenClawConfig;
|
||||
}
|
||||
@@ -118,7 +152,12 @@ function resetVideoGenerateMocks() {
|
||||
}
|
||||
|
||||
describe("createVideoGenerateTool", () => {
|
||||
beforeEach(resetVideoGenerateMocks);
|
||||
beforeEach(() => {
|
||||
resetVideoGenerateMocks();
|
||||
for (const envVar of GENERATION_PROVIDER_ENV_VARS) {
|
||||
vi.stubEnv(envVar, "");
|
||||
}
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllEnvs();
|
||||
|
||||
@@ -28,10 +28,21 @@ describe("check-changelog-attributions", () => {
|
||||
});
|
||||
|
||||
it("keeps PR changelog gates on the same attribution policy", () => {
|
||||
const commonLib = readFileSync("scripts/pr-lib/common.sh", "utf8");
|
||||
const changelogLib = readFileSync("scripts/pr-lib/changelog.sh", "utf8");
|
||||
const gates = readFileSync("scripts/pr-lib/gates.sh", "utf8");
|
||||
const mergeLib = readFileSync("scripts/pr-lib/merge.sh", "utf8");
|
||||
const prepareCore = readFileSync("scripts/pr-lib/prepare-core.sh", "utf8");
|
||||
|
||||
expect(commonLib).toContain("pr_contributor_allows_human_trailers");
|
||||
expect(commonLib).toContain("resolve_contributor_coauthor_email");
|
||||
expect(changelogLib).toContain("node scripts/check-changelog-attributions.mjs CHANGELOG.md");
|
||||
expect(changelogLib).toContain("changelog_thanks_required_for_contributor");
|
||||
expect(changelogLib).toContain('"app/"*');
|
||||
expect(changelogLib).toContain('"clawsweeper"');
|
||||
expect(gates).toContain("validate_changelog_attribution_policy");
|
||||
expect(prepareCore).toContain("resolve_contributor_coauthor_email");
|
||||
expect(mergeLib).toContain("pr_contributor_allows_human_trailers");
|
||||
expect(mergeLib).toContain("Skipping PR author co-author trailer check for bot/app author");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { windowsUpdateScript } from "../../scripts/e2e/parallels/npm-update-scripts.ts";
|
||||
|
||||
const SCRIPT_PATH = "scripts/e2e/parallels/npm-update-smoke.ts";
|
||||
const UPDATE_SCRIPTS_PATH = "scripts/e2e/parallels/npm-update-scripts.ts";
|
||||
const TEST_AUTH = {
|
||||
authChoice: "openai",
|
||||
authKeyFlag: "--openai-api-key",
|
||||
apiKeyEnv: "OPENAI_API_KEY",
|
||||
apiKeyValue: "test-key",
|
||||
modelId: "gpt-5.4",
|
||||
};
|
||||
|
||||
describe("parallels npm update smoke", () => {
|
||||
it("does not leave guard/server children attached to the wrapper", () => {
|
||||
@@ -49,4 +57,33 @@ describe("parallels npm update smoke", () => {
|
||||
);
|
||||
expect(script).toContain("OPENCLAW_DISABLE_BUNDLED_PLUGINS=1 openclaw gateway stop");
|
||||
});
|
||||
|
||||
it("generates a .NET-safe Windows stale import regex in the update-failure guard", () => {
|
||||
const script = windowsUpdateScript({
|
||||
auth: TEST_AUTH,
|
||||
expectedNeedle: "2026.4.30",
|
||||
updateTarget: "latest",
|
||||
});
|
||||
const staleImportLine = script.match(/\$stalePostSwapImport = [^\n]+/)?.[0];
|
||||
const staleImportMatch = script.match(/\$updateText -match '(node_modules[^']+)'/);
|
||||
const staleImportPattern = staleImportMatch?.[1];
|
||||
|
||||
if (!staleImportLine) {
|
||||
throw new Error("missing generated Windows stale import guard");
|
||||
}
|
||||
if (!staleImportPattern) {
|
||||
throw new Error("missing generated Windows stale import regex");
|
||||
}
|
||||
expect(staleImportLine).toContain("$updateText -match 'ERR_MODULE_NOT_FOUND'");
|
||||
expect(staleImportLine).toContain(`$updateText -match '${staleImportPattern}'`);
|
||||
expect(staleImportPattern).toBe(
|
||||
String.raw`node_modules\\openclaw\\dist\\[^\\]+-[A-Za-z0-9_-]+\.js`,
|
||||
);
|
||||
expect(staleImportPattern).not.toContain("node_modules\\openclaw\\dist\\");
|
||||
expect(staleImportPattern.match(/\\\\/g)).toHaveLength(4);
|
||||
const representativeUpdateFailure = String.raw`Error [ERR_MODULE_NOT_FOUND]: Cannot find module 'C:\Users\runner\AppData\Roaming\npm\node_modules\openclaw\dist\main-a1_B2.js' imported from C:\Users\runner\AppData\Roaming\npm\node_modules\openclaw\dist\cli.js`;
|
||||
const generatedRegex = new RegExp(staleImportPattern);
|
||||
expect(generatedRegex.test(representativeUpdateFailure)).toBe(true);
|
||||
expect(generatedRegex.test(String.raw`node_modules\openclaw\dist\main.js`)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user