plugin-updater 1.0.8 → 1.0.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.
@@ -26,4 +26,4 @@ jobs:
26
26
  VERSION="${VERSION#v}"
27
27
  npm version $VERSION --allow-same-version --no-git-tag-version
28
28
 
29
- - run: npm publish --access public
29
+ - run: npm publish --provenance --access public
package/index.js CHANGED
@@ -1,4 +1,4 @@
1
- const fs = require('fs');
1
+ const fs = require('fs');
2
2
  const path = require('path');
3
3
  const os = require('os');
4
4
  const { execSync } = require('child_process');
@@ -15,6 +15,31 @@ function getAppConfigDir(appName) {
15
15
  return fs.existsSync(directPath) ? directPath : configPath;
16
16
  }
17
17
 
18
+ function writeLog(message, isError = false) {
19
+ try {
20
+ const date = new Date();
21
+ const dateStr = date.toISOString().split('T')[0];
22
+ const isClaude = process.argv.join(' ').includes('claude');
23
+ const appName = isClaude ? "claude" : "opencode";
24
+ const configDir = getAppConfigDir(appName);
25
+
26
+ const logsDir = path.join(configDir, "logs", dateStr);
27
+ if (!fs.existsSync(logsDir)) {
28
+ fs.mkdirSync(logsDir, { recursive: true });
29
+ }
30
+
31
+ const logFile = path.join(logsDir, `updater-${dateStr}.log`);
32
+ const prefix = isError ? "[ERROR]" : "[INFO]";
33
+ const logMsg = `[${date.toISOString()}] ${prefix} ${message}\n`;
34
+
35
+ fs.appendFileSync(logFile, logMsg);
36
+ } catch (e) {
37
+ // Silent fallback if logging fails
38
+ }
39
+ if (isError) console.error(message);
40
+ else console.log(message);
41
+ }
42
+
18
43
  function getReposDir() {
19
44
  const isClaude = process.argv.join(' ').includes('claude');
20
45
  const appName = isClaude ? "claude" : "opencode";
@@ -22,11 +47,12 @@ function getReposDir() {
22
47
  }
23
48
 
24
49
  function executeGit(command, cwd) {
50
+ writeLog(`Executing git: ${command} in ${cwd}`);
25
51
  try {
26
52
  execSync(command, { cwd, stdio: "ignore" });
27
53
  return true;
28
54
  } catch (error) {
29
- console.error(`[Updater] Git error in ${cwd}: ${error.message}`);
55
+ writeLog(`Git error in ${cwd}: ${error.message}`, true);
30
56
  return false;
31
57
  }
32
58
  }
@@ -67,12 +93,23 @@ const updaterAPI = {
67
93
  const sourceDir = path.join(getReposDir(), pluginName);
68
94
  if (!fs.existsSync(sourceDir)) return false;
69
95
 
70
- if (fs.existsSync(path.join(sourceDir, "package.json"))) {
96
+ const packageJsonPath = path.join(sourceDir, "package.json");
97
+ if (fs.existsSync(packageJsonPath)) {
71
98
  try {
99
+ writeLog(`Running npm install for ${pluginName}`);
72
100
  execSync("npm install", { cwd: sourceDir, stdio: "ignore" });
73
- execSync("npm run build", { cwd: sourceDir, stdio: "ignore" });
101
+ writeLog(`Finished npm install for ${pluginName}`);
102
+
103
+ // Safely check if a build script exists before executing
104
+ const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
105
+ if (pkg.scripts && pkg.scripts.build) {
106
+ execSync("npm run build", { cwd: sourceDir, stdio: "ignore" });
107
+ writeLog(`Finished npm run build for ${pluginName}`);
108
+ } else {
109
+ writeLog(`Skipped npm run build for ${pluginName} (no build script found)`);
110
+ }
74
111
  } catch (error) {
75
- console.error(`[Updater] Build failed for ${pluginName}: ${error.message}`);
112
+ writeLog(`Build/Install failed for ${pluginName}: ${error.message}`, true);
76
113
  }
77
114
  }
78
115
 
@@ -85,31 +122,50 @@ const updaterAPI = {
85
122
  }
86
123
 
87
124
  try {
125
+ writeLog(`Running cpSync for ${pluginName}`);
88
126
  fs.cpSync(deploySource, pluginExecutionPath, { recursive: true, force: true });
89
- return true;
90
- } catch (error) {
91
- console.error(`[Updater] Deploy failed for ${pluginName}: ${error.message}`);
92
- return false;
127
+ writeLog(`Finished cpSync for ${pluginName}`);
128
+ } catch (e) {
129
+ writeLog(`cpSync failed for ${pluginName}: ${e.message}`, true);
93
130
  }
131
+ return true;
94
132
  },
95
133
 
96
- rebuild: function(pluginObjOrName) {
97
- const pluginName = typeof pluginObjOrName === 'string' ? pluginObjOrName : pluginObjOrName.name;
98
- const targetDir = path.join(getReposDir(), pluginName);
134
+ rebuild: function(pluginName) {
135
+ const isClaude = process.argv.join(' ').includes('claude');
136
+ const configDir = getAppConfigDir(isClaude ? "claude" : "opencode");
137
+ this.deployToExecutionDir(pluginName, path.join(configDir, "plugin"));
138
+ return "Rebuilt " + pluginName;
139
+ },
140
+
141
+ downgrade: function(pluginName, commitHash) {
142
+ const reposDir = getReposDir();
143
+ const targetDir = path.join(reposDir, pluginName);
99
144
  if (fs.existsSync(targetDir)) {
100
- try { fs.rmSync(targetDir, { recursive: true, force: true }); } catch (e) {}
145
+ executeGit(`git fetch origin`, targetDir);
146
+ executeGit(`git checkout ${commitHash}`, targetDir);
147
+ executeGit(`git submodule update --init --recursive`, targetDir);
148
+ return this.rebuild(pluginName);
101
149
  }
102
- return null;
150
+ return "Repo not found";
103
151
  },
104
152
 
105
153
  disable: function(plugin) {
106
- try {
107
- const configDir = EARLY_LAUNCH_CONFIG_DIR || path.dirname(getReposDir());
108
- const pluginExecutionPath = path.join(configDir, "plugin", plugin.name);
109
- if (fs.existsSync(pluginExecutionPath)) {
110
- fs.rmSync(pluginExecutionPath, { recursive: true, force: true });
154
+ const isClaude = process.argv.join(' ').includes('claude');
155
+ const configDir = getAppConfigDir(isClaude ? "claude" : "opencode");
156
+ const pluginsJsonPath = path.join(configDir, "config", "plugins.json");
157
+ if (fs.existsSync(pluginsJsonPath)) {
158
+ let plugins = JSON.parse(fs.readFileSync(pluginsJsonPath, "utf-8"));
159
+ const pluginIndex = plugins.findIndex(p => p.name === plugin.name);
160
+ if (pluginIndex >= 0) {
161
+ plugins[pluginIndex].enabled = false;
162
+ fs.writeFileSync(pluginsJsonPath, JSON.stringify(plugins, null, 2), "utf-8");
111
163
  }
112
- } catch (e) {}
164
+ }
165
+ const pluginExecutionPath = path.join(configDir, "plugin", plugin.name);
166
+ if (fs.existsSync(pluginExecutionPath)) {
167
+ try { fs.rmSync(pluginExecutionPath, { recursive: true, force: true }); } catch (e) {}
168
+ }
113
169
  },
114
170
 
115
171
  uninstall: function(plugin) {
@@ -122,8 +178,15 @@ const updaterAPI = {
122
178
  };
123
179
 
124
180
  const pluginUpdaterEntry = async function(input) {
181
+ const configDir = (input && input.configDir) ? input.configDir : path.dirname(getReposDir());
182
+
183
+ // 1. GUARANTEE BASE DIRECTORIES EXIST ON LAUNCH
184
+ const reposDir = path.join(configDir, "repos");
185
+ const pluginsDir = path.join(configDir, "plugin");
186
+ if (!fs.existsSync(reposDir)) fs.mkdirSync(reposDir, { recursive: true });
187
+ if (!fs.existsSync(pluginsDir)) fs.mkdirSync(pluginsDir, { recursive: true });
188
+
125
189
  if (!global.__PLUGIN_UPDATER_HANDLED_BY_HUB__) {
126
- const configDir = (input && input.configDir) ? input.configDir : path.dirname(getReposDir());
127
190
  updaterAPI.earlyLaunch(configDir);
128
191
 
129
192
  const pluginsJsonPath = path.join(configDir, "config", "plugins.json");
@@ -132,19 +195,28 @@ const pluginUpdaterEntry = async function(input) {
132
195
  const plugins = JSON.parse(fs.readFileSync(pluginsJsonPath, "utf-8"));
133
196
  for (const plugin of plugins) {
134
197
  if (plugin.url && plugin.enabled !== false && plugin.type !== "npm") {
135
- updaterAPI.updatePlugin(plugin.name, plugin.url, plugin.branch || null, plugin.commit || null);
136
- updaterAPI.deployToExecutionDir(plugin.name, path.join(configDir, "plugin"));
198
+ const branch = plugin.branch || null;
199
+ const commit = plugin.commit || null;
200
+ updaterAPI.updatePlugin(plugin.name, plugin.url, branch, commit);
201
+ updaterAPI.deployToExecutionDir(plugin.name, pluginsDir);
137
202
  }
138
203
  }
139
204
  } catch (e) {
140
- console.error("[Updater] Failed to parse plugins.json", e);
205
+ writeLog(`Failed to parse plugins.json: ${e.message}`, true);
141
206
  }
142
207
  }
143
208
  }
144
209
  return {};
145
210
  };
146
211
 
147
- const apiMethods = { ...updaterAPI };
148
- delete apiMethods.name;
149
- Object.assign(pluginUpdaterEntry, apiMethods);
150
- module.exports = pluginUpdaterEntry;
212
+ module.exports = {
213
+ activate: async function() { return await pluginUpdaterEntry(); },
214
+ earlyLaunch: updaterAPI.earlyLaunch,
215
+ updatePlugin: updaterAPI.updatePlugin,
216
+ deployToExecutionDir: updaterAPI.deployToExecutionDir,
217
+ rebuild: updaterAPI.rebuild,
218
+ downgrade: updaterAPI.downgrade,
219
+ disable: updaterAPI.disable,
220
+ uninstall: updaterAPI.uninstall,
221
+ default: pluginUpdaterEntry
222
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plugin-updater",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "description": "Plugin lifecycle manager for OpenCode and Claude Code launchers",
5
5
  "main": "index.js",
6
6
  "license": "MIT",