mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 09:50:42 +00:00
fix: centralize source reply delivery mode
This commit is contained in:
@@ -250,17 +250,31 @@ const TOOLING_SOURCE_TEST_TARGETS = new Map([
|
||||
["scripts/test-projects.mjs", ["test/scripts/test-projects.test.ts"]],
|
||||
["scripts/test-projects.test-support.d.mts", ["test/scripts/test-projects.test.ts"]],
|
||||
["scripts/test-projects.test-support.mjs", ["test/scripts/test-projects.test.ts"]],
|
||||
["scripts/testbox-sync-sanity.mjs", ["test/scripts/testbox-sync-sanity.test.ts"]],
|
||||
]);
|
||||
const TOOLING_TEST_TARGETS = new Map([
|
||||
["test/scripts/barnacle-auto-response.test.ts", ["test/scripts/barnacle-auto-response.test.ts"]],
|
||||
["test/scripts/changed-lanes.test.ts", ["test/scripts/changed-lanes.test.ts"]],
|
||||
["test/scripts/live-docker-stage.test.ts", ["test/scripts/live-docker-stage.test.ts"]],
|
||||
["test/scripts/test-projects.test.ts", ["test/scripts/test-projects.test.ts"]],
|
||||
["test/scripts/testbox-sync-sanity.test.ts", ["test/scripts/testbox-sync-sanity.test.ts"]],
|
||||
[
|
||||
"test/scripts/vitest-local-scheduling.test.ts",
|
||||
["test/scripts/vitest-local-scheduling.test.ts"],
|
||||
],
|
||||
]);
|
||||
const GROUP_VISIBLE_REPLY_TEST_TARGETS = [
|
||||
"src/auto-reply/reply/dispatch-acp.test.ts",
|
||||
"src/auto-reply/reply/dispatch-from-config.test.ts",
|
||||
"src/auto-reply/reply/followup-runner.test.ts",
|
||||
"src/auto-reply/reply/groups.test.ts",
|
||||
"extensions/discord/src/monitor/message-handler.process.test.ts",
|
||||
"extensions/slack/src/monitor.tool-result.test.ts",
|
||||
];
|
||||
const GROUP_VISIBLE_REPLY_PROMPT_TEST_TARGETS = [
|
||||
"src/agents/system-prompt.test.ts",
|
||||
...GROUP_VISIBLE_REPLY_TEST_TARGETS,
|
||||
];
|
||||
const SOURCE_TEST_TARGETS = new Map([
|
||||
...PRECISE_SOURCE_TEST_TARGETS,
|
||||
[
|
||||
@@ -271,6 +285,11 @@ const SOURCE_TEST_TARGETS = new Map([
|
||||
"extensions/telegram/src/directory-contract.test.ts",
|
||||
],
|
||||
],
|
||||
[
|
||||
"src/plugin-sdk/channel-reply-pipeline.ts",
|
||||
["src/plugins/contracts/plugin-sdk-subpaths.test.ts", ...GROUP_VISIBLE_REPLY_TEST_TARGETS],
|
||||
],
|
||||
["src/plugin-sdk/reply-runtime.ts", ["src/plugins/contracts/plugin-sdk-subpaths.test.ts"]],
|
||||
[
|
||||
"test/helpers/channels/directory-ids.ts",
|
||||
[
|
||||
@@ -306,10 +325,8 @@ const SOURCE_TEST_TARGETS = new Map([
|
||||
"extensions/telegram/src/directory-contract.test.ts",
|
||||
],
|
||||
],
|
||||
[
|
||||
"src/auto-reply/reply/dispatch-from-config.ts",
|
||||
["src/auto-reply/reply/dispatch-from-config.test.ts"],
|
||||
],
|
||||
["src/auto-reply/reply/dispatch-from-config.ts", GROUP_VISIBLE_REPLY_TEST_TARGETS],
|
||||
["src/auto-reply/reply/source-reply-delivery-mode.ts", GROUP_VISIBLE_REPLY_TEST_TARGETS],
|
||||
[
|
||||
"src/auto-reply/reply/effective-reply-route.ts",
|
||||
[
|
||||
@@ -317,6 +334,12 @@ const SOURCE_TEST_TARGETS = new Map([
|
||||
"src/auto-reply/reply/dispatch-from-config.test.ts",
|
||||
],
|
||||
],
|
||||
["src/auto-reply/reply/get-reply-run.ts", ["src/auto-reply/reply/followup-runner.test.ts"]],
|
||||
["src/auto-reply/reply/groups.ts", GROUP_VISIBLE_REPLY_TEST_TARGETS],
|
||||
["src/auto-reply/get-reply-options.types.ts", GROUP_VISIBLE_REPLY_TEST_TARGETS],
|
||||
["src/agents/system-prompt.ts", GROUP_VISIBLE_REPLY_PROMPT_TEST_TARGETS],
|
||||
["src/config/types.messages.ts", GROUP_VISIBLE_REPLY_TEST_TARGETS],
|
||||
["src/config/zod-schema.core.ts", GROUP_VISIBLE_REPLY_TEST_TARGETS],
|
||||
["src/auto-reply/reply/commands-acp.ts", ["src/auto-reply/reply/commands-acp.test.ts"]],
|
||||
[
|
||||
"src/auto-reply/reply/dispatch-acp-command-bypass.ts",
|
||||
|
||||
110
scripts/testbox-sync-sanity.mjs
Normal file
110
scripts/testbox-sync-sanity.mjs
Normal file
@@ -0,0 +1,110 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { execFileSync } from "node:child_process";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
|
||||
const DEFAULT_DELETION_THRESHOLD = 200;
|
||||
const REQUIRED_ROOT_FILES = ["package.json", "pnpm-lock.yaml", ".gitignore"];
|
||||
|
||||
function parseBooleanEnv(value) {
|
||||
return ["1", "true", "yes", "on"].includes(value?.trim().toLowerCase() ?? "");
|
||||
}
|
||||
|
||||
function parsePositiveInteger(value, fallback) {
|
||||
if (!value) {
|
||||
return fallback;
|
||||
}
|
||||
const parsed = Number.parseInt(value, 10);
|
||||
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
||||
}
|
||||
|
||||
export function parseGitShortStatus(raw) {
|
||||
return raw
|
||||
.split(/\r?\n/u)
|
||||
.map((line) => line.trimEnd())
|
||||
.filter(Boolean)
|
||||
.map((line) => {
|
||||
const status = line.slice(0, 2);
|
||||
const rawPath = line.slice(3);
|
||||
return {
|
||||
line,
|
||||
path: rawPath.includes(" -> ") ? (rawPath.split(" -> ").at(-1) ?? rawPath) : rawPath,
|
||||
status,
|
||||
trackedDeletion: status.includes("D") && status !== "??",
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function evaluateTestboxSyncSanity({
|
||||
cwd,
|
||||
statusRaw,
|
||||
exists = fs.existsSync,
|
||||
deletionThreshold = DEFAULT_DELETION_THRESHOLD,
|
||||
allowMassDeletions = false,
|
||||
}) {
|
||||
const missingRootFiles = REQUIRED_ROOT_FILES.filter((file) => !exists(path.join(cwd, file)));
|
||||
const statusEntries = parseGitShortStatus(statusRaw);
|
||||
const trackedDeletions = statusEntries.filter((entry) => entry.trackedDeletion);
|
||||
const problems = [];
|
||||
|
||||
if (missingRootFiles.length > 0) {
|
||||
problems.push(`missing required root files: ${missingRootFiles.join(", ")}`);
|
||||
}
|
||||
if (!allowMassDeletions && trackedDeletions.length >= deletionThreshold) {
|
||||
const examples = trackedDeletions
|
||||
.slice(0, 8)
|
||||
.map((entry) => entry.path)
|
||||
.join(", ");
|
||||
problems.push(
|
||||
`remote git status has ${trackedDeletions.length} tracked deletions (threshold ${deletionThreshold}); examples: ${examples}`,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
ok: problems.length === 0,
|
||||
missingRootFiles,
|
||||
problems,
|
||||
statusEntryCount: statusEntries.length,
|
||||
trackedDeletionCount: trackedDeletions.length,
|
||||
};
|
||||
}
|
||||
|
||||
function git(args, cwd) {
|
||||
return execFileSync("git", args, { cwd, encoding: "utf8" });
|
||||
}
|
||||
|
||||
export function runTestboxSyncSanity({
|
||||
cwd = process.cwd(),
|
||||
env = process.env,
|
||||
stdout = process.stdout,
|
||||
stderr = process.stderr,
|
||||
} = {}) {
|
||||
const root = git(["rev-parse", "--show-toplevel"], cwd).trim();
|
||||
const statusRaw = git(["status", "--short", "--untracked-files=all"], root);
|
||||
const result = evaluateTestboxSyncSanity({
|
||||
cwd: root,
|
||||
statusRaw,
|
||||
deletionThreshold: parsePositiveInteger(
|
||||
env.OPENCLAW_TESTBOX_DELETION_THRESHOLD,
|
||||
DEFAULT_DELETION_THRESHOLD,
|
||||
),
|
||||
allowMassDeletions: parseBooleanEnv(env.OPENCLAW_TESTBOX_ALLOW_MASS_DELETIONS),
|
||||
});
|
||||
|
||||
if (!result.ok) {
|
||||
stderr.write(`Testbox sync sanity failed:\n- ${result.problems.join("\n- ")}\n`);
|
||||
stderr.write("Warm a fresh box or rerun from a clean repo root before spending a gate.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
stdout.write(
|
||||
`Testbox sync sanity ok: ${result.statusEntryCount} changed entries, ${result.trackedDeletionCount} tracked deletions.\n`,
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) {
|
||||
process.exitCode = runTestboxSyncSanity();
|
||||
}
|
||||
Reference in New Issue
Block a user