opencode-plugin-auto-update 0.1.1 → 0.2.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 +139 -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
|
+
- Logs update output to the console sidebar with a toast summary
|
|
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) {
|
|
@@ -361,26 +370,142 @@ function envNumber(name, fallback) {
|
|
|
361
370
|
const parsed = Number(raw);
|
|
362
371
|
return Number.isFinite(parsed) ? parsed : fallback;
|
|
363
372
|
}
|
|
373
|
+
function formatLogMessage(args) {
|
|
374
|
+
return args.map((arg) => {
|
|
375
|
+
if (typeof arg === "string") {
|
|
376
|
+
return arg;
|
|
377
|
+
}
|
|
378
|
+
if (arg instanceof Error) {
|
|
379
|
+
return arg.message || arg.name;
|
|
380
|
+
}
|
|
381
|
+
if (arg && typeof arg === "object" && "message" in arg) {
|
|
382
|
+
const message = arg.message;
|
|
383
|
+
if (typeof message === "string" && message.trim().length > 0) {
|
|
384
|
+
return message;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
try {
|
|
388
|
+
return JSON.stringify(arg);
|
|
389
|
+
} catch {
|
|
390
|
+
return String(arg);
|
|
391
|
+
}
|
|
392
|
+
}).join(" ");
|
|
393
|
+
}
|
|
364
394
|
|
|
365
395
|
// src/index.ts
|
|
396
|
+
var CONFIG_PATH = join3(homedir3(), ".config", "opencode", "opencode-plugin-auto-update.json");
|
|
397
|
+
var DEBUG_FILE = "/tmp/opencode-auto-update-debug.log";
|
|
398
|
+
var DEBUG_ENABLED = false;
|
|
366
399
|
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
400
|
const envBypassThrottle = process.env.OPENCODE_AUTO_UPDATE_BYPASS_THROTTLE?.toLowerCase() === "true";
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
401
|
+
let configIgnoreThrottle = false;
|
|
402
|
+
let updateStarted = false;
|
|
403
|
+
let startupTimer = null;
|
|
404
|
+
let updateMessage = null;
|
|
405
|
+
let lastToastMessage = null;
|
|
406
|
+
const localConfig = await readLocalConfig();
|
|
407
|
+
configIgnoreThrottle = localConfig.ignoreThrottle ?? false;
|
|
408
|
+
const shouldIgnoreThrottle = () => envBypassThrottle || configIgnoreThrottle;
|
|
409
|
+
const notifyUser = async (message) => {
|
|
410
|
+
await writeDebug("notifyUser invoked");
|
|
411
|
+
const toastMessage = summarizeMessage(message);
|
|
412
|
+
if (toastMessage === lastToastMessage) {
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
lastToastMessage = toastMessage;
|
|
416
|
+
if (typeof ctx?.client?.tui?.showToast === "function") {
|
|
417
|
+
await ctx.client.tui.showToast({
|
|
418
|
+
body: {
|
|
419
|
+
title: "Auto-update",
|
|
420
|
+
message: toastMessage,
|
|
421
|
+
variant: "info"
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
await writeDebug("toast shown");
|
|
425
|
+
}
|
|
426
|
+
console.log(message);
|
|
427
|
+
await writeDebug("console fallback used");
|
|
428
|
+
};
|
|
429
|
+
const startUpdate = () => {
|
|
430
|
+
if (updateStarted) {
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
updateStarted = true;
|
|
434
|
+
void writeDebug(`startUpdate invoked (ignoreThrottle=${shouldIgnoreThrottle()})`);
|
|
435
|
+
const logEntries = [];
|
|
436
|
+
const errorEntries = [];
|
|
437
|
+
runAutoUpdate({
|
|
438
|
+
debug: false,
|
|
439
|
+
ignoreThrottle: shouldIgnoreThrottle(),
|
|
440
|
+
onLog: (message) => logEntries.push(message),
|
|
441
|
+
onError: (message) => errorEntries.push(message)
|
|
442
|
+
}).then(() => {
|
|
443
|
+
updateMessage = formatUpdateMessage(logEntries, errorEntries);
|
|
444
|
+
void writeDebug(`update finished (logs=${logEntries.length}, errors=${errorEntries.length})`);
|
|
445
|
+
notifyUser(updateMessage).catch((error) => {
|
|
446
|
+
void writeDebug(`notifyUser error: ${String(error)}`);
|
|
447
|
+
console.error("[opencode-plugin-auto-update] Failed to notify user:", error);
|
|
448
|
+
});
|
|
449
|
+
}).catch((error) => {
|
|
450
|
+
void writeDebug(`runAutoUpdate error: ${String(error)}`);
|
|
451
|
+
console.error("[opencode-plugin-auto-update] Update check failed:", error);
|
|
378
452
|
});
|
|
379
|
-
}
|
|
453
|
+
};
|
|
454
|
+
startupTimer = setTimeout(() => {
|
|
455
|
+
if (!updateStarted) {
|
|
456
|
+
void writeDebug("startup timer fired");
|
|
457
|
+
startUpdate();
|
|
458
|
+
}
|
|
459
|
+
}, 1500);
|
|
380
460
|
return {
|
|
381
|
-
name: "opencode-plugin-auto-update"
|
|
461
|
+
name: "opencode-plugin-auto-update",
|
|
462
|
+
config: async (config) => {
|
|
463
|
+
await writeDebug(`config hook invoked (logLevel=${config?.logLevel ?? "unset"})`);
|
|
464
|
+
if (startupTimer) {
|
|
465
|
+
clearTimeout(startupTimer);
|
|
466
|
+
startupTimer = null;
|
|
467
|
+
}
|
|
468
|
+
startUpdate();
|
|
469
|
+
}
|
|
382
470
|
};
|
|
383
471
|
}
|
|
472
|
+
function formatUpdateMessage(logs, errors) {
|
|
473
|
+
const lines = ["Auto-update logs"];
|
|
474
|
+
const logLines = logs.length > 0 ? logs : ["No update output recorded."];
|
|
475
|
+
lines.push("", ...limitLines(logLines, 40));
|
|
476
|
+
if (errors.length > 0) {
|
|
477
|
+
lines.push("", "Errors:", ...limitLines(errors, 10));
|
|
478
|
+
}
|
|
479
|
+
return lines.join("\n");
|
|
480
|
+
}
|
|
481
|
+
function limitLines(lines, maxLines) {
|
|
482
|
+
if (lines.length <= maxLines) {
|
|
483
|
+
return lines;
|
|
484
|
+
}
|
|
485
|
+
return [...lines.slice(0, maxLines), `... (${lines.length - maxLines} more lines)`];
|
|
486
|
+
}
|
|
487
|
+
async function writeDebug(message) {
|
|
488
|
+
if (!DEBUG_ENABLED) {
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
492
|
+
await appendFile(DEBUG_FILE, `[${timestamp}] ${message}
|
|
493
|
+
`);
|
|
494
|
+
}
|
|
495
|
+
async function readLocalConfig() {
|
|
496
|
+
try {
|
|
497
|
+
const raw = await readFile3(CONFIG_PATH, "utf-8");
|
|
498
|
+
const parsed = JSON.parse(raw);
|
|
499
|
+
return parsed ?? {};
|
|
500
|
+
} catch {
|
|
501
|
+
return {};
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
function summarizeMessage(message) {
|
|
505
|
+
const lines = message.split("\n").filter((line) => line.trim().length > 0);
|
|
506
|
+
const summary = lines.slice(0, 4).join(" | ");
|
|
507
|
+
return summary.length > 240 ? `${summary.slice(0, 237)}...` : summary;
|
|
508
|
+
}
|
|
384
509
|
export {
|
|
385
510
|
src_default as default
|
|
386
511
|
};
|