opencode-acp 1.18.1-pr.416.174 → 1.18.1-pr.416.176

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 CHANGED
@@ -3020,8 +3020,8 @@ function saveSessionState(sessionState, logger, sessionName) {
3020
3020
  queue = { pending: [], waiters: [], draining: false };
3021
3021
  saveQueues.set(key, queue);
3022
3022
  }
3023
- const promise = new Promise((resolve2, reject) => {
3024
- queue.waiters.push({ resolve: resolve2, reject });
3023
+ const promise = new Promise((resolve3, reject) => {
3024
+ queue.waiters.push({ resolve: resolve3, reject });
3025
3025
  });
3026
3026
  queue.pending.push({
3027
3027
  sessionId: sessionState.sessionId,
@@ -4619,8 +4619,8 @@ function restoreForkCompressionState(state, forkMessages, parent, parentMessages
4619
4619
  effectiveToolIds,
4620
4620
  // [Issue #415] The parent record is raw persisted JSON; legacy
4621
4621
  // 4-digit boundary refs must be normalized like every other ref.
4622
- startId: migrateMessageRef(block.startId),
4623
- endId: migrateMessageRef(block.endId)
4622
+ startId: typeof block.startId === "string" ? migrateMessageRef(block.startId) : block.startId,
4623
+ endId: typeof block.endId === "string" ? migrateMessageRef(block.endId) : block.endId
4624
4624
  });
4625
4625
  }
4626
4626
  const activeParentIds = new Set(parent.prune.messages.activeBlockIds);
@@ -9598,6 +9598,102 @@ function extractTextContent(msg) {
9598
9598
  return textParts.join(" ").replace(/\s+/g, " ").trim();
9599
9599
  }
9600
9600
 
9601
+ // lib/compress/tofile-target.ts
9602
+ import * as fsp from "fs/promises";
9603
+ import { homedir as homedir3, tmpdir } from "os";
9604
+ import { dirname as dirname2, join as join4, relative, resolve } from "path";
9605
+ async function resolveSafeToFileTarget(targetPath, options) {
9606
+ const osTmp = tmpdir();
9607
+ const allowedDirs = options?.allowedDirs ?? [osTmp, join4(homedir3(), ".cache", "opencode")];
9608
+ const dirList = allowedDirs.join(" or ");
9609
+ if (typeof targetPath !== "string" || targetPath.length === 0) {
9610
+ return {
9611
+ ok: false,
9612
+ error: `Error: toFile path must be under ${dirList}. Got: ${targetPath}`
9613
+ };
9614
+ }
9615
+ const resolvedPath = resolve(targetPath);
9616
+ const lexicallyAllowed = allowedDirs.some((dir) => {
9617
+ const rel = relative(resolve(dir), resolvedPath);
9618
+ return rel === "" || !rel.startsWith("..") && !rel.startsWith("/") && !isWinAbsolute(rel);
9619
+ });
9620
+ if (!lexicallyAllowed) {
9621
+ return {
9622
+ ok: false,
9623
+ error: `Error: toFile path must be under ${dirList}. Got: ${targetPath}`
9624
+ };
9625
+ }
9626
+ let realAncestor = null;
9627
+ const tail = [];
9628
+ let current = resolvedPath;
9629
+ while (true) {
9630
+ try {
9631
+ realAncestor = await fsp.realpath(current);
9632
+ break;
9633
+ } catch (err) {
9634
+ const code = err.code;
9635
+ if (code !== "ENOENT" && code !== "ENOTDIR") {
9636
+ return {
9637
+ ok: false,
9638
+ error: `Error: cannot validate toFile path: ${err.message}`
9639
+ };
9640
+ }
9641
+ const parent = dirname2(current);
9642
+ if (parent === current) {
9643
+ return {
9644
+ ok: false,
9645
+ error: `Error: cannot validate toFile path: no existing ancestor found`
9646
+ };
9647
+ }
9648
+ tail.unshift(basename(current));
9649
+ current = parent;
9650
+ }
9651
+ }
9652
+ const physicalPath = tail.length > 0 ? join4(realAncestor, ...tail) : realAncestor;
9653
+ const realAllowedDirs = [];
9654
+ for (const dir of allowedDirs) {
9655
+ try {
9656
+ realAllowedDirs.push(await fsp.realpath(resolve(dir)));
9657
+ } catch {
9658
+ }
9659
+ }
9660
+ const physicallyAllowed = realAllowedDirs.every((dir) => !containsOrIs(join4(dir), physicalPath) === false) && realAllowedDirs.some((dir) => containsOrIs(join4(dir), physicalPath));
9661
+ if (!physicallyAllowed) {
9662
+ return {
9663
+ ok: false,
9664
+ error: `Error: toFile path resolves outside the allowed directories via a symlink. Got: ${targetPath}`
9665
+ };
9666
+ }
9667
+ try {
9668
+ const stat = await fsp.lstat(resolvedPath);
9669
+ if (stat.isSymbolicLink()) {
9670
+ return {
9671
+ ok: false,
9672
+ error: `Error: toFile target exists as a symlink and will not be followed. Got: ${targetPath}`
9673
+ };
9674
+ }
9675
+ } catch (err) {
9676
+ const code = err.code;
9677
+ if (code !== "ENOENT") {
9678
+ return {
9679
+ ok: false,
9680
+ error: `Error: cannot validate toFile path: ${err.message}`
9681
+ };
9682
+ }
9683
+ }
9684
+ return { ok: true, filePath: resolvedPath };
9685
+ }
9686
+ function isWinAbsolute(p) {
9687
+ return /^[a-zA-Z]:[\\/]/.test(p);
9688
+ }
9689
+ function basename(p) {
9690
+ return p.split(/[\\/]/).filter(Boolean).pop() ?? p;
9691
+ }
9692
+ function containsOrIs(root, p) {
9693
+ const rel = relative(root, p);
9694
+ return rel === "" || !rel.startsWith("..") && !rel.startsWith("/") && !isWinAbsolute(rel);
9695
+ }
9696
+
9601
9697
  // lib/compress/decompress.ts
9602
9698
  async function prepareDecompressSession(ctx, toolCtx) {
9603
9699
  await toolCtx.ask({
@@ -9631,7 +9727,13 @@ function resolveTargets(args, state, rawMessages, logger) {
9631
9727
  if (mode.mode === "block") {
9632
9728
  return resolveSingleBlockTarget(messagesState, args.blockId);
9633
9729
  }
9634
- return resolveRangeTarget(state, rawMessages, args.startId, args.endId, logger);
9730
+ return resolveRangeTarget(
9731
+ state,
9732
+ rawMessages,
9733
+ args.startId,
9734
+ args.endId,
9735
+ logger
9736
+ );
9635
9737
  }
9636
9738
  function resolveSingleBlockTarget(messagesState, blockIdArg) {
9637
9739
  const targetBlockId = parseBlockIdArg(blockIdArg);
@@ -9743,11 +9845,21 @@ IMPORTANT:
9743
9845
  - Do NOT call this tool in parallel with compress \u2014 their state mutations may conflict.`;
9744
9846
  function buildSchema2() {
9745
9847
  return {
9746
- blockId: tool3.schema.string().optional().describe('Block reference to decompress (e.g., "b0", "b2"). Mutually exclusive with startId/endId.'),
9747
- startId: tool3.schema.string().optional().describe('Range start: message ref (e.g., "m00150") or block ref (e.g., "b2"). Used with endId.'),
9748
- endId: tool3.schema.string().optional().describe('Range end: message ref (e.g., "m00200") or block ref (e.g., "b5"). Used with startId.'),
9749
- toFile: tool3.schema.string().optional().describe("If provided, writes restored content to this file path instead of inflating context. Block stays compressed. Path must be under /tmp or ~/.cache/opencode/. Example: '/tmp/block52.txt'"),
9750
- full: tool3.schema.boolean().optional().describe("If true, restores ALL content down to original messages (multi-level decompress). Default: false \u2014 restores one tier up (e.g., decompressing a T2 block restores T1 summaries, not raw messages). Use full:true only when you need the exact original content and have context budget for it.")
9848
+ blockId: tool3.schema.string().optional().describe(
9849
+ 'Block reference to decompress (e.g., "b0", "b2"). Mutually exclusive with startId/endId.'
9850
+ ),
9851
+ startId: tool3.schema.string().optional().describe(
9852
+ 'Range start: message ref (e.g., "m00150") or block ref (e.g., "b2"). Used with endId.'
9853
+ ),
9854
+ endId: tool3.schema.string().optional().describe(
9855
+ 'Range end: message ref (e.g., "m00200") or block ref (e.g., "b5"). Used with startId.'
9856
+ ),
9857
+ toFile: tool3.schema.string().optional().describe(
9858
+ "If provided, writes restored content to this file path instead of inflating context. Block stays compressed. Path must be under /tmp or ~/.cache/opencode/. Example: '/tmp/block52.txt'"
9859
+ ),
9860
+ full: tool3.schema.boolean().optional().describe(
9861
+ "If true, restores ALL content down to original messages (multi-level decompress). Default: false \u2014 restores one tier up (e.g., decompressing a T2 block restores T1 summaries, not raw messages). Use full:true only when you need the exact original content and have context budget for it."
9862
+ )
9751
9863
  };
9752
9864
  }
9753
9865
  function extractMessageId(m) {
@@ -9771,7 +9883,12 @@ function createDecompressTool(factoryCtx) {
9771
9883
  const contextUsageBefore = effectiveLimitBefore ? Math.round(
9772
9884
  getCurrentTokenUsage(ctx.state, rawMessages) / effectiveLimitBefore.limit * 100
9773
9885
  ) : void 0;
9774
- const resolved = resolveTargets(args, ctx.state, rawMessages, ctx.logger);
9886
+ const resolved = resolveTargets(
9887
+ args,
9888
+ ctx.state,
9889
+ rawMessages,
9890
+ ctx.logger
9891
+ );
9775
9892
  if (!resolved.ok) {
9776
9893
  return resolved.error;
9777
9894
  }
@@ -9787,19 +9904,9 @@ function createDecompressTool(factoryCtx) {
9787
9904
  }
9788
9905
  if (args.toFile) {
9789
9906
  const targetPath = args.toFile;
9790
- const os = await import("os");
9791
- const path = await import("path");
9792
- const allowedDirs = [
9793
- os.tmpdir() + "/",
9794
- path.join(os.homedir(), ".cache", "opencode") + "/"
9795
- ];
9796
- const resolvedPath = path.resolve(targetPath);
9797
- const isAllowed = allowedDirs.some((dir) => {
9798
- const rel = path.relative(dir, resolvedPath);
9799
- return rel === "" || !rel.startsWith("..") && !path.isAbsolute(rel);
9800
- });
9801
- if (!isAllowed) {
9802
- return `Error: toFile path must be under ${os.tmpdir()} or ~/.cache/opencode/. Got: ${targetPath}`;
9907
+ const safe = await resolveSafeToFileTarget(targetPath);
9908
+ if (!safe.ok) {
9909
+ return safe.error;
9803
9910
  }
9804
9911
  const msgIdSet = /* @__PURE__ */ new Set();
9805
9912
  for (const block of activeBlocks) {
@@ -9809,9 +9916,16 @@ function createDecompressTool(factoryCtx) {
9809
9916
  }
9810
9917
  const blockMessages = rawMessages.filter((m) => msgIdSet.has(extractMessageId(m)));
9811
9918
  const lines2 = blockMessages.map(extractMessageText2);
9812
- const { writeFile: writeFile4 } = await import("fs/promises");
9813
9919
  const fileContent = lines2.length > 0 ? lines2.join("\n\n---\n\n") : targets[0]?.blocks[0]?.summary ?? "(no content available)";
9814
- await writeFile4(targetPath, fileContent, "utf-8");
9920
+ const fsp2 = await import("fs/promises");
9921
+ const { constants } = await import("fs");
9922
+ const flags = constants.O_WRONLY | constants.O_CREAT | constants.O_TRUNC | (process.platform === "win32" ? 0 : constants.O_NOFOLLOW);
9923
+ const handle = await fsp2.open(safe.filePath, flags, 384);
9924
+ try {
9925
+ await handle.writeFile(fileContent, "utf-8");
9926
+ } finally {
9927
+ await handle.close();
9928
+ }
9815
9929
  const displayIds2 = targets.map((t) => `b${t.displayId}`).join(", ");
9816
9930
  return `Block(s) ${displayIds2} content (${blockMessages.length} messages, ${fileContent.length} chars) written to ${targetPath}. Block(s) stay compressed \u2014 context unchanged. Use read tool to access specific parts.`;
9817
9931
  }
@@ -9888,7 +10002,9 @@ function isLiveBlock(block) {
9888
10002
  return block.active && !block.deactivatedByUser && !block.deactivatedByUserDeep;
9889
10003
  }
9890
10004
  function rangeKey(startId, endId) {
9891
- return `${startId}::${endId}`;
10005
+ const s = typeof startId === "string" ? migrateMessageRef(startId) : startId;
10006
+ const e = typeof endId === "string" ? migrateMessageRef(endId) : endId;
10007
+ return `${s}::${e}`;
9892
10008
  }
9893
10009
  function rewriteCompressInput(part, liveKeys) {
9894
10010
  if (part.type !== "tool") return null;
@@ -10551,10 +10667,10 @@ function hideFailedCompressCalls(messages) {
10551
10667
 
10552
10668
  // lib/logger.ts
10553
10669
  import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
10554
- import { join as join4 } from "path";
10670
+ import { join as join5 } from "path";
10555
10671
  import { existsSync as existsSync4 } from "fs";
10556
- import { homedir as homedir3 } from "os";
10557
- var LOG_VERSION = true ? "1.18.1-pr.416.174" : "dev";
10672
+ import { homedir as homedir4 } from "os";
10673
+ var LOG_VERSION = true ? "1.18.1-pr.416.176" : "dev";
10558
10674
  var LEVEL_RANK = {
10559
10675
  debug: 10,
10560
10676
  info: 20,
@@ -10574,8 +10690,8 @@ var Logger = class {
10574
10690
  constructor(enabled, level) {
10575
10691
  const resolved = level !== void 0 && LEVEL_RANK[level] !== void 0 ? level : void 0;
10576
10692
  this.level = resolved ?? (enabled ? "debug" : "warn");
10577
- const configHome = process.env.XDG_CONFIG_HOME || join4(homedir3(), ".config");
10578
- this.logDir = join4(configHome, "opencode", "logs", "acp");
10693
+ const configHome = process.env.XDG_CONFIG_HOME || join5(homedir4(), ".config");
10694
+ this.logDir = join5(configHome, "opencode", "logs", "acp");
10579
10695
  }
10580
10696
  /** Back-compat view of the old flag: true only at full debug verbosity. */
10581
10697
  get enabled() {
@@ -10637,11 +10753,11 @@ var Logger = class {
10637
10753
  const dataStr = this.formatData(data);
10638
10754
  const logLine = `${timestamp} ${level.padEnd(5)} ${component}: ${message}${dataStr ? " | " + dataStr : ""} | v=${LOG_VERSION}
10639
10755
  `;
10640
- const dailyLogDir = join4(this.logDir, "daily");
10756
+ const dailyLogDir = join5(this.logDir, "daily");
10641
10757
  if (!existsSync4(dailyLogDir)) {
10642
10758
  await mkdir2(dailyLogDir, { recursive: true });
10643
10759
  }
10644
- const logFile = join4(dailyLogDir, `${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.log`);
10760
+ const logFile = join5(dailyLogDir, `${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.log`);
10645
10761
  await writeFile2(logFile, logLine, { flag: "a" });
10646
10762
  } catch (error) {
10647
10763
  }
@@ -10752,7 +10868,7 @@ var Logger = class {
10752
10868
  async saveContext(sessionId, messages) {
10753
10869
  if (this.level !== "debug") return;
10754
10870
  try {
10755
- const contextDir = join4(this.logDir, "context", sessionId);
10871
+ const contextDir = join5(this.logDir, "context", sessionId);
10756
10872
  if (!existsSync4(contextDir)) {
10757
10873
  await mkdir2(contextDir, { recursive: true });
10758
10874
  }
@@ -10760,9 +10876,9 @@ var Logger = class {
10760
10876
  (msg) => msg.parts && msg.parts.length > 0
10761
10877
  );
10762
10878
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
10763
- const contextFile = join4(contextDir, `${timestamp}.json`);
10879
+ const contextFile = join5(contextDir, `${timestamp}.json`);
10764
10880
  await writeFile2(contextFile, JSON.stringify(minimized, null, 2));
10765
- const versionFile = join4(contextDir, "_version");
10881
+ const versionFile = join5(contextDir, "_version");
10766
10882
  if (!existsSync4(versionFile)) {
10767
10883
  await writeFile2(versionFile, LOG_VERSION);
10768
10884
  }
@@ -10773,8 +10889,8 @@ var Logger = class {
10773
10889
 
10774
10890
  // lib/prompts/store.ts
10775
10891
  import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, statSync as statSync2 } from "fs";
10776
- import { join as join5, dirname as dirname2 } from "path";
10777
- import { homedir as homedir4 } from "os";
10892
+ import { join as join6, dirname as dirname3 } from "path";
10893
+ import { homedir as homedir5 } from "os";
10778
10894
 
10779
10895
  // lib/prompts/system.ts
10780
10896
  var ACP_STATUS_TOOL_LINE_RANGES = `- \`acp_status\` \u2014 Context status with compressible ranges. No args = overview + ranges. \`scope:"uncompressed"\` for range view; add \`view:"messages"\` for per-message listing with \`tool\`/\`sort\` filters. \`scope:"compressed"\` for block details.`;
@@ -11081,7 +11197,7 @@ function createBundledRuntimePrompts(candidatesEnabled) {
11081
11197
  function findOpencodeDir2(startDir) {
11082
11198
  let current = startDir;
11083
11199
  while (current !== "/") {
11084
- const candidate = join5(current, ".opencode");
11200
+ const candidate = join6(current, ".opencode");
11085
11201
  if (existsSync5(candidate)) {
11086
11202
  try {
11087
11203
  if (statSync2(candidate).isDirectory()) {
@@ -11090,7 +11206,7 @@ function findOpencodeDir2(startDir) {
11090
11206
  } catch {
11091
11207
  }
11092
11208
  }
11093
- const parent = dirname2(current);
11209
+ const parent = dirname3(current);
11094
11210
  if (parent === current) {
11095
11211
  break;
11096
11212
  }
@@ -11099,13 +11215,13 @@ function findOpencodeDir2(startDir) {
11099
11215
  return null;
11100
11216
  }
11101
11217
  function resolvePromptPaths(workingDirectory) {
11102
- const configHome = process.env.XDG_CONFIG_HOME || join5(homedir4(), ".config");
11103
- const globalRoot = join5(configHome, "opencode", "acp-prompts");
11104
- const defaultsDir = join5(globalRoot, "defaults");
11105
- const globalOverridesDir = join5(globalRoot, "overrides");
11106
- const configDirOverridesDir = process.env.OPENCODE_CONFIG_DIR ? join5(process.env.OPENCODE_CONFIG_DIR, "acp-prompts", "overrides") : null;
11218
+ const configHome = process.env.XDG_CONFIG_HOME || join6(homedir5(), ".config");
11219
+ const globalRoot = join6(configHome, "opencode", "acp-prompts");
11220
+ const defaultsDir = join6(globalRoot, "defaults");
11221
+ const globalOverridesDir = join6(globalRoot, "overrides");
11222
+ const configDirOverridesDir = process.env.OPENCODE_CONFIG_DIR ? join6(process.env.OPENCODE_CONFIG_DIR, "acp-prompts", "overrides") : null;
11107
11223
  const opencodeDir = findOpencodeDir2(workingDirectory);
11108
- const projectOverridesDir = opencodeDir ? join5(opencodeDir, "acp-prompts", "overrides") : null;
11224
+ const projectOverridesDir = opencodeDir ? join6(opencodeDir, "acp-prompts", "overrides") : null;
11109
11225
  return {
11110
11226
  defaultsDir,
11111
11227
  globalOverridesDir,
@@ -11278,16 +11394,16 @@ var PromptStore = class {
11278
11394
  const candidates = [];
11279
11395
  if (this.paths.projectOverridesDir) {
11280
11396
  candidates.push({
11281
- path: join5(this.paths.projectOverridesDir, fileName)
11397
+ path: join6(this.paths.projectOverridesDir, fileName)
11282
11398
  });
11283
11399
  }
11284
11400
  if (this.paths.configDirOverridesDir) {
11285
11401
  candidates.push({
11286
- path: join5(this.paths.configDirOverridesDir, fileName)
11402
+ path: join6(this.paths.configDirOverridesDir, fileName)
11287
11403
  });
11288
11404
  }
11289
11405
  candidates.push({
11290
- path: join5(this.paths.globalOverridesDir, fileName)
11406
+ path: join6(this.paths.globalOverridesDir, fileName)
11291
11407
  });
11292
11408
  return candidates;
11293
11409
  }
@@ -11311,7 +11427,7 @@ var PromptStore = class {
11311
11427
  const managedContent = buildDefaultPromptFileContent(
11312
11428
  bundledEditable || bundledPrompts[definition.runtimeField]
11313
11429
  );
11314
- const filePath = join5(this.paths.defaultsDir, definition.fileName);
11430
+ const filePath = join6(this.paths.defaultsDir, definition.fileName);
11315
11431
  try {
11316
11432
  const existing = readFileIfExists(filePath);
11317
11433
  if (existing === managedContent) {
@@ -11325,7 +11441,7 @@ var PromptStore = class {
11325
11441
  });
11326
11442
  }
11327
11443
  }
11328
- const readmePath = join5(this.paths.defaultsDir, DEFAULTS_README_FILE);
11444
+ const readmePath = join6(this.paths.defaultsDir, DEFAULTS_README_FILE);
11329
11445
  const readmeContent = buildDefaultsReadmeContent();
11330
11446
  try {
11331
11447
  const existing = readFileIfExists(readmePath);
@@ -11790,7 +11906,7 @@ ${report}`;
11790
11906
  // lib/commands/export.ts
11791
11907
  import * as fs2 from "fs/promises";
11792
11908
  import { existsSync as existsSync6 } from "fs";
11793
- import { dirname as dirname3, isAbsolute as isAbsolute2, join as join6, resolve } from "path";
11909
+ import { dirname as dirname4, isAbsolute as isAbsolute2, join as join7, resolve as resolve2 } from "path";
11794
11910
  var ALL_TIERS = [1, 2, 3];
11795
11911
  var TIER_NAMES = {
11796
11912
  1: "Tier 1 \u2014 Capture",
@@ -11903,7 +12019,7 @@ function parseTiers(raw) {
11903
12019
  }
11904
12020
  function resolveDefaultOutputPath(sessionId, cwd2) {
11905
12021
  const short = (sessionId || "session").slice(0, 8);
11906
- return join6(cwd2, ".opencode", `acp-export-${short}.md`);
12022
+ return join7(cwd2, ".opencode", `acp-export-${short}.md`);
11907
12023
  }
11908
12024
  function formatTokens2(n) {
11909
12025
  if (!Number.isFinite(n) || n <= 0) return "0";
@@ -12031,9 +12147,9 @@ async function handleExportCommand(ctx, args) {
12031
12147
  return;
12032
12148
  }
12033
12149
  const cwd2 = ctx.workingDirectory || process.cwd();
12034
- const targetPath = options.outputPath !== "" ? isAbsolute2(options.outputPath) ? options.outputPath : resolve(cwd2, options.outputPath) : resolveDefaultOutputPath(sessionId, cwd2);
12150
+ const targetPath = options.outputPath !== "" ? isAbsolute2(options.outputPath) ? options.outputPath : resolve2(cwd2, options.outputPath) : resolveDefaultOutputPath(sessionId, cwd2);
12035
12151
  try {
12036
- const dir = dirname3(targetPath);
12152
+ const dir = dirname4(targetPath);
12037
12153
  if (!existsSync6(dir)) {
12038
12154
  await fs2.mkdir(dir, { recursive: true });
12039
12155
  }
@@ -13026,7 +13142,7 @@ function findBiliProxyProviders(provider) {
13026
13142
 
13027
13143
  // lib/update.ts
13028
13144
  import { readFile as readFile2, rm } from "fs/promises";
13029
- import { basename, dirname as dirname4, join as join7 } from "path";
13145
+ import { basename as basename2, dirname as dirname5, join as join8 } from "path";
13030
13146
  import { fileURLToPath } from "url";
13031
13147
  var PACKAGE_NAME = "opencode-acp";
13032
13148
  function startAutoUpdate(ctx, enabled, logger) {
@@ -13073,7 +13189,7 @@ async function checkAutoUpdate(signal, logger) {
13073
13189
  logger?.info("Auto-update skipped: package dir not found");
13074
13190
  return { updated: false };
13075
13191
  }
13076
- const pkg = await readPackageJson(join7(packageDir, "package.json"));
13192
+ const pkg = await readPackageJson(join8(packageDir, "package.json"));
13077
13193
  if (!pkg?.name || !pkg.version) {
13078
13194
  logger?.info("Auto-update skipped: package.json unreadable", { packageDir });
13079
13195
  return { updated: false };
@@ -13112,21 +13228,21 @@ async function checkAutoUpdate(signal, logger) {
13112
13228
  return { updated: true, name: pkg.name, current: pkg.version, latest };
13113
13229
  }
13114
13230
  async function findPackageDir(name) {
13115
- let dir = dirname4(fileURLToPath(import.meta.url));
13231
+ let dir = dirname5(fileURLToPath(import.meta.url));
13116
13232
  for (; ; ) {
13117
- const pkg = await readPackageJson(join7(dir, "package.json"));
13233
+ const pkg = await readPackageJson(join8(dir, "package.json"));
13118
13234
  if (pkg?.name === name) return dir;
13119
- const parent = dirname4(dir);
13235
+ const parent = dirname5(dir);
13120
13236
  if (parent === dir) return void 0;
13121
13237
  dir = parent;
13122
13238
  }
13123
13239
  }
13124
13240
  async function updateTarget(packageDir, name) {
13125
- const packageParent = dirname4(packageDir);
13126
- const nodeModulesDir = basename(packageParent).startsWith("@") ? dirname4(packageParent) : packageParent;
13127
- if (basename(nodeModulesDir) !== "node_modules") return void 0;
13128
- const wrapperDir = dirname4(nodeModulesDir);
13129
- const wrapperPkg = await readPackageJson(join7(wrapperDir, "package.json"));
13241
+ const packageParent = dirname5(packageDir);
13242
+ const nodeModulesDir = basename2(packageParent).startsWith("@") ? dirname5(packageParent) : packageParent;
13243
+ if (basename2(nodeModulesDir) !== "node_modules") return void 0;
13244
+ const wrapperDir = dirname5(nodeModulesDir);
13245
+ const wrapperPkg = await readPackageJson(join8(wrapperDir, "package.json"));
13130
13246
  const spec = wrapperSpec(wrapperDir, name) ?? wrapperPkg?.dependencies?.[name];
13131
13247
  if (!spec || !isAutoUpdatableSpec(spec)) return void 0;
13132
13248
  return { removeDir: wrapperDir, spec };
@@ -13134,13 +13250,13 @@ async function updateTarget(packageDir, name) {
13134
13250
  function wrapperSpec(wrapperDir, name) {
13135
13251
  if (name.startsWith("@")) {
13136
13252
  const [scope, pkg] = name.split("/");
13137
- if (!scope || !pkg || basename(dirname4(wrapperDir)) !== scope) return void 0;
13253
+ if (!scope || !pkg || basename2(dirname5(wrapperDir)) !== scope) return void 0;
13138
13254
  const prefix2 = `${pkg}@`;
13139
- const base2 = basename(wrapperDir);
13255
+ const base2 = basename2(wrapperDir);
13140
13256
  return base2.startsWith(prefix2) ? base2.slice(prefix2.length) : void 0;
13141
13257
  }
13142
13258
  const prefix = `${name}@`;
13143
- const base = basename(wrapperDir);
13259
+ const base = basename2(wrapperDir);
13144
13260
  return base.startsWith(prefix) ? base.slice(prefix.length) : void 0;
13145
13261
  }
13146
13262
  function isAutoUpdatableSpec(spec) {
@@ -13238,7 +13354,7 @@ var server = (async (ctx) => {
13238
13354
  }
13239
13355
  const logger = new Logger(config.debug, config.debug ? "debug" : config.logLevel);
13240
13356
  logger.info("ACP plugin initialized", {
13241
- version: true ? "1.18.1-pr.416.174" : "dev",
13357
+ version: true ? "1.18.1-pr.416.176" : "dev",
13242
13358
  workspace: ctx.directory,
13243
13359
  logLevel: logger.level,
13244
13360
  debug: config.debug,