@andyqiu/codeforge 0.5.10 → 0.5.11
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 +100 -12
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -13262,12 +13262,25 @@ async function mergeSessionBack(opts) {
|
|
|
13262
13262
|
}
|
|
13263
13263
|
const hasDevOnce = await packageHasScript(mainRoot, "dev:once");
|
|
13264
13264
|
if (hasDevOnce) {
|
|
13265
|
-
|
|
13266
|
-
|
|
13267
|
-
|
|
13268
|
-
|
|
13269
|
-
|
|
13270
|
-
|
|
13265
|
+
const stagedRaw = await runGit2(mainRoot, [
|
|
13266
|
+
"diff",
|
|
13267
|
+
"--cached",
|
|
13268
|
+
"--name-only",
|
|
13269
|
+
"--diff-filter=ACMR"
|
|
13270
|
+
]);
|
|
13271
|
+
const stagedPaths = stagedRaw.split(/\r?\n/).map((s) => s.trim()).filter(Boolean);
|
|
13272
|
+
const canSkipDevOnce = await shouldSkipDevOnce(mainRoot, stagedPaths);
|
|
13273
|
+
if (canSkipDevOnce) {
|
|
13274
|
+
const sourceCount = stagedPaths.filter((p) => /^(plugins|lib|src)\//.test(p) && !/\.(md|test\.ts)$/.test(p)).length;
|
|
13275
|
+
console.log(`[session-worktree] skip dev:once: dist 已是最新(${sourceCount} staged 源文件 mtime <= dist mtime)`);
|
|
13276
|
+
} else {
|
|
13277
|
+
try {
|
|
13278
|
+
await runCmd("npm", ["run", "dev:once"], mainRoot);
|
|
13279
|
+
} catch (err) {
|
|
13280
|
+
await runGit2(mainRoot, ["reset", "--hard", "HEAD"]).catch(() => {});
|
|
13281
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
13282
|
+
throw new Error(`dev:once 失败已 reset 主仓: ${msg}`);
|
|
13283
|
+
}
|
|
13271
13284
|
}
|
|
13272
13285
|
} else {
|
|
13273
13286
|
console.log(`[session-worktree] skip dev:once: not configured in ${mainRoot}/package.json`);
|
|
@@ -13275,7 +13288,9 @@ async function mergeSessionBack(opts) {
|
|
|
13275
13288
|
const squashedRaw = await runGit2(wt, ["log", "--format=%s", `${baseSha}..HEAD`]);
|
|
13276
13289
|
const squashedCommits = squashedRaw.split(/\r?\n/).map((s) => s.trim()).filter(Boolean);
|
|
13277
13290
|
const message = opts.commitMessage ?? buildMergeMessage(opts.sessionId, branch, baseSha, squashedCommits);
|
|
13278
|
-
await
|
|
13291
|
+
await runGitWithEnv(mainRoot, ["commit", "-m", message], {
|
|
13292
|
+
SKIP_DEV_SYNC_CHECK: "1"
|
|
13293
|
+
});
|
|
13279
13294
|
const newSha = (await runGit2(mainRoot, ["rev-parse", "HEAD"])).trim();
|
|
13280
13295
|
try {
|
|
13281
13296
|
await removeWorktree({ root: mainRoot, worktree_path: wt, force: true });
|
|
@@ -13376,6 +13391,24 @@ function runGit2(cwd, args, timeoutMs = 1e4) {
|
|
|
13376
13391
|
});
|
|
13377
13392
|
});
|
|
13378
13393
|
}
|
|
13394
|
+
function runGitWithEnv(cwd, args, envOverrides, timeoutMs = 1e4) {
|
|
13395
|
+
const inheritedEnv = process["env"];
|
|
13396
|
+
return new Promise((resolve11, reject) => {
|
|
13397
|
+
execFile3("git", args, {
|
|
13398
|
+
cwd,
|
|
13399
|
+
timeout: timeoutMs,
|
|
13400
|
+
windowsHide: true,
|
|
13401
|
+
encoding: "utf8",
|
|
13402
|
+
env: Object.assign({}, inheritedEnv, envOverrides)
|
|
13403
|
+
}, (err, stdout, stderr) => {
|
|
13404
|
+
if (err) {
|
|
13405
|
+
reject(new Error(`git ${args.join(" ")} (cwd=${cwd}) 失败: ${stderr?.trim() || err.message}`));
|
|
13406
|
+
return;
|
|
13407
|
+
}
|
|
13408
|
+
resolve11(stdout);
|
|
13409
|
+
});
|
|
13410
|
+
});
|
|
13411
|
+
}
|
|
13379
13412
|
async function packageHasScript(mainRoot, scriptName) {
|
|
13380
13413
|
try {
|
|
13381
13414
|
const pkgPath = path13.join(mainRoot, "package.json");
|
|
@@ -13388,7 +13421,31 @@ async function packageHasScript(mainRoot, scriptName) {
|
|
|
13388
13421
|
return false;
|
|
13389
13422
|
}
|
|
13390
13423
|
}
|
|
13391
|
-
function
|
|
13424
|
+
async function shouldSkipDevOnce(mainRoot, stagedPaths) {
|
|
13425
|
+
let distMtimeSec;
|
|
13426
|
+
try {
|
|
13427
|
+
const st = await fs10.stat(path13.join(mainRoot, "dist/index.js"));
|
|
13428
|
+
distMtimeSec = Math.floor(st.mtimeMs / 1000);
|
|
13429
|
+
} catch {
|
|
13430
|
+
return false;
|
|
13431
|
+
}
|
|
13432
|
+
const relevant = stagedPaths.filter((p) => /^(plugins|lib|src)\//.test(p) && !/\.(md|test\.ts)$/.test(p));
|
|
13433
|
+
if (relevant.length === 0)
|
|
13434
|
+
return true;
|
|
13435
|
+
for (const rel of relevant) {
|
|
13436
|
+
try {
|
|
13437
|
+
const abs = path13.join(mainRoot, rel);
|
|
13438
|
+
const st = await fs10.stat(abs);
|
|
13439
|
+
const srcMtimeSec = Math.floor(st.mtimeMs / 1000);
|
|
13440
|
+
if (srcMtimeSec > distMtimeSec)
|
|
13441
|
+
return false;
|
|
13442
|
+
} catch {
|
|
13443
|
+
return false;
|
|
13444
|
+
}
|
|
13445
|
+
}
|
|
13446
|
+
return true;
|
|
13447
|
+
}
|
|
13448
|
+
function runCmd(cmd, args, cwd, timeoutMs = 300000) {
|
|
13392
13449
|
return new Promise((resolve11, reject) => {
|
|
13393
13450
|
execFile3(cmd, args, { cwd, timeout: timeoutMs, windowsHide: true, encoding: "utf8" }, (err, stdout, stderr) => {
|
|
13394
13451
|
if (err) {
|
|
@@ -21232,7 +21289,11 @@ var RISK_PATTERNS = [
|
|
|
21232
21289
|
kinds: ["bash", "other"],
|
|
21233
21290
|
re: /\b(DROP\s+(DATABASE|TABLE)|TRUNCATE\s+TABLE|DROP\s+SCHEMA)\b/i
|
|
21234
21291
|
},
|
|
21235
|
-
{
|
|
21292
|
+
{
|
|
21293
|
+
tag: "write_secrets",
|
|
21294
|
+
re: /(\.env(?:\.\w+)?|id_[edr]sa|\.ssh\/id_|\.pem|\.p12|secret\.json)/i,
|
|
21295
|
+
matchOn: ["command", "filePath", "path"]
|
|
21296
|
+
},
|
|
21236
21297
|
{
|
|
21237
21298
|
tag: "write_etc",
|
|
21238
21299
|
kinds: ["bash", "edit"],
|
|
@@ -21313,11 +21374,11 @@ function classifyTool(tool2) {
|
|
|
21313
21374
|
}
|
|
21314
21375
|
function evaluateRisk(tool2, args) {
|
|
21315
21376
|
const kind = classifyTool(tool2);
|
|
21316
|
-
const haystack = buildHaystack(args);
|
|
21317
21377
|
const hits = [];
|
|
21318
21378
|
for (const pattern of RISK_PATTERNS) {
|
|
21319
21379
|
if (pattern.kinds && !pattern.kinds.includes(kind))
|
|
21320
21380
|
continue;
|
|
21381
|
+
const haystack = buildHaystackFor(args, pattern.matchOn);
|
|
21321
21382
|
const m = haystack.match(pattern.re);
|
|
21322
21383
|
if (m) {
|
|
21323
21384
|
hits.push({
|
|
@@ -21338,6 +21399,19 @@ function buildHaystack(args) {
|
|
|
21338
21399
|
return String(args);
|
|
21339
21400
|
}
|
|
21340
21401
|
}
|
|
21402
|
+
function buildHaystackFor(args, matchOn) {
|
|
21403
|
+
if (!matchOn || matchOn.length === 0) {
|
|
21404
|
+
return buildHaystack(args);
|
|
21405
|
+
}
|
|
21406
|
+
const parts = [];
|
|
21407
|
+
for (const key of matchOn) {
|
|
21408
|
+
if (key in args) {
|
|
21409
|
+
const val = args[key];
|
|
21410
|
+
parts.push(typeof val === "string" ? val : JSON.stringify(val));
|
|
21411
|
+
}
|
|
21412
|
+
}
|
|
21413
|
+
return parts.join(" ");
|
|
21414
|
+
}
|
|
21341
21415
|
|
|
21342
21416
|
// lib/file-regex-acl.ts
|
|
21343
21417
|
import * as path23 from "node:path";
|
|
@@ -21636,7 +21710,7 @@ import * as zlib from "node:zlib";
|
|
|
21636
21710
|
// lib/version-injected.ts
|
|
21637
21711
|
function getInjectedVersion() {
|
|
21638
21712
|
try {
|
|
21639
|
-
const v = "0.5.
|
|
21713
|
+
const v = "0.5.11";
|
|
21640
21714
|
if (typeof v === "string" && /^\d+\.\d+\.\d+/.test(v)) {
|
|
21641
21715
|
return v;
|
|
21642
21716
|
}
|
|
@@ -22970,6 +23044,20 @@ function commandContainsMainRoot(command, mainRoot) {
|
|
|
22970
23044
|
const re = new RegExp(`${escapeRegex2(mainRoot)}(?=[\\s'"\`)]|$)`);
|
|
22971
23045
|
return re.test(command);
|
|
22972
23046
|
}
|
|
23047
|
+
function commandContainsMainRootExcludingWorktree(command, mainRoot, worktreePath) {
|
|
23048
|
+
if (!worktreePath || worktreePath === mainRoot) {
|
|
23049
|
+
return commandContainsMainRoot(command, mainRoot);
|
|
23050
|
+
}
|
|
23051
|
+
const wtpIdx = command.indexOf(worktreePath);
|
|
23052
|
+
if (wtpIdx !== -1) {
|
|
23053
|
+
const afterWtp = command.slice(wtpIdx + worktreePath.length);
|
|
23054
|
+
if (/(?:^|[/\\])\.\.(?:[/\\\s'";|<>]|$)/.test(afterWtp)) {
|
|
23055
|
+
return true;
|
|
23056
|
+
}
|
|
23057
|
+
}
|
|
23058
|
+
const sanitized = command.split(worktreePath).join("");
|
|
23059
|
+
return commandContainsMainRoot(sanitized, mainRoot);
|
|
23060
|
+
}
|
|
22973
23061
|
function detectBashWriteIntent(command, mainRoot) {
|
|
22974
23062
|
if (isReadOnlyBashCommand(command))
|
|
22975
23063
|
return false;
|
|
@@ -23227,7 +23315,7 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
|
|
|
23227
23315
|
}
|
|
23228
23316
|
if (toolName === "bash") {
|
|
23229
23317
|
const command = argsObj["command"];
|
|
23230
|
-
if (typeof command === "string" &&
|
|
23318
|
+
if (typeof command === "string" && commandContainsMainRootExcludingWorktree(command, mainRoot, worktreePath) && detectBashWriteIntent(command, mainRoot)) {
|
|
23231
23319
|
const caller = await resolveAgentForGuard({ sessionID: input.sessionID, agent: input.agent }, ctx.client, log14);
|
|
23232
23320
|
if (caller !== null && CLASS_B_CALLER_WHITELIST.has(caller)) {
|
|
23233
23321
|
log14.debug?.(`[class-b-whitelist] allow caller=${caller}`, { sessionId, tool: toolName, command: command.slice(0, 200) });
|