opencode-plugin-auto-update 0.1.1 → 0.3.0
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/README.md +17 -12
- package/dist/index.d.ts +10 -18
- package/dist/index.js +151 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ Automatically updates OpenCode plugins in the background on startup. No prompts,
|
|
|
16
16
|
|
|
17
17
|
- Zero startup delay (async, detached update)
|
|
18
18
|
- Bun-first installs with npm fallback
|
|
19
|
-
-
|
|
19
|
+
- Shows a brief toast summary after updates
|
|
20
20
|
- Skips local/path/git plugins
|
|
21
21
|
|
|
22
22
|
## 📦 Installation
|
|
@@ -94,29 +94,34 @@ Configure via environment variables:
|
|
|
94
94
|
|----------|---------|-------------|
|
|
95
95
|
| `OPENCODE_AUTO_UPDATE_DISABLED` | `false` | Disable all updates when `true` |
|
|
96
96
|
| `OPENCODE_AUTO_UPDATE_INTERVAL_HOURS` | `24` | Throttle interval in hours |
|
|
97
|
-
| `OPENCODE_AUTO_UPDATE_DEBUG` | `false` | Enable debug logs |
|
|
98
97
|
| `OPENCODE_AUTO_UPDATE_PINNED` | `false` | Preserve pinned versions |
|
|
99
98
|
| `OPENCODE_AUTO_UPDATE_BYPASS_THROTTLE` | `false` | Ignore throttle (useful for testing) |
|
|
100
99
|
|
|
101
|
-
###
|
|
100
|
+
### Local Config File
|
|
102
101
|
|
|
103
|
-
|
|
102
|
+
You can bypass throttling without relying on CLI flags by creating:
|
|
103
|
+
|
|
104
|
+
`~/.config/opencode/opencode-plugin-auto-update.json`
|
|
105
|
+
|
|
106
|
+
```json
|
|
107
|
+
{
|
|
108
|
+
"ignoreThrottle": true
|
|
109
|
+
}
|
|
110
|
+
```
|
|
104
111
|
|
|
105
112
|
## ❓ Troubleshooting
|
|
106
113
|
|
|
107
114
|
1. **Updates not running**: ensure `OPENCODE_AUTO_UPDATE_DISABLED` is not set to `true`.
|
|
108
|
-
2. **No logs**:
|
|
115
|
+
2. **No logs**: confirm the plugin is enabled and watch for the "Auto-update logs" output after startup.
|
|
109
116
|
3. **Plugin not loading**: check the `plugin` array in `~/.config/opencode/opencode.json`.
|
|
110
|
-
4. **Testing updates**:
|
|
117
|
+
4. **Testing updates**: set `OPENCODE_AUTO_UPDATE_BYPASS_THROTTLE=true` or the local config `ignoreThrottle=true`.
|
|
111
118
|
|
|
112
119
|
## 🚀 Release Process
|
|
113
120
|
|
|
114
|
-
1.
|
|
115
|
-
2.
|
|
116
|
-
3.
|
|
117
|
-
4. `npm
|
|
118
|
-
5. `git tag vX.Y.Z && git push --tags`
|
|
119
|
-
6. `gh release create vX.Y.Z --notes "..."`
|
|
121
|
+
1. Land changes on `main` using conventional commits.
|
|
122
|
+
2. The Release workflow opens a Release Please PR with version + changelog updates.
|
|
123
|
+
3. Merge the Release Please PR to create the tag + GitHub release.
|
|
124
|
+
4. The Publish workflow (environment: `release`) builds and publishes to npm via OIDC.
|
|
120
125
|
|
|
121
126
|
## 📄 License
|
|
122
127
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,30 +1,22 @@
|
|
|
1
1
|
interface PluginInput {
|
|
2
2
|
directory: string;
|
|
3
|
-
serverUrl?: URL | string;
|
|
4
3
|
client: {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
properties?: unknown;
|
|
14
|
-
}) => void): () => void;
|
|
4
|
+
tui?: {
|
|
5
|
+
showToast?: (input: {
|
|
6
|
+
body: {
|
|
7
|
+
title?: string;
|
|
8
|
+
message: string;
|
|
9
|
+
variant?: 'info' | 'success' | 'warning' | 'error';
|
|
10
|
+
};
|
|
11
|
+
}) => Promise<unknown>;
|
|
15
12
|
};
|
|
16
13
|
};
|
|
17
14
|
}
|
|
18
15
|
interface PluginOutput {
|
|
19
16
|
name: string;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
type: string;
|
|
23
|
-
properties?: unknown;
|
|
24
|
-
};
|
|
17
|
+
config?: (input: {
|
|
18
|
+
logLevel?: string;
|
|
25
19
|
}) => Promise<void>;
|
|
26
|
-
tool?: Record<string, unknown>;
|
|
27
|
-
config?: unknown;
|
|
28
20
|
}
|
|
29
21
|
declare function export_default(ctx: PluginInput): Promise<PluginOutput>;
|
|
30
22
|
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { appendFile, readFile as readFile3 } from "fs/promises";
|
|
3
|
+
import { join as join3 } from "path";
|
|
4
|
+
import { homedir as homedir3 } from "os";
|
|
5
|
+
|
|
1
6
|
// src/update.ts
|
|
2
7
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
3
8
|
import { dirname, join as join2 } from "path";
|
|
@@ -133,14 +138,18 @@ async function runAutoUpdate(options = {}) {
|
|
|
133
138
|
const configDir = options.configDir ?? DEFAULT_CONFIG_DIR2;
|
|
134
139
|
const configPath = join2(configDir, "opencode.json");
|
|
135
140
|
const log = (...args) => {
|
|
141
|
+
const message = formatLogMessage(args);
|
|
136
142
|
if (debug) {
|
|
137
|
-
console.log(
|
|
143
|
+
console.log(message);
|
|
138
144
|
}
|
|
145
|
+
options.onLog?.(message);
|
|
139
146
|
};
|
|
140
147
|
const error = (...args) => {
|
|
148
|
+
const message = formatLogMessage(args);
|
|
141
149
|
if (debug) {
|
|
142
|
-
console.error(
|
|
150
|
+
console.error(message);
|
|
143
151
|
}
|
|
152
|
+
options.onError?.(message);
|
|
144
153
|
};
|
|
145
154
|
const lockAcquired = await acquireLock({ debug, configDir });
|
|
146
155
|
if (!lockAcquired) {
|
|
@@ -181,6 +190,21 @@ async function runAutoUpdate(options = {}) {
|
|
|
181
190
|
const updatedConfig = { ...config, [key]: updateResult.plugins };
|
|
182
191
|
await writeConfig(configPath, updatedConfig);
|
|
183
192
|
}
|
|
193
|
+
const hasOcx = await commandExists("ocx");
|
|
194
|
+
if (hasOcx) {
|
|
195
|
+
log("[auto-update] Found ocx, checking for extension updates...");
|
|
196
|
+
const ocxResult = await runCommand("ocx", ["update"]);
|
|
197
|
+
if (ocxResult.code === 0) {
|
|
198
|
+
const output = ocxResult.stdout.trim();
|
|
199
|
+
if (output) {
|
|
200
|
+
log("[auto-update] ocx update result:", output);
|
|
201
|
+
} else {
|
|
202
|
+
log("[auto-update] ocx update complete (no output).");
|
|
203
|
+
}
|
|
204
|
+
} else {
|
|
205
|
+
error("[auto-update] ocx update failed:", ocxResult.stderr || ocxResult.stdout);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
184
208
|
await writeThrottleState(
|
|
185
209
|
{ ...state, lastRun: now, lastSuccess: Date.now() },
|
|
186
210
|
{ debug, configDir }
|
|
@@ -361,26 +385,139 @@ function envNumber(name, fallback) {
|
|
|
361
385
|
const parsed = Number(raw);
|
|
362
386
|
return Number.isFinite(parsed) ? parsed : fallback;
|
|
363
387
|
}
|
|
388
|
+
function formatLogMessage(args) {
|
|
389
|
+
return args.map((arg) => {
|
|
390
|
+
if (typeof arg === "string") {
|
|
391
|
+
return arg;
|
|
392
|
+
}
|
|
393
|
+
if (arg instanceof Error) {
|
|
394
|
+
return arg.message || arg.name;
|
|
395
|
+
}
|
|
396
|
+
if (arg && typeof arg === "object" && "message" in arg) {
|
|
397
|
+
const message = arg.message;
|
|
398
|
+
if (typeof message === "string" && message.trim().length > 0) {
|
|
399
|
+
return message;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
try {
|
|
403
|
+
return JSON.stringify(arg);
|
|
404
|
+
} catch {
|
|
405
|
+
return String(arg);
|
|
406
|
+
}
|
|
407
|
+
}).join(" ");
|
|
408
|
+
}
|
|
364
409
|
|
|
365
410
|
// src/index.ts
|
|
411
|
+
var CONFIG_PATH = join3(homedir3(), ".config", "opencode", "opencode-plugin-auto-update.json");
|
|
412
|
+
var DEBUG_FILE = "/tmp/opencode-auto-update-debug.log";
|
|
413
|
+
var DEBUG_ENABLED = false;
|
|
366
414
|
async function src_default(ctx) {
|
|
367
|
-
const args = process.argv ?? [];
|
|
368
|
-
const debugLevelFlag = args.includes("--log-level") && args.includes("DEBUG");
|
|
369
|
-
const envDebug = process.env.OPENCODE_AUTO_UPDATE_DEBUG?.toLowerCase() === "true";
|
|
370
415
|
const envBypassThrottle = process.env.OPENCODE_AUTO_UPDATE_BYPASS_THROTTLE?.toLowerCase() === "true";
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
416
|
+
let configIgnoreThrottle = false;
|
|
417
|
+
let updateStarted = false;
|
|
418
|
+
let startupTimer = null;
|
|
419
|
+
let updateMessage = null;
|
|
420
|
+
let lastToastMessage = null;
|
|
421
|
+
const localConfig = await readLocalConfig();
|
|
422
|
+
configIgnoreThrottle = localConfig.ignoreThrottle ?? false;
|
|
423
|
+
const configDebug = localConfig.debug ?? false;
|
|
424
|
+
const shouldIgnoreThrottle = () => envBypassThrottle || configIgnoreThrottle;
|
|
425
|
+
const notifyUser = async (message) => {
|
|
426
|
+
await writeDebug("notifyUser invoked");
|
|
427
|
+
const toastMessage = summarizeMessage(message);
|
|
428
|
+
if (toastMessage === lastToastMessage) {
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
lastToastMessage = toastMessage;
|
|
432
|
+
if (typeof ctx?.client?.tui?.showToast === "function") {
|
|
433
|
+
await ctx.client.tui.showToast({
|
|
434
|
+
body: {
|
|
435
|
+
title: "Auto-update",
|
|
436
|
+
message: toastMessage,
|
|
437
|
+
variant: "info"
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
await writeDebug("toast shown");
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
const startUpdate = () => {
|
|
444
|
+
if (updateStarted) {
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
updateStarted = true;
|
|
448
|
+
void writeDebug(`startUpdate invoked (ignoreThrottle=${shouldIgnoreThrottle()})`);
|
|
449
|
+
const logEntries = [];
|
|
450
|
+
const errorEntries = [];
|
|
451
|
+
runAutoUpdate({
|
|
452
|
+
debug: configDebug,
|
|
453
|
+
ignoreThrottle: shouldIgnoreThrottle(),
|
|
454
|
+
onLog: (message) => logEntries.push(message),
|
|
455
|
+
onError: (message) => errorEntries.push(message)
|
|
456
|
+
}).then(() => {
|
|
457
|
+
updateMessage = formatUpdateMessage(logEntries, errorEntries);
|
|
458
|
+
void writeDebug(`update finished (logs=${logEntries.length}, errors=${errorEntries.length})`);
|
|
459
|
+
notifyUser(updateMessage).catch((error) => {
|
|
460
|
+
void writeDebug(`notifyUser error: ${String(error)}`);
|
|
461
|
+
});
|
|
462
|
+
}).catch((error) => {
|
|
463
|
+
void writeDebug(`runAutoUpdate error: ${String(error)}`);
|
|
378
464
|
});
|
|
379
|
-
}
|
|
465
|
+
};
|
|
466
|
+
startupTimer = setTimeout(() => {
|
|
467
|
+
if (!updateStarted) {
|
|
468
|
+
void writeDebug("startup timer fired");
|
|
469
|
+
startUpdate();
|
|
470
|
+
}
|
|
471
|
+
}, 1500);
|
|
380
472
|
return {
|
|
381
|
-
name: "opencode-plugin-auto-update"
|
|
473
|
+
name: "opencode-plugin-auto-update",
|
|
474
|
+
config: async (config) => {
|
|
475
|
+
await writeDebug(`config hook invoked (logLevel=${config?.logLevel ?? "unset"})`);
|
|
476
|
+
if (startupTimer) {
|
|
477
|
+
clearTimeout(startupTimer);
|
|
478
|
+
startupTimer = null;
|
|
479
|
+
}
|
|
480
|
+
startUpdate();
|
|
481
|
+
}
|
|
382
482
|
};
|
|
383
483
|
}
|
|
484
|
+
function formatUpdateMessage(logs, errors) {
|
|
485
|
+
const lines = ["Auto-update logs"];
|
|
486
|
+
const logLines = logs.length > 0 ? logs : ["No update output recorded."];
|
|
487
|
+
lines.push("", ...limitLines(logLines, 40));
|
|
488
|
+
if (errors.length > 0) {
|
|
489
|
+
lines.push("", "Errors:", ...limitLines(errors, 10));
|
|
490
|
+
}
|
|
491
|
+
return lines.join("\n");
|
|
492
|
+
}
|
|
493
|
+
function limitLines(lines, maxLines) {
|
|
494
|
+
if (lines.length <= maxLines) {
|
|
495
|
+
return lines;
|
|
496
|
+
}
|
|
497
|
+
return [...lines.slice(0, maxLines), `... (${lines.length - maxLines} more lines)`];
|
|
498
|
+
}
|
|
499
|
+
async function writeDebug(message) {
|
|
500
|
+
if (!DEBUG_ENABLED) {
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
504
|
+
await appendFile(DEBUG_FILE, `[${timestamp}] ${message}
|
|
505
|
+
`);
|
|
506
|
+
}
|
|
507
|
+
async function readLocalConfig() {
|
|
508
|
+
try {
|
|
509
|
+
const raw = await readFile3(CONFIG_PATH, "utf-8");
|
|
510
|
+
const parsed = JSON.parse(raw);
|
|
511
|
+
return parsed ?? {};
|
|
512
|
+
} catch {
|
|
513
|
+
return {};
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
function summarizeMessage(message) {
|
|
517
|
+
const lines = message.split("\n").filter((line) => line.trim().length > 0);
|
|
518
|
+
const summary = lines.slice(0, 4).join(" | ");
|
|
519
|
+
return summary.length > 240 ? `${summary.slice(0, 237)}...` : summary;
|
|
520
|
+
}
|
|
384
521
|
export {
|
|
385
522
|
src_default as default
|
|
386
523
|
};
|