Skip to content

Commit 7350fa4

Browse files
freshtechbrocodex
andauthored
Merge pull request #58 from freshtechbro/codex/test-opencode-inspiredesign-output
fix: clear OpenCode package alias cache Co-authored-by: Codex <noreply@openai.com>
2 parents 2d61ff7 + 11b74c0 commit 7350fa4

4 files changed

Lines changed: 60 additions & 5 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -740,7 +740,7 @@ See [docs/SURFACE_REFERENCE.md](docs/SURFACE_REFERENCE.md) for the source-accura
740740
| `npx opendevbrowser --with-config` | Also create opendevbrowser.jsonc |
741741
| `npx opendevbrowser --full` | Full install (config + extension assets) |
742742
| `npm install -g opendevbrowser` | Install persistent global CLI |
743-
| `npx opendevbrowser --update` | Repair cached plugin pins |
743+
| `npx opendevbrowser --update` | Repair OpenCode package caches and plugin pins |
744744
| `npx opendevbrowser --uninstall` | Remove from config |
745745
| `npx opendevbrowser --version` | Show version |
746746

@@ -806,7 +806,7 @@ OpenDevBrowser is **secure by default** with defense-in-depth protections:
806806
## Updating
807807

808808
```bash
809-
# Repair OpenCode's cached package and manifest pin, then restart OpenCode
809+
# Repair OpenCode's cached packages, manifest pin, and lockfile, then restart OpenCode
810810
npx opendevbrowser --update
811811
```
812812

docs/CLI.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,8 +236,9 @@ npx opendevbrowser --update
236236
npx opendevbrowser -u
237237
```
238238

239-
This removes cached files from `~/.cache/opencode/node_modules/opendevbrowser/`, removes stale `opendevbrowser`
240-
dependency pins from `~/.cache/opencode/package.json`, and deletes the OpenCode cache lockfile when present. OpenCode
239+
This removes cached files from `~/.cache/opencode/node_modules/opendevbrowser/` and
240+
`~/.cache/opencode/packages/opendevbrowser@latest/`, removes stale `opendevbrowser`
241+
dependency pins from `~/.cache/opencode/package.json`, and deletes `~/.cache/opencode/package-lock.json` when present. OpenCode
241242
will download the latest version on next run.
242243

243244
### Uninstall

src/cli/commands/update.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const PLUGIN_NAME = "opendevbrowser";
77
const CACHE_MANIFEST = "package.json";
88
const CACHE_LOCKFILE = "package-lock.json";
99
const CACHE_UPDATE_LOCK = ".opendevbrowser-update.lock";
10+
const OPENCODE_PACKAGE_ALIAS = `${PLUGIN_NAME}@latest`;
1011
const CACHE_LOCK_STALE_MS = 30 * 60 * 1000;
1112
const DEPENDENCY_SECTIONS = [
1213
"dependencies",
@@ -308,6 +309,7 @@ export function runUpdate(): UpdateResult {
308309
const cacheDir = getCacheDir();
309310
const nodeModulesDir = path.join(cacheDir, "node_modules");
310311
const pluginCacheDir = path.join(nodeModulesDir, PLUGIN_NAME);
312+
const pluginPackageCacheDir = path.join(cacheDir, "packages", OPENCODE_PACKAGE_ALIAS);
311313
const lockfilePath = path.join(cacheDir, CACHE_LOCKFILE);
312314

313315
try {
@@ -322,14 +324,16 @@ export function runUpdate(): UpdateResult {
322324
preflightCacheMutationPaths(cacheDir, [
323325
path.join(cacheDir, CACHE_MANIFEST),
324326
pluginCacheDir,
327+
pluginPackageCacheDir,
325328
lockfilePath,
326329
path.join(cacheDir, CACHE_UPDATE_LOCK)
327330
]);
328331
const cleared = withCacheMutationLock(cacheDir, () => {
329332
const manifestPinRemoved = removeManifestPin(cacheDir);
330333
const packageRemoved = removePathIfExists(pluginCacheDir, cacheDir);
334+
const aliasedPackageRemoved = removePathIfExists(pluginPackageCacheDir, cacheDir);
331335
const lockfileRemoved = removePathIfExists(lockfilePath, cacheDir);
332-
return packageRemoved || manifestPinRemoved || lockfileRemoved;
336+
return packageRemoved || aliasedPackageRemoved || manifestPinRemoved || lockfileRemoved;
333337
});
334338

335339
if (!cleared) {

tests/cli-update.test.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ describe("runUpdate", () => {
5858
});
5959
mkdirSync(makePath("node_modules", "opendevbrowser"), { recursive: true });
6060
mkdirSync(makePath("node_modules", "oh-my-opencode"), { recursive: true });
61+
mkdirSync(makePath("packages", "opendevbrowser@latest"), { recursive: true });
62+
mkdirSync(makePath("packages", "oh-my-opencode@latest"), { recursive: true });
6163
writeFileSync(makePath("package-lock.json"), "{\"lockfileVersion\":3}\n", "utf8");
6264

6365
const result = runUpdate();
@@ -69,6 +71,8 @@ describe("runUpdate", () => {
6971
});
7072
expect(existsSync(makePath("node_modules", "opendevbrowser"))).toBe(false);
7173
expect(existsSync(makePath("node_modules", "oh-my-opencode"))).toBe(true);
74+
expect(existsSync(makePath("packages", "opendevbrowser@latest"))).toBe(false);
75+
expect(existsSync(makePath("packages", "oh-my-opencode@latest"))).toBe(true);
7276
expect(existsSync(makePath("package-lock.json"))).toBe(false);
7377
expect(readManifest()).toEqual({
7478
dependencies: {
@@ -107,9 +111,25 @@ describe("runUpdate", () => {
107111
expect(existsSync(makePath("package-lock.json"))).toBe(false);
108112
});
109113

114+
it("repairs OpenCode package alias cache state", () => {
115+
mkdirSync(makePath("packages", "opendevbrowser@latest"), { recursive: true });
116+
mkdirSync(makePath("packages", "yaml-language-server"), { recursive: true });
117+
118+
const result = runUpdate();
119+
120+
expect(result).toEqual({
121+
success: true,
122+
message: "Cache repaired. OpenCode will install the latest version on next run.",
123+
cleared: true
124+
});
125+
expect(existsSync(makePath("packages", "opendevbrowser@latest"))).toBe(false);
126+
expect(existsSync(makePath("packages", "yaml-language-server"))).toBe(true);
127+
});
128+
110129
it("refuses to mutate cache state while another update lock is held", () => {
111130
writeManifest({ dependencies: { opendevbrowser: "0.0.24" } });
112131
mkdirSync(makePath("node_modules", "opendevbrowser"), { recursive: true });
132+
mkdirSync(makePath("packages", "opendevbrowser@latest"), { recursive: true });
113133
writeFileSync(makePath("package-lock.json"), "{\"lockfileVersion\":3}\n", "utf8");
114134
writeFileSync(
115135
makePath(".opendevbrowser-update.lock"),
@@ -124,6 +144,7 @@ describe("runUpdate", () => {
124144
expect(result.message).toContain("another update is already running");
125145
expect(readManifest()).toEqual({ dependencies: { opendevbrowser: "0.0.24" } });
126146
expect(existsSync(makePath("node_modules", "opendevbrowser"))).toBe(true);
147+
expect(existsSync(makePath("packages", "opendevbrowser@latest"))).toBe(true);
127148
expect(existsSync(makePath("package-lock.json"))).toBe(true);
128149
});
129150

@@ -252,13 +273,15 @@ describe("runUpdate", () => {
252273

253274
it("does not delete package cache before validating a malformed manifest", () => {
254275
mkdirSync(makePath("node_modules", "opendevbrowser"), { recursive: true });
276+
mkdirSync(makePath("packages", "opendevbrowser@latest"), { recursive: true });
255277
writeFileSync(makePath("package.json"), "{bad-json}", "utf8");
256278

257279
const result = runUpdate();
258280

259281
expect(result.success).toBe(false);
260282
expect(result.cleared).toBe(false);
261283
expect(existsSync(makePath("node_modules", "opendevbrowser"))).toBe(true);
284+
expect(existsSync(makePath("packages", "opendevbrowser@latest"))).toBe(true);
262285
});
263286

264287
it("refuses to rewrite symlinked cache manifests", () => {
@@ -298,6 +321,33 @@ describe("runUpdate", () => {
298321
expect(result.message).toContain("refusing to modify symlinked cache path");
299322
});
300323

324+
it("refuses to delete through a symlinked packages parent", () => {
325+
const outsidePackages = join(cacheDir, "..", "outside-packages");
326+
mkdirSync(outsidePackages, { recursive: true });
327+
writeFileSync(join(outsidePackages, "sentinel.txt"), "keep\n", "utf8");
328+
symlinkSync(outsidePackages, makePath("packages"));
329+
330+
const result = runUpdate();
331+
332+
expect(result.success).toBe(false);
333+
expect(result.message).toContain("refusing to modify symlinked cache path");
334+
expect(readFileSync(join(outsidePackages, "sentinel.txt"), "utf8")).toBe("keep\n");
335+
});
336+
337+
it("refuses to delete a symlinked package alias cache", () => {
338+
const outsideAlias = join(cacheDir, "..", "outside-opendevbrowser-alias");
339+
mkdirSync(makePath("packages"), { recursive: true });
340+
mkdirSync(outsideAlias, { recursive: true });
341+
writeFileSync(join(outsideAlias, "sentinel.txt"), "keep\n", "utf8");
342+
symlinkSync(outsideAlias, makePath("packages", "opendevbrowser@latest"));
343+
344+
const result = runUpdate();
345+
346+
expect(result.success).toBe(false);
347+
expect(result.message).toContain("refusing to modify symlinked cache path");
348+
expect(readFileSync(join(outsideAlias, "sentinel.txt"), "utf8")).toBe("keep\n");
349+
});
350+
301351
it("preflights symlinked cache paths before rewriting stale manifest pins", () => {
302352
const outsideModules = join(cacheDir, "..", "outside-node-modules-with-manifest");
303353
mkdirSync(outsideModules, { recursive: true });

0 commit comments

Comments
 (0)