backeer-opencode 0.1.4 → 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/CHANGELOG.md +19 -0
- package/README.md +60 -9
- package/dist/backeer-permission.js +553 -7
- package/package.json +2 -2
- package/src/config.js +45 -0
- package/src/lifecycle-controller.js +205 -0
- package/src/lifecycle-publishers.js +187 -0
- package/src/management.js +51 -5
- package/src/plugin.js +21 -1
- package/src/updates.js +136 -0
package/src/updates.js
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
export const PLUGIN_VERSION = "0.2.0"
|
|
2
|
+
|
|
3
|
+
const PACKAGE_NAME = "backeer-opencode"
|
|
4
|
+
const NPM_REGISTRY_URL = "https://registry.npmjs.org"
|
|
5
|
+
const CHECK_TIMEOUT_MS = 3000
|
|
6
|
+
const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000
|
|
7
|
+
|
|
8
|
+
export function parseVersion(value) {
|
|
9
|
+
const match = String(value).trim().match(
|
|
10
|
+
/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/,
|
|
11
|
+
)
|
|
12
|
+
if (!match) return
|
|
13
|
+
return {
|
|
14
|
+
parts: [Number(match[1]), Number(match[2]), Number(match[3])],
|
|
15
|
+
prerelease: match[4],
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function isVersionNewer(latestValue, currentValue) {
|
|
20
|
+
const latest = parseVersion(latestValue)
|
|
21
|
+
const current = parseVersion(currentValue)
|
|
22
|
+
if (!latest || !current) return false
|
|
23
|
+
|
|
24
|
+
for (let index = 0; index < 3; index++) {
|
|
25
|
+
if (latest.parts[index] > current.parts[index]) return true
|
|
26
|
+
if (latest.parts[index] < current.parts[index]) return false
|
|
27
|
+
}
|
|
28
|
+
if (!latest.prerelease && current.prerelease) return true
|
|
29
|
+
if (latest.prerelease && !current.prerelease) return false
|
|
30
|
+
return Boolean(
|
|
31
|
+
latest.prerelease &&
|
|
32
|
+
current.prerelease &&
|
|
33
|
+
latest.prerelease !== current.prerelease,
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function createUpdateChecker({
|
|
38
|
+
fetchImplementation = globalThis.fetch,
|
|
39
|
+
now = () => Date.now(),
|
|
40
|
+
} = {}) {
|
|
41
|
+
let checkedAt = 0
|
|
42
|
+
let cachedResult
|
|
43
|
+
let pendingCheck
|
|
44
|
+
|
|
45
|
+
async function fetchLatest() {
|
|
46
|
+
const controller = new AbortController()
|
|
47
|
+
const timeout = setTimeout(() => controller.abort(), CHECK_TIMEOUT_MS)
|
|
48
|
+
try {
|
|
49
|
+
const response = await fetchImplementation(
|
|
50
|
+
`${NPM_REGISTRY_URL}/${PACKAGE_NAME}/latest`,
|
|
51
|
+
{ signal: controller.signal },
|
|
52
|
+
)
|
|
53
|
+
if (!response.ok) return { state: "unavailable", currentVersion: PLUGIN_VERSION }
|
|
54
|
+
const data = await response.json()
|
|
55
|
+
const latestVersion = typeof data?.version === "string"
|
|
56
|
+
? data.version.trim()
|
|
57
|
+
: ""
|
|
58
|
+
if (!parseVersion(latestVersion)) {
|
|
59
|
+
return { state: "unavailable", currentVersion: PLUGIN_VERSION }
|
|
60
|
+
}
|
|
61
|
+
if (!isVersionNewer(latestVersion, PLUGIN_VERSION)) {
|
|
62
|
+
return {
|
|
63
|
+
state: "current",
|
|
64
|
+
currentVersion: PLUGIN_VERSION,
|
|
65
|
+
latestVersion,
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
state: "available",
|
|
70
|
+
currentVersion: PLUGIN_VERSION,
|
|
71
|
+
latestVersion,
|
|
72
|
+
updateCommand:
|
|
73
|
+
`opencode plugin ${PACKAGE_NAME}@${latestVersion} -g --force`,
|
|
74
|
+
}
|
|
75
|
+
} catch {
|
|
76
|
+
return { state: "unavailable", currentVersion: PLUGIN_VERSION }
|
|
77
|
+
} finally {
|
|
78
|
+
clearTimeout(timeout)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
async check({ force = false } = {}) {
|
|
84
|
+
if (!force && checkedAt && now() - checkedAt < CHECK_INTERVAL_MS) {
|
|
85
|
+
return cachedResult
|
|
86
|
+
}
|
|
87
|
+
if (pendingCheck) return pendingCheck
|
|
88
|
+
|
|
89
|
+
pendingCheck = fetchLatest()
|
|
90
|
+
.then((result) => {
|
|
91
|
+
cachedResult = result
|
|
92
|
+
checkedAt = now()
|
|
93
|
+
return result
|
|
94
|
+
})
|
|
95
|
+
.finally(() => { pendingCheck = undefined })
|
|
96
|
+
return pendingCheck
|
|
97
|
+
},
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function formatAgentUpdateInstruction(info) {
|
|
102
|
+
return [
|
|
103
|
+
"[BACKEER UPDATE REQUESTED]",
|
|
104
|
+
`The user explicitly requested the Backeer OpenCode plugin update from v${info.currentVersion} to v${info.latestVersion}.`,
|
|
105
|
+
`Run exactly: ${info.updateCommand}`,
|
|
106
|
+
"Then verify that the global OpenCode config is pinned to the exact new version and that the matching package exists in the OpenCode cache.",
|
|
107
|
+
"Do not read, print, replace, or otherwise expose the Backeer token.",
|
|
108
|
+
"Do not use @latest and do not claim success until both installation and verification pass.",
|
|
109
|
+
"Do not terminate the active OpenCode process; tell the user to restart OpenCode after success.",
|
|
110
|
+
].join("\n")
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function createUpdateNoticeHook(updateChecker) {
|
|
114
|
+
const notifiedSessions = new Set()
|
|
115
|
+
|
|
116
|
+
return async (input, output) => {
|
|
117
|
+
if (notifiedSessions.has(input.sessionID)) return
|
|
118
|
+
notifiedSessions.add(input.sessionID)
|
|
119
|
+
|
|
120
|
+
const info = await updateChecker.check()
|
|
121
|
+
if (info.state !== "available") return
|
|
122
|
+
output.parts.push({
|
|
123
|
+
id: `prt_backeer-update-${Date.now()}`,
|
|
124
|
+
sessionID: input.sessionID,
|
|
125
|
+
messageID: output.message.id,
|
|
126
|
+
type: "text",
|
|
127
|
+
synthetic: true,
|
|
128
|
+
text: [
|
|
129
|
+
"[BACKEER UPDATE AVAILABLE]",
|
|
130
|
+
`Backeer OpenCode plugin v${info.latestVersion} is available; current version is v${info.currentVersion}.`,
|
|
131
|
+
"Inform the user that an update is available and suggest `/backeer update`.",
|
|
132
|
+
"Do not install it solely because of this notice.",
|
|
133
|
+
].join("\n"),
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
}
|