@comfanion/workflow 4.36.19 → 4.36.21
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/package.json
CHANGED
package/src/build-info.json
CHANGED
|
@@ -32,6 +32,24 @@ npx opencode-workflow index --index code
|
|
|
32
32
|
|
|
33
33
|
---
|
|
34
34
|
|
|
35
|
+
### version-check.ts
|
|
36
|
+
|
|
37
|
+
Checks for updates and shows a toast notification if a newer version is available.
|
|
38
|
+
|
|
39
|
+
**Features:**
|
|
40
|
+
- Checks npm registry for latest @comfanion/workflow version
|
|
41
|
+
- Compares with locally installed version
|
|
42
|
+
- Shows toast notification if update available
|
|
43
|
+
- Caches check result for 1 hour
|
|
44
|
+
- Supports EN/UK/RU languages
|
|
45
|
+
|
|
46
|
+
**Toast Example:**
|
|
47
|
+
```
|
|
48
|
+
🚀 Update available! 4.36.19 → 4.37.0. Run: npx @comfanion/workflow update
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
35
53
|
### custom-compaction.ts
|
|
36
54
|
|
|
37
55
|
Intelligent session compaction that preserves flow context.
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import type { Plugin } from "@opencode-ai/plugin"
|
|
2
|
+
import path from "path"
|
|
3
|
+
import fs from "fs/promises"
|
|
4
|
+
import https from "https"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Version Check Plugin
|
|
8
|
+
*
|
|
9
|
+
* Checks if a newer version of @comfanion/workflow is available on npm.
|
|
10
|
+
* Shows a toast notification if an update is available.
|
|
11
|
+
*
|
|
12
|
+
* Configuration in .opencode/config.yaml:
|
|
13
|
+
* version_check:
|
|
14
|
+
* enabled: true # Enable version checking
|
|
15
|
+
* check_interval: 3600000 # Check once per hour (ms)
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const DEBUG = process.env.DEBUG?.includes('version-check') || process.env.DEBUG === '*'
|
|
19
|
+
const PACKAGE_NAME = '@comfanion/workflow'
|
|
20
|
+
const CACHE_FILE = '.version-check-cache.json'
|
|
21
|
+
|
|
22
|
+
function log(msg: string): void {
|
|
23
|
+
if (DEBUG) console.log(`[version-check] ${msg}`)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface VersionCache {
|
|
27
|
+
lastCheck: number
|
|
28
|
+
latestVersion: string
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function getLocalVersion(directory: string): Promise<string | null> {
|
|
32
|
+
try {
|
|
33
|
+
// Try build-info.json first (created during npm publish)
|
|
34
|
+
const buildInfoPath = path.join(directory, '.opencode', 'build-info.json')
|
|
35
|
+
const buildInfo = JSON.parse(await fs.readFile(buildInfoPath, 'utf8'))
|
|
36
|
+
if (buildInfo.version) return buildInfo.version
|
|
37
|
+
} catch {}
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
// Fallback to config.yaml version field
|
|
41
|
+
const configPath = path.join(directory, '.opencode', 'config.yaml')
|
|
42
|
+
const config = await fs.readFile(configPath, 'utf8')
|
|
43
|
+
const match = config.match(/^version:\s*["']?([\d.]+)["']?/m)
|
|
44
|
+
if (match) return match[1]
|
|
45
|
+
} catch {}
|
|
46
|
+
|
|
47
|
+
return null
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function getLatestVersion(): Promise<string | null> {
|
|
51
|
+
return new Promise((resolve) => {
|
|
52
|
+
const timeout = setTimeout(() => resolve(null), 5000) // 5s timeout
|
|
53
|
+
|
|
54
|
+
https.get(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`, (res) => {
|
|
55
|
+
let data = ''
|
|
56
|
+
res.on('data', chunk => data += chunk)
|
|
57
|
+
res.on('end', () => {
|
|
58
|
+
clearTimeout(timeout)
|
|
59
|
+
try {
|
|
60
|
+
const json = JSON.parse(data)
|
|
61
|
+
resolve(json.version || null)
|
|
62
|
+
} catch {
|
|
63
|
+
resolve(null)
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
}).on('error', () => {
|
|
67
|
+
clearTimeout(timeout)
|
|
68
|
+
resolve(null)
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function loadCache(directory: string): Promise<VersionCache | null> {
|
|
74
|
+
try {
|
|
75
|
+
const cachePath = path.join(directory, '.opencode', CACHE_FILE)
|
|
76
|
+
const data = await fs.readFile(cachePath, 'utf8')
|
|
77
|
+
return JSON.parse(data)
|
|
78
|
+
} catch {
|
|
79
|
+
return null
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function saveCache(directory: string, cache: VersionCache): Promise<void> {
|
|
84
|
+
try {
|
|
85
|
+
const cachePath = path.join(directory, '.opencode', CACHE_FILE)
|
|
86
|
+
await fs.writeFile(cachePath, JSON.stringify(cache))
|
|
87
|
+
} catch {}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function compareVersions(local: string, latest: string): number {
|
|
91
|
+
const localParts = local.split('.').map(Number)
|
|
92
|
+
const latestParts = latest.split('.').map(Number)
|
|
93
|
+
|
|
94
|
+
for (let i = 0; i < 3; i++) {
|
|
95
|
+
const l = localParts[i] || 0
|
|
96
|
+
const r = latestParts[i] || 0
|
|
97
|
+
if (l < r) return -1
|
|
98
|
+
if (l > r) return 1
|
|
99
|
+
}
|
|
100
|
+
return 0
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Fun update messages
|
|
104
|
+
const UPDATE_MESSAGES = {
|
|
105
|
+
en: (local: string, latest: string) => `🚀 Update available! ${local} → ${latest}. Run: npx @comfanion/workflow update`,
|
|
106
|
+
uk: (local: string, latest: string) => `🚀 Є оновлення! ${local} → ${latest}. Виконай: npx @comfanion/workflow update`,
|
|
107
|
+
ru: (local: string, latest: string) => `🚀 Доступно обновление! ${local} → ${latest}. Выполни: npx @comfanion/workflow update`,
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function getLanguage(directory: string): Promise<'en' | 'uk' | 'ru'> {
|
|
111
|
+
try {
|
|
112
|
+
const configPath = path.join(directory, '.opencode', 'config.yaml')
|
|
113
|
+
const config = await fs.readFile(configPath, 'utf8')
|
|
114
|
+
if (config.includes('communication_language: Ukrainian') || config.includes('communication_language: uk')) return 'uk'
|
|
115
|
+
if (config.includes('communication_language: Russian') || config.includes('communication_language: ru')) return 'ru'
|
|
116
|
+
} catch {}
|
|
117
|
+
return 'en'
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export const VersionCheckPlugin: Plugin = async ({ directory, client }) => {
|
|
121
|
+
const CHECK_INTERVAL = 60 * 60 * 1000 // 1 hour (you release often! 🚀)
|
|
122
|
+
|
|
123
|
+
const toast = async (message: string, variant: 'info' | 'success' | 'error' = 'info') => {
|
|
124
|
+
try {
|
|
125
|
+
await client?.tui?.showToast?.({ body: { message, variant } })
|
|
126
|
+
} catch {}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
log(`Plugin loaded`)
|
|
130
|
+
|
|
131
|
+
// Run check after short delay (let TUI initialize)
|
|
132
|
+
setTimeout(async () => {
|
|
133
|
+
try {
|
|
134
|
+
// Check cache first
|
|
135
|
+
const cache = await loadCache(directory)
|
|
136
|
+
const now = Date.now()
|
|
137
|
+
|
|
138
|
+
// Skip if checked recently
|
|
139
|
+
if (cache && (now - cache.lastCheck) < CHECK_INTERVAL) {
|
|
140
|
+
log(`Skipping check (cached ${Math.round((now - cache.lastCheck) / 1000 / 60)}min ago)`)
|
|
141
|
+
|
|
142
|
+
// But still show toast if update was available
|
|
143
|
+
const localVersion = await getLocalVersion(directory)
|
|
144
|
+
if (localVersion && cache.latestVersion && compareVersions(localVersion, cache.latestVersion) < 0) {
|
|
145
|
+
const lang = await getLanguage(directory)
|
|
146
|
+
await toast(UPDATE_MESSAGES[lang](localVersion, cache.latestVersion), 'info')
|
|
147
|
+
}
|
|
148
|
+
return
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Get versions
|
|
152
|
+
const localVersion = await getLocalVersion(directory)
|
|
153
|
+
if (!localVersion) {
|
|
154
|
+
log(`Could not determine local version`)
|
|
155
|
+
return
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
log(`Local version: ${localVersion}`)
|
|
159
|
+
|
|
160
|
+
const latestVersion = await getLatestVersion()
|
|
161
|
+
if (!latestVersion) {
|
|
162
|
+
log(`Could not fetch latest version from npm`)
|
|
163
|
+
return
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
log(`Latest version: ${latestVersion}`)
|
|
167
|
+
|
|
168
|
+
// Save to cache
|
|
169
|
+
await saveCache(directory, { lastCheck: now, latestVersion })
|
|
170
|
+
|
|
171
|
+
// Compare and notify
|
|
172
|
+
if (compareVersions(localVersion, latestVersion) < 0) {
|
|
173
|
+
log(`Update available: ${localVersion} → ${latestVersion}`)
|
|
174
|
+
const lang = await getLanguage(directory)
|
|
175
|
+
await toast(UPDATE_MESSAGES[lang](localVersion, latestVersion), 'info')
|
|
176
|
+
} else {
|
|
177
|
+
log(`Up to date!`)
|
|
178
|
+
}
|
|
179
|
+
} catch (e) {
|
|
180
|
+
log(`Error: ${(e as Error).message}`)
|
|
181
|
+
}
|
|
182
|
+
}, 2000)
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
event: async () => {}, // No events needed
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export default VersionCheckPlugin
|