opencode-plugin-auto-update 0.3.0 → 0.3.1

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/dist/index.js +92 -16
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/index.ts
2
- import { appendFile, readFile as readFile3 } from "fs/promises";
2
+ import { appendFile, readFile as readFile4 } from "fs/promises";
3
3
  import { join as join3 } from "path";
4
4
  import { homedir as homedir3 } from "os";
5
5
 
@@ -126,6 +126,22 @@ async function getHostname() {
126
126
 
127
127
  // src/update.ts
128
128
  var DEFAULT_CONFIG_DIR2 = join2(homedir2(), ".config", "opencode");
129
+ function normalizePluginConfig(config) {
130
+ const existingPlugin = Array.isArray(config.plugin) ? [...config.plugin] : [];
131
+ const existingPlugins = Array.isArray(config.plugins) ? [...config.plugins] : [];
132
+ if (existingPlugins.length === 0) {
133
+ return { config, changed: false };
134
+ }
135
+ const merged = [];
136
+ for (const entry of [...existingPlugin, ...existingPlugins]) {
137
+ if (!merged.includes(entry)) {
138
+ merged.push(entry);
139
+ }
140
+ }
141
+ config.plugin = merged;
142
+ delete config.plugins;
143
+ return { config, changed: true };
144
+ }
129
145
  async function runAutoUpdate(options = {}) {
130
146
  const disabled = options.disabled ?? envFlag("OPENCODE_AUTO_UPDATE_DISABLED");
131
147
  if (disabled) {
@@ -133,7 +149,7 @@ async function runAutoUpdate(options = {}) {
133
149
  }
134
150
  const debug = options.debug ?? envFlag("OPENCODE_AUTO_UPDATE_DEBUG");
135
151
  const ignoreThrottle = options.ignoreThrottle ?? envFlag("OPENCODE_AUTO_UPDATE_BYPASS_THROTTLE");
136
- const intervalHours = options.intervalHours ?? envNumber("OPENCODE_AUTO_UPDATE_INTERVAL_HOURS", 24);
152
+ const intervalHours = options.intervalHours ?? envNumber("OPENCODE_AUTO_UPDATE_INTERVAL_HOURS", 0);
137
153
  const preservePinned = options.preservePinned ?? envFlag("OPENCODE_AUTO_UPDATE_PINNED");
138
154
  const configDir = options.configDir ?? DEFAULT_CONFIG_DIR2;
139
155
  const configPath = join2(configDir, "opencode.json");
@@ -159,15 +175,24 @@ async function runAutoUpdate(options = {}) {
159
175
  try {
160
176
  const state = await readThrottleState({ configDir });
161
177
  const now = Date.now();
162
- const intervalMs = Math.max(intervalHours, 1) * 60 * 60 * 1e3;
178
+ const intervalMs = intervalHours * 60 * 60 * 1e3;
163
179
  if (!ignoreThrottle && state.lastRun && now - state.lastRun < intervalMs) {
164
180
  log("[auto-update] Throttled, skipping update.");
165
181
  return;
166
182
  }
167
183
  await writeThrottleState({ ...state, lastRun: now }, { debug, configDir });
168
- const config = await readConfig(configPath);
169
- const { plugins, key } = getPluginList(config);
170
- if (!plugins || plugins.length === 0 || !key) {
184
+ const rawConfig = await readConfig(configPath);
185
+ if (!rawConfig) {
186
+ log("[auto-update] No config found, skipping.");
187
+ return;
188
+ }
189
+ const normalized = normalizePluginConfig(rawConfig);
190
+ if (normalized.changed) {
191
+ await writeConfig(configPath, normalized.config);
192
+ log("[auto-update] Migrated config.plugins -> config.plugin");
193
+ }
194
+ const { plugins } = getPluginList(normalized.config);
195
+ if (!plugins || plugins.length === 0) {
171
196
  log("[auto-update] No plugins found to update.");
172
197
  return;
173
198
  }
@@ -187,7 +212,11 @@ async function runAutoUpdate(options = {}) {
187
212
  error
188
213
  });
189
214
  if (updateResult.changed) {
190
- const updatedConfig = { ...config, [key]: updateResult.plugins };
215
+ const updatedConfig = {
216
+ ...normalized.config,
217
+ plugin: updateResult.plugins
218
+ };
219
+ delete updatedConfig.plugins;
191
220
  await writeConfig(configPath, updatedConfig);
192
221
  }
193
222
  const hasOcx = await commandExists("ocx");
@@ -231,16 +260,10 @@ async function writeConfig(configPath, config) {
231
260
  `, "utf-8");
232
261
  }
233
262
  function getPluginList(config) {
234
- if (!config) {
235
- return { plugins: null, key: null };
236
- }
237
263
  if (Array.isArray(config.plugin)) {
238
- return { plugins: config.plugin, key: "plugin" };
239
- }
240
- if (Array.isArray(config.plugins)) {
241
- return { plugins: config.plugins, key: "plugins" };
264
+ return { plugins: config.plugin };
242
265
  }
243
- return { plugins: null, key: null };
266
+ return { plugins: null };
244
267
  }
245
268
  async function updatePlugins(options) {
246
269
  const { plugins, configDir, preservePinned, useBun, log, error } = options;
@@ -407,8 +430,47 @@ function formatLogMessage(args) {
407
430
  }).join(" ");
408
431
  }
409
432
 
433
+ // src/utils/circular-log.ts
434
+ import { readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
435
+ var MAX_ENTRIES = 5;
436
+ async function readCircularLog(logPath) {
437
+ try {
438
+ const contents = await readFile3(logPath, "utf-8");
439
+ return JSON.parse(contents);
440
+ } catch {
441
+ return { entries: [] };
442
+ }
443
+ }
444
+ async function appendCircularLog(logPath, entry) {
445
+ const log = await readCircularLog(logPath);
446
+ log.entries.push(entry);
447
+ if (log.entries.length > MAX_ENTRIES) {
448
+ log.entries = log.entries.slice(-MAX_ENTRIES);
449
+ }
450
+ await writeFile3(logPath, JSON.stringify(log, null, 2) + "\n", "utf-8");
451
+ }
452
+ function formatLogEntry(plugins, errors, duration, success) {
453
+ return {
454
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
455
+ pluginsUpdated: plugins,
456
+ errors: errors.slice(0, 10),
457
+ duration,
458
+ success
459
+ };
460
+ }
461
+
410
462
  // src/index.ts
411
463
  var CONFIG_PATH = join3(homedir3(), ".config", "opencode", "opencode-plugin-auto-update.json");
464
+ function extractUpdatedPlugins(logEntries) {
465
+ const plugins = [];
466
+ for (const entry of logEntries) {
467
+ const match = entry.match(/Updated\s+(.+?)\s+from\s+v[\d.]+\s+to\s+v[\d.]+/i);
468
+ if (match && match[1]) {
469
+ plugins.push(match[1]);
470
+ }
471
+ }
472
+ return plugins;
473
+ }
412
474
  var DEBUG_FILE = "/tmp/opencode-auto-update-debug.log";
413
475
  var DEBUG_ENABLED = false;
414
476
  async function src_default(ctx) {
@@ -440,6 +502,7 @@ async function src_default(ctx) {
440
502
  await writeDebug("toast shown");
441
503
  }
442
504
  };
505
+ const CIRCULAR_LOG_PATH = join3(ctx.directory, "..", ".auto-update-history.json");
443
506
  const startUpdate = () => {
444
507
  if (updateStarted) {
445
508
  return;
@@ -448,18 +511,31 @@ async function src_default(ctx) {
448
511
  void writeDebug(`startUpdate invoked (ignoreThrottle=${shouldIgnoreThrottle()})`);
449
512
  const logEntries = [];
450
513
  const errorEntries = [];
514
+ const startTime = Date.now();
451
515
  runAutoUpdate({
452
516
  debug: configDebug,
453
517
  ignoreThrottle: shouldIgnoreThrottle(),
454
518
  onLog: (message) => logEntries.push(message),
455
519
  onError: (message) => errorEntries.push(message)
456
520
  }).then(() => {
521
+ const duration = Date.now() - startTime;
522
+ const success = errorEntries.length === 0;
523
+ const pluginsUpdated = extractUpdatedPlugins(logEntries);
524
+ void appendCircularLog(
525
+ CIRCULAR_LOG_PATH,
526
+ formatLogEntry(pluginsUpdated, errorEntries, duration, success)
527
+ );
457
528
  updateMessage = formatUpdateMessage(logEntries, errorEntries);
458
529
  void writeDebug(`update finished (logs=${logEntries.length}, errors=${errorEntries.length})`);
459
530
  notifyUser(updateMessage).catch((error) => {
460
531
  void writeDebug(`notifyUser error: ${String(error)}`);
461
532
  });
462
533
  }).catch((error) => {
534
+ const duration = Date.now() - startTime;
535
+ void appendCircularLog(
536
+ CIRCULAR_LOG_PATH,
537
+ formatLogEntry([], [String(error)], duration, false)
538
+ );
463
539
  void writeDebug(`runAutoUpdate error: ${String(error)}`);
464
540
  });
465
541
  };
@@ -506,7 +582,7 @@ async function writeDebug(message) {
506
582
  }
507
583
  async function readLocalConfig() {
508
584
  try {
509
- const raw = await readFile3(CONFIG_PATH, "utf-8");
585
+ const raw = await readFile4(CONFIG_PATH, "utf-8");
510
586
  const parsed = JSON.parse(raw);
511
587
  return parsed ?? {};
512
588
  } catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-plugin-auto-update",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "OpenCode plugin that auto-updates plugins in the background",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",