opencode-readseek 0.6.9 → 0.6.10

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/index.ts +46 -2
  2. package/package.json +2 -2
package/index.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  // Copyright (C) Jarkko Sakkinen 2026
3
3
 
4
- import { lstat, mkdir, readFile, realpath, stat, writeFile } from "node:fs/promises";
4
+ import { lstat, mkdir, readFile, readlink, realpath, stat, writeFile } from "node:fs/promises";
5
5
  import { createRequire } from "node:module";
6
6
  import path from "node:path";
7
7
 
@@ -116,12 +116,56 @@ function containsPath(directory: string, filePath: string): boolean {
116
116
  return relative === "" || (!relative.startsWith(`..${path.sep}`) && relative !== ".." && !path.isAbsolute(relative));
117
117
  }
118
118
 
119
+ async function resolveSymlinks(filePath: string): Promise<string> {
120
+ let unresolved = path.resolve(filePath);
121
+ let symlinks = 0;
122
+
123
+ while (true) {
124
+ const parsed = path.parse(unresolved);
125
+ const parts = unresolved.slice(parsed.root.length).split(path.sep).filter(Boolean);
126
+ let resolved = parsed.root;
127
+ let restart = false;
128
+
129
+ for (let index = 0; index < parts.length; index++) {
130
+ const candidate = path.join(resolved, parts[index]);
131
+ const info = await lstat(candidate);
132
+ if (!info.isSymbolicLink()) {
133
+ resolved = candidate;
134
+ continue;
135
+ }
136
+
137
+ symlinks++;
138
+ if (symlinks > 40) {
139
+ const error = new Error(`too many symbolic links: ${filePath}`) as NodeJS.ErrnoException;
140
+ error.code = "ELOOP";
141
+ throw error;
142
+ }
143
+
144
+ const target = await readlink(candidate);
145
+ unresolved = path.resolve(path.dirname(candidate), target, ...parts.slice(index + 1));
146
+ restart = true;
147
+ break;
148
+ }
149
+
150
+ if (!restart) return resolved;
151
+ }
152
+ }
153
+
154
+ async function portableRealpath(filePath: string): Promise<string> {
155
+ try {
156
+ return await realpath(filePath);
157
+ } catch (error) {
158
+ if ((error as NodeJS.ErrnoException).code !== "EACCES") throw error;
159
+ return resolveSymlinks(filePath);
160
+ }
161
+ }
162
+
119
163
  async function canonicalPath(filePath: string): Promise<string> {
120
164
  const missing: string[] = [];
121
165
  let existing = filePath;
122
166
  while (true) {
123
167
  try {
124
- return path.join(await realpath(existing), ...missing.reverse());
168
+ return path.join(await portableRealpath(existing), ...missing.reverse());
125
169
  } catch (error) {
126
170
  if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
127
171
  const parent = path.dirname(existing);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-readseek",
3
- "version": "0.6.9",
3
+ "version": "0.6.10",
4
4
  "description": "OpenCode plugin for readseek-backed structural code navigation",
5
5
  "type": "module",
6
6
  "main": "./index.ts",
@@ -30,7 +30,7 @@
30
30
  "node": ">=20.0.0"
31
31
  },
32
32
  "dependencies": {
33
- "@jarkkojs/readseek": "^0.6.9",
33
+ "@jarkkojs/readseek": "^0.6.10",
34
34
  "@opencode-ai/plugin": "^1.17.20"
35
35
  },
36
36
  "devDependencies": {