@andyqiu/codeforge 0.5.5 → 0.5.6

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.
Files changed (2) hide show
  1. package/dist/index.js +250 -96
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -8205,11 +8205,11 @@ function shouldStopByStuck(history, cfg) {
8205
8205
  async function withTimeout4(p, timeoutMs) {
8206
8206
  if (timeoutMs <= 0)
8207
8207
  return await p;
8208
- return await new Promise((resolve15, reject) => {
8208
+ return await new Promise((resolve16, reject) => {
8209
8209
  const timer = setTimeout(() => reject(new Error(`timeout after ${timeoutMs}ms`)), timeoutMs);
8210
8210
  Promise.resolve(p).then((v) => {
8211
8211
  clearTimeout(timer);
8212
- resolve15(v);
8212
+ resolve16(v);
8213
8213
  }, (err) => {
8214
8214
  clearTimeout(timer);
8215
8215
  reject(err);
@@ -14676,8 +14676,8 @@ async function sendParentNotice(client, sessionID, text, opts = {}) {
14676
14676
  id: makePartId(),
14677
14677
  type: "text",
14678
14678
  text,
14679
- synthetic: false,
14680
- ignored: false
14679
+ synthetic: true,
14680
+ ignored: true
14681
14681
  }
14682
14682
  ]
14683
14683
  }
@@ -15879,7 +15879,12 @@ var codeforgeToolsServer = async (ctx) => {
15879
15879
  ...sid ? { currentSessionId: sid } : {},
15880
15880
  ...sid ? {
15881
15881
  sendProgress: async (state, detail) => {
15882
- await sendParentNotice(ctx.client, sid, `[merge-loop] ${state}: ${detail}`, ctx.directory ? { directory: ctx.directory } : {});
15882
+ const client = ctx.client;
15883
+ if (typeof client?.tui?.showToast === "function") {
15884
+ await client.tui.showToast({
15885
+ body: { message: `[merge-loop] ${state}: ${detail}`, variant: "default", duration: 4000, title: "CodeForge" }
15886
+ });
15887
+ }
15883
15888
  }
15884
15889
  } : {}
15885
15890
  });
@@ -17856,9 +17861,103 @@ var handler12 = modelFallbackServer;
17856
17861
 
17857
17862
  // plugins/subtask-heartbeat.ts
17858
17863
  import { promises as fsPromises } from "node:fs";
17859
- import * as path18 from "node:path";
17864
+ import * as path19 from "node:path";
17860
17865
  init_runtime_paths();
17861
17866
  init_global_config();
17867
+
17868
+ // lib/parent-map-store.ts
17869
+ init_runtime_paths();
17870
+ import { promises as fs15 } from "node:fs";
17871
+ import * as path18 from "node:path";
17872
+ var PARENT_MAP_VERSION = 1;
17873
+ var PARENT_MAP_LOCK_TIMEOUT_MS = 2000;
17874
+ function parentMapDir(mainRoot) {
17875
+ return path18.join(runtimeDir(path18.resolve(mainRoot), { ensure: false }), "session-worktrees");
17876
+ }
17877
+ function parentMapPath(mainRoot) {
17878
+ return path18.join(parentMapDir(mainRoot), "parent-map.json");
17879
+ }
17880
+ function parentMapLockPath(mainRoot) {
17881
+ return path18.join(parentMapDir(mainRoot), "parent-map.lock");
17882
+ }
17883
+ async function readParentMapFile(mainRoot) {
17884
+ const file = parentMapPath(mainRoot);
17885
+ try {
17886
+ const raw = await fs15.readFile(file, "utf8");
17887
+ const parsed = JSON.parse(raw);
17888
+ if (parsed.version !== PARENT_MAP_VERSION || !Array.isArray(parsed.entries)) {
17889
+ return { version: PARENT_MAP_VERSION, entries: [] };
17890
+ }
17891
+ return parsed;
17892
+ } catch (err) {
17893
+ const e = err;
17894
+ if (e.code === "ENOENT")
17895
+ return { version: PARENT_MAP_VERSION, entries: [] };
17896
+ return { version: PARENT_MAP_VERSION, entries: [] };
17897
+ }
17898
+ }
17899
+ async function writeParentMapFile(mainRoot, payload) {
17900
+ const file = parentMapPath(mainRoot);
17901
+ await fs15.mkdir(path18.dirname(file), { recursive: true });
17902
+ const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
17903
+ await fs15.writeFile(tmp, JSON.stringify(payload, null, 2), "utf8");
17904
+ await fs15.rename(tmp, file);
17905
+ }
17906
+ async function loadParentMap(mainRoot) {
17907
+ const out = new Map;
17908
+ let file;
17909
+ try {
17910
+ file = await readParentMapFile(mainRoot);
17911
+ } catch {
17912
+ return out;
17913
+ }
17914
+ for (const e of file.entries) {
17915
+ if (!e || typeof e.childID !== "string" || typeof e.parentID !== "string")
17916
+ continue;
17917
+ if (!e.childID || !e.parentID)
17918
+ continue;
17919
+ const ts = typeof e.ts === "number" ? e.ts : Date.now();
17920
+ out.set(e.childID, { parentID: e.parentID, ts });
17921
+ }
17922
+ return out;
17923
+ }
17924
+ async function writeParentMap(mainRoot, snapshot, opts = {}) {
17925
+ const lockPath = parentMapLockPath(mainRoot);
17926
+ await fs15.mkdir(path18.dirname(lockPath), { recursive: true });
17927
+ const lockOpts = {
17928
+ timeoutMs: opts.timeoutMs ?? PARENT_MAP_LOCK_TIMEOUT_MS,
17929
+ ...opts
17930
+ };
17931
+ await withFileLock(lockPath, async () => {
17932
+ const entries = [];
17933
+ for (const [childID, v] of snapshot.entries()) {
17934
+ entries.push({ childID, parentID: v.parentID, ts: v.ts });
17935
+ }
17936
+ await writeParentMapFile(mainRoot, { version: PARENT_MAP_VERSION, entries });
17937
+ }, lockOpts);
17938
+ }
17939
+ async function appendParentEntry(mainRoot, childID, parentID, ts, opts = {}) {
17940
+ if (!childID || !parentID)
17941
+ return;
17942
+ const lockPath = parentMapLockPath(mainRoot);
17943
+ await fs15.mkdir(path18.dirname(lockPath), { recursive: true });
17944
+ const lockOpts = {
17945
+ timeoutMs: opts.timeoutMs ?? PARENT_MAP_LOCK_TIMEOUT_MS,
17946
+ ...opts
17947
+ };
17948
+ await withFileLock(lockPath, async () => {
17949
+ const current = await readParentMapFile(mainRoot);
17950
+ const idx = current.entries.findIndex((e) => e.childID === childID);
17951
+ if (idx >= 0) {
17952
+ current.entries[idx] = { childID, parentID, ts };
17953
+ } else {
17954
+ current.entries.push({ childID, parentID, ts });
17955
+ }
17956
+ await writeParentMapFile(mainRoot, current);
17957
+ }, lockOpts);
17958
+ }
17959
+
17960
+ // plugins/subtask-heartbeat.ts
17862
17961
  var PLUGIN_NAME13 = "subtask-heartbeat";
17863
17962
  logLifecycle(PLUGIN_NAME13, "import", {});
17864
17963
  var HEARTBEAT_INTERVAL_MS2 = 30000;
@@ -17876,6 +17975,14 @@ var inflight3 = new Map;
17876
17975
  var pendingTask = new Map;
17877
17976
  var sessionParentMap = new Map;
17878
17977
  var _parentParseFailLogged = 0;
17978
+ var _persistRoot = null;
17979
+ function _bulkInjectSessionParentMap(entries) {
17980
+ for (const e of entries) {
17981
+ if (!e.childID || !e.parentID)
17982
+ continue;
17983
+ sessionParentMap.set(e.childID, { parentID: e.parentID, ts: e.ts });
17984
+ }
17985
+ }
17879
17986
  function _snapshotInflight() {
17880
17987
  return [...inflight3.values()].map((r) => ({ ...r }));
17881
17988
  }
@@ -17884,21 +17991,29 @@ function recordSessionParent(childID, parentID, now = Date.now()) {
17884
17991
  return;
17885
17992
  if (sessionParentMap.has(childID)) {
17886
17993
  sessionParentMap.set(childID, { parentID, ts: now });
17887
- return;
17888
- }
17889
- if (sessionParentMap.size >= SESSION_PARENT_MAP_MAX_SIZE) {
17890
- let oldestKey = null;
17891
- let oldestTs = Number.POSITIVE_INFINITY;
17892
- for (const [k, v] of sessionParentMap.entries()) {
17893
- if (v.ts < oldestTs) {
17894
- oldestTs = v.ts;
17895
- oldestKey = k;
17994
+ } else {
17995
+ if (sessionParentMap.size >= SESSION_PARENT_MAP_MAX_SIZE) {
17996
+ let oldestKey = null;
17997
+ let oldestTs = Number.POSITIVE_INFINITY;
17998
+ for (const [k, v] of sessionParentMap.entries()) {
17999
+ if (v.ts < oldestTs) {
18000
+ oldestTs = v.ts;
18001
+ oldestKey = k;
18002
+ }
17896
18003
  }
18004
+ if (oldestKey !== null)
18005
+ sessionParentMap.delete(oldestKey);
17897
18006
  }
17898
- if (oldestKey !== null)
17899
- sessionParentMap.delete(oldestKey);
18007
+ sessionParentMap.set(childID, { parentID, ts: now });
18008
+ }
18009
+ if (_persistRoot) {
18010
+ appendParentEntry(_persistRoot, childID, parentID, now).catch((err) => {
18011
+ log7.warn("appendParentEntry 失败(已隔离)", {
18012
+ error: err instanceof Error ? err.message : String(err),
18013
+ childID
18014
+ });
18015
+ });
17900
18016
  }
17901
- sessionParentMap.set(childID, { parentID, ts: now });
17902
18017
  }
17903
18018
  function lookupParentSessionId(childID, now = Date.now()) {
17904
18019
  const entry = sessionParentMap.get(childID);
@@ -17912,6 +18027,15 @@ function lookupParentSessionId(childID, now = Date.now()) {
17912
18027
  }
17913
18028
  function deleteSessionParent(childID) {
17914
18029
  sessionParentMap.delete(childID);
18030
+ if (_persistRoot) {
18031
+ const snapshot = new Map(sessionParentMap);
18032
+ writeParentMap(_persistRoot, snapshot).catch((err) => {
18033
+ log7.warn("writeParentMap (delete) 失败(已隔离)", {
18034
+ error: err instanceof Error ? err.message : String(err),
18035
+ childID
18036
+ });
18037
+ });
18038
+ }
17915
18039
  }
17916
18040
  function sweepExpiredSessionParents(now = Date.now()) {
17917
18041
  let removed = 0;
@@ -17921,6 +18045,15 @@ function sweepExpiredSessionParents(now = Date.now()) {
17921
18045
  removed++;
17922
18046
  }
17923
18047
  }
18048
+ if (removed > 0 && _persistRoot) {
18049
+ const snapshot = new Map(sessionParentMap);
18050
+ writeParentMap(_persistRoot, snapshot).catch((err) => {
18051
+ log7.warn("writeParentMap (sweep) 失败(已隔离)", {
18052
+ error: err instanceof Error ? err.message : String(err),
18053
+ removed
18054
+ });
18055
+ });
18056
+ }
17924
18057
  return removed;
17925
18058
  }
17926
18059
  function detectUnparsedParentID(event) {
@@ -18218,7 +18351,7 @@ function buildFailureNotice(r, endedType, logPath, worktreePath, now = Date.now(
18218
18351
  }
18219
18352
  async function appendSubagentLog(filePath, line, log7) {
18220
18353
  try {
18221
- await fsPromises.mkdir(path18.dirname(filePath), { recursive: true });
18354
+ await fsPromises.mkdir(path19.dirname(filePath), { recursive: true });
18222
18355
  await fsPromises.appendFile(filePath, line + `
18223
18356
  `, "utf8");
18224
18357
  } catch (err) {
@@ -18277,6 +18410,27 @@ var subtaskHeartbeatServer = async (ctx) => {
18277
18410
  });
18278
18411
  const client = ctx.client;
18279
18412
  const cwd = ctx.directory;
18413
+ _persistRoot = cwd;
18414
+ try {
18415
+ const restored = await loadParentMap(cwd);
18416
+ if (restored.size > 0) {
18417
+ const entries = [...restored.entries()].map(([childID, v]) => ({
18418
+ childID,
18419
+ parentID: v.parentID,
18420
+ ts: v.ts
18421
+ }));
18422
+ _bulkInjectSessionParentMap(entries);
18423
+ safeWriteLog(PLUGIN_NAME13, {
18424
+ hook: "activate",
18425
+ type: "parent-map.restore",
18426
+ restored: restored.size
18427
+ });
18428
+ }
18429
+ } catch (err) {
18430
+ log7.warn("loadParentMap 失败(已隔离),降级为空表", {
18431
+ error: err instanceof Error ? err.message : String(err)
18432
+ });
18433
+ }
18280
18434
  const interval = setInterval(() => {
18281
18435
  safeAsync(PLUGIN_NAME13, "interval", async () => {
18282
18436
  const swept = sweepExpiredPendingTasks();
@@ -18551,7 +18705,7 @@ var handler14 = parallelStatusServer;
18551
18705
 
18552
18706
  // plugins/parallel-tool-nudge.ts
18553
18707
  import { readFileSync as readFileSync4, readdirSync as readdirSync2, statSync as statSync4 } from "node:fs";
18554
- import { join as join15 } from "node:path";
18708
+ import { join as join16 } from "node:path";
18555
18709
  import { homedir as homedir6 } from "node:os";
18556
18710
  var PLUGIN_NAME15 = "parallel-tool-nudge";
18557
18711
  logLifecycle(PLUGIN_NAME15, "import", {});
@@ -18607,10 +18761,10 @@ function loadAgentToolsMap(rootDir, opts = {}) {
18607
18761
  const reader = opts.reader ?? defaultReader2;
18608
18762
  const dirReader = opts.dirReader ?? defaultDirReader2;
18609
18763
  const dirExists = opts.dirExists ?? defaultDirExists2;
18610
- const homeAgentsDir = opts.homeAgentsDir ?? join15(homedir6(), ".config", "opencode", "agents");
18764
+ const homeAgentsDir = opts.homeAgentsDir ?? join16(homedir6(), ".config", "opencode", "agents");
18611
18765
  const candidateDirs = [
18612
- join15(rootDir, ".codeforge", "agents"),
18613
- join15(rootDir, "agents"),
18766
+ join16(rootDir, ".codeforge", "agents"),
18767
+ join16(rootDir, "agents"),
18614
18768
  homeAgentsDir
18615
18769
  ];
18616
18770
  const result = new Map;
@@ -18633,20 +18787,20 @@ function loadAgentToolsMap(rootDir, opts = {}) {
18633
18787
  for (const entry of entries) {
18634
18788
  if (!entry.endsWith(".md"))
18635
18789
  continue;
18636
- const path19 = join15(dir, entry);
18790
+ const path20 = join16(dir, entry);
18637
18791
  let content;
18638
18792
  try {
18639
- content = reader(path19);
18793
+ content = reader(path20);
18640
18794
  } catch (err) {
18641
18795
  log8.warn(`agent.md 读取失败(已跳过)`, {
18642
- path: path19,
18796
+ path: path20,
18643
18797
  error: err instanceof Error ? err.message : String(err)
18644
18798
  });
18645
18799
  continue;
18646
18800
  }
18647
18801
  const parsed = parseAgentFrontmatter(content);
18648
18802
  if (!parsed) {
18649
- log8.warn(`agent frontmatter 解析失败(已跳过)`, { path: path19 });
18803
+ log8.warn(`agent frontmatter 解析失败(已跳过)`, { path: path20 });
18650
18804
  continue;
18651
18805
  }
18652
18806
  if (result.has(parsed.name))
@@ -18837,19 +18991,19 @@ var handler16 = async (_ctx3) => {
18837
18991
  };
18838
18992
 
18839
18993
  // lib/event-stream.ts
18840
- import { promises as fs15 } from "node:fs";
18994
+ import { promises as fs16 } from "node:fs";
18841
18995
  init_runtime_paths();
18842
- import * as path19 from "node:path";
18996
+ import * as path20 from "node:path";
18843
18997
  async function loadSession(id, opts = {}) {
18844
18998
  const file = resolveSessionFile(id, opts);
18845
- const raw = await fs15.readFile(file, "utf8");
18999
+ const raw = await fs16.readFile(file, "utf8");
18846
19000
  return parseJsonl(id, raw);
18847
19001
  }
18848
19002
  async function listSessions(opts = {}) {
18849
19003
  const dir = resolveDir(opts);
18850
19004
  let entries;
18851
19005
  try {
18852
- entries = await fs15.readdir(dir, { withFileTypes: true });
19006
+ entries = await fs16.readdir(dir, { withFileTypes: true });
18853
19007
  } catch (err) {
18854
19008
  if (err.code === "ENOENT")
18855
19009
  return [];
@@ -18859,10 +19013,10 @@ async function listSessions(opts = {}) {
18859
19013
  for (const e of entries) {
18860
19014
  if (!e.isFile() || !e.name.endsWith(".jsonl"))
18861
19015
  continue;
18862
- const file = path19.join(dir, e.name);
19016
+ const file = path20.join(dir, e.name);
18863
19017
  const id = e.name.replace(/\.jsonl$/, "");
18864
19018
  try {
18865
- const stat = await fs15.stat(file);
19019
+ const stat = await fs16.stat(file);
18866
19020
  const headerLine = await readFirstLine(file);
18867
19021
  let started_at = stat.birthtimeMs;
18868
19022
  if (headerLine) {
@@ -18886,11 +19040,11 @@ async function listSessions(opts = {}) {
18886
19040
  return out;
18887
19041
  }
18888
19042
  function resolveDir(opts = {}) {
18889
- const root = path19.resolve(opts.root ?? process.cwd());
18890
- return opts.sessions_dir ? path19.resolve(root, opts.sessions_dir) : path19.join(runtimeDir(root), "sessions");
19043
+ const root = path20.resolve(opts.root ?? process.cwd());
19044
+ return opts.sessions_dir ? path20.resolve(root, opts.sessions_dir) : path20.join(runtimeDir(root), "sessions");
18891
19045
  }
18892
19046
  function resolveSessionFile(id, opts = {}) {
18893
- return path19.join(resolveDir(opts), `${id}.jsonl`);
19047
+ return path20.join(resolveDir(opts), `${id}.jsonl`);
18894
19048
  }
18895
19049
  function parseJsonl(id, raw) {
18896
19050
  const events = [];
@@ -18925,7 +19079,7 @@ function isEvent(obj) {
18925
19079
  }
18926
19080
  async function readFirstLine(file) {
18927
19081
  const buf = Buffer.alloc(4096);
18928
- const fh = await fs15.open(file, "r");
19082
+ const fh = await fs16.open(file, "r");
18929
19083
  try {
18930
19084
  const { bytesRead } = await fh.read(buf, 0, buf.length, 0);
18931
19085
  const s = buf.subarray(0, bytesRead).toString("utf8");
@@ -19154,11 +19308,11 @@ function isRecoveryWorthShowing(plan) {
19154
19308
 
19155
19309
  // lib/block-pending.ts
19156
19310
  init_runtime_paths();
19157
- import { promises as fs16 } from "node:fs";
19158
- import * as path20 from "node:path";
19311
+ import { promises as fs17 } from "node:fs";
19312
+ import * as path21 from "node:path";
19159
19313
  function blockPendingFilePath(absRoot) {
19160
19314
  const rd = runtimeDir(absRoot, { ensure: false });
19161
- return path20.join(rd, "sessions", "autonomous-blocks.ndjson");
19315
+ return path21.join(rd, "sessions", "autonomous-blocks.ndjson");
19162
19316
  }
19163
19317
  function consumeLockPath(absRoot) {
19164
19318
  return blockPendingFilePath(absRoot) + ".consume.lock";
@@ -19167,7 +19321,7 @@ async function scanBlockPending(absRoot, filterSessionId) {
19167
19321
  const file = blockPendingFilePath(absRoot);
19168
19322
  let raw;
19169
19323
  try {
19170
- raw = await fs16.readFile(file, "utf8");
19324
+ raw = await fs17.readFile(file, "utf8");
19171
19325
  } catch {
19172
19326
  return [];
19173
19327
  }
@@ -19223,7 +19377,7 @@ async function markBlocksConsumed(absRoot, entries) {
19223
19377
  if (entries.length === 0)
19224
19378
  return;
19225
19379
  const file = blockPendingFilePath(absRoot);
19226
- await fs16.mkdir(path20.dirname(file), { recursive: true });
19380
+ await fs17.mkdir(path21.dirname(file), { recursive: true });
19227
19381
  const now = new Date().toISOString();
19228
19382
  const lines = entries.map((e) => ({
19229
19383
  type: "consume",
@@ -19234,7 +19388,7 @@ async function markBlocksConsumed(absRoot, entries) {
19234
19388
  `) + `
19235
19389
  `;
19236
19390
  await withFileLock(consumeLockPath(absRoot), async () => {
19237
- await fs16.appendFile(file, lines, "utf8");
19391
+ await fs17.appendFile(file, lines, "utf8");
19238
19392
  });
19239
19393
  }
19240
19394
 
@@ -19372,8 +19526,8 @@ var sessionRecoveryServer = async (ctx) => {
19372
19526
  var handler17 = sessionRecoveryServer;
19373
19527
 
19374
19528
  // plugins/subtasks.ts
19375
- import { promises as fs17 } from "node:fs";
19376
- import * as path21 from "node:path";
19529
+ import { promises as fs18 } from "node:fs";
19530
+ import * as path22 from "node:path";
19377
19531
 
19378
19532
  // lib/parallel-merge.ts
19379
19533
  init_autonomy();
@@ -20191,14 +20345,14 @@ function describe8(err) {
20191
20345
  }
20192
20346
  }
20193
20347
  function sleep2(ms) {
20194
- return new Promise((resolve15) => setTimeout(resolve15, ms));
20348
+ return new Promise((resolve16) => setTimeout(resolve16, ms));
20195
20349
  }
20196
20350
 
20197
20351
  // plugins/subtasks.ts
20198
20352
  init_runtime_paths();
20199
20353
  var PLUGIN_NAME18 = "subtasks";
20200
20354
  function getLogFile(root = process.cwd()) {
20201
- return path21.join(runtimeDir(root), "logs", "subtasks.log");
20355
+ return path22.join(runtimeDir(root), "logs", "subtasks.log");
20202
20356
  }
20203
20357
  var VERB_RE = /^([a-zA-Z]{3,12})/;
20204
20358
  var CN_VERBS = [
@@ -20503,8 +20657,8 @@ async function writeLog(level, msg, data) {
20503
20657
  `;
20504
20658
  try {
20505
20659
  const logFile = getLogFile();
20506
- await fs17.mkdir(path21.dirname(logFile), { recursive: true });
20507
- await fs17.appendFile(logFile, line, "utf8");
20660
+ await fs18.mkdir(path22.dirname(logFile), { recursive: true });
20661
+ await fs18.appendFile(logFile, line, "utf8");
20508
20662
  } catch {}
20509
20663
  }
20510
20664
  logLifecycle(PLUGIN_NAME18, "import");
@@ -21020,12 +21174,12 @@ var tokenManagerServer = async (ctx) => {
21020
21174
  var handler20 = tokenManagerServer;
21021
21175
 
21022
21176
  // plugins/tool-policy.ts
21023
- import { promises as fs18 } from "node:fs";
21024
- import * as path23 from "node:path";
21177
+ import { promises as fs19 } from "node:fs";
21178
+ import * as path24 from "node:path";
21025
21179
  init_autonomy();
21026
21180
 
21027
21181
  // lib/file-regex-acl.ts
21028
- import * as path22 from "node:path";
21182
+ import * as path23 from "node:path";
21029
21183
  function compileRule(r) {
21030
21184
  if (r instanceof RegExp)
21031
21185
  return r;
@@ -21091,7 +21245,7 @@ function normalizePath2(p) {
21091
21245
  let s = p.replace(/\\/g, "/");
21092
21246
  if (s.startsWith("./"))
21093
21247
  s = s.slice(2);
21094
- s = path22.posix.normalize(s);
21248
+ s = path23.posix.normalize(s);
21095
21249
  return s;
21096
21250
  }
21097
21251
  function checkFileAccess(acl, file, op) {
@@ -21201,11 +21355,11 @@ function decideToolCall(ctx, cfg = {}, currentAgent) {
21201
21355
  action = "deny";
21202
21356
  return { action, reasons, autonomy: a, acl: aclResults };
21203
21357
  }
21204
- var POLICY_PATH = path23.join(".codeforge", "policy.json");
21358
+ var POLICY_PATH = path24.join(".codeforge", "policy.json");
21205
21359
  async function loadPolicy(root = process.cwd()) {
21206
- const file = path23.join(root, POLICY_PATH);
21360
+ const file = path24.join(root, POLICY_PATH);
21207
21361
  try {
21208
- const raw = await fs18.readFile(file, "utf8");
21362
+ const raw = await fs19.readFile(file, "utf8");
21209
21363
  const data = JSON.parse(raw);
21210
21364
  return data;
21211
21365
  } catch {
@@ -21315,7 +21469,7 @@ var handler21 = toolPolicyServer;
21315
21469
  // plugins/update-checker.ts
21316
21470
  import { existsSync as existsSync5 } from "node:fs";
21317
21471
  import { homedir as homedir8 } from "node:os";
21318
- import { join as join21 } from "node:path";
21472
+ import { join as join22 } from "node:path";
21319
21473
 
21320
21474
  // lib/update-checker-impl.ts
21321
21475
  import { createHash as createHash5 } from "node:crypto";
@@ -21332,7 +21486,7 @@ import {
21332
21486
  writeFileSync as writeFileSync2
21333
21487
  } from "node:fs";
21334
21488
  import { homedir as homedir7, tmpdir } from "node:os";
21335
- import { dirname as dirname12, join as join20 } from "node:path";
21489
+ import { dirname as dirname13, join as join21 } from "node:path";
21336
21490
  import { fileURLToPath } from "node:url";
21337
21491
  import * as https from "node:https";
21338
21492
  import * as zlib from "node:zlib";
@@ -21340,7 +21494,7 @@ import * as zlib from "node:zlib";
21340
21494
  // lib/version-injected.ts
21341
21495
  function getInjectedVersion() {
21342
21496
  try {
21343
- const v = "0.5.5";
21497
+ const v = "0.5.6";
21344
21498
  if (typeof v === "string" && /^\d+\.\d+\.\d+/.test(v)) {
21345
21499
  return v;
21346
21500
  }
@@ -21429,18 +21583,18 @@ function readLocalVersion() {
21429
21583
  return injected;
21430
21584
  try {
21431
21585
  const here = fileURLToPath(import.meta.url);
21432
- const root = dirname12(dirname12(here));
21433
- const pkg = JSON.parse(readFileSync5(join20(root, "package.json"), "utf8"));
21586
+ const root = dirname13(dirname13(here));
21587
+ const pkg = JSON.parse(readFileSync5(join21(root, "package.json"), "utf8"));
21434
21588
  return typeof pkg.version === "string" ? pkg.version : "0.0.0";
21435
21589
  } catch {
21436
21590
  return "0.0.0";
21437
21591
  }
21438
21592
  }
21439
21593
  function defaultCacheDir() {
21440
- return process.env["CODEFORGE_CACHE_DIR"] ?? join20(homedir7(), ".cache", "codeforge");
21594
+ return process.env["CODEFORGE_CACHE_DIR"] ?? join21(homedir7(), ".cache", "codeforge");
21441
21595
  }
21442
21596
  function defaultCacheFile() {
21443
- return join20(defaultCacheDir(), "update-check.json");
21597
+ return join21(defaultCacheDir(), "update-check.json");
21444
21598
  }
21445
21599
  function readCache(file) {
21446
21600
  try {
@@ -21458,7 +21612,7 @@ function readCache(file) {
21458
21612
  }
21459
21613
  function writeCache(file, entry) {
21460
21614
  try {
21461
- mkdirSync3(dirname12(file), { recursive: true });
21615
+ mkdirSync3(dirname13(file), { recursive: true });
21462
21616
  writeFileSync2(file, JSON.stringify(entry, null, 2), "utf8");
21463
21617
  } catch {}
21464
21618
  }
@@ -21477,7 +21631,7 @@ function fetchLatestTagFromGitHub(repo) {
21477
21631
  });
21478
21632
  }
21479
21633
  function getJsonWithRedirect(url, hopsLeft) {
21480
- return new Promise((resolve15, reject) => {
21634
+ return new Promise((resolve16, reject) => {
21481
21635
  const u = new URL(url);
21482
21636
  const headers = {
21483
21637
  "User-Agent": "codeforge-update-checker",
@@ -21501,12 +21655,12 @@ function getJsonWithRedirect(url, hopsLeft) {
21501
21655
  return;
21502
21656
  }
21503
21657
  const next = new URL(res.headers.location, url).toString();
21504
- getJsonWithRedirect(next, hopsLeft - 1).then(resolve15, reject);
21658
+ getJsonWithRedirect(next, hopsLeft - 1).then(resolve16, reject);
21505
21659
  return;
21506
21660
  }
21507
21661
  if (status === 404) {
21508
21662
  res.resume();
21509
- resolve15(null);
21663
+ resolve16(null);
21510
21664
  return;
21511
21665
  }
21512
21666
  if (status >= 400) {
@@ -21517,7 +21671,7 @@ function getJsonWithRedirect(url, hopsLeft) {
21517
21671
  let body = "";
21518
21672
  res.setEncoding("utf8");
21519
21673
  res.on("data", (chunk) => body += chunk);
21520
- res.on("end", () => resolve15(body));
21674
+ res.on("end", () => resolve16(body));
21521
21675
  });
21522
21676
  req.on("timeout", () => {
21523
21677
  req.destroy();
@@ -21557,7 +21711,7 @@ async function fetchLatestFromNpm(opts) {
21557
21711
  return { version, tarballUrl, integrity };
21558
21712
  }
21559
21713
  function defaultHttpFetcher(url, timeoutMs) {
21560
- return new Promise((resolve15, reject) => {
21714
+ return new Promise((resolve16, reject) => {
21561
21715
  const u = new URL(url);
21562
21716
  const headers = {
21563
21717
  "User-Agent": "codeforge-update-checker",
@@ -21574,7 +21728,7 @@ function defaultHttpFetcher(url, timeoutMs) {
21574
21728
  const status = res.statusCode ?? 0;
21575
21729
  if (status === 404) {
21576
21730
  res.resume();
21577
- resolve15(null);
21731
+ resolve16(null);
21578
21732
  return;
21579
21733
  }
21580
21734
  if (status >= 400) {
@@ -21585,7 +21739,7 @@ function defaultHttpFetcher(url, timeoutMs) {
21585
21739
  let body = "";
21586
21740
  res.setEncoding("utf8");
21587
21741
  res.on("data", (chunk) => body += chunk);
21588
- res.on("end", () => resolve15(body));
21742
+ res.on("end", () => resolve16(body));
21589
21743
  });
21590
21744
  req.on("timeout", () => {
21591
21745
  req.destroy();
@@ -21596,14 +21750,14 @@ function defaultHttpFetcher(url, timeoutMs) {
21596
21750
  });
21597
21751
  }
21598
21752
  async function downloadAndExtractBundle(opts) {
21599
- const tmpRoot = opts.tmpDir ?? mkdtempSync(join20(tmpdir(), "codeforge-update-"));
21753
+ const tmpRoot = opts.tmpDir ?? mkdtempSync(join21(tmpdir(), "codeforge-update-"));
21600
21754
  mkdirSync3(tmpRoot, { recursive: true });
21601
21755
  const fetcher = opts.tarballFetcher ?? defaultBinaryFetcher;
21602
21756
  const tarballBuf = await fetcher(opts.tarballUrl);
21603
21757
  verifyIntegrity(tarballBuf, opts.expectedIntegrity);
21604
21758
  const tarBuf = zlib.gunzipSync(tarballBuf);
21605
21759
  extractTarToDir(tarBuf, tmpRoot);
21606
- const bundlePath = join20(tmpRoot, "package", "dist", "index.js");
21760
+ const bundlePath = join21(tmpRoot, "package", "dist", "index.js");
21607
21761
  if (!existsSync4(bundlePath)) {
21608
21762
  throw new Error(`bundle_not_found: ${bundlePath}`);
21609
21763
  }
@@ -21643,11 +21797,11 @@ function extractTarToDir(tarBuf, destRoot) {
21643
21797
  offset += 512;
21644
21798
  if (typeFlag === "0" || typeFlag === "" || typeFlag === "\x00") {
21645
21799
  const fileBuf = tarBuf.subarray(offset, offset + size);
21646
- const dest = join20(destRoot, fullName);
21647
- mkdirSync3(dirname12(dest), { recursive: true });
21800
+ const dest = join21(destRoot, fullName);
21801
+ mkdirSync3(dirname13(dest), { recursive: true });
21648
21802
  writeFileSync2(dest, fileBuf);
21649
21803
  } else if (typeFlag === "5") {
21650
- mkdirSync3(join20(destRoot, fullName), { recursive: true });
21804
+ mkdirSync3(join21(destRoot, fullName), { recursive: true });
21651
21805
  }
21652
21806
  offset += Math.ceil(size / 512) * 512;
21653
21807
  }
@@ -21656,7 +21810,7 @@ function defaultBinaryFetcher(url) {
21656
21810
  return downloadBinary(url, 3);
21657
21811
  }
21658
21812
  function downloadBinary(url, hopsLeft) {
21659
- return new Promise((resolve15, reject) => {
21813
+ return new Promise((resolve16, reject) => {
21660
21814
  const u = new URL(url);
21661
21815
  const req = https.request({
21662
21816
  host: u.hostname,
@@ -21674,7 +21828,7 @@ function downloadBinary(url, hopsLeft) {
21674
21828
  return;
21675
21829
  }
21676
21830
  const next = new URL(res.headers.location, url).toString();
21677
- downloadBinary(next, hopsLeft - 1).then(resolve15, reject);
21831
+ downloadBinary(next, hopsLeft - 1).then(resolve16, reject);
21678
21832
  return;
21679
21833
  }
21680
21834
  if (status >= 400) {
@@ -21684,7 +21838,7 @@ function downloadBinary(url, hopsLeft) {
21684
21838
  }
21685
21839
  const chunks = [];
21686
21840
  res.on("data", (chunk) => chunks.push(chunk));
21687
- res.on("end", () => resolve15(Buffer.concat(chunks)));
21841
+ res.on("end", () => resolve16(Buffer.concat(chunks)));
21688
21842
  });
21689
21843
  req.on("timeout", () => {
21690
21844
  req.destroy();
@@ -21700,7 +21854,7 @@ function atomicReplaceBundle(opts) {
21700
21854
  if (!existsSync4(source)) {
21701
21855
  throw new Error(`atomic_source_missing: ${source}`);
21702
21856
  }
21703
- mkdirSync3(dirname12(target), { recursive: true });
21857
+ mkdirSync3(dirname13(target), { recursive: true });
21704
21858
  const newPath = `${target}.new`;
21705
21859
  const backupPath = `${target}.bak.${oldVersion}`;
21706
21860
  let strategy = "rename";
@@ -21752,11 +21906,11 @@ function cleanupOldBackups(target, keep) {
21752
21906
  if (keep <= 0)
21753
21907
  return;
21754
21908
  try {
21755
- const dir = dirname12(target);
21909
+ const dir = dirname13(target);
21756
21910
  const base = target.substring(dir.length + 1);
21757
21911
  const prefix = `${base}.bak.`;
21758
21912
  const all = readdirSync3(dir).filter((f) => f.startsWith(prefix)).map((f) => {
21759
- const full = join20(dir, f);
21913
+ const full = join21(dir, f);
21760
21914
  let mtimeMs = 0;
21761
21915
  try {
21762
21916
  mtimeMs = statSync5(full).mtimeMs;
@@ -21778,7 +21932,7 @@ function loadCompatibility(opts) {
21778
21932
  const root = opts?.cwd ?? inferPluginRoot();
21779
21933
  if (!root)
21780
21934
  return null;
21781
- file = join20(root, "compatibility.json");
21935
+ file = join21(root, "compatibility.json");
21782
21936
  }
21783
21937
  if (!existsSync4(file))
21784
21938
  return null;
@@ -21803,7 +21957,7 @@ function loadCompatibility(opts) {
21803
21957
  function inferPluginRoot() {
21804
21958
  try {
21805
21959
  const here = fileURLToPath(import.meta.url);
21806
- return dirname12(dirname12(here));
21960
+ return dirname13(dirname13(here));
21807
21961
  } catch {
21808
21962
  return null;
21809
21963
  }
@@ -21998,14 +22152,14 @@ function detectOpencodeVersion() {
21998
22152
  }
21999
22153
  function getOpencodeBundlePath() {
22000
22154
  const candidates = [];
22001
- candidates.push(join21(homedir8(), ".config", "opencode", "codeforge", "index.js"));
22155
+ candidates.push(join22(homedir8(), ".config", "opencode", "codeforge", "index.js"));
22002
22156
  if (process.platform === "win32") {
22003
22157
  const appData = process.env["APPDATA"];
22004
22158
  if (appData)
22005
- candidates.push(join21(appData, "opencode", "codeforge", "index.js"));
22159
+ candidates.push(join22(appData, "opencode", "codeforge", "index.js"));
22006
22160
  const localAppData = process.env["LOCALAPPDATA"];
22007
22161
  if (localAppData)
22008
- candidates.push(join21(localAppData, "opencode", "codeforge", "index.js"));
22162
+ candidates.push(join22(localAppData, "opencode", "codeforge", "index.js"));
22009
22163
  }
22010
22164
  for (const c of candidates) {
22011
22165
  if (existsSync5(c))
@@ -22066,11 +22220,11 @@ async function postToast(ctx, message) {
22066
22220
  var handler22 = updateCheckerServer;
22067
22221
 
22068
22222
  // plugins/workflow-engine.ts
22069
- import * as path25 from "node:path";
22223
+ import * as path26 from "node:path";
22070
22224
 
22071
22225
  // lib/workflow-loader.ts
22072
- import { promises as fs19 } from "node:fs";
22073
- import * as path24 from "node:path";
22226
+ import { promises as fs20 } from "node:fs";
22227
+ import * as path25 from "node:path";
22074
22228
  import { z as z32 } from "zod";
22075
22229
  var ActionSchema = z32.object({
22076
22230
  tool: z32.string().min(1, "action.tool 不能为空"),
@@ -22156,7 +22310,7 @@ function parseWorkflowYaml(yaml, sourcePath = "<inline>") {
22156
22310
  async function loadWorkflowFromFile(filePath) {
22157
22311
  let txt;
22158
22312
  try {
22159
- txt = await fs19.readFile(filePath, "utf8");
22313
+ txt = await fs20.readFile(filePath, "utf8");
22160
22314
  } catch (err) {
22161
22315
  return {
22162
22316
  ok: false,
@@ -22171,7 +22325,7 @@ async function loadWorkflowsFromDir(dir) {
22171
22325
  const failed = [];
22172
22326
  let entries;
22173
22327
  try {
22174
- entries = await fs19.readdir(dir);
22328
+ entries = await fs20.readdir(dir);
22175
22329
  } catch (err) {
22176
22330
  const e = err;
22177
22331
  if (e.code === "ENOENT")
@@ -22183,7 +22337,7 @@ async function loadWorkflowsFromDir(dir) {
22183
22337
  continue;
22184
22338
  if (!/\.ya?ml$/i.test(name))
22185
22339
  continue;
22186
- const full = path24.join(dir, name);
22340
+ const full = path25.join(dir, name);
22187
22341
  const r = await loadWorkflowFromFile(full);
22188
22342
  if (r.ok)
22189
22343
  loaded.push(r);
@@ -22573,7 +22727,7 @@ async function handleCommandInvoked(raw, workflowsDir = "workflows") {
22573
22727
  }
22574
22728
  var workflowEngineServer = async (ctx) => {
22575
22729
  const directory = ctx.directory ?? process.cwd();
22576
- const workflowsDir = path25.join(directory, "workflows");
22730
+ const workflowsDir = path26.join(directory, "workflows");
22577
22731
  ensureRegistry(workflowsDir).catch((err) => fallbackLog2.warn(`[${PLUGIN_NAME23}] preload workflows failed`, {
22578
22732
  error: err instanceof Error ? err.message : String(err)
22579
22733
  }));
@@ -22617,7 +22771,7 @@ var workflowEngineServer = async (ctx) => {
22617
22771
  var handler23 = workflowEngineServer;
22618
22772
 
22619
22773
  // plugins/session-worktree-guard.ts
22620
- import path26 from "node:path";
22774
+ import path27 from "node:path";
22621
22775
  var PLUGIN_NAME24 = "session-worktree-guard";
22622
22776
  logLifecycle(PLUGIN_NAME24, "import", {});
22623
22777
  var WRITE_INTENT_RE = />(?!=)|\btee\b|\brm\b|\bmv\b|\bcp\b|\bmkdir\b|\btouch\b|\bchmod\b|\bchown\b|\bln\b/;
@@ -22646,7 +22800,7 @@ var WRITE_TOOLS = new Set(["write", "edit", "ast_edit"]);
22646
22800
  function rewritePath(value, mainRoot, worktreeRoot) {
22647
22801
  if (!value)
22648
22802
  return null;
22649
- const resolved = path26.isAbsolute(value) ? value : path26.resolve(mainRoot, value);
22803
+ const resolved = path27.isAbsolute(value) ? value : path27.resolve(mainRoot, value);
22650
22804
  if (resolved === mainRoot)
22651
22805
  return worktreeRoot;
22652
22806
  const prefix = mainRoot.endsWith("/") ? mainRoot : mainRoot + "/";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andyqiu/codeforge",
3
- "version": "0.5.5",
3
+ "version": "0.5.6",
4
4
  "description": "CodeForge — opencode 的零侵入扩展包",
5
5
  "type": "module",
6
6
  "private": false,