opencode-acp 1.18.1-pr.416.175 → 1.18.1-pr.416.177

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,
@@ -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.some((dir) => containsOrIs(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");
9919
+ const fsp2 = await import("fs/promises");
9920
+ const { constants } = await import("fs");
9813
9921
  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");
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.write(Buffer.from(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
  }
@@ -10553,10 +10667,10 @@ function hideFailedCompressCalls(messages) {
10553
10667
 
10554
10668
  // lib/logger.ts
10555
10669
  import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
10556
- import { join as join4 } from "path";
10670
+ import { join as join5 } from "path";
10557
10671
  import { existsSync as existsSync4 } from "fs";
10558
- import { homedir as homedir3 } from "os";
10559
- var LOG_VERSION = true ? "1.18.1-pr.416.175" : "dev";
10672
+ import { homedir as homedir4 } from "os";
10673
+ var LOG_VERSION = true ? "1.18.1-pr.416.177" : "dev";
10560
10674
  var LEVEL_RANK = {
10561
10675
  debug: 10,
10562
10676
  info: 20,
@@ -10576,8 +10690,8 @@ var Logger = class {
10576
10690
  constructor(enabled, level) {
10577
10691
  const resolved = level !== void 0 && LEVEL_RANK[level] !== void 0 ? level : void 0;
10578
10692
  this.level = resolved ?? (enabled ? "debug" : "warn");
10579
- const configHome = process.env.XDG_CONFIG_HOME || join4(homedir3(), ".config");
10580
- 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");
10581
10695
  }
10582
10696
  /** Back-compat view of the old flag: true only at full debug verbosity. */
10583
10697
  get enabled() {
@@ -10639,11 +10753,11 @@ var Logger = class {
10639
10753
  const dataStr = this.formatData(data);
10640
10754
  const logLine = `${timestamp} ${level.padEnd(5)} ${component}: ${message}${dataStr ? " | " + dataStr : ""} | v=${LOG_VERSION}
10641
10755
  `;
10642
- const dailyLogDir = join4(this.logDir, "daily");
10756
+ const dailyLogDir = join5(this.logDir, "daily");
10643
10757
  if (!existsSync4(dailyLogDir)) {
10644
10758
  await mkdir2(dailyLogDir, { recursive: true });
10645
10759
  }
10646
- 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`);
10647
10761
  await writeFile2(logFile, logLine, { flag: "a" });
10648
10762
  } catch (error) {
10649
10763
  }
@@ -10754,7 +10868,7 @@ var Logger = class {
10754
10868
  async saveContext(sessionId, messages) {
10755
10869
  if (this.level !== "debug") return;
10756
10870
  try {
10757
- const contextDir = join4(this.logDir, "context", sessionId);
10871
+ const contextDir = join5(this.logDir, "context", sessionId);
10758
10872
  if (!existsSync4(contextDir)) {
10759
10873
  await mkdir2(contextDir, { recursive: true });
10760
10874
  }
@@ -10762,9 +10876,9 @@ var Logger = class {
10762
10876
  (msg) => msg.parts && msg.parts.length > 0
10763
10877
  );
10764
10878
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
10765
- const contextFile = join4(contextDir, `${timestamp}.json`);
10879
+ const contextFile = join5(contextDir, `${timestamp}.json`);
10766
10880
  await writeFile2(contextFile, JSON.stringify(minimized, null, 2));
10767
- const versionFile = join4(contextDir, "_version");
10881
+ const versionFile = join5(contextDir, "_version");
10768
10882
  if (!existsSync4(versionFile)) {
10769
10883
  await writeFile2(versionFile, LOG_VERSION);
10770
10884
  }
@@ -10775,8 +10889,8 @@ var Logger = class {
10775
10889
 
10776
10890
  // lib/prompts/store.ts
10777
10891
  import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, statSync as statSync2 } from "fs";
10778
- import { join as join5, dirname as dirname2 } from "path";
10779
- import { homedir as homedir4 } from "os";
10892
+ import { join as join6, dirname as dirname3 } from "path";
10893
+ import { homedir as homedir5 } from "os";
10780
10894
 
10781
10895
  // lib/prompts/system.ts
10782
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.`;
@@ -11083,7 +11197,7 @@ function createBundledRuntimePrompts(candidatesEnabled) {
11083
11197
  function findOpencodeDir2(startDir) {
11084
11198
  let current = startDir;
11085
11199
  while (current !== "/") {
11086
- const candidate = join5(current, ".opencode");
11200
+ const candidate = join6(current, ".opencode");
11087
11201
  if (existsSync5(candidate)) {
11088
11202
  try {
11089
11203
  if (statSync2(candidate).isDirectory()) {
@@ -11092,7 +11206,7 @@ function findOpencodeDir2(startDir) {
11092
11206
  } catch {
11093
11207
  }
11094
11208
  }
11095
- const parent = dirname2(current);
11209
+ const parent = dirname3(current);
11096
11210
  if (parent === current) {
11097
11211
  break;
11098
11212
  }
@@ -11101,13 +11215,13 @@ function findOpencodeDir2(startDir) {
11101
11215
  return null;
11102
11216
  }
11103
11217
  function resolvePromptPaths(workingDirectory) {
11104
- const configHome = process.env.XDG_CONFIG_HOME || join5(homedir4(), ".config");
11105
- const globalRoot = join5(configHome, "opencode", "acp-prompts");
11106
- const defaultsDir = join5(globalRoot, "defaults");
11107
- const globalOverridesDir = join5(globalRoot, "overrides");
11108
- 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;
11109
11223
  const opencodeDir = findOpencodeDir2(workingDirectory);
11110
- const projectOverridesDir = opencodeDir ? join5(opencodeDir, "acp-prompts", "overrides") : null;
11224
+ const projectOverridesDir = opencodeDir ? join6(opencodeDir, "acp-prompts", "overrides") : null;
11111
11225
  return {
11112
11226
  defaultsDir,
11113
11227
  globalOverridesDir,
@@ -11280,16 +11394,16 @@ var PromptStore = class {
11280
11394
  const candidates = [];
11281
11395
  if (this.paths.projectOverridesDir) {
11282
11396
  candidates.push({
11283
- path: join5(this.paths.projectOverridesDir, fileName)
11397
+ path: join6(this.paths.projectOverridesDir, fileName)
11284
11398
  });
11285
11399
  }
11286
11400
  if (this.paths.configDirOverridesDir) {
11287
11401
  candidates.push({
11288
- path: join5(this.paths.configDirOverridesDir, fileName)
11402
+ path: join6(this.paths.configDirOverridesDir, fileName)
11289
11403
  });
11290
11404
  }
11291
11405
  candidates.push({
11292
- path: join5(this.paths.globalOverridesDir, fileName)
11406
+ path: join6(this.paths.globalOverridesDir, fileName)
11293
11407
  });
11294
11408
  return candidates;
11295
11409
  }
@@ -11313,7 +11427,7 @@ var PromptStore = class {
11313
11427
  const managedContent = buildDefaultPromptFileContent(
11314
11428
  bundledEditable || bundledPrompts[definition.runtimeField]
11315
11429
  );
11316
- const filePath = join5(this.paths.defaultsDir, definition.fileName);
11430
+ const filePath = join6(this.paths.defaultsDir, definition.fileName);
11317
11431
  try {
11318
11432
  const existing = readFileIfExists(filePath);
11319
11433
  if (existing === managedContent) {
@@ -11327,7 +11441,7 @@ var PromptStore = class {
11327
11441
  });
11328
11442
  }
11329
11443
  }
11330
- const readmePath = join5(this.paths.defaultsDir, DEFAULTS_README_FILE);
11444
+ const readmePath = join6(this.paths.defaultsDir, DEFAULTS_README_FILE);
11331
11445
  const readmeContent = buildDefaultsReadmeContent();
11332
11446
  try {
11333
11447
  const existing = readFileIfExists(readmePath);
@@ -11792,7 +11906,7 @@ ${report}`;
11792
11906
  // lib/commands/export.ts
11793
11907
  import * as fs2 from "fs/promises";
11794
11908
  import { existsSync as existsSync6 } from "fs";
11795
- 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";
11796
11910
  var ALL_TIERS = [1, 2, 3];
11797
11911
  var TIER_NAMES = {
11798
11912
  1: "Tier 1 \u2014 Capture",
@@ -11905,7 +12019,7 @@ function parseTiers(raw) {
11905
12019
  }
11906
12020
  function resolveDefaultOutputPath(sessionId, cwd2) {
11907
12021
  const short = (sessionId || "session").slice(0, 8);
11908
- return join6(cwd2, ".opencode", `acp-export-${short}.md`);
12022
+ return join7(cwd2, ".opencode", `acp-export-${short}.md`);
11909
12023
  }
11910
12024
  function formatTokens2(n) {
11911
12025
  if (!Number.isFinite(n) || n <= 0) return "0";
@@ -12033,9 +12147,9 @@ async function handleExportCommand(ctx, args) {
12033
12147
  return;
12034
12148
  }
12035
12149
  const cwd2 = ctx.workingDirectory || process.cwd();
12036
- 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);
12037
12151
  try {
12038
- const dir = dirname3(targetPath);
12152
+ const dir = dirname4(targetPath);
12039
12153
  if (!existsSync6(dir)) {
12040
12154
  await fs2.mkdir(dir, { recursive: true });
12041
12155
  }
@@ -13028,7 +13142,7 @@ function findBiliProxyProviders(provider) {
13028
13142
 
13029
13143
  // lib/update.ts
13030
13144
  import { readFile as readFile2, rm } from "fs/promises";
13031
- import { basename, dirname as dirname4, join as join7 } from "path";
13145
+ import { basename as basename2, dirname as dirname5, join as join8 } from "path";
13032
13146
  import { fileURLToPath } from "url";
13033
13147
  var PACKAGE_NAME = "opencode-acp";
13034
13148
  function startAutoUpdate(ctx, enabled, logger) {
@@ -13075,7 +13189,7 @@ async function checkAutoUpdate(signal, logger) {
13075
13189
  logger?.info("Auto-update skipped: package dir not found");
13076
13190
  return { updated: false };
13077
13191
  }
13078
- const pkg = await readPackageJson(join7(packageDir, "package.json"));
13192
+ const pkg = await readPackageJson(join8(packageDir, "package.json"));
13079
13193
  if (!pkg?.name || !pkg.version) {
13080
13194
  logger?.info("Auto-update skipped: package.json unreadable", { packageDir });
13081
13195
  return { updated: false };
@@ -13114,21 +13228,21 @@ async function checkAutoUpdate(signal, logger) {
13114
13228
  return { updated: true, name: pkg.name, current: pkg.version, latest };
13115
13229
  }
13116
13230
  async function findPackageDir(name) {
13117
- let dir = dirname4(fileURLToPath(import.meta.url));
13231
+ let dir = dirname5(fileURLToPath(import.meta.url));
13118
13232
  for (; ; ) {
13119
- const pkg = await readPackageJson(join7(dir, "package.json"));
13233
+ const pkg = await readPackageJson(join8(dir, "package.json"));
13120
13234
  if (pkg?.name === name) return dir;
13121
- const parent = dirname4(dir);
13235
+ const parent = dirname5(dir);
13122
13236
  if (parent === dir) return void 0;
13123
13237
  dir = parent;
13124
13238
  }
13125
13239
  }
13126
13240
  async function updateTarget(packageDir, name) {
13127
- const packageParent = dirname4(packageDir);
13128
- const nodeModulesDir = basename(packageParent).startsWith("@") ? dirname4(packageParent) : packageParent;
13129
- if (basename(nodeModulesDir) !== "node_modules") return void 0;
13130
- const wrapperDir = dirname4(nodeModulesDir);
13131
- 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"));
13132
13246
  const spec = wrapperSpec(wrapperDir, name) ?? wrapperPkg?.dependencies?.[name];
13133
13247
  if (!spec || !isAutoUpdatableSpec(spec)) return void 0;
13134
13248
  return { removeDir: wrapperDir, spec };
@@ -13136,13 +13250,13 @@ async function updateTarget(packageDir, name) {
13136
13250
  function wrapperSpec(wrapperDir, name) {
13137
13251
  if (name.startsWith("@")) {
13138
13252
  const [scope, pkg] = name.split("/");
13139
- if (!scope || !pkg || basename(dirname4(wrapperDir)) !== scope) return void 0;
13253
+ if (!scope || !pkg || basename2(dirname5(wrapperDir)) !== scope) return void 0;
13140
13254
  const prefix2 = `${pkg}@`;
13141
- const base2 = basename(wrapperDir);
13255
+ const base2 = basename2(wrapperDir);
13142
13256
  return base2.startsWith(prefix2) ? base2.slice(prefix2.length) : void 0;
13143
13257
  }
13144
13258
  const prefix = `${name}@`;
13145
- const base = basename(wrapperDir);
13259
+ const base = basename2(wrapperDir);
13146
13260
  return base.startsWith(prefix) ? base.slice(prefix.length) : void 0;
13147
13261
  }
13148
13262
  function isAutoUpdatableSpec(spec) {
@@ -13240,7 +13354,7 @@ var server = (async (ctx) => {
13240
13354
  }
13241
13355
  const logger = new Logger(config.debug, config.debug ? "debug" : config.logLevel);
13242
13356
  logger.info("ACP plugin initialized", {
13243
- version: true ? "1.18.1-pr.416.175" : "dev",
13357
+ version: true ? "1.18.1-pr.416.177" : "dev",
13244
13358
  workspace: ctx.directory,
13245
13359
  logLevel: logger.level,
13246
13360
  debug: config.debug,