plugin-updater 1.0.14 → 1.0.15

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.js +114 -123
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -57,125 +57,75 @@ function executeGit(command, cwd) {
57
57
  }
58
58
  }
59
59
 
60
- const updaterAPI = {
61
- earlyLaunch: function(configDir) {
62
- EARLY_LAUNCH_CONFIG_DIR = configDir;
63
- global.__PLUGIN_UPDATER_HANDLED_BY_HUB__ = true;
64
- },
65
-
66
- updatePlugin: function(pluginName, gitUrl, branch = null, commitHash = null) {
67
- const reposDir = getReposDir();
68
- const targetDir = path.join(reposDir, pluginName);
69
-
70
- if (!fs.existsSync(targetDir)) {
71
- if (!fs.existsSync(reposDir)) fs.mkdirSync(reposDir, { recursive: true });
72
- const branchFlag = branch ? `--branch ${branch}` : "";
73
- executeGit(`git clone --recurse-submodules ${branchFlag} ${gitUrl} ${pluginName}`, reposDir);
60
+ function updatePlugin(pluginName, gitUrl, branch, commitHash) {
61
+ const reposDir = getReposDir();
62
+ const targetDir = path.join(reposDir, pluginName);
63
+
64
+ if (!fs.existsSync(targetDir)) {
65
+ if (!fs.existsSync(reposDir)) fs.mkdirSync(reposDir, { recursive: true });
66
+ const branchFlag = branch ? `--branch ${branch}` : "";
67
+ executeGit(`git clone --recurse-submodules ${branchFlag} ${gitUrl} ${pluginName}`, reposDir);
68
+ } else {
69
+ executeGit("git fetch origin", targetDir);
70
+ if (commitHash) {
71
+ executeGit(`git checkout ${commitHash}`, targetDir);
72
+ } else if (branch) {
73
+ executeGit(`git checkout ${branch}`, targetDir);
74
+ executeGit(`git pull --ff-only origin ${branch}`, targetDir);
74
75
  } else {
75
- executeGit("git fetch origin", targetDir);
76
- if (commitHash) {
77
- executeGit(`git checkout ${commitHash}`, targetDir);
78
- } else if (branch) {
79
- executeGit(`git checkout ${branch}`, targetDir);
80
- executeGit(`git pull --ff-only origin ${branch}`, targetDir);
81
- } else {
82
- executeGit("git checkout main || git checkout master", targetDir);
83
- executeGit("git pull --ff-only", targetDir);
84
- }
85
- executeGit("git submodule update --init --recursive", targetDir);
86
- }
87
- return true;
88
- },
89
-
90
- deployToExecutionDir: function(pluginName, executionPath) {
91
- const sourceDir = path.join(getReposDir(), pluginName);
92
- if (!fs.existsSync(sourceDir)) return false;
93
-
94
- const packageJsonPath = path.join(sourceDir, "package.json");
95
- if (fs.existsSync(packageJsonPath)) {
96
- try {
97
- writeLog(`Running npm install for ${pluginName}`);
98
- execSync("npm install", { cwd: sourceDir, stdio: "ignore" });
99
- writeLog(`Finished npm install for ${pluginName}`);
100
-
101
- const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
102
- if (pkg.scripts && pkg.scripts.build) {
103
- execSync("npm run build", { cwd: sourceDir, stdio: "ignore" });
104
- writeLog(`Finished npm run build for ${pluginName}`);
105
- } else {
106
- writeLog(`Skipped npm run build for ${pluginName} (no build script found)`);
107
- }
108
- } catch (error) {
109
- writeLog(`Build/Install failed for ${pluginName}: ${error.message}`, true);
110
- }
76
+ executeGit("git checkout main || git checkout master", targetDir);
77
+ executeGit("git pull --ff-only", targetDir);
111
78
  }
79
+ executeGit("git submodule update --init --recursive", targetDir);
80
+ }
81
+ return true;
82
+ }
112
83
 
113
- const distPath = path.join(sourceDir, "dist");
114
- const deploySource = fs.existsSync(distPath) ? distPath : sourceDir;
115
- const pluginExecutionPath = path.join(executionPath, pluginName);
116
-
117
- if (!fs.existsSync(pluginExecutionPath)) {
118
- fs.mkdirSync(pluginExecutionPath, { recursive: true });
119
- }
84
+ function deployToExecutionDir(pluginName, executionPath) {
85
+ const sourceDir = path.join(getReposDir(), pluginName);
86
+ if (!fs.existsSync(sourceDir)) return false;
120
87
 
88
+ const packageJsonPath = path.join(sourceDir, "package.json");
89
+ if (fs.existsSync(packageJsonPath)) {
121
90
  try {
122
- writeLog(`Running cpSync for ${pluginName}`);
123
- fs.cpSync(deploySource, pluginExecutionPath, { recursive: true, force: true });
124
- writeLog(`Finished cpSync for ${pluginName}`);
125
- } catch (e) {
126
- writeLog(`cpSync failed for ${pluginName}: ${e.message}`, true);
91
+ writeLog(`Running npm install for ${pluginName}`);
92
+ execSync("npm install", { cwd: sourceDir, stdio: "ignore" });
93
+ writeLog(`Finished npm install for ${pluginName}`);
94
+
95
+ const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
96
+ if (pkg.scripts && pkg.scripts.build) {
97
+ execSync("npm run build", { cwd: sourceDir, stdio: "ignore" });
98
+ writeLog(`Finished npm run build for ${pluginName}`);
99
+ } else {
100
+ writeLog(`Skipped npm run build for ${pluginName} (no build script found)`);
101
+ }
102
+ } catch (error) {
103
+ writeLog(`Build/Install failed for ${pluginName}: ${error.message}`, true);
127
104
  }
128
- return true;
129
- },
105
+ }
130
106
 
131
- rebuild: function(pluginName) {
132
- const isClaude = process.argv.join(' ').includes('claude');
133
- const configDir = getAppConfigDir(isClaude ? "claude" : "opencode");
134
- this.deployToExecutionDir(pluginName, path.join(configDir, "plugin"));
135
- return "Rebuilt " + pluginName;
136
- },
137
-
138
- downgrade: function(pluginName, commitHash) {
139
- const reposDir = getReposDir();
140
- const targetDir = path.join(reposDir, pluginName);
141
- if (fs.existsSync(targetDir)) {
142
- executeGit(`git fetch origin`, targetDir);
143
- executeGit(`git checkout ${commitHash}`, targetDir);
144
- executeGit(`git submodule update --init --recursive`, targetDir);
145
- return this.rebuild(pluginName);
146
- }
147
- return "Repo not found";
148
- },
107
+ const distPath = path.join(sourceDir, "dist");
108
+ const deploySource = fs.existsSync(distPath) ? distPath : sourceDir;
109
+ const pluginExecutionPath = path.join(executionPath, pluginName);
149
110
 
150
- disable: function(plugin) {
151
- const isClaude = process.argv.join(' ').includes('claude');
152
- const configDir = getAppConfigDir(isClaude ? "claude" : "opencode");
153
- const pluginsJsonPath = path.join(configDir, "config", "plugins.json");
154
- if (fs.existsSync(pluginsJsonPath)) {
155
- let plugins = JSON.parse(fs.readFileSync(pluginsJsonPath, "utf-8"));
156
- const pluginIndex = plugins.findIndex(p => p.name === plugin.name);
157
- if (pluginIndex >= 0) {
158
- plugins[pluginIndex].enabled = false;
159
- fs.writeFileSync(pluginsJsonPath, JSON.stringify(plugins, null, 2), "utf-8");
160
- }
161
- }
162
- const pluginExecutionPath = path.join(configDir, "plugin", plugin.name);
163
- if (fs.existsSync(pluginExecutionPath)) {
164
- try { fs.rmSync(pluginExecutionPath, { recursive: true, force: true }); } catch (e) {}
165
- }
166
- },
111
+ if (!fs.existsSync(pluginExecutionPath)) {
112
+ fs.mkdirSync(pluginExecutionPath, { recursive: true });
113
+ }
167
114
 
168
- uninstall: function(plugin) {
169
- this.disable(plugin);
170
- const targetDir = path.join(getReposDir(), plugin.name);
171
- if (fs.existsSync(targetDir)) {
172
- try { fs.rmSync(targetDir, { recursive: true, force: true }); } catch (e) {}
173
- }
115
+ try {
116
+ writeLog(`Running cpSync for ${pluginName}`);
117
+ fs.cpSync(deploySource, pluginExecutionPath, { recursive: true, force: true });
118
+ writeLog(`Finished cpSync for ${pluginName}`);
119
+ } catch (e) {
120
+ writeLog(`cpSync failed for ${pluginName}: ${e.message}`, true);
174
121
  }
175
- };
122
+ return true;
123
+ }
176
124
 
177
- // OpenCode plugin server function called by opencode with plugin input context
178
- const serverPlugin = async function(input) {
125
+ // OpenCode NPM plugin contract: export default must be a function.
126
+ // opencode iterates Object.entries(mod) and calls each export as fn(input).
127
+ // ONLY export a single default function — no named exports.
128
+ async function pluginUpdaterEntry(input) {
179
129
  const configDir = (input && input.directory)
180
130
  ? path.dirname(input.directory)
181
131
  : path.dirname(getReposDir());
@@ -189,7 +139,8 @@ const serverPlugin = async function(input) {
189
139
  writeLog(`plugin-updater activated. configDir=${configDir}`);
190
140
 
191
141
  if (!global.__PLUGIN_UPDATER_HANDLED_BY_HUB__) {
192
- updaterAPI.earlyLaunch(configDir);
142
+ EARLY_LAUNCH_CONFIG_DIR = configDir;
143
+ global.__PLUGIN_UPDATER_HANDLED_BY_HUB__ = true;
193
144
 
194
145
  const pluginsJsonPath = path.join(configDir, "config", "plugins.json");
195
146
  if (fs.existsSync(pluginsJsonPath)) {
@@ -199,8 +150,8 @@ const serverPlugin = async function(input) {
199
150
  if (plugin.url && plugin.enabled !== false && plugin.type !== "npm") {
200
151
  const branch = plugin.branch || null;
201
152
  const commit = plugin.commit || null;
202
- updaterAPI.updatePlugin(plugin.name, plugin.url, branch, commit);
203
- updaterAPI.deployToExecutionDir(plugin.name, pluginsDir);
153
+ updatePlugin(plugin.name, plugin.url, branch, commit);
154
+ deployToExecutionDir(plugin.name, pluginsDir);
204
155
  }
205
156
  }
206
157
  } catch (e) {
@@ -209,18 +160,58 @@ const serverPlugin = async function(input) {
209
160
  }
210
161
  }
211
162
 
212
- // Return hooks object (can be empty opencode expects this shape)
163
+ // Return empty hooks object required by opencode plugin contract
213
164
  return {};
214
- };
215
-
216
- // Expose updaterAPI on serverPlugin for hub access: import('plugin-updater').then(m => m.default.server.earlyLaunch(...))
217
- Object.keys(updaterAPI).forEach(key => {
218
- serverPlugin[key] = updaterAPI[key];
219
- });
165
+ }
220
166
 
221
- // OpenCode V1 plugin contract: export default { id, server }
222
- // opencode calls mod.default.server(input) to initialize the plugin
223
- export default {
224
- id: "plugin-updater",
225
- server: serverPlugin
167
+ // Attach API methods for hub access via: import('plugin-updater').then(m => m.default.earlyLaunch(...))
168
+ // These are function properties, NOT module-level named exports — they won't appear in Object.entries(mod)
169
+ pluginUpdaterEntry.earlyLaunch = function(configDir) {
170
+ EARLY_LAUNCH_CONFIG_DIR = configDir;
171
+ global.__PLUGIN_UPDATER_HANDLED_BY_HUB__ = true;
172
+ };
173
+ pluginUpdaterEntry.updatePlugin = updatePlugin;
174
+ pluginUpdaterEntry.deployToExecutionDir = deployToExecutionDir;
175
+ pluginUpdaterEntry.rebuild = function(pluginName) {
176
+ const isClaude = process.argv.join(' ').includes('claude');
177
+ const configDir = getAppConfigDir(isClaude ? "claude" : "opencode");
178
+ deployToExecutionDir(pluginName, path.join(configDir, "plugin"));
179
+ return "Rebuilt " + pluginName;
226
180
  };
181
+ pluginUpdaterEntry.downgrade = function(pluginName, commitHash) {
182
+ const reposDir = getReposDir();
183
+ const targetDir = path.join(reposDir, pluginName);
184
+ if (fs.existsSync(targetDir)) {
185
+ executeGit(`git fetch origin`, targetDir);
186
+ executeGit(`git checkout ${commitHash}`, targetDir);
187
+ executeGit(`git submodule update --init --recursive`, targetDir);
188
+ return pluginUpdaterEntry.rebuild(pluginName);
189
+ }
190
+ return "Repo not found";
191
+ };
192
+ pluginUpdaterEntry.disable = function(plugin) {
193
+ const isClaude = process.argv.join(' ').includes('claude');
194
+ const configDir = getAppConfigDir(isClaude ? "claude" : "opencode");
195
+ const pluginsJsonPath = path.join(configDir, "config", "plugins.json");
196
+ if (fs.existsSync(pluginsJsonPath)) {
197
+ let plugins = JSON.parse(fs.readFileSync(pluginsJsonPath, "utf-8"));
198
+ const pluginIndex = plugins.findIndex(p => p.name === plugin.name);
199
+ if (pluginIndex >= 0) {
200
+ plugins[pluginIndex].enabled = false;
201
+ fs.writeFileSync(pluginsJsonPath, JSON.stringify(plugins, null, 2), "utf-8");
202
+ }
203
+ }
204
+ const pluginExecutionPath = path.join(configDir, "plugin", plugin.name);
205
+ if (fs.existsSync(pluginExecutionPath)) {
206
+ try { fs.rmSync(pluginExecutionPath, { recursive: true, force: true }); } catch (e) {}
207
+ }
208
+ };
209
+ pluginUpdaterEntry.uninstall = function(plugin) {
210
+ pluginUpdaterEntry.disable(plugin);
211
+ const targetDir = path.join(getReposDir(), plugin.name);
212
+ if (fs.existsSync(targetDir)) {
213
+ try { fs.rmSync(targetDir, { recursive: true, force: true }); } catch (e) {}
214
+ }
215
+ };
216
+
217
+ export default pluginUpdaterEntry;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plugin-updater",
3
- "version": "1.0.14",
3
+ "version": "1.0.15",
4
4
  "description": "Plugin lifecycle manager for OpenCode and Claude Code launchers",
5
5
  "type": "module",
6
6
  "main": "index.js",