dmux
Version:
Tmux pane manager with AI agent integration for parallel development workflows
236 lines • 8.79 kB
JavaScript
import { execSync } from 'child_process';
import fs from 'fs/promises';
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
const packageJson = require('../package.json');
export class AutoUpdater {
configFile;
checkIntervalMs = 24 * 60 * 60 * 1000; // 24 hours
constructor(configFile) {
this.configFile = configFile;
}
async loadSettings() {
try {
const content = await fs.readFile(this.configFile, 'utf-8');
const config = JSON.parse(content);
return config.updateSettings || {
checkIntervalHours: 24,
autoUpdateEnabled: true
};
}
catch {
return {
checkIntervalHours: 24,
autoUpdateEnabled: true
};
}
}
async saveSettings(settings) {
let config = {};
try {
const content = await fs.readFile(this.configFile, 'utf-8');
config = JSON.parse(content);
}
catch { }
config.updateSettings = settings;
config.lastUpdated = new Date().toISOString();
await fs.writeFile(this.configFile, JSON.stringify(config, null, 2));
}
async shouldCheckForUpdates() {
const settings = await this.loadSettings();
const now = Date.now();
if (!settings.lastCheckTime) {
return true;
}
const intervalMs = (settings.checkIntervalHours || 24) * 60 * 60 * 1000;
return now - settings.lastCheckTime > intervalMs;
}
async getLatestVersion() {
try {
// First try using npm view which is usually faster
const result = execSync(`npm view ${packageJson.name} version`, {
encoding: 'utf-8',
stdio: 'pipe',
timeout: 10000
}).trim();
if (result && this.isValidVersion(result)) {
return result;
}
}
catch {
// Fallback to npm registry API
try {
const response = await fetch(`https://registry.npmjs.org/${packageJson.name}/latest`, {
method: 'GET',
headers: {
'User-Agent': `${packageJson.name}/${packageJson.version}`
}
});
if (response.ok) {
const data = await response.json();
if (data.version && this.isValidVersion(data.version)) {
return data.version;
}
}
}
catch {
// Network error or API unavailable
}
}
return null;
}
isValidVersion(version) {
return /^\d+\.\d+\.\d+/.test(version);
}
compareVersions(current, latest) {
const currentParts = current.split('.').map(n => parseInt(n));
const latestParts = latest.split('.').map(n => parseInt(n));
for (let i = 0; i < Math.max(currentParts.length, latestParts.length); i++) {
const currentPart = currentParts[i] || 0;
const latestPart = latestParts[i] || 0;
if (latestPart > currentPart)
return true;
if (latestPart < currentPart)
return false;
}
return false;
}
async detectInstallMethod() {
try {
// Check if dmux is globally installed and how
// Method 1: Check npm global packages
try {
const npmGlobals = execSync('npm list -g --depth=0', {
encoding: 'utf-8',
stdio: 'pipe'
});
if (npmGlobals.includes(`${packageJson.name}@`)) {
return { packageManager: 'npm', installMethod: 'global' };
}
}
catch { }
// Method 2: Check pnpm global packages
try {
const pnpmGlobals = execSync('pnpm list -g --depth=0', {
encoding: 'utf-8',
stdio: 'pipe'
});
if (pnpmGlobals.includes(`${packageJson.name}@`)) {
return { packageManager: 'pnpm', installMethod: 'global' };
}
}
catch { }
// Method 3: Check yarn global packages
try {
const yarnGlobals = execSync('yarn global list --depth=0', {
encoding: 'utf-8',
stdio: 'pipe'
});
if (yarnGlobals.includes(`${packageJson.name}@`)) {
return { packageManager: 'yarn', installMethod: 'global' };
}
}
catch { }
// Method 4: Check where dmux is installed by looking at the executable path
try {
const dmuxPath = execSync('which dmux', {
encoding: 'utf-8',
stdio: 'pipe'
}).trim();
if (dmuxPath.includes('/.npm/') || dmuxPath.includes('/npm/')) {
return { packageManager: 'npm', installMethod: 'global' };
}
else if (dmuxPath.includes('/.pnpm/')) {
return { packageManager: 'pnpm', installMethod: 'global' };
}
else if (dmuxPath.includes('/.yarn/')) {
return { packageManager: 'yarn', installMethod: 'global' };
}
else if (dmuxPath.includes('/node_modules/.bin/')) {
// Local installation
return { packageManager: null, installMethod: 'local' };
}
}
catch { }
return { packageManager: null, installMethod: 'unknown' };
}
catch {
return { packageManager: null, installMethod: 'unknown' };
}
}
async checkForUpdates() {
const latestVersion = await this.getLatestVersion();
const currentVersion = packageJson.version;
const { packageManager, installMethod } = await this.detectInstallMethod();
const hasUpdate = latestVersion ? this.compareVersions(currentVersion, latestVersion) : false;
// Update last check time
const settings = await this.loadSettings();
settings.lastCheckTime = Date.now();
await this.saveSettings(settings);
return {
currentVersion,
latestVersion: latestVersion || 'unknown',
hasUpdate,
packageManager,
installMethod
};
}
async performUpdate(updateInfo) {
if (!updateInfo.hasUpdate || !updateInfo.packageManager || updateInfo.installMethod !== 'global') {
return false;
}
try {
let updateCommand;
switch (updateInfo.packageManager) {
case 'npm':
updateCommand = `npm update -g ${packageJson.name}`;
break;
case 'pnpm':
updateCommand = `pnpm update -g ${packageJson.name}`;
break;
case 'yarn':
updateCommand = `yarn global upgrade ${packageJson.name}`;
break;
default:
return false;
}
// Run the update command with a timeout
execSync(updateCommand, {
stdio: 'pipe',
timeout: 60000 // 1 minute timeout
});
// Verify the update was successful
const newUpdateInfo = await this.checkForUpdates();
return newUpdateInfo.currentVersion === updateInfo.latestVersion;
}
catch {
return false;
}
}
async skipVersion(version) {
const settings = await this.loadSettings();
settings.skipVersion = version;
await this.saveSettings(settings);
}
async shouldShowUpdateNotification(updateInfo) {
if (!updateInfo.hasUpdate) {
return false;
}
const settings = await this.loadSettings();
// Don't show if user has disabled auto-updates
if (settings.autoUpdateEnabled === false) {
return false;
}
// Don't show if user has skipped this version
if (settings.skipVersion === updateInfo.latestVersion) {
return false;
}
return true;
}
async setAutoUpdateEnabled(enabled) {
const settings = await this.loadSettings();
settings.autoUpdateEnabled = enabled;
await this.saveSettings(settings);
}
}
//# sourceMappingURL=AutoUpdater.js.map