

























@@ -426,8 +426,8 @@ function runCommandArgs(commandArgs) {
426426return start >= 0 ? commandArgs.slice(start) : [];
427427}
428428429-function commandRuntimeEntrypoint(commandArgs) {
430-const words = commandArgs.length === 1 ? commandArgs[0].split(/\s+/u) : commandArgs;
429+function normalizedCommandWords(commandArgs) {
430+const words = commandArgs.length === 1 ? commandArgs[0].split(/\s+/u) : [...commandArgs];
431431while (words[0] === "env") {
432432words.shift();
433433while (/^[A-Za-z_][A-Za-z0-9_]*=/.test(words[0] ?? "")) {
@@ -437,13 +437,43 @@ function commandRuntimeEntrypoint(commandArgs) {
437437while (/^[A-Za-z_][A-Za-z0-9_]*=/.test(words[0] ?? "")) {
438438words.shift();
439439}
440-const first = (words[0] ?? "")
441-.replace(/^['"]|['";|&()]+$/g, "")
442-.split("/")
443-.pop();
440+return words.map((word) => word.replace(/^['"]|['";|&()]+$/g, ""));
441+}
442+443+function commandRuntimeEntrypoint(commandArgs) {
444+const words = normalizedCommandWords(commandArgs);
445+const first = (words[0] ?? "").split("/").pop();
444446return ["pnpm", "npm", "npx", "corepack", "node", "yarn", "bun"].includes(first) ? first : "";
445447}
446448449+function isChangedGateCommand(commandArgs) {
450+const words = normalizedCommandWords(commandArgs);
451+if (words[0] === "corepack") {
452+words.shift();
453+}
454+return (
455+(words[0] === "pnpm" && words[1] === "check:changed") ||
456+(words[0] === "pnpm" && words[1] === "run" && words[2] === "check:changed") ||
457+(words[0] === "node" && (words[1] ?? "").endsWith("scripts/check-changed.mjs"))
458+);
459+}
460+461+function headInRemoteRefs() {
462+const refs = gitOutput([
463+"for-each-ref",
464+"--contains",
465+"HEAD",
466+"--format=%(refname)",
467+"refs/remotes",
468+]);
469+return refs.status === 0 && refs.stdout !== "";
470+}
471+472+function mergeBaseForChangedGate() {
473+const base = gitOutput(["merge-base", "origin/main", "HEAD"]);
474+return base.status === 0 && base.stdout ? base.stdout : "origin/main";
475+}
476+447477function isSparseCheckout() {
448478const config = gitOutput(["config", "--bool", "core.sparseCheckout"]);
449479if (config.status === 0 && config.stdout === "true") {
@@ -457,8 +487,8 @@ function isWorktreeClean() {
457487return gitOutput(["status", "--porcelain=v1"]).stdout === "";
458488}
459489460-function shouldUseFullCheckoutForCleanSparseBlacksmithSync(commandArgs, providerName) {
461-if (commandArgs[0] !== "run" || providerName !== "blacksmith-testbox") {
490+function shouldUseFullCheckoutForCleanSparseRemoteSync(commandArgs, providerName) {
491+if (commandArgs[0] !== "run" || isLocalContainerProvider(providerName)) {
462492return false;
463493}
464494if (
@@ -471,7 +501,7 @@ function shouldUseFullCheckoutForCleanSparseBlacksmithSync(commandArgs, provider
471501return isSparseCheckout() && isWorktreeClean();
472502}
473503474-function prepareFullCheckoutForSync() {
504+function prepareFullCheckoutForSync(options = {}) {
475505const dir = mkdtempSync(resolve(tmpdir(), "openclaw-crabbox-sync-"));
476506let active = false;
477507const add = gitOutput(["worktree", "add", "--detach", dir, "HEAD"]);
@@ -487,8 +517,17 @@ function prepareFullCheckoutForSync() {
487517throw new Error(`git sparse-checkout disable failed: ${disableSparse.text}`);
488518}
489519520+if (options.changedGateBase) {
521+const reset = gitOutput(["-C", dir, "reset", "--mixed", "--quiet", options.changedGateBase]);
522+if (reset.status !== 0) {
523+cleanupFullCheckout(dir, active);
524+throw new Error(`git reset for changed-gate sync failed: ${reset.text}`);
525+}
526+}
527+490528return {
491529 dir,
530+changedGateBase: options.changedGateBase ?? "",
492531cleanup() {
493532cleanupFullCheckout(dir, active);
494533active = false;
@@ -645,13 +684,21 @@ if (provider === "blacksmith-testbox") {
645684let childCwd = repoRoot;
646685let cleanupChildCwd = () => {};
647686let cleanupDone = false;
648-if (shouldUseFullCheckoutForCleanSparseBlacksmithSync(args, provider)) {
649-const checkout = prepareFullCheckoutForSync();
687+if (shouldUseFullCheckoutForCleanSparseRemoteSync(args, provider)) {
688+const runWords = runCommandArgs(args);
689+const changedGateBase =
690+isChangedGateCommand(runWords) && !headInRemoteRefs() ? mergeBaseForChangedGate() : "";
691+const checkout = prepareFullCheckoutForSync({ changedGateBase });
650692childCwd = checkout.dir;
651693cleanupChildCwd = () => checkout.cleanup();
652694console.error(
653695`[crabbox] sparse clean checkout detected; syncing from temporary full checkout ${checkout.dir}`,
654696);
697+if (checkout.changedGateBase) {
698+console.error(
699+`[crabbox] remote changed gate detected; overlaying local HEAD as worktree changes from ${checkout.changedGateBase}`,
700+);
701+}
655702}
656703657704function cleanupOnce() {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。