@andyqiu/codeforge 0.3.10 → 0.3.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/README.md +106 -3
- package/agents/codeforge.md +35 -1
- package/agents/coder-deep.md +93 -0
- package/agents/coder-quick.md +92 -0
- package/agents/coder.md +5 -4
- package/agents/planner.md +1 -0
- package/agents/reviewer.md +1 -0
- package/codeforge.json +28 -2
- package/commands/deep.md +87 -0
- package/commands/quick.md +92 -0
- package/dist/index.js +532 -65
- package/install.sh +8 -0
- package/package.json +1 -1
- package/schemas/codeforge.schema.json +230 -224
- package/scripts/sync-agent-models.mjs +22 -3
package/dist/index.js
CHANGED
|
@@ -9007,7 +9007,7 @@ var handler5 = autoLearningServer;
|
|
|
9007
9007
|
|
|
9008
9008
|
// plugins/channels.ts
|
|
9009
9009
|
init_opencode_plugin_helpers();
|
|
9010
|
-
import { promises as fs2 } from "node:fs";
|
|
9010
|
+
import { promises as fs2, statSync as statSync2 } from "node:fs";
|
|
9011
9011
|
import * as path4 from "node:path";
|
|
9012
9012
|
|
|
9013
9013
|
// lib/channels.ts
|
|
@@ -9285,6 +9285,13 @@ function transformSlackToWebhook(ch, ev) {
|
|
|
9285
9285
|
text: { type: "plain_text", text: titleText.slice(0, 150), emoji: true }
|
|
9286
9286
|
}
|
|
9287
9287
|
];
|
|
9288
|
+
const sevLabel = ch.show_severity_label !== false ? severityToLabel(ev.severity) : "";
|
|
9289
|
+
if (sevLabel) {
|
|
9290
|
+
blocks.push({
|
|
9291
|
+
type: "section",
|
|
9292
|
+
text: { type: "mrkdwn", text: sevLabel }
|
|
9293
|
+
});
|
|
9294
|
+
}
|
|
9288
9295
|
if (message.rendered.trim()) {
|
|
9289
9296
|
blocks.push({
|
|
9290
9297
|
type: "section",
|
|
@@ -9297,6 +9304,56 @@ function transformSlackToWebhook(ch, ev) {
|
|
|
9297
9304
|
elements: [{ type: "mrkdwn", text: mentionText }]
|
|
9298
9305
|
});
|
|
9299
9306
|
}
|
|
9307
|
+
const showFields = ch.show_fields ?? [...DEFAULT_SHOW_FIELDS];
|
|
9308
|
+
const labels = { ...DEFAULT_FIELD_LABELS, ...ch.field_labels ?? {} };
|
|
9309
|
+
const fieldLines = pickFieldLines(ev.data, showFields, labels);
|
|
9310
|
+
if (fieldLines.length > 0) {
|
|
9311
|
+
blocks.push({ type: "divider" });
|
|
9312
|
+
blocks.push({
|
|
9313
|
+
type: "section",
|
|
9314
|
+
text: { type: "mrkdwn", text: fieldLines.join(`
|
|
9315
|
+
`) }
|
|
9316
|
+
});
|
|
9317
|
+
}
|
|
9318
|
+
if (ev.severity !== undefined && ev.severity >= 20) {
|
|
9319
|
+
const errMsg = ev.data?.["error"];
|
|
9320
|
+
const stack = ev.data?.["stack"];
|
|
9321
|
+
if (typeof errMsg === "string" && errMsg) {
|
|
9322
|
+
let errContent = `**❌ Error**: ${errMsg.slice(0, 500)}`;
|
|
9323
|
+
if (typeof stack === "string" && stack) {
|
|
9324
|
+
const stackHead = stack.split(`
|
|
9325
|
+
`).slice(0, 5).join(`
|
|
9326
|
+
`);
|
|
9327
|
+
errContent += "\n```\n" + stackHead.slice(0, 1000) + "\n```";
|
|
9328
|
+
}
|
|
9329
|
+
blocks.push({ type: "divider" });
|
|
9330
|
+
blocks.push({
|
|
9331
|
+
type: "section",
|
|
9332
|
+
text: { type: "mrkdwn", text: errContent }
|
|
9333
|
+
});
|
|
9334
|
+
}
|
|
9335
|
+
}
|
|
9336
|
+
const sessionUrl = ev.data?.["session_url"];
|
|
9337
|
+
const logsUrl = ev.data?.["logs_url"];
|
|
9338
|
+
const slackActions = [];
|
|
9339
|
+
if (isValidCtaUrl(sessionUrl)) {
|
|
9340
|
+
slackActions.push({
|
|
9341
|
+
type: "button",
|
|
9342
|
+
text: { type: "plain_text", text: "查看会话", emoji: true },
|
|
9343
|
+
url: sessionUrl,
|
|
9344
|
+
style: "primary"
|
|
9345
|
+
});
|
|
9346
|
+
}
|
|
9347
|
+
if (isValidCtaUrl(logsUrl)) {
|
|
9348
|
+
slackActions.push({
|
|
9349
|
+
type: "button",
|
|
9350
|
+
text: { type: "plain_text", text: "查看日志", emoji: true },
|
|
9351
|
+
url: logsUrl
|
|
9352
|
+
});
|
|
9353
|
+
}
|
|
9354
|
+
if (slackActions.length > 0) {
|
|
9355
|
+
blocks.push({ type: "actions", elements: slackActions });
|
|
9356
|
+
}
|
|
9300
9357
|
const footerParts = [`event=\`${ev.event}\``];
|
|
9301
9358
|
if (ev.session_id)
|
|
9302
9359
|
footerParts.push(`session=\`${ev.session_id.slice(0, 8)}\``);
|
|
@@ -9387,6 +9444,13 @@ function transformLarkToWebhook(ch, ev) {
|
|
|
9387
9444
|
}).join(" ");
|
|
9388
9445
|
const headerTemplate = severityToLarkHeader(ev.severity);
|
|
9389
9446
|
const elements = [];
|
|
9447
|
+
const sevLabel = ch.show_severity_label !== false ? severityToLabel(ev.severity) : "";
|
|
9448
|
+
if (sevLabel) {
|
|
9449
|
+
elements.push({
|
|
9450
|
+
tag: "div",
|
|
9451
|
+
text: { tag: "lark_md", content: sevLabel }
|
|
9452
|
+
});
|
|
9453
|
+
}
|
|
9390
9454
|
if (messageText || mentionMarkdown) {
|
|
9391
9455
|
const fullMsg = [messageText, mentionMarkdown].filter(Boolean).join(`
|
|
9392
9456
|
|
|
@@ -9396,6 +9460,57 @@ function transformLarkToWebhook(ch, ev) {
|
|
|
9396
9460
|
text: { tag: "lark_md", content: fullMsg.slice(0, 3000) }
|
|
9397
9461
|
});
|
|
9398
9462
|
}
|
|
9463
|
+
const showFields = ch.show_fields ?? [...DEFAULT_SHOW_FIELDS];
|
|
9464
|
+
const labels = { ...DEFAULT_FIELD_LABELS, ...ch.field_labels ?? {} };
|
|
9465
|
+
const fieldLines = pickFieldLines(ev.data, showFields, labels);
|
|
9466
|
+
if (fieldLines.length > 0) {
|
|
9467
|
+
elements.push({ tag: "hr" });
|
|
9468
|
+
elements.push({
|
|
9469
|
+
tag: "div",
|
|
9470
|
+
text: { tag: "lark_md", content: fieldLines.join(`
|
|
9471
|
+
`) }
|
|
9472
|
+
});
|
|
9473
|
+
}
|
|
9474
|
+
if (ev.severity !== undefined && ev.severity >= 20) {
|
|
9475
|
+
const errMsg = ev.data?.["error"];
|
|
9476
|
+
const stack = ev.data?.["stack"];
|
|
9477
|
+
if (typeof errMsg === "string" && errMsg) {
|
|
9478
|
+
let errContent = `**❌ Error**: ${errMsg.slice(0, 500)}`;
|
|
9479
|
+
if (typeof stack === "string" && stack) {
|
|
9480
|
+
const stackHead = stack.split(`
|
|
9481
|
+
`).slice(0, 5).join(`
|
|
9482
|
+
`);
|
|
9483
|
+
errContent += "\n```\n" + stackHead.slice(0, 1000) + "\n```";
|
|
9484
|
+
}
|
|
9485
|
+
elements.push({ tag: "hr" });
|
|
9486
|
+
elements.push({
|
|
9487
|
+
tag: "div",
|
|
9488
|
+
text: { tag: "lark_md", content: errContent }
|
|
9489
|
+
});
|
|
9490
|
+
}
|
|
9491
|
+
}
|
|
9492
|
+
const sessionUrl = ev.data?.["session_url"];
|
|
9493
|
+
const logsUrl = ev.data?.["logs_url"];
|
|
9494
|
+
const larkActions = [];
|
|
9495
|
+
if (isValidCtaUrl(sessionUrl)) {
|
|
9496
|
+
larkActions.push({
|
|
9497
|
+
tag: "button",
|
|
9498
|
+
text: { tag: "plain_text", content: "查看会话" },
|
|
9499
|
+
type: "primary",
|
|
9500
|
+
url: sessionUrl
|
|
9501
|
+
});
|
|
9502
|
+
}
|
|
9503
|
+
if (isValidCtaUrl(logsUrl)) {
|
|
9504
|
+
larkActions.push({
|
|
9505
|
+
tag: "button",
|
|
9506
|
+
text: { tag: "plain_text", content: "查看日志" },
|
|
9507
|
+
type: "default",
|
|
9508
|
+
url: logsUrl
|
|
9509
|
+
});
|
|
9510
|
+
}
|
|
9511
|
+
if (larkActions.length > 0) {
|
|
9512
|
+
elements.push({ tag: "action", actions: larkActions });
|
|
9513
|
+
}
|
|
9399
9514
|
const footer = [
|
|
9400
9515
|
`**event**: \`${ev.event}\``,
|
|
9401
9516
|
ev.session_id ? `**session**: \`${ev.session_id.slice(0, 8)}\`` : null,
|
|
@@ -9431,6 +9546,60 @@ function severityToLarkHeader(sev) {
|
|
|
9431
9546
|
return "blue";
|
|
9432
9547
|
return "grey";
|
|
9433
9548
|
}
|
|
9549
|
+
function severityToLabel(sev) {
|
|
9550
|
+
if (sev === undefined)
|
|
9551
|
+
return "";
|
|
9552
|
+
if (sev >= 40)
|
|
9553
|
+
return "\uD83D\uDEA8 CRITICAL";
|
|
9554
|
+
if (sev >= 30)
|
|
9555
|
+
return "\uD83D\uDD34 ERROR";
|
|
9556
|
+
if (sev >= 20)
|
|
9557
|
+
return "\uD83D\uDFE0 WARN";
|
|
9558
|
+
if (sev >= 10)
|
|
9559
|
+
return "\uD83D\uDD35 INFO";
|
|
9560
|
+
return "";
|
|
9561
|
+
}
|
|
9562
|
+
var DEFAULT_SHOW_FIELDS = [
|
|
9563
|
+
"agent",
|
|
9564
|
+
"model",
|
|
9565
|
+
"duration_ms",
|
|
9566
|
+
"cost",
|
|
9567
|
+
"files_changed",
|
|
9568
|
+
"status"
|
|
9569
|
+
];
|
|
9570
|
+
var DEFAULT_FIELD_LABELS = {
|
|
9571
|
+
agent: "Agent",
|
|
9572
|
+
model: "Model",
|
|
9573
|
+
duration_ms: "耗时",
|
|
9574
|
+
cost: "费用",
|
|
9575
|
+
files_changed: "改动文件数",
|
|
9576
|
+
status: "状态",
|
|
9577
|
+
error: "错误"
|
|
9578
|
+
};
|
|
9579
|
+
function pickFieldLines(data, showFields, labels) {
|
|
9580
|
+
if (!data)
|
|
9581
|
+
return [];
|
|
9582
|
+
const out = [];
|
|
9583
|
+
for (const key of showFields) {
|
|
9584
|
+
const v = data[key];
|
|
9585
|
+
if (v === undefined || v === null || v === "")
|
|
9586
|
+
continue;
|
|
9587
|
+
const label = labels[key] ?? key;
|
|
9588
|
+
let valueStr;
|
|
9589
|
+
if (key === "duration_ms" && typeof v === "number") {
|
|
9590
|
+
valueStr = v >= 60000 ? `${(v / 60000).toFixed(1)}m` : `${(v / 1000).toFixed(1)}s`;
|
|
9591
|
+
} else if (key === "cost" && typeof v === "number") {
|
|
9592
|
+
valueStr = `$${v.toFixed(4)}`;
|
|
9593
|
+
} else {
|
|
9594
|
+
valueStr = String(v);
|
|
9595
|
+
}
|
|
9596
|
+
out.push(`**${label}**: ${valueStr}`);
|
|
9597
|
+
}
|
|
9598
|
+
return out;
|
|
9599
|
+
}
|
|
9600
|
+
function isValidCtaUrl(url) {
|
|
9601
|
+
return typeof url === "string" && url.startsWith("https://");
|
|
9602
|
+
}
|
|
9434
9603
|
function computeLarkSign(secret, timestampSec) {
|
|
9435
9604
|
const stringToSign = `${timestampSec}
|
|
9436
9605
|
${secret}`;
|
|
@@ -9524,11 +9693,88 @@ function makeChannelEvent(event, data = {}) {
|
|
|
9524
9693
|
}
|
|
9525
9694
|
|
|
9526
9695
|
// plugins/channels.ts
|
|
9696
|
+
init_global_config();
|
|
9527
9697
|
var PLUGIN_NAME6 = "channels";
|
|
9528
9698
|
logLifecycle(PLUGIN_NAME6, "import", {});
|
|
9529
9699
|
var fallbackLog = makePluginLogger(PLUGIN_NAME6);
|
|
9530
9700
|
var _channelsCache = null;
|
|
9531
|
-
|
|
9701
|
+
var _activatedDirectory;
|
|
9702
|
+
var KNOWN_TYPES = new Set([
|
|
9703
|
+
"webhook",
|
|
9704
|
+
"file",
|
|
9705
|
+
"exec",
|
|
9706
|
+
"kh",
|
|
9707
|
+
"mcp",
|
|
9708
|
+
"slack",
|
|
9709
|
+
"lark"
|
|
9710
|
+
]);
|
|
9711
|
+
var REQUIRED_FIELDS = {
|
|
9712
|
+
webhook: ["url"],
|
|
9713
|
+
file: ["path"],
|
|
9714
|
+
exec: [],
|
|
9715
|
+
kh: [],
|
|
9716
|
+
mcp: ["server", "tool"],
|
|
9717
|
+
slack: ["webhook_url"],
|
|
9718
|
+
lark: ["webhook_url"]
|
|
9719
|
+
};
|
|
9720
|
+
function safePeek(o) {
|
|
9721
|
+
const out = {};
|
|
9722
|
+
for (const k of ["type", "name"]) {
|
|
9723
|
+
if (k in o)
|
|
9724
|
+
out[k] = o[k];
|
|
9725
|
+
}
|
|
9726
|
+
return out;
|
|
9727
|
+
}
|
|
9728
|
+
function isChannel(x, log4) {
|
|
9729
|
+
if (!x || typeof x !== "object")
|
|
9730
|
+
return false;
|
|
9731
|
+
const o = x;
|
|
9732
|
+
const t = o["type"];
|
|
9733
|
+
const n = o["name"];
|
|
9734
|
+
if (typeof n !== "string" || n.length === 0) {
|
|
9735
|
+
log4?.warn(`[channels] 配置被忽略:缺少 name 字段`, { sample: safePeek(o) });
|
|
9736
|
+
return false;
|
|
9737
|
+
}
|
|
9738
|
+
if (typeof t !== "string" || !KNOWN_TYPES.has(t)) {
|
|
9739
|
+
log4?.warn(`[channels] 配置 '${n}' 被忽略:未知 type='${String(t)}',合法值=${[...KNOWN_TYPES].join(",")}`);
|
|
9740
|
+
return false;
|
|
9741
|
+
}
|
|
9742
|
+
const required = REQUIRED_FIELDS[t] ?? [];
|
|
9743
|
+
for (const f of required) {
|
|
9744
|
+
const v = o[f];
|
|
9745
|
+
if (typeof v !== "string" || v.length === 0) {
|
|
9746
|
+
log4?.warn(`[channels] 配置 '${n}' (type=${t}) 被忽略:缺少必填字段 '${f}'`);
|
|
9747
|
+
return false;
|
|
9748
|
+
}
|
|
9749
|
+
}
|
|
9750
|
+
if (t === "exec") {
|
|
9751
|
+
const hasArgv = Array.isArray(o["argv"]) && o["argv"].length > 0;
|
|
9752
|
+
const hasCmd = typeof o["command"] === "string" && o["command"].length > 0;
|
|
9753
|
+
if (!hasArgv && !hasCmd) {
|
|
9754
|
+
log4?.warn(`[channels] 配置 '${n}' (type=exec) 被忽略:argv 与 command 均为空`);
|
|
9755
|
+
return false;
|
|
9756
|
+
}
|
|
9757
|
+
}
|
|
9758
|
+
return true;
|
|
9759
|
+
}
|
|
9760
|
+
function resolveGlobalConfigPath() {
|
|
9761
|
+
return path4.join(globalConfigDir(), "channels.json");
|
|
9762
|
+
}
|
|
9763
|
+
function resolveProjectConfigPaths(root) {
|
|
9764
|
+
const r = root ?? _activatedDirectory ?? process.cwd();
|
|
9765
|
+
return {
|
|
9766
|
+
recommended: path4.join(projectConfigDir(r), "channels.json"),
|
|
9767
|
+
legacy: path4.join(projectConfigDir(r), "config", "channels.json")
|
|
9768
|
+
};
|
|
9769
|
+
}
|
|
9770
|
+
var _warnedKeys2 = new Set;
|
|
9771
|
+
function warnOnce3(key, msg, log4) {
|
|
9772
|
+
if (_warnedKeys2.has(key))
|
|
9773
|
+
return;
|
|
9774
|
+
_warnedKeys2.add(key);
|
|
9775
|
+
log4.warn(msg);
|
|
9776
|
+
}
|
|
9777
|
+
function loadChannelsFromEnv(log4 = fallbackLog) {
|
|
9532
9778
|
const raw = process.env.CODEFORGE_CHANNELS_JSON;
|
|
9533
9779
|
if (!raw)
|
|
9534
9780
|
return [];
|
|
@@ -9536,22 +9782,103 @@ function loadChannelsFromEnv() {
|
|
|
9536
9782
|
const parsed = JSON.parse(raw);
|
|
9537
9783
|
if (!Array.isArray(parsed))
|
|
9538
9784
|
return [];
|
|
9539
|
-
return parsed.filter((c) => isChannel(c));
|
|
9785
|
+
return parsed.filter((c) => isChannel(c, log4));
|
|
9540
9786
|
} catch {
|
|
9787
|
+
log4.warn(`[channels] CODEFORGE_CHANNELS_JSON JSON 解析失败,忽略整段 env 配置`);
|
|
9541
9788
|
return [];
|
|
9542
9789
|
}
|
|
9543
9790
|
}
|
|
9544
|
-
function
|
|
9545
|
-
|
|
9546
|
-
|
|
9547
|
-
|
|
9548
|
-
|
|
9549
|
-
|
|
9791
|
+
function loadChannelsFromGlobal(log4 = fallbackLog) {
|
|
9792
|
+
const filePath = resolveGlobalConfigPath();
|
|
9793
|
+
const raw = loadJsonIfExists(filePath);
|
|
9794
|
+
if (!raw)
|
|
9795
|
+
return [];
|
|
9796
|
+
try {
|
|
9797
|
+
const st = statSync2(filePath);
|
|
9798
|
+
const perm = st.mode & 511;
|
|
9799
|
+
if ((perm & 63) !== 0) {
|
|
9800
|
+
warnOnce3(`global-perm:${filePath}`, `[channels] 全局 channels.json 权限 0${perm.toString(8)} 含 group/other 可读位,` + `含 webhook 时建议 chmod 600 ${filePath}`, log4);
|
|
9801
|
+
}
|
|
9802
|
+
} catch {}
|
|
9803
|
+
const arr = Array.isArray(raw) ? raw : raw.channels;
|
|
9804
|
+
if (!Array.isArray(arr))
|
|
9805
|
+
return [];
|
|
9806
|
+
return arr.filter((c) => isChannel(c, log4));
|
|
9807
|
+
}
|
|
9808
|
+
function loadChannelsFromFile(root, log4 = fallbackLog) {
|
|
9809
|
+
const { recommended, legacy } = resolveProjectConfigPaths(root);
|
|
9810
|
+
let raw = loadJsonIfExists(recommended);
|
|
9811
|
+
if (raw === null) {
|
|
9812
|
+
const legacyRaw = loadJsonIfExists(legacy);
|
|
9813
|
+
if (legacyRaw !== null) {
|
|
9814
|
+
log4.warn(`[channels] 检测到 0.3.10 兼容路径 ${legacy},建议迁移到 0.3.11 推荐路径 ${recommended}` + `(与 KH 配置惯例统一)。两条路径同时存在时只读推荐路径。`);
|
|
9815
|
+
raw = legacyRaw;
|
|
9816
|
+
}
|
|
9817
|
+
}
|
|
9818
|
+
if (!raw)
|
|
9819
|
+
return [];
|
|
9820
|
+
const arr = Array.isArray(raw) ? raw : raw.channels;
|
|
9821
|
+
if (!Array.isArray(arr))
|
|
9822
|
+
return [];
|
|
9823
|
+
return arr.filter((c) => isChannel(c, log4));
|
|
9824
|
+
}
|
|
9825
|
+
function parseRawEnvLength(raw) {
|
|
9826
|
+
if (raw === undefined || raw === "")
|
|
9827
|
+
return "not-set";
|
|
9828
|
+
try {
|
|
9829
|
+
const p = JSON.parse(raw);
|
|
9830
|
+
return Array.isArray(p) ? p.length : "invalid";
|
|
9831
|
+
} catch {
|
|
9832
|
+
return "invalid";
|
|
9833
|
+
}
|
|
9550
9834
|
}
|
|
9551
|
-
function
|
|
9835
|
+
function mergeByName(layers, log4) {
|
|
9836
|
+
const map = new Map;
|
|
9837
|
+
for (const { layer, items } of layers) {
|
|
9838
|
+
for (const c of items) {
|
|
9839
|
+
const prev = map.get(c.name);
|
|
9840
|
+
if (prev) {
|
|
9841
|
+
const typeNote = prev.channel.type !== c.type ? `(type: ${prev.channel.type} → ${c.type})` : "";
|
|
9842
|
+
log4.warn(`[channels] channel '${c.name}' (${layer} 层) 覆盖 ${prev.layer} 层同名配置${typeNote}`);
|
|
9843
|
+
}
|
|
9844
|
+
map.set(c.name, { layer, channel: c });
|
|
9845
|
+
}
|
|
9846
|
+
}
|
|
9847
|
+
return [...map.values()].map((e) => e.channel);
|
|
9848
|
+
}
|
|
9849
|
+
function ensureChannels(log4 = fallbackLog) {
|
|
9552
9850
|
if (_channelsCache)
|
|
9553
9851
|
return _channelsCache;
|
|
9554
|
-
|
|
9852
|
+
const rawEnv = process.env.CODEFORGE_CHANNELS_JSON;
|
|
9853
|
+
const rawLen = parseRawEnvLength(rawEnv);
|
|
9854
|
+
if (rawLen === 0) {
|
|
9855
|
+
log4.warn(`[channels] CODEFORGE_CHANNELS_JSON 显式为空数组,跳过 global + project 两层(全部通知已禁用)`);
|
|
9856
|
+
_channelsCache = [];
|
|
9857
|
+
return _channelsCache;
|
|
9858
|
+
}
|
|
9859
|
+
const fromGlobal = loadChannelsFromGlobal(log4);
|
|
9860
|
+
const fromFile = loadChannelsFromFile(undefined, log4);
|
|
9861
|
+
const fromEnv = rawLen === "not-set" ? [] : loadChannelsFromEnv(log4);
|
|
9862
|
+
if (rawLen === "not-set") {
|
|
9863
|
+
_channelsCache = mergeByName([
|
|
9864
|
+
{ layer: "global", items: fromGlobal },
|
|
9865
|
+
{ layer: "project", items: fromFile }
|
|
9866
|
+
], log4);
|
|
9867
|
+
return _channelsCache;
|
|
9868
|
+
}
|
|
9869
|
+
if (fromEnv.length === 0) {
|
|
9870
|
+
log4.warn(`[channels] CODEFORGE_CHANNELS_JSON 含 ${rawLen === "invalid" ? "非数组" : rawLen} 项但全部被过滤,` + `退回 global + project 两层(global ${fromGlobal.length} 项 + project ${fromFile.length} 项)`);
|
|
9871
|
+
_channelsCache = mergeByName([
|
|
9872
|
+
{ layer: "global", items: fromGlobal },
|
|
9873
|
+
{ layer: "project", items: fromFile }
|
|
9874
|
+
], log4);
|
|
9875
|
+
return _channelsCache;
|
|
9876
|
+
}
|
|
9877
|
+
_channelsCache = mergeByName([
|
|
9878
|
+
{ layer: "global", items: fromGlobal },
|
|
9879
|
+
{ layer: "project", items: fromFile },
|
|
9880
|
+
{ layer: "env", items: fromEnv }
|
|
9881
|
+
], log4);
|
|
9555
9882
|
return _channelsCache;
|
|
9556
9883
|
}
|
|
9557
9884
|
var _limiter = null;
|
|
@@ -9698,6 +10025,7 @@ var TRIGGER_EVENT_TYPES3 = new Set([
|
|
|
9698
10025
|
"subtasks.completed"
|
|
9699
10026
|
]);
|
|
9700
10027
|
var channelsServer = async (ctx) => {
|
|
10028
|
+
_activatedDirectory = ctx.directory;
|
|
9701
10029
|
logLifecycle(PLUGIN_NAME6, "activate", {
|
|
9702
10030
|
directory: ctx.directory,
|
|
9703
10031
|
triggerEventTypes: [...TRIGGER_EVENT_TYPES3],
|
|
@@ -12211,6 +12539,11 @@ import { z as z26 } from "zod";
|
|
|
12211
12539
|
// lib/model-config.ts
|
|
12212
12540
|
import { promises as fs7 } from "node:fs";
|
|
12213
12541
|
import * as path10 from "node:path";
|
|
12542
|
+
|
|
12543
|
+
// lib/model-tier.ts
|
|
12544
|
+
var TIER_ORDER = ["quick", "balanced", "deep", "ultra"];
|
|
12545
|
+
|
|
12546
|
+
// lib/model-config.ts
|
|
12214
12547
|
var CONFIG_FILE = "codeforge.json";
|
|
12215
12548
|
var DEFAULT_RUNTIME_FALLBACK = {
|
|
12216
12549
|
enabled: true,
|
|
@@ -12315,11 +12648,29 @@ function validateConfig(input) {
|
|
|
12315
12648
|
return { ok: false, warnings, error: r.error };
|
|
12316
12649
|
runtime = r.cfg;
|
|
12317
12650
|
}
|
|
12651
|
+
let tiers;
|
|
12652
|
+
if (obj.tiers !== undefined) {
|
|
12653
|
+
const r = normalizeTiers(obj.tiers);
|
|
12654
|
+
if (!r.ok)
|
|
12655
|
+
return { ok: false, warnings, error: r.error };
|
|
12656
|
+
tiers = r.cfg;
|
|
12657
|
+
}
|
|
12318
12658
|
for (const [name, agent] of Object.entries(agents)) {
|
|
12319
12659
|
if (agent.category && !categories?.[agent.category]) {
|
|
12320
12660
|
warnings.push(`agent[${name}].category="${agent.category}" 未在 categories 定义,将忽略 category 链`);
|
|
12321
12661
|
}
|
|
12322
12662
|
}
|
|
12663
|
+
for (const [name, agent] of Object.entries(agents)) {
|
|
12664
|
+
if (!agent.tier)
|
|
12665
|
+
continue;
|
|
12666
|
+
const mappedCat = tiers?.category_map?.[agent.tier];
|
|
12667
|
+
const hasOverride = agent.tier_overrides?.[agent.tier] !== undefined;
|
|
12668
|
+
if (!mappedCat && !hasOverride) {
|
|
12669
|
+
warnings.push(`agent[${name}].tier="${agent.tier}" 既无 models.tiers.category_map["${agent.tier}"] 映射,` + `也无 tier_overrides["${agent.tier}"];该 agent 将不参与 tier 体系(adapter 返 null)。`);
|
|
12670
|
+
} else if (mappedCat && !categories?.[mappedCat] && !hasOverride) {
|
|
12671
|
+
warnings.push(`agent[${name}].tier="${agent.tier}" 通过 category_map 映射到 "${mappedCat}",` + `但 categories.${mappedCat} 不存在;该 agent 将不参与 tier 体系。`);
|
|
12672
|
+
}
|
|
12673
|
+
}
|
|
12323
12674
|
return {
|
|
12324
12675
|
ok: true,
|
|
12325
12676
|
warnings,
|
|
@@ -12328,7 +12679,8 @@ function validateConfig(input) {
|
|
|
12328
12679
|
_doc: typeof obj._doc === "string" ? obj._doc : undefined,
|
|
12329
12680
|
agents,
|
|
12330
12681
|
categories,
|
|
12331
|
-
runtime_fallback: runtime
|
|
12682
|
+
runtime_fallback: runtime,
|
|
12683
|
+
tiers
|
|
12332
12684
|
}
|
|
12333
12685
|
};
|
|
12334
12686
|
}
|
|
@@ -12349,6 +12701,23 @@ function normalizeAgent(name, raw) {
|
|
|
12349
12701
|
const thinking = o.thinking !== undefined ? normalizeThinking(`agent[${name}]`, o.thinking) : undefined;
|
|
12350
12702
|
if (thinking && !thinking.ok)
|
|
12351
12703
|
return { ok: false, error: thinking.error };
|
|
12704
|
+
let tier;
|
|
12705
|
+
if (o.tier !== undefined) {
|
|
12706
|
+
if (typeof o.tier !== "string" || !isValidTierLevel(o.tier)) {
|
|
12707
|
+
return {
|
|
12708
|
+
ok: false,
|
|
12709
|
+
error: `agent[${name}].tier="${String(o.tier)}" 不是合法 TierLevel (期望 ${TIER_ORDER.join("/")})`
|
|
12710
|
+
};
|
|
12711
|
+
}
|
|
12712
|
+
tier = o.tier;
|
|
12713
|
+
}
|
|
12714
|
+
let tierOverrides;
|
|
12715
|
+
if (o.tier_overrides !== undefined) {
|
|
12716
|
+
const r = normalizeTierOverrides(name, o.tier_overrides);
|
|
12717
|
+
if (!r.ok)
|
|
12718
|
+
return { ok: false, error: r.error };
|
|
12719
|
+
tierOverrides = r.value;
|
|
12720
|
+
}
|
|
12352
12721
|
return {
|
|
12353
12722
|
ok: true,
|
|
12354
12723
|
binding: {
|
|
@@ -12357,6 +12726,8 @@ function normalizeAgent(name, raw) {
|
|
|
12357
12726
|
category: typeof o.category === "string" ? o.category : undefined,
|
|
12358
12727
|
thinking: thinking?.value,
|
|
12359
12728
|
fallback_models: fallbacks.value,
|
|
12729
|
+
tier,
|
|
12730
|
+
tier_overrides: tierOverrides,
|
|
12360
12731
|
_doc: typeof o._doc === "string" ? o._doc : undefined
|
|
12361
12732
|
}
|
|
12362
12733
|
};
|
|
@@ -12453,6 +12824,99 @@ function normalizeRuntime(raw) {
|
|
|
12453
12824
|
cfg._doc = o._doc;
|
|
12454
12825
|
return { ok: true, cfg };
|
|
12455
12826
|
}
|
|
12827
|
+
function isValidTierLevel(x) {
|
|
12828
|
+
return typeof x === "string" && TIER_ORDER.includes(x);
|
|
12829
|
+
}
|
|
12830
|
+
function normalizeTierOverrides(agentName, raw) {
|
|
12831
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
12832
|
+
return {
|
|
12833
|
+
ok: false,
|
|
12834
|
+
error: `agent[${agentName}].tier_overrides 必须是 object(不是 array / null / 其他类型)`
|
|
12835
|
+
};
|
|
12836
|
+
}
|
|
12837
|
+
const o = raw;
|
|
12838
|
+
const result = {};
|
|
12839
|
+
for (const [key, value] of Object.entries(o)) {
|
|
12840
|
+
if (!isValidTierLevel(key)) {
|
|
12841
|
+
return {
|
|
12842
|
+
ok: false,
|
|
12843
|
+
error: `agent[${agentName}].tier_overrides.${key}: 非法 TierLevel key (期望 ${TIER_ORDER.join("/")})`
|
|
12844
|
+
};
|
|
12845
|
+
}
|
|
12846
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
12847
|
+
return {
|
|
12848
|
+
ok: false,
|
|
12849
|
+
error: `agent[${agentName}].tier_overrides.${key}: 必须是 object`
|
|
12850
|
+
};
|
|
12851
|
+
}
|
|
12852
|
+
const ov = value;
|
|
12853
|
+
const partial = {};
|
|
12854
|
+
if (ov.level !== undefined) {
|
|
12855
|
+
if (ov.level !== key) {
|
|
12856
|
+
return {
|
|
12857
|
+
ok: false,
|
|
12858
|
+
error: `agent[${agentName}].tier_overrides.${key}.level="${String(ov.level)}" 与 key "${key}" 错配`
|
|
12859
|
+
};
|
|
12860
|
+
}
|
|
12861
|
+
partial.level = key;
|
|
12862
|
+
}
|
|
12863
|
+
if (ov.model !== undefined) {
|
|
12864
|
+
if (typeof ov.model !== "string" || !PROVIDER_MODEL_RE.test(ov.model)) {
|
|
12865
|
+
return {
|
|
12866
|
+
ok: false,
|
|
12867
|
+
error: `agent[${agentName}].tier_overrides.${key}.model="${String(ov.model)}" 格式非法 (期望 <provider>/<id>)`
|
|
12868
|
+
};
|
|
12869
|
+
}
|
|
12870
|
+
partial.model = ov.model;
|
|
12871
|
+
}
|
|
12872
|
+
if (ov.thinking !== undefined) {
|
|
12873
|
+
const t = normalizeThinking(`agent[${agentName}].tier_overrides.${key}`, ov.thinking);
|
|
12874
|
+
if (!t.ok)
|
|
12875
|
+
return { ok: false, error: t.error };
|
|
12876
|
+
partial.thinking = t.value;
|
|
12877
|
+
}
|
|
12878
|
+
if (ov.fallback_models !== undefined) {
|
|
12879
|
+
const f = normalizeFallbackList(`agent[${agentName}].tier_overrides.${key}.fallback_models`, ov.fallback_models);
|
|
12880
|
+
if (!f.ok)
|
|
12881
|
+
return { ok: false, error: f.error };
|
|
12882
|
+
partial.fallback_models = f.value;
|
|
12883
|
+
}
|
|
12884
|
+
result[key] = partial;
|
|
12885
|
+
}
|
|
12886
|
+
return { ok: true, value: result };
|
|
12887
|
+
}
|
|
12888
|
+
function normalizeTiers(raw) {
|
|
12889
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
12890
|
+
return { ok: false, error: "models.tiers 必须是 object(不是 array / null)" };
|
|
12891
|
+
}
|
|
12892
|
+
const o = raw;
|
|
12893
|
+
const cfg = {};
|
|
12894
|
+
if (o.category_map !== undefined) {
|
|
12895
|
+
if (!o.category_map || typeof o.category_map !== "object" || Array.isArray(o.category_map)) {
|
|
12896
|
+
return { ok: false, error: "models.tiers.category_map 必须是 object" };
|
|
12897
|
+
}
|
|
12898
|
+
const map = {};
|
|
12899
|
+
for (const [key, value] of Object.entries(o.category_map)) {
|
|
12900
|
+
if (!isValidTierLevel(key)) {
|
|
12901
|
+
return {
|
|
12902
|
+
ok: false,
|
|
12903
|
+
error: `models.tiers.category_map.${key}: 非法 TierLevel key (期望 ${TIER_ORDER.join("/")})`
|
|
12904
|
+
};
|
|
12905
|
+
}
|
|
12906
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
12907
|
+
return {
|
|
12908
|
+
ok: false,
|
|
12909
|
+
error: `models.tiers.category_map.${key}: 必须是非空字符串(category 名)`
|
|
12910
|
+
};
|
|
12911
|
+
}
|
|
12912
|
+
map[key] = value;
|
|
12913
|
+
}
|
|
12914
|
+
cfg.category_map = map;
|
|
12915
|
+
}
|
|
12916
|
+
if (typeof o._doc === "string")
|
|
12917
|
+
cfg._doc = o._doc;
|
|
12918
|
+
return { ok: true, cfg };
|
|
12919
|
+
}
|
|
12456
12920
|
function resolveAgentModel(config, agent) {
|
|
12457
12921
|
const a = config.agents[agent];
|
|
12458
12922
|
if (!a)
|
|
@@ -15002,25 +15466,17 @@ function extractEndedSessionID(event) {
|
|
|
15002
15466
|
}
|
|
15003
15467
|
return null;
|
|
15004
15468
|
}
|
|
15005
|
-
function
|
|
15006
|
-
if (!
|
|
15007
|
-
return null;
|
|
15008
|
-
const e = event;
|
|
15009
|
-
if (e.type !== "message.part.updated")
|
|
15010
|
-
return null;
|
|
15011
|
-
const part = e.properties?.part;
|
|
15012
|
-
if (!part || typeof part !== "object")
|
|
15013
|
-
return null;
|
|
15014
|
-
const p = part;
|
|
15015
|
-
if (p.type !== "subtask")
|
|
15469
|
+
function extractTaskArgs(args) {
|
|
15470
|
+
if (!args || typeof args !== "object")
|
|
15016
15471
|
return null;
|
|
15017
|
-
|
|
15472
|
+
const a = args;
|
|
15473
|
+
const rawDesc = typeof a["description"] === "string" ? a["description"] : null;
|
|
15474
|
+
const rawPrompt = typeof a["prompt"] === "string" ? a["prompt"] : null;
|
|
15475
|
+
const description26 = rawDesc ?? (rawPrompt ? rawPrompt.slice(0, 60) : null);
|
|
15476
|
+
const subagentType = typeof a["subagent_type"] === "string" && a["subagent_type"] || typeof a["agent"] === "string" && a["agent"] || typeof a["agentType"] === "string" && a["agentType"] || typeof a["agent_type"] === "string" && a["agent_type"] || null;
|
|
15477
|
+
if (!description26 && !subagentType)
|
|
15018
15478
|
return null;
|
|
15019
|
-
|
|
15020
|
-
return null;
|
|
15021
|
-
if (typeof p.description !== "string" || p.description === "")
|
|
15022
|
-
return null;
|
|
15023
|
-
return { parentID: p.sessionID, agent: p.agent, description: p.description };
|
|
15479
|
+
return { description: description26, subagentType };
|
|
15024
15480
|
}
|
|
15025
15481
|
function enqueuePendingTask(parentID, entry, now = Date.now()) {
|
|
15026
15482
|
const ts = entry.ts ?? now;
|
|
@@ -15249,21 +15705,6 @@ var subtaskHeartbeatServer = async (ctx) => {
|
|
|
15249
15705
|
return {
|
|
15250
15706
|
event: async ({ event }) => {
|
|
15251
15707
|
await safeAsync(PLUGIN_NAME13, "event", async () => {
|
|
15252
|
-
const subtask = extractSubtaskPart(event);
|
|
15253
|
-
if (subtask) {
|
|
15254
|
-
enqueuePendingTask(subtask.parentID, {
|
|
15255
|
-
agent: subtask.agent,
|
|
15256
|
-
description: subtask.description
|
|
15257
|
-
});
|
|
15258
|
-
safeWriteLog(PLUGIN_NAME13, {
|
|
15259
|
-
hook: "event",
|
|
15260
|
-
type: "message.part.updated.subtask",
|
|
15261
|
-
parent: subtask.parentID,
|
|
15262
|
-
agent: subtask.agent,
|
|
15263
|
-
description_len: subtask.description.length
|
|
15264
|
-
});
|
|
15265
|
-
return;
|
|
15266
|
-
}
|
|
15267
15708
|
const created = extractCreatedChild(event);
|
|
15268
15709
|
if (created) {
|
|
15269
15710
|
const pending = dequeuePendingTask(created.parentID);
|
|
@@ -15279,7 +15720,8 @@ var subtaskHeartbeatServer = async (ctx) => {
|
|
|
15279
15720
|
child: created.childID,
|
|
15280
15721
|
parent: created.parentID,
|
|
15281
15722
|
pending_task_matched: pending !== null,
|
|
15282
|
-
agent: record.agent
|
|
15723
|
+
agent: record.agent,
|
|
15724
|
+
description_len: record.description?.length ?? 0
|
|
15283
15725
|
});
|
|
15284
15726
|
const startToast = buildStartToast(record);
|
|
15285
15727
|
const sent = await showToast3(client, { ...startToast, duration: START_TOAST_DURATION_MS }, log7);
|
|
@@ -15310,13 +15752,38 @@ var subtaskHeartbeatServer = async (ctx) => {
|
|
|
15310
15752
|
}
|
|
15311
15753
|
});
|
|
15312
15754
|
},
|
|
15313
|
-
"tool.execute.before": async (input) => {
|
|
15314
|
-
|
|
15755
|
+
"tool.execute.before": async (input, output) => {
|
|
15756
|
+
const isTaskTool = input?.tool === "task";
|
|
15757
|
+
if (inflight3.size === 0 && !isTaskTool)
|
|
15315
15758
|
return;
|
|
15316
15759
|
await safeAsync(PLUGIN_NAME13, "tool.execute.before", async () => {
|
|
15317
15760
|
if (!input || typeof input.sessionID !== "string" || typeof input.tool !== "string")
|
|
15318
15761
|
return;
|
|
15319
|
-
|
|
15762
|
+
if (isTaskTool) {
|
|
15763
|
+
const args = output?.args ?? null;
|
|
15764
|
+
safeWriteLog(PLUGIN_NAME13, {
|
|
15765
|
+
hook: "tool.execute.before.task",
|
|
15766
|
+
sessionID: input.sessionID,
|
|
15767
|
+
args_keys: args && typeof args === "object" ? Object.keys(args) : null,
|
|
15768
|
+
args_raw: args
|
|
15769
|
+
});
|
|
15770
|
+
const extracted = extractTaskArgs(args);
|
|
15771
|
+
if (extracted) {
|
|
15772
|
+
enqueuePendingTask(input.sessionID, {
|
|
15773
|
+
agent: extracted.subagentType,
|
|
15774
|
+
description: extracted.description
|
|
15775
|
+
});
|
|
15776
|
+
safeWriteLog(PLUGIN_NAME13, {
|
|
15777
|
+
hook: "tool.execute.before.task.enqueued",
|
|
15778
|
+
parent: input.sessionID,
|
|
15779
|
+
agent: extracted.subagentType,
|
|
15780
|
+
description_len: extracted.description?.length ?? 0
|
|
15781
|
+
});
|
|
15782
|
+
}
|
|
15783
|
+
}
|
|
15784
|
+
if (inflight3.has(input.sessionID)) {
|
|
15785
|
+
recordToolBeat(input.sessionID, input.tool);
|
|
15786
|
+
}
|
|
15320
15787
|
});
|
|
15321
15788
|
}
|
|
15322
15789
|
};
|
|
@@ -17473,7 +17940,7 @@ var handler20 = toolPolicyServer;
|
|
|
17473
17940
|
init_opencode_plugin_helpers();
|
|
17474
17941
|
import { existsSync as existsSync5 } from "node:fs";
|
|
17475
17942
|
import { homedir as homedir7 } from "node:os";
|
|
17476
|
-
import { join as
|
|
17943
|
+
import { join as join16 } from "node:path";
|
|
17477
17944
|
|
|
17478
17945
|
// lib/update-checker-impl.ts
|
|
17479
17946
|
import { createHash as createHash6 } from "node:crypto";
|
|
@@ -17485,12 +17952,12 @@ import {
|
|
|
17485
17952
|
readFileSync as readFileSync4,
|
|
17486
17953
|
readdirSync,
|
|
17487
17954
|
renameSync,
|
|
17488
|
-
statSync as
|
|
17955
|
+
statSync as statSync3,
|
|
17489
17956
|
unlinkSync,
|
|
17490
17957
|
writeFileSync as writeFileSync2
|
|
17491
17958
|
} from "node:fs";
|
|
17492
17959
|
import { homedir as homedir6, tmpdir } from "node:os";
|
|
17493
|
-
import { dirname as dirname7, join as
|
|
17960
|
+
import { dirname as dirname7, join as join15 } from "node:path";
|
|
17494
17961
|
import { fileURLToPath } from "node:url";
|
|
17495
17962
|
import * as https from "node:https";
|
|
17496
17963
|
import * as zlib from "node:zlib";
|
|
@@ -17498,7 +17965,7 @@ import * as zlib from "node:zlib";
|
|
|
17498
17965
|
// lib/version-injected.ts
|
|
17499
17966
|
function getInjectedVersion() {
|
|
17500
17967
|
try {
|
|
17501
|
-
const v = "0.3.
|
|
17968
|
+
const v = "0.3.11";
|
|
17502
17969
|
if (typeof v === "string" && /^\d+\.\d+\.\d+/.test(v)) {
|
|
17503
17970
|
return v;
|
|
17504
17971
|
}
|
|
@@ -17588,17 +18055,17 @@ function readLocalVersion() {
|
|
|
17588
18055
|
try {
|
|
17589
18056
|
const here = fileURLToPath(import.meta.url);
|
|
17590
18057
|
const root = dirname7(dirname7(here));
|
|
17591
|
-
const pkg = JSON.parse(readFileSync4(
|
|
18058
|
+
const pkg = JSON.parse(readFileSync4(join15(root, "package.json"), "utf8"));
|
|
17592
18059
|
return typeof pkg.version === "string" ? pkg.version : "0.0.0";
|
|
17593
18060
|
} catch {
|
|
17594
18061
|
return "0.0.0";
|
|
17595
18062
|
}
|
|
17596
18063
|
}
|
|
17597
18064
|
function defaultCacheDir() {
|
|
17598
|
-
return process.env["CODEFORGE_CACHE_DIR"] ??
|
|
18065
|
+
return process.env["CODEFORGE_CACHE_DIR"] ?? join15(homedir6(), ".cache", "codeforge");
|
|
17599
18066
|
}
|
|
17600
18067
|
function defaultCacheFile() {
|
|
17601
|
-
return
|
|
18068
|
+
return join15(defaultCacheDir(), "update-check.json");
|
|
17602
18069
|
}
|
|
17603
18070
|
function readCache(file) {
|
|
17604
18071
|
try {
|
|
@@ -17754,14 +18221,14 @@ function defaultHttpFetcher(url, timeoutMs) {
|
|
|
17754
18221
|
});
|
|
17755
18222
|
}
|
|
17756
18223
|
async function downloadAndExtractBundle(opts) {
|
|
17757
|
-
const tmpRoot = opts.tmpDir ?? mkdtempSync(
|
|
18224
|
+
const tmpRoot = opts.tmpDir ?? mkdtempSync(join15(tmpdir(), "codeforge-update-"));
|
|
17758
18225
|
mkdirSync3(tmpRoot, { recursive: true });
|
|
17759
18226
|
const fetcher = opts.tarballFetcher ?? defaultBinaryFetcher;
|
|
17760
18227
|
const tarballBuf = await fetcher(opts.tarballUrl);
|
|
17761
18228
|
verifyIntegrity(tarballBuf, opts.expectedIntegrity);
|
|
17762
18229
|
const tarBuf = zlib.gunzipSync(tarballBuf);
|
|
17763
18230
|
extractTarToDir(tarBuf, tmpRoot);
|
|
17764
|
-
const bundlePath =
|
|
18231
|
+
const bundlePath = join15(tmpRoot, "package", "dist", "index.js");
|
|
17765
18232
|
if (!existsSync4(bundlePath)) {
|
|
17766
18233
|
throw new Error(`bundle_not_found: ${bundlePath}`);
|
|
17767
18234
|
}
|
|
@@ -17801,11 +18268,11 @@ function extractTarToDir(tarBuf, destRoot) {
|
|
|
17801
18268
|
offset += 512;
|
|
17802
18269
|
if (typeFlag === "0" || typeFlag === "" || typeFlag === "\x00") {
|
|
17803
18270
|
const fileBuf = tarBuf.subarray(offset, offset + size);
|
|
17804
|
-
const dest =
|
|
18271
|
+
const dest = join15(destRoot, fullName);
|
|
17805
18272
|
mkdirSync3(dirname7(dest), { recursive: true });
|
|
17806
18273
|
writeFileSync2(dest, fileBuf);
|
|
17807
18274
|
} else if (typeFlag === "5") {
|
|
17808
|
-
mkdirSync3(
|
|
18275
|
+
mkdirSync3(join15(destRoot, fullName), { recursive: true });
|
|
17809
18276
|
}
|
|
17810
18277
|
offset += Math.ceil(size / 512) * 512;
|
|
17811
18278
|
}
|
|
@@ -17914,10 +18381,10 @@ function cleanupOldBackups(target, keep) {
|
|
|
17914
18381
|
const base = target.substring(dir.length + 1);
|
|
17915
18382
|
const prefix = `${base}.bak.`;
|
|
17916
18383
|
const all = readdirSync(dir).filter((f) => f.startsWith(prefix)).map((f) => {
|
|
17917
|
-
const full =
|
|
18384
|
+
const full = join15(dir, f);
|
|
17918
18385
|
let mtimeMs = 0;
|
|
17919
18386
|
try {
|
|
17920
|
-
mtimeMs =
|
|
18387
|
+
mtimeMs = statSync3(full).mtimeMs;
|
|
17921
18388
|
} catch {}
|
|
17922
18389
|
return { full, mtimeMs };
|
|
17923
18390
|
}).sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
@@ -17936,7 +18403,7 @@ function loadCompatibility(opts) {
|
|
|
17936
18403
|
const root = opts?.cwd ?? inferPluginRoot();
|
|
17937
18404
|
if (!root)
|
|
17938
18405
|
return null;
|
|
17939
|
-
file =
|
|
18406
|
+
file = join15(root, "compatibility.json");
|
|
17940
18407
|
}
|
|
17941
18408
|
if (!existsSync4(file))
|
|
17942
18409
|
return null;
|
|
@@ -18156,14 +18623,14 @@ function detectOpencodeVersion() {
|
|
|
18156
18623
|
}
|
|
18157
18624
|
function getOpencodeBundlePath() {
|
|
18158
18625
|
const candidates = [];
|
|
18159
|
-
candidates.push(
|
|
18626
|
+
candidates.push(join16(homedir7(), ".config", "opencode", "codeforge", "index.js"));
|
|
18160
18627
|
if (process.platform === "win32") {
|
|
18161
18628
|
const appData = process.env["APPDATA"];
|
|
18162
18629
|
if (appData)
|
|
18163
|
-
candidates.push(
|
|
18630
|
+
candidates.push(join16(appData, "opencode", "codeforge", "index.js"));
|
|
18164
18631
|
const localAppData = process.env["LOCALAPPDATA"];
|
|
18165
18632
|
if (localAppData)
|
|
18166
|
-
candidates.push(
|
|
18633
|
+
candidates.push(join16(localAppData, "opencode", "codeforge", "index.js"));
|
|
18167
18634
|
}
|
|
18168
18635
|
for (const c of candidates) {
|
|
18169
18636
|
if (existsSync5(c))
|