@colmbus72/yeehaw 0.1.0 → 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/dist/index.js +12 -0
- package/dist/lib/update-check.d.ts +14 -0
- package/dist/lib/update-check.js +116 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5,9 +5,21 @@ import { execaSync } from 'execa';
|
|
|
5
5
|
import { App } from './app.js';
|
|
6
6
|
import { isInsideYeehawSession, yeehawSessionExists, createYeehawSession, attachToYeehaw, hasTmux, } from './lib/tmux.js';
|
|
7
7
|
import { ensureConfigDirs } from './lib/config.js';
|
|
8
|
+
import { checkForUpdates, formatUpdateMessage } from './lib/update-check.js';
|
|
8
9
|
function main() {
|
|
9
10
|
// Ensure config directories exist
|
|
10
11
|
ensureConfigDirs();
|
|
12
|
+
// Check for updates (non-blocking, uses cache)
|
|
13
|
+
try {
|
|
14
|
+
const updateInfo = checkForUpdates();
|
|
15
|
+
if (updateInfo?.updateAvailable) {
|
|
16
|
+
console.log('\x1b[33m%s\x1b[0m', formatUpdateMessage(updateInfo));
|
|
17
|
+
console.log('');
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
// Ignore update check errors
|
|
22
|
+
}
|
|
11
23
|
// Check if tmux is available
|
|
12
24
|
if (!hasTmux()) {
|
|
13
25
|
console.error('Error: tmux is required but not installed');
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface UpdateInfo {
|
|
2
|
+
updateAvailable: boolean;
|
|
3
|
+
currentVersion: string;
|
|
4
|
+
latestVersion: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Check for updates. Returns immediately with cached data if available,
|
|
8
|
+
* otherwise fetches from npm (with 5s timeout).
|
|
9
|
+
*/
|
|
10
|
+
export declare function checkForUpdates(): UpdateInfo | null;
|
|
11
|
+
/**
|
|
12
|
+
* Format update notification message
|
|
13
|
+
*/
|
|
14
|
+
export declare function formatUpdateMessage(info: UpdateInfo): string;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { execFileSync } from 'child_process';
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { YEEHAW_DIR } from './paths.js';
|
|
5
|
+
const PACKAGE_NAME = '@colmbus72/yeehaw';
|
|
6
|
+
const CACHE_FILE = join(YEEHAW_DIR, '.update-check');
|
|
7
|
+
const CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
8
|
+
function getCurrentVersion() {
|
|
9
|
+
try {
|
|
10
|
+
const packagePath = new URL('../../package.json', import.meta.url);
|
|
11
|
+
const pkg = JSON.parse(readFileSync(packagePath, 'utf-8'));
|
|
12
|
+
return pkg.version;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return '0.0.0';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function readCache() {
|
|
19
|
+
try {
|
|
20
|
+
if (!existsSync(CACHE_FILE))
|
|
21
|
+
return null;
|
|
22
|
+
const data = JSON.parse(readFileSync(CACHE_FILE, 'utf-8'));
|
|
23
|
+
if (Date.now() - data.checkedAt < CACHE_TTL_MS) {
|
|
24
|
+
return data;
|
|
25
|
+
}
|
|
26
|
+
return null; // Cache expired
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function writeCache(latestVersion) {
|
|
33
|
+
try {
|
|
34
|
+
if (!existsSync(YEEHAW_DIR)) {
|
|
35
|
+
mkdirSync(YEEHAW_DIR, { recursive: true });
|
|
36
|
+
}
|
|
37
|
+
const data = {
|
|
38
|
+
latestVersion,
|
|
39
|
+
checkedAt: Date.now(),
|
|
40
|
+
};
|
|
41
|
+
writeFileSync(CACHE_FILE, JSON.stringify(data), 'utf-8');
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// Ignore cache write errors
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function fetchLatestVersion() {
|
|
48
|
+
try {
|
|
49
|
+
// Use execFileSync for safety (no shell injection possible)
|
|
50
|
+
const result = execFileSync('npm', ['view', PACKAGE_NAME, 'version'], {
|
|
51
|
+
encoding: 'utf-8',
|
|
52
|
+
timeout: 5000,
|
|
53
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
54
|
+
});
|
|
55
|
+
return result.trim();
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function compareVersions(current, latest) {
|
|
62
|
+
const parseVersion = (v) => v.split('.').map(n => parseInt(n, 10) || 0);
|
|
63
|
+
const [cMajor, cMinor, cPatch] = parseVersion(current);
|
|
64
|
+
const [lMajor, lMinor, lPatch] = parseVersion(latest);
|
|
65
|
+
if (lMajor > cMajor)
|
|
66
|
+
return 1;
|
|
67
|
+
if (lMajor < cMajor)
|
|
68
|
+
return -1;
|
|
69
|
+
if (lMinor > cMinor)
|
|
70
|
+
return 1;
|
|
71
|
+
if (lMinor < cMinor)
|
|
72
|
+
return -1;
|
|
73
|
+
if (lPatch > cPatch)
|
|
74
|
+
return 1;
|
|
75
|
+
if (lPatch < cPatch)
|
|
76
|
+
return -1;
|
|
77
|
+
return 0;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Check for updates. Returns immediately with cached data if available,
|
|
81
|
+
* otherwise fetches from npm (with 5s timeout).
|
|
82
|
+
*/
|
|
83
|
+
export function checkForUpdates() {
|
|
84
|
+
try {
|
|
85
|
+
const currentVersion = getCurrentVersion();
|
|
86
|
+
// Try cache first
|
|
87
|
+
const cached = readCache();
|
|
88
|
+
if (cached) {
|
|
89
|
+
return {
|
|
90
|
+
updateAvailable: compareVersions(currentVersion, cached.latestVersion) > 0,
|
|
91
|
+
currentVersion,
|
|
92
|
+
latestVersion: cached.latestVersion,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
// Fetch from npm
|
|
96
|
+
const latestVersion = fetchLatestVersion();
|
|
97
|
+
if (!latestVersion)
|
|
98
|
+
return null;
|
|
99
|
+
// Update cache
|
|
100
|
+
writeCache(latestVersion);
|
|
101
|
+
return {
|
|
102
|
+
updateAvailable: compareVersions(currentVersion, latestVersion) > 0,
|
|
103
|
+
currentVersion,
|
|
104
|
+
latestVersion,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Format update notification message
|
|
113
|
+
*/
|
|
114
|
+
export function formatUpdateMessage(info) {
|
|
115
|
+
return `Update available: ${info.currentVersion} → ${info.latestVersion}\nRun: npm install -g ${PACKAGE_NAME}`;
|
|
116
|
+
}
|