plugin-updater 1.0.25 → 1.0.26

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.d.ts CHANGED
@@ -16,7 +16,7 @@ export declare function getNpmPlugins(configDir: string): NpmPlugin[];
16
16
  export declare function installNpmPlugin(name: string, configDir: string): string;
17
17
  export declare function uninstallNpmPlugin(name: string, configDir: string): string;
18
18
  export declare function updateNpmPlugin(name: string, configDir: string, updateInterval?: number): string;
19
- export declare function updatePluginPublic(pluginName: string, gitUrl: string, branch?: string, commitHash?: string): Promise<void>;
20
- export declare function earlyLaunch(configDir: string, plugins: Plugin[]): Promise<void>;
21
- export declare function activate(): Promise<void>;
19
+ export declare function updatePluginPublic(pluginName: string, gitUrl: string, branch?: string, commitHash?: string): Promise<void | object>;
20
+ export declare function earlyLaunch(configDir: string, plugins: Plugin[]): Promise<void | object>;
21
+ export declare function activate(opencodeHookInput?: unknown): Promise<void | object>;
22
22
  export {};
package/dist/index.js CHANGED
@@ -120,7 +120,15 @@ function writeOpencodeJson(configDir, data) {
120
120
  fs.writeFileSync(ocPath, JSON.stringify(data, null, 2), "utf8");
121
121
  }
122
122
  // ── Public API ────────────────────────────────────────────────────────────────
123
+ // opencode invokes every exported function as a plugin hook, passing a context
124
+ // object instead of our protocol arguments; exports detect that and return an
125
+ // inert value so opencode gets a valid (empty) plugin instance
126
+ function isOpencodeHookInvocation(firstArgument) {
127
+ return typeof firstArgument !== "string";
128
+ }
123
129
  export function getNpmPlugins(configDir) {
130
+ if (isOpencodeHookInvocation(configDir))
131
+ return [];
124
132
  const { plugins } = readOpencodeJson(configDir);
125
133
  return plugins.map((raw) => {
126
134
  const name = raw.replace(/@[^@/]+$/, "") || raw;
@@ -129,6 +137,8 @@ export function getNpmPlugins(configDir) {
129
137
  });
130
138
  }
131
139
  export function installNpmPlugin(name, configDir) {
140
+ if (isOpencodeHookInvocation(name))
141
+ return "";
132
142
  writeLog(`Installing npm plugin: ${name}`);
133
143
  try {
134
144
  const { plugins, raw } = readOpencodeJson(configDir);
@@ -147,6 +157,8 @@ export function installNpmPlugin(name, configDir) {
147
157
  }
148
158
  }
149
159
  export function uninstallNpmPlugin(name, configDir) {
160
+ if (isOpencodeHookInvocation(name))
161
+ return "";
150
162
  writeLog(`Uninstalling npm plugin: ${name}`);
151
163
  try {
152
164
  const { plugins, raw } = readOpencodeJson(configDir);
@@ -166,6 +178,8 @@ export function uninstallNpmPlugin(name, configDir) {
166
178
  }
167
179
  }
168
180
  export function updateNpmPlugin(name, configDir, updateInterval = 1) {
181
+ if (isOpencodeHookInvocation(name))
182
+ return "";
169
183
  writeLog(`Updating npm plugin: ${name}`);
170
184
  const checkFile = path.join(configDir, "cache", `.npm-lastcheck-${name.replace(/[^a-z0-9]/gi, "_")}`);
171
185
  try {
@@ -262,6 +276,42 @@ async function callPluginCleanup(pluginExecutionFile, configDir) {
262
276
  writeLog(`cleanup() call failed for ${pluginExecutionFile}: ${e.message}`, true);
263
277
  }
264
278
  }
279
+ const BUILD_OUTPUT_DIRS = ["dist", path.join("core", "dist")];
280
+ // npm install creates node_modules/.bin symlinks, which fail on filesystems
281
+ // without symlink support (e.g. Windows-backed Docker bind mounts) — build in
282
+ // the OS temp dir and copy the outputs back instead
283
+ function buildInTempDir(pluginName, sourceDir) {
284
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), `plugin-updater-${pluginName}-`));
285
+ try {
286
+ fs.cpSync(sourceDir, tempDir, {
287
+ recursive: true,
288
+ filter: (src) => {
289
+ const name = path.basename(src);
290
+ return name !== ".git" && name !== "node_modules";
291
+ },
292
+ });
293
+ writeLog(`Running npm install for ${pluginName}`);
294
+ execSync("npm install", { cwd: tempDir, stdio: "pipe" });
295
+ writeLog(`Finished npm install for ${pluginName}`);
296
+ const pkg = JSON.parse(fs.readFileSync(path.join(tempDir, "package.json"), "utf8"));
297
+ if (pkg.scripts?.build) {
298
+ execSync("npm run build", { cwd: tempDir, stdio: "pipe" });
299
+ writeLog(`Finished npm run build for ${pluginName}`);
300
+ }
301
+ else {
302
+ writeLog(`Skipped npm run build for ${pluginName} (no build script found)`);
303
+ }
304
+ for (const outputDir of BUILD_OUTPUT_DIRS) {
305
+ const builtDir = path.join(tempDir, outputDir);
306
+ if (fs.existsSync(builtDir)) {
307
+ fs.cpSync(builtDir, path.join(sourceDir, outputDir), { recursive: true });
308
+ }
309
+ }
310
+ }
311
+ finally {
312
+ fs.rmSync(tempDir, { recursive: true, force: true });
313
+ }
314
+ }
265
315
  async function deployToExecutionDir(pluginName, executionPath, changed, configDir) {
266
316
  const sourceDir = path.join(getReposDir(), pluginName);
267
317
  if (!fs.existsSync(sourceDir))
@@ -275,19 +325,7 @@ async function deployToExecutionDir(pluginName, executionPath, changed, configDi
275
325
  else {
276
326
  if (fs.existsSync(packageJsonPath)) {
277
327
  try {
278
- writeLog(`Running npm install for ${pluginName}`);
279
- execSync("npm install", { cwd: sourceDir, stdio: "pipe" });
280
- writeLog(`Finished npm install for ${pluginName}`);
281
- const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
282
- if (pkg.main)
283
- entryFile = pkg.main;
284
- if (pkg.scripts?.build) {
285
- execSync("npm run build", { cwd: sourceDir, stdio: "pipe" });
286
- writeLog(`Finished npm run build for ${pluginName}`);
287
- }
288
- else {
289
- writeLog(`Skipped npm run build for ${pluginName} (no build script found)`);
290
- }
328
+ buildInTempDir(pluginName, sourceDir);
291
329
  }
292
330
  catch (error) {
293
331
  const err = error;
@@ -345,6 +383,8 @@ async function pluginUpdaterEntry(input) {
345
383
  }
346
384
  }
347
385
  export async function updatePluginPublic(pluginName, gitUrl, branch, commitHash) {
386
+ if (isOpencodeHookInvocation(pluginName))
387
+ return {};
348
388
  writeLog(`Public API update call for ${pluginName}`);
349
389
  const appName = process.argv.join(" ").includes("claude") ? "claude" : "opencode";
350
390
  const configDir = getAppConfigDir(appName);
@@ -352,6 +392,8 @@ export async function updatePluginPublic(pluginName, gitUrl, branch, commitHash)
352
392
  await deployToExecutionDir(pluginName, path.join(configDir, "plugin"), result.changed, configDir);
353
393
  }
354
394
  export async function earlyLaunch(configDir, plugins) {
395
+ if (isOpencodeHookInvocation(configDir))
396
+ return {};
355
397
  EARLY_LAUNCH_CONFIG_DIR = configDir;
356
398
  writeLog("Starting earlyLaunch updater sequence");
357
399
  // Self-update first
@@ -393,7 +435,11 @@ export async function earlyLaunch(configDir, plugins) {
393
435
  }
394
436
  }
395
437
  }
396
- export async function activate() {
438
+ export async function activate(opencodeHookInput) {
439
+ // module load below calls activate() with no argument; opencode passes a
440
+ // context object when re-invoking the export — return an inert plugin instance
441
+ if (opencodeHookInput !== undefined)
442
+ return {};
397
443
  const isClaude = process.argv.join(" ").includes("claude");
398
444
  const appName = isClaude ? "claude" : "opencode";
399
445
  const configDir = getAppConfigDir(appName);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plugin-updater",
3
- "version": "1.0.25",
3
+ "version": "1.0.26",
4
4
  "description": "Plugin lifecycle manager for OpenCode and Claude Code launchers",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",