refactor: remove CLI dead code

Remove unused daemon installation/uninstallation logic and unnecessary re-exports:
- Delete cli/src/daemon/mac/ directory (macOS LaunchDaemon install/uninstall not used)
- Delete cli/src/daemon/install.ts and uninstall.ts wrappers
- Remove daemon install/uninstall subcommands from cli/src/commands/daemon.ts
- Delete cli/src/api/encryption.ts (misleading name, just base64 encoding)
- Inline base64 encoding directly in cli/src/persistence.ts
- Delete cli/src/modules/common/gitHandlers.ts (unnecessary re-export)
- Update cli/src/modules/common/registerCommonHandlers.ts to import directly from handlers/git

Part of refactoring plan item #1 "删除 CLI 死代码".
This commit is contained in:
weishu
2026-01-05 13:00:48 +08:00
parent 23887ce040
commit d388c92114
9 changed files with 2 additions and 206 deletions
-3
View File
@@ -1,3 +0,0 @@
export function encodeBase64(buffer: Uint8Array): string {
return Buffer.from(buffer).toString('base64')
}
-22
View File
@@ -9,8 +9,6 @@ import {
import { getLatestDaemonLog } from '@/ui/logger'
import { spawnHappyCLI } from '@/utils/spawnHappyCLI'
import { runDoctorCommand } from '@/ui/doctor'
import { install } from '@/daemon/install'
import { uninstall } from '@/daemon/uninstall'
import { initializeToken } from '@/ui/tokenInit'
import type { CommandDefinition } from './types'
@@ -104,26 +102,6 @@ export const daemonCommand: CommandDefinition = {
process.exit(0)
}
if (daemonSubcommand === 'install') {
try {
await install()
} catch (error) {
console.error(chalk.red('Error:'), error instanceof Error ? error.message : 'Unknown error')
process.exit(1)
}
return
}
if (daemonSubcommand === 'uninstall') {
try {
await uninstall()
} catch (error) {
console.error(chalk.red('Error:'), error instanceof Error ? error.message : 'Unknown error')
process.exit(1)
}
return
}
console.log(`
${chalk.bold('hapi daemon')} - Daemon management
-19
View File
@@ -1,19 +0,0 @@
import { logger } from '@/ui/logger';
import { install as installMac } from './mac/install';
export async function install(): Promise<void> {
if (process.platform === 'win32') {
throw new Error('Daemon installation as Windows service not yet supported. Use "hapi daemon start".');
}
if (process.platform !== 'darwin') {
throw new Error('Daemon installation is currently only supported on macOS');
}
if (process.getuid && process.getuid() !== 0) {
throw new Error('Daemon installation requires sudo privileges. Please run with sudo.');
}
logger.info('Installing HAPI CLI daemon for macOS...');
await installMac();
}
-94
View File
@@ -1,94 +0,0 @@
/**
* Installation script for HAPI daemon using macOS LaunchDaemons
*
* NOTE: This installation method is currently NOT USED in favor of auto-starting
* the daemon when the user runs the hapi command.
*
* Why we're not using this approach:
* 1. Installing a LaunchDaemon requires sudo permissions, which users might not be comfortable with
* 2. We assume users will run hapi frequently (every time they open their laptop)
* 3. The auto-start approach provides the same functionality without requiring elevated permissions
*
* This code is kept for potential future use if we decide to offer system-level installation as an option.
*/
import { writeFileSync, chmodSync, existsSync } from 'fs';
import { execSync } from 'child_process';
import { logger } from '@/ui/logger';
import { trimIdent } from '@/utils/trimIdent';
import os from 'os';
const PLIST_LABEL = 'com.hapi-cli.daemon';
const PLIST_FILE = `/Library/LaunchDaemons/${PLIST_LABEL}.plist`;
// NOTE: Local installation like --local does not make too much sense I feel like
export async function install(): Promise<void> {
try {
// Check if already installed
if (existsSync(PLIST_FILE)) {
logger.info('Daemon plist already exists. Uninstalling first...');
execSync(`launchctl unload ${PLIST_FILE}`, { stdio: 'inherit' });
}
// Get the path to the hapi CLI executable
const happyPath = process.argv[0]; // Node.js executable
const scriptPath = process.argv[1]; // Script path
// Create plist content
const plistContent = trimIdent(`
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>${PLIST_LABEL}</string>
<key>ProgramArguments</key>
<array>
<string>${happyPath}</string>
<string>${scriptPath}</string>
<string>hapi-daemon</string>
</array>
<key>EnvironmentVariables</key>
<dict>
<key>HAPI_DAEMON_MODE</key>
<string>true</string>
</dict>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardErrorPath</key>
<string>${os.homedir()}/.hapi/daemon.err</string>
<key>StandardOutPath</key>
<string>${os.homedir()}/.hapi/daemon.log</string>
<key>WorkingDirectory</key>
<string>/tmp</string>
</dict>
</plist>
`);
// Write plist file
writeFileSync(PLIST_FILE, plistContent);
chmodSync(PLIST_FILE, 0o644);
logger.info(`Created daemon plist at ${PLIST_FILE}`);
// Load the daemon
execSync(`launchctl load ${PLIST_FILE}`, { stdio: 'inherit' });
logger.info('Daemon installed and started successfully');
logger.info('Check logs at ~/.hapi/daemon.log');
} catch (error) {
logger.debug('Failed to install daemon:', error);
throw error;
}
}
-45
View File
@@ -1,45 +0,0 @@
/**
* Uninstallation script for HAPI daemon LaunchDaemon
*
* NOTE: This uninstallation method is currently NOT USED since we moved away from
* system-level daemon installation. See install.ts for the full explanation.
*
* This code is kept for potential future use if we decide to offer system-level
* installation/uninstallation as an option.
*/
import { existsSync, unlinkSync } from 'fs';
import { execSync } from 'child_process';
import { logger } from '@/ui/logger';
const PLIST_LABEL = 'com.hapi-cli.daemon';
const PLIST_FILE = `/Library/LaunchDaemons/${PLIST_LABEL}.plist`;
export async function uninstall(): Promise<void> {
try {
// Check if plist exists
if (!existsSync(PLIST_FILE)) {
logger.info('Daemon plist not found. Nothing to uninstall.');
return;
}
// Unload the daemon
try {
execSync(`launchctl unload ${PLIST_FILE}`, { stdio: 'inherit' });
logger.info('Daemon stopped successfully');
} catch (error) {
// Daemon might not be loaded, continue with removal
logger.info('Failed to unload daemon (it might not be running)');
}
// Remove the plist file
unlinkSync(PLIST_FILE);
logger.info(`Removed daemon plist from ${PLIST_FILE}`);
logger.info('Daemon uninstalled successfully');
} catch (error) {
logger.debug('Failed to uninstall daemon:', error);
throw error;
}
}
-19
View File
@@ -1,19 +0,0 @@
import { logger } from '@/ui/logger';
import { uninstall as uninstallMac } from './mac/uninstall';
export async function uninstall(): Promise<void> {
if (process.platform === 'win32') {
throw new Error('Daemon uninstallation as Windows service not yet supported. Use "hapi daemon start".');
}
if (process.platform !== 'darwin') {
throw new Error('Daemon uninstallation is currently only supported on macOS');
}
if (process.getuid && process.getuid() !== 0) {
throw new Error('Daemon uninstallation requires sudo privileges. Please run with sudo.');
}
logger.info('Uninstalling HAPI CLI daemon for macOS...');
await uninstallMac();
}
-1
View File
@@ -1 +0,0 @@
export { registerGitHandlers } from './handlers/git'
@@ -3,7 +3,7 @@ import { registerBashHandlers } from './handlers/bash'
import { registerDirectoryHandlers } from './handlers/directories'
import { registerDifftasticHandlers } from './handlers/difftastic'
import { registerFileHandlers } from './handlers/files'
import { registerGitHandlers } from './gitHandlers'
import { registerGitHandlers } from './handlers/git'
import { registerRipgrepHandlers } from './handlers/ripgrep'
import { registerSlashCommandHandlers } from './handlers/slashCommands'
+1 -2
View File
@@ -8,7 +8,6 @@ import { FileHandle } from 'node:fs/promises'
import { readFile, writeFile, mkdir, open, unlink, rename, stat } from 'node:fs/promises'
import { existsSync, writeFileSync, readFileSync, unlinkSync } from 'node:fs'
import { configuration } from '@/configuration'
import { encodeBase64 } from '@/api/encryption';
import { isProcessAlive } from '@/utils/process';
interface Settings {
@@ -136,7 +135,7 @@ export async function writeCredentialsDataKey(credentials: { publicKey: Uint8Arr
await mkdir(configuration.happyHomeDir, { recursive: true })
}
await writeFile(configuration.privateKeyFile, JSON.stringify({
encryption: { publicKey: encodeBase64(credentials.publicKey), machineKey: encodeBase64(credentials.machineKey) },
encryption: { publicKey: Buffer.from(credentials.publicKey).toString('base64'), machineKey: Buffer.from(credentials.machineKey).toString('base64') },
token: credentials.token
}, null, 2));
}