plugin-updater 1.0.13 → 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 +118 -120
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -57,127 +57,78 @@ function executeGit(command, cwd) {
57
57
  }
58
58
  }
59
59
 
60
- const updaterAPI = {
61
- name: "plugin-updater",
62
-
63
- earlyLaunch: function(configDir) {
64
- EARLY_LAUNCH_CONFIG_DIR = configDir;
65
- global.__PLUGIN_UPDATER_HANDLED_BY_HUB__ = true;
66
- },
67
-
68
- updatePlugin: function(pluginName, gitUrl, branch = null, commitHash = null) {
69
- const reposDir = getReposDir();
70
- const targetDir = path.join(reposDir, pluginName);
71
-
72
- if (!fs.existsSync(targetDir)) {
73
- if (!fs.existsSync(reposDir)) fs.mkdirSync(reposDir, { recursive: true });
74
- const branchFlag = branch ? `--branch ${branch}` : "";
75
- 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);
76
75
  } else {
77
- executeGit("git fetch origin", targetDir);
78
- if (commitHash) {
79
- executeGit(`git checkout ${commitHash}`, targetDir);
80
- } else if (branch) {
81
- executeGit(`git checkout ${branch}`, targetDir);
82
- executeGit(`git pull --ff-only origin ${branch}`, targetDir);
83
- } else {
84
- executeGit("git checkout main || git checkout master", targetDir);
85
- executeGit("git pull --ff-only", targetDir);
86
- }
87
- executeGit("git submodule update --init --recursive", targetDir);
88
- }
89
- return true;
90
- },
91
-
92
- deployToExecutionDir: function(pluginName, executionPath) {
93
- const sourceDir = path.join(getReposDir(), pluginName);
94
- if (!fs.existsSync(sourceDir)) return false;
95
-
96
- const packageJsonPath = path.join(sourceDir, "package.json");
97
- if (fs.existsSync(packageJsonPath)) {
98
- try {
99
- writeLog(`Running npm install for ${pluginName}`);
100
- execSync("npm install", { cwd: sourceDir, stdio: "ignore" });
101
- writeLog(`Finished npm install for ${pluginName}`);
102
-
103
- const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
104
- if (pkg.scripts && pkg.scripts.build) {
105
- execSync("npm run build", { cwd: sourceDir, stdio: "ignore" });
106
- writeLog(`Finished npm run build for ${pluginName}`);
107
- } else {
108
- writeLog(`Skipped npm run build for ${pluginName} (no build script found)`);
109
- }
110
- } catch (error) {
111
- writeLog(`Build/Install failed for ${pluginName}: ${error.message}`, true);
112
- }
76
+ executeGit("git checkout main || git checkout master", targetDir);
77
+ executeGit("git pull --ff-only", targetDir);
113
78
  }
79
+ executeGit("git submodule update --init --recursive", targetDir);
80
+ }
81
+ return true;
82
+ }
114
83
 
115
- const distPath = path.join(sourceDir, "dist");
116
- const deploySource = fs.existsSync(distPath) ? distPath : sourceDir;
117
- const pluginExecutionPath = path.join(executionPath, pluginName);
118
-
119
- if (!fs.existsSync(pluginExecutionPath)) {
120
- fs.mkdirSync(pluginExecutionPath, { recursive: true });
121
- }
84
+ function deployToExecutionDir(pluginName, executionPath) {
85
+ const sourceDir = path.join(getReposDir(), pluginName);
86
+ if (!fs.existsSync(sourceDir)) return false;
122
87
 
88
+ const packageJsonPath = path.join(sourceDir, "package.json");
89
+ if (fs.existsSync(packageJsonPath)) {
123
90
  try {
124
- writeLog(`Running cpSync for ${pluginName}`);
125
- fs.cpSync(deploySource, pluginExecutionPath, { recursive: true, force: true });
126
- writeLog(`Finished cpSync for ${pluginName}`);
127
- } catch (e) {
128
- 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);
129
104
  }
130
- return true;
131
- },
105
+ }
132
106
 
133
- rebuild: function(pluginName) {
134
- const isClaude = process.argv.join(' ').includes('claude');
135
- const configDir = getAppConfigDir(isClaude ? "claude" : "opencode");
136
- this.deployToExecutionDir(pluginName, path.join(configDir, "plugin"));
137
- return "Rebuilt " + pluginName;
138
- },
139
-
140
- downgrade: function(pluginName, commitHash) {
141
- const reposDir = getReposDir();
142
- const targetDir = path.join(reposDir, pluginName);
143
- if (fs.existsSync(targetDir)) {
144
- executeGit(`git fetch origin`, targetDir);
145
- executeGit(`git checkout ${commitHash}`, targetDir);
146
- executeGit(`git submodule update --init --recursive`, targetDir);
147
- return this.rebuild(pluginName);
148
- }
149
- return "Repo not found";
150
- },
107
+ const distPath = path.join(sourceDir, "dist");
108
+ const deploySource = fs.existsSync(distPath) ? distPath : sourceDir;
109
+ const pluginExecutionPath = path.join(executionPath, pluginName);
151
110
 
152
- disable: function(plugin) {
153
- const isClaude = process.argv.join(' ').includes('claude');
154
- const configDir = getAppConfigDir(isClaude ? "claude" : "opencode");
155
- const pluginsJsonPath = path.join(configDir, "config", "plugins.json");
156
- if (fs.existsSync(pluginsJsonPath)) {
157
- let plugins = JSON.parse(fs.readFileSync(pluginsJsonPath, "utf-8"));
158
- const pluginIndex = plugins.findIndex(p => p.name === plugin.name);
159
- if (pluginIndex >= 0) {
160
- plugins[pluginIndex].enabled = false;
161
- fs.writeFileSync(pluginsJsonPath, JSON.stringify(plugins, null, 2), "utf-8");
162
- }
163
- }
164
- const pluginExecutionPath = path.join(configDir, "plugin", plugin.name);
165
- if (fs.existsSync(pluginExecutionPath)) {
166
- try { fs.rmSync(pluginExecutionPath, { recursive: true, force: true }); } catch (e) {}
167
- }
168
- },
111
+ if (!fs.existsSync(pluginExecutionPath)) {
112
+ fs.mkdirSync(pluginExecutionPath, { recursive: true });
113
+ }
169
114
 
170
- uninstall: function(plugin) {
171
- this.disable(plugin);
172
- const targetDir = path.join(getReposDir(), plugin.name);
173
- if (fs.existsSync(targetDir)) {
174
- try { fs.rmSync(targetDir, { recursive: true, force: true }); } catch (e) {}
175
- }
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);
176
121
  }
177
- };
122
+ return true;
123
+ }
178
124
 
179
- const pluginUpdaterEntry = async function(input) {
180
- const configDir = (input && input.configDir) ? input.configDir : path.dirname(getReposDir());
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) {
129
+ const configDir = (input && input.directory)
130
+ ? path.dirname(input.directory)
131
+ : path.dirname(getReposDir());
181
132
 
182
133
  // 1. GUARANTEE BASE DIRECTORIES EXIST ON LAUNCH
183
134
  const reposDir = path.join(configDir, "repos");
@@ -185,8 +136,11 @@ const pluginUpdaterEntry = async function(input) {
185
136
  if (!fs.existsSync(reposDir)) fs.mkdirSync(reposDir, { recursive: true });
186
137
  if (!fs.existsSync(pluginsDir)) fs.mkdirSync(pluginsDir, { recursive: true });
187
138
 
139
+ writeLog(`plugin-updater activated. configDir=${configDir}`);
140
+
188
141
  if (!global.__PLUGIN_UPDATER_HANDLED_BY_HUB__) {
189
- updaterAPI.earlyLaunch(configDir);
142
+ EARLY_LAUNCH_CONFIG_DIR = configDir;
143
+ global.__PLUGIN_UPDATER_HANDLED_BY_HUB__ = true;
190
144
 
191
145
  const pluginsJsonPath = path.join(configDir, "config", "plugins.json");
192
146
  if (fs.existsSync(pluginsJsonPath)) {
@@ -196,8 +150,8 @@ const pluginUpdaterEntry = async function(input) {
196
150
  if (plugin.url && plugin.enabled !== false && plugin.type !== "npm") {
197
151
  const branch = plugin.branch || null;
198
152
  const commit = plugin.commit || null;
199
- updaterAPI.updatePlugin(plugin.name, plugin.url, branch, commit);
200
- updaterAPI.deployToExecutionDir(plugin.name, pluginsDir);
153
+ updatePlugin(plugin.name, plugin.url, branch, commit);
154
+ deployToExecutionDir(plugin.name, pluginsDir);
201
155
  }
202
156
  }
203
157
  } catch (e) {
@@ -205,15 +159,59 @@ const pluginUpdaterEntry = async function(input) {
205
159
  }
206
160
  }
207
161
  }
162
+
163
+ // Return empty hooks object — required by opencode plugin contract
208
164
  return {};
209
- };
165
+ }
210
166
 
211
- // Attach API methods to the entry function (skip 'name' conflicts with Function.name)
212
- for (const [key, value] of Object.entries(updaterAPI)) {
213
- if (key !== 'name') {
214
- pluginUpdaterEntry[key] = value;
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;
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);
215
189
  }
216
- }
217
- pluginUpdaterEntry.pluginName = updaterAPI.name;
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
+ };
218
216
 
219
217
  export default pluginUpdaterEntry;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plugin-updater",
3
- "version": "1.0.13",
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",