@andyqiu/codeforge 0.5.9 → 0.5.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +79 -30
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -13563,8 +13563,8 @@ async function pruneOrphanWorktrees(mainRoot) {
|
|
|
13563
13563
|
var DEFAULT_MERGE_LOOP_CONFIG = {
|
|
13564
13564
|
maxReviewLoops: 3,
|
|
13565
13565
|
autoCoder: true,
|
|
13566
|
-
reviewTimeoutMs:
|
|
13567
|
-
coderTimeoutMs:
|
|
13566
|
+
reviewTimeoutMs: 180000,
|
|
13567
|
+
coderTimeoutMs: 600000,
|
|
13568
13568
|
abortDirtyStrategy: "checkpoint"
|
|
13569
13569
|
};
|
|
13570
13570
|
async function runMergeLoop(opts) {
|
|
@@ -13614,7 +13614,11 @@ async function runMergeLoop(opts) {
|
|
|
13614
13614
|
maxRounds: config.maxReviewLoops,
|
|
13615
13615
|
...lastReviewSummary ? { prevSummary: lastReviewSummary } : {},
|
|
13616
13616
|
...opts.signal ? { signal: opts.signal } : {}
|
|
13617
|
-
}), config.reviewTimeoutMs, `reviewer 第 ${loops} 轮`, opts.signal
|
|
13617
|
+
}), config.reviewTimeoutMs, `reviewer 第 ${loops} 轮`, opts.signal, {
|
|
13618
|
+
onHeartbeat: (elapsedMs) => {
|
|
13619
|
+
progress("dispatch_review", `reviewer 第 ${loops}/${config.maxReviewLoops} 轮仍在运行,已等待 ${Math.round(elapsedMs / 1000)}s`);
|
|
13620
|
+
}
|
|
13621
|
+
});
|
|
13618
13622
|
} catch (err) {
|
|
13619
13623
|
const e = err;
|
|
13620
13624
|
if (isAbortError2(e)) {
|
|
@@ -13683,7 +13687,11 @@ async function runMergeLoop(opts) {
|
|
|
13683
13687
|
...opts.planId ? { planId: opts.planId } : {},
|
|
13684
13688
|
reviewerSummary: reviewResult.summary,
|
|
13685
13689
|
...opts.signal ? { signal: opts.signal } : {}
|
|
13686
|
-
}), config.coderTimeoutMs, `coder round ${loops}`, opts.signal
|
|
13690
|
+
}), config.coderTimeoutMs, `coder round ${loops}`, opts.signal, {
|
|
13691
|
+
onHeartbeat: (elapsedMs) => {
|
|
13692
|
+
progress("dispatch_coder", `coder round ${loops} 仍在运行,已等待 ${Math.round(elapsedMs / 1000)}s`);
|
|
13693
|
+
}
|
|
13694
|
+
});
|
|
13687
13695
|
progress("wait_coder", `coder 完成: ${coderResult.ok ? "ok" : "fail"} - ${coderResult.summary}`);
|
|
13688
13696
|
if (!coderResult.ok) {
|
|
13689
13697
|
return {
|
|
@@ -13766,34 +13774,50 @@ async function handleAbortDirty(opts, config, entry) {
|
|
|
13766
13774
|
function isAbortError2(err) {
|
|
13767
13775
|
return err instanceof Error && err.name === "AbortError";
|
|
13768
13776
|
}
|
|
13769
|
-
function withTimeout2(p, ms, label, signal) {
|
|
13777
|
+
function withTimeout2(p, ms, label, signal, hbOpts) {
|
|
13770
13778
|
return new Promise((resolve11, reject) => {
|
|
13771
|
-
const
|
|
13779
|
+
const startedAt = Date.now();
|
|
13780
|
+
let hbTimer = null;
|
|
13781
|
+
let timer;
|
|
13782
|
+
const cleanup = () => {
|
|
13783
|
+
clearTimeout(timer);
|
|
13784
|
+
if (hbTimer)
|
|
13785
|
+
clearInterval(hbTimer);
|
|
13786
|
+
if (signal)
|
|
13787
|
+
signal.removeEventListener("abort", onAbort);
|
|
13788
|
+
};
|
|
13789
|
+
timer = setTimeout(() => {
|
|
13790
|
+
cleanup();
|
|
13772
13791
|
reject(new Error(`${label} 超时 (${ms}ms)`));
|
|
13773
13792
|
}, ms);
|
|
13793
|
+
const hbInterval = hbOpts?.heartbeatIntervalMs ?? 30000;
|
|
13794
|
+
const hbCb = hbOpts?.onHeartbeat;
|
|
13795
|
+
if (hbCb) {
|
|
13796
|
+
hbTimer = setInterval(() => {
|
|
13797
|
+
try {
|
|
13798
|
+
hbCb(Date.now() - startedAt);
|
|
13799
|
+
} catch {}
|
|
13800
|
+
}, hbInterval);
|
|
13801
|
+
}
|
|
13774
13802
|
const onAbort = () => {
|
|
13775
|
-
|
|
13803
|
+
cleanup();
|
|
13776
13804
|
const err = new Error(`${label} aborted by signal`);
|
|
13777
13805
|
err.name = "AbortError";
|
|
13778
13806
|
reject(err);
|
|
13779
13807
|
};
|
|
13780
13808
|
if (signal) {
|
|
13781
13809
|
if (signal.aborted) {
|
|
13782
|
-
|
|
13810
|
+
cleanup();
|
|
13783
13811
|
onAbort();
|
|
13784
13812
|
return;
|
|
13785
13813
|
}
|
|
13786
13814
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
13787
13815
|
}
|
|
13788
13816
|
p.then((v) => {
|
|
13789
|
-
|
|
13790
|
-
if (signal)
|
|
13791
|
-
signal.removeEventListener("abort", onAbort);
|
|
13817
|
+
cleanup();
|
|
13792
13818
|
resolve11(v);
|
|
13793
13819
|
}, (e) => {
|
|
13794
|
-
|
|
13795
|
-
if (signal)
|
|
13796
|
-
signal.removeEventListener("abort", onAbort);
|
|
13820
|
+
cleanup();
|
|
13797
13821
|
reject(e);
|
|
13798
13822
|
});
|
|
13799
13823
|
});
|
|
@@ -14901,7 +14925,7 @@ class ProductionSpawner {
|
|
|
14901
14925
|
prompt,
|
|
14902
14926
|
title: `[merge-review] sess=${args.sessionId.slice(0, 8)} r=${args.round}/${args.maxRounds}`,
|
|
14903
14927
|
...args.signal ? { signal: args.signal } : {},
|
|
14904
|
-
timeoutMs: this.opts.reviewerTimeoutMs ??
|
|
14928
|
+
timeoutMs: this.opts.reviewerTimeoutMs ?? 180000
|
|
14905
14929
|
}, args.sessionId);
|
|
14906
14930
|
} catch (err) {
|
|
14907
14931
|
throw err;
|
|
@@ -14933,7 +14957,7 @@ ${r.text.slice(0, 800)}`
|
|
|
14933
14957
|
prompt,
|
|
14934
14958
|
title: `[merge-fix] sess=${args.sessionId.slice(0, 8)}`,
|
|
14935
14959
|
...args.signal ? { signal: args.signal } : {},
|
|
14936
|
-
timeoutMs: this.opts.coderTimeoutMs ??
|
|
14960
|
+
timeoutMs: this.opts.coderTimeoutMs ?? 600000
|
|
14937
14961
|
}, args.sessionId);
|
|
14938
14962
|
} catch (err) {
|
|
14939
14963
|
throw err;
|
|
@@ -21612,7 +21636,7 @@ import * as zlib from "node:zlib";
|
|
|
21612
21636
|
// lib/version-injected.ts
|
|
21613
21637
|
function getInjectedVersion() {
|
|
21614
21638
|
try {
|
|
21615
|
-
const v = "0.5.
|
|
21639
|
+
const v = "0.5.10";
|
|
21616
21640
|
if (typeof v === "string" && /^\d+\.\d+\.\d+/.test(v)) {
|
|
21617
21641
|
return v;
|
|
21618
21642
|
}
|
|
@@ -22915,10 +22939,19 @@ function buildGitVcsWriteRegex(mainRoot) {
|
|
|
22915
22939
|
return new RegExp(`git\\b[^\\n]*(?:-C\\s+|--work-tree[=\\s])${esc}`);
|
|
22916
22940
|
}
|
|
22917
22941
|
var WRITE_TOOLS = new Set(["write", "edit", "ast_edit"]);
|
|
22942
|
+
var CLASS_B_CALLER_WHITELIST = new Set([
|
|
22943
|
+
"codeforge",
|
|
22944
|
+
"reviewer",
|
|
22945
|
+
"general"
|
|
22946
|
+
]);
|
|
22918
22947
|
function rewritePath(value, mainRoot, worktreeRoot) {
|
|
22919
22948
|
if (!value)
|
|
22920
22949
|
return null;
|
|
22921
22950
|
const resolved = path27.isAbsolute(value) ? value : path27.resolve(mainRoot, value);
|
|
22951
|
+
const wtPrefix2 = worktreeRoot.endsWith("/") ? worktreeRoot : worktreeRoot + "/";
|
|
22952
|
+
if (resolved === worktreeRoot || resolved.startsWith(wtPrefix2)) {
|
|
22953
|
+
return null;
|
|
22954
|
+
}
|
|
22922
22955
|
if (resolved === mainRoot)
|
|
22923
22956
|
return worktreeRoot;
|
|
22924
22957
|
const prefix = mainRoot.endsWith("/") ? mainRoot : mainRoot + "/";
|
|
@@ -23195,19 +23228,35 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
|
|
|
23195
23228
|
if (toolName === "bash") {
|
|
23196
23229
|
const command = argsObj["command"];
|
|
23197
23230
|
if (typeof command === "string" && commandContainsMainRoot(command, mainRoot) && detectBashWriteIntent(command, mainRoot)) {
|
|
23198
|
-
const
|
|
23199
|
-
|
|
23200
|
-
|
|
23201
|
-
|
|
23202
|
-
|
|
23203
|
-
|
|
23204
|
-
|
|
23205
|
-
|
|
23206
|
-
|
|
23207
|
-
|
|
23208
|
-
|
|
23209
|
-
|
|
23210
|
-
|
|
23231
|
+
const caller = await resolveAgentForGuard({ sessionID: input.sessionID, agent: input.agent }, ctx.client, log14);
|
|
23232
|
+
if (caller !== null && CLASS_B_CALLER_WHITELIST.has(caller)) {
|
|
23233
|
+
log14.debug?.(`[class-b-whitelist] allow caller=${caller}`, { sessionId, tool: toolName, command: command.slice(0, 200) });
|
|
23234
|
+
safeWriteLog(PLUGIN_NAME25, {
|
|
23235
|
+
hook: "tool.execute.before",
|
|
23236
|
+
tool: toolName,
|
|
23237
|
+
sessionID: input.sessionID,
|
|
23238
|
+
action: "allow-whitelist",
|
|
23239
|
+
source: "class-b-caller-whitelist",
|
|
23240
|
+
caller,
|
|
23241
|
+
command: command.slice(0, 200)
|
|
23242
|
+
});
|
|
23243
|
+
} else {
|
|
23244
|
+
const callerTag = caller === null ? "unresolved" : caller;
|
|
23245
|
+
const snippet = command.length > 60 ? command.slice(0, 60) + "…" : command;
|
|
23246
|
+
const reason = `[session-worktree-guard] DENIED: bash.command 含主仓绝对路径写操作 (${snippet}) [caller=${callerTag}],请在当前 session worktree (${worktreePath}) 内操作`;
|
|
23247
|
+
log14.warn(reason, { sessionId, caller: callerTag, command: command.slice(0, 200) });
|
|
23248
|
+
safeWriteLog(PLUGIN_NAME25, {
|
|
23249
|
+
hook: "tool.execute.before",
|
|
23250
|
+
tool: toolName,
|
|
23251
|
+
sessionID: input.sessionID,
|
|
23252
|
+
action: "deny",
|
|
23253
|
+
source: "bash-write-intent",
|
|
23254
|
+
caller: callerTag,
|
|
23255
|
+
command: command.slice(0, 200)
|
|
23256
|
+
});
|
|
23257
|
+
denied = new DeniedError(reason);
|
|
23258
|
+
return;
|
|
23259
|
+
}
|
|
23211
23260
|
}
|
|
23212
23261
|
}
|
|
23213
23262
|
if (toolName === "write" || toolName === "edit") {
|