improvements
This commit is contained in:
parent
427c5478e1
commit
995b6ef843
10 changed files with 48 additions and 42 deletions
|
|
@ -9,11 +9,10 @@
|
|||
* - Handle JSON parse errors gracefully
|
||||
*/
|
||||
|
||||
import { setClaudeBasePathOverride } from '@main/utils/pathDecoder';
|
||||
import { getHomeDir, setClaudeBasePathOverride } from '@main/utils/pathDecoder';
|
||||
import { validateRegexPattern } from '@main/utils/regexValidation';
|
||||
import { createLogger } from '@shared/utils/logger';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
|
||||
import { DEFAULT_TRIGGERS, TriggerManager } from './TriggerManager';
|
||||
|
|
@ -23,7 +22,7 @@ import type { SshConnectionProfile } from '@shared/types/api';
|
|||
|
||||
const logger = createLogger('Service:ConfigManager');
|
||||
|
||||
const CONFIG_DIR = path.join(os.homedir(), '.claude');
|
||||
const CONFIG_DIR = path.join(getHomeDir(), '.claude');
|
||||
const CONFIG_FILENAME = 'claude-devtools-config.json';
|
||||
const DEFAULT_CONFIG_PATH = path.join(CONFIG_DIR, CONFIG_FILENAME);
|
||||
|
||||
|
|
|
|||
|
|
@ -12,11 +12,11 @@
|
|||
* - Emit IPC events to renderer: notification:new, notification:updated
|
||||
*/
|
||||
|
||||
import { getHomeDir } from '@main/utils/pathDecoder';
|
||||
import { createLogger } from '@shared/utils/logger';
|
||||
import { type BrowserWindow, Notification } from 'electron';
|
||||
import { EventEmitter } from 'events';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
|
||||
import { type DetectedError } from '../error/ErrorMessageBuilder';
|
||||
|
|
@ -77,7 +77,7 @@ const MAX_NOTIFICATIONS = 100;
|
|||
const THROTTLE_MS = 5000;
|
||||
|
||||
/** Path to notifications storage file */
|
||||
const NOTIFICATIONS_PATH = path.join(os.homedir(), '.claude', 'claude-devtools-notifications.json');
|
||||
const NOTIFICATIONS_PATH = path.join(getHomeDir(), '.claude', 'claude-devtools-notifications.json');
|
||||
|
||||
// =============================================================================
|
||||
// NotificationManager Class
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@
|
|||
* - Gracefully handle missing/unreadable files
|
||||
*/
|
||||
|
||||
import { getHomeDir } from '@main/utils/pathDecoder';
|
||||
import { createLogger } from '@shared/utils/logger';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import SSHConfig from 'ssh-config';
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ export class SshConfigParser {
|
|||
private configPath: string;
|
||||
|
||||
constructor(configPath?: string) {
|
||||
this.configPath = configPath ?? path.join(os.homedir(), '.ssh', 'config');
|
||||
this.configPath = configPath ?? path.join(getHomeDir(), '.ssh', 'config');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -151,7 +151,7 @@ export class SshConfigParser {
|
|||
}
|
||||
|
||||
const pattern = match[1].trim();
|
||||
const expandedPattern = pattern.replace(/^~/, os.homedir());
|
||||
const expandedPattern = pattern.replace(/^~/, getHomeDir());
|
||||
|
||||
try {
|
||||
// Handle glob-like patterns by checking if the path contains wildcards
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
* - Handle reconnection on errors
|
||||
*/
|
||||
|
||||
import { getHomeDir } from '@main/utils/pathDecoder';
|
||||
import { createLogger } from '@shared/utils/logger';
|
||||
import { execFile } from 'child_process';
|
||||
import { EventEmitter } from 'events';
|
||||
|
|
@ -267,7 +268,7 @@ export class SshConnectionManager extends EventEmitter {
|
|||
break;
|
||||
|
||||
case 'privateKey': {
|
||||
const keyPath = config.privateKeyPath ?? path.join(os.homedir(), '.ssh', 'id_rsa');
|
||||
const keyPath = config.privateKeyPath ?? path.join(getHomeDir(), '.ssh', 'id_rsa');
|
||||
try {
|
||||
const keyData = await fs.promises.readFile(keyPath, 'utf8');
|
||||
connectConfig.privateKey = keyData;
|
||||
|
|
@ -347,15 +348,15 @@ export class SshConnectionManager extends EventEmitter {
|
|||
const knownPaths = [
|
||||
// 1Password SSH agent
|
||||
path.join(
|
||||
os.homedir(),
|
||||
getHomeDir(),
|
||||
'Library',
|
||||
'Group Containers',
|
||||
'2BUA8C4S2C.com.1password',
|
||||
'agent.sock'
|
||||
),
|
||||
path.join(os.homedir(), '.1password', 'agent.sock'),
|
||||
path.join(getHomeDir(), '.1password', 'agent.sock'),
|
||||
// Common user agent socket
|
||||
path.join(os.homedir(), '.ssh', 'agent.sock'),
|
||||
path.join(getHomeDir(), '.ssh', 'agent.sock'),
|
||||
];
|
||||
|
||||
// Linux: add system paths
|
||||
|
|
@ -395,8 +396,8 @@ export class SshConnectionManager extends EventEmitter {
|
|||
// The config parser already told us there's an identity file.
|
||||
// Try common identity file locations from config
|
||||
const configKeyPaths = [
|
||||
path.join(os.homedir(), '.ssh', 'id_ed25519'),
|
||||
path.join(os.homedir(), '.ssh', 'id_rsa'),
|
||||
path.join(getHomeDir(), '.ssh', 'id_ed25519'),
|
||||
path.join(getHomeDir(), '.ssh', 'id_rsa'),
|
||||
];
|
||||
for (const keyPath of configKeyPaths) {
|
||||
try {
|
||||
|
|
@ -417,9 +418,9 @@ export class SshConnectionManager extends EventEmitter {
|
|||
|
||||
// Try default key files
|
||||
const defaultKeys = [
|
||||
path.join(os.homedir(), '.ssh', 'id_ed25519'),
|
||||
path.join(os.homedir(), '.ssh', 'id_rsa'),
|
||||
path.join(os.homedir(), '.ssh', 'id_ecdsa'),
|
||||
path.join(getHomeDir(), '.ssh', 'id_ed25519'),
|
||||
path.join(getHomeDir(), '.ssh', 'id_rsa'),
|
||||
path.join(getHomeDir(), '.ssh', 'id_ecdsa'),
|
||||
];
|
||||
|
||||
for (const keyPath of defaultKeys) {
|
||||
|
|
|
|||
|
|
@ -8,10 +8,9 @@
|
|||
* - Support tilde (~) expansion to home directory
|
||||
*/
|
||||
|
||||
import { encodePath, getClaudeBasePath } from '@main/utils/pathDecoder';
|
||||
import { encodePath, getClaudeBasePath, getHomeDir } from '@main/utils/pathDecoder';
|
||||
import { countTokens } from '@main/utils/tokenizer';
|
||||
import { createLogger } from '@shared/utils/logger';
|
||||
import { app } from 'electron';
|
||||
import * as path from 'path';
|
||||
|
||||
import { LocalFileSystemProvider } from '../infrastructure/LocalFileSystemProvider';
|
||||
|
|
@ -48,7 +47,7 @@ export interface ClaudeMdReadResult {
|
|||
*/
|
||||
function expandTilde(filePath: string): string {
|
||||
if (filePath.startsWith('~')) {
|
||||
const homeDir = app.getPath('home');
|
||||
const homeDir = getHomeDir();
|
||||
return path.join(homeDir, filePath.slice(1));
|
||||
}
|
||||
return filePath;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { getHomeDir } from '@main/utils/pathDecoder';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
|
||||
async function isExecutable(filePath: string): Promise<boolean> {
|
||||
|
|
@ -61,7 +61,7 @@ function expandWindowsBinaryNames(binaryName: string): string[] {
|
|||
}
|
||||
|
||||
async function collectNvmCandidates(): Promise<string[]> {
|
||||
const nvmNodeRoot = path.join(os.homedir(), '.nvm', 'versions', 'node');
|
||||
const nvmNodeRoot = path.join(getHomeDir(), '.nvm', 'versions', 'node');
|
||||
let versions: string[];
|
||||
try {
|
||||
versions = await fs.promises.readdir(nvmNodeRoot);
|
||||
|
|
@ -172,9 +172,9 @@ export class ClaudeBinaryResolver {
|
|||
|
||||
const candidateDirs: string[] = [
|
||||
// Native binary installation path (claude install)
|
||||
path.join(os.homedir(), '.local', 'bin'),
|
||||
path.join(os.homedir(), '.npm-global', 'bin'),
|
||||
path.join(os.homedir(), '.npm', 'bin'),
|
||||
path.join(getHomeDir(), '.local', 'bin'),
|
||||
path.join(getHomeDir(), '.npm-global', 'bin'),
|
||||
path.join(getHomeDir(), '.npm', 'bin'),
|
||||
process.platform === 'win32'
|
||||
? process.env.APPDATA
|
||||
? path.join(process.env.APPDATA, 'npm')
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { getHomeDir } from '@main/utils/pathDecoder';
|
||||
import { createLogger } from '@shared/utils/logger';
|
||||
import { diffLines } from 'diff';
|
||||
import { createReadStream } from 'fs';
|
||||
|
|
@ -257,8 +258,13 @@ export class FileContentResolver {
|
|||
if (!backupFileName) continue;
|
||||
|
||||
// Construct the file-history path
|
||||
const homeDir = process.env.HOME || process.env.USERPROFILE || '';
|
||||
const historyPath = path.join(homeDir, '.claude', 'file-history', sessionId, backupFileName);
|
||||
const historyPath = path.join(
|
||||
getHomeDir(),
|
||||
'.claude',
|
||||
'file-history',
|
||||
sessionId,
|
||||
backupFileName
|
||||
);
|
||||
|
||||
try {
|
||||
await access(historyPath);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import {
|
|||
extractBaseDir,
|
||||
getAutoDetectedClaudeBasePath,
|
||||
getClaudeBasePath,
|
||||
getHomeDir,
|
||||
getProjectsBasePath,
|
||||
getTasksBasePath,
|
||||
getTeamsBasePath,
|
||||
|
|
@ -2439,7 +2440,7 @@ export class TeamProvisioningService {
|
|||
|
||||
private async buildProvisioningEnv(): Promise<ProvisioningEnvResolution> {
|
||||
const shellEnv = await resolveInteractiveShellEnv();
|
||||
const home = shellEnv.HOME?.trim() || process.env.HOME?.trim() || os.homedir();
|
||||
const home = shellEnv.HOME?.trim() || process.env.HOME?.trim() || getHomeDir();
|
||||
const user = shellEnv.USER?.trim() || process.env.USER?.trim() || os.userInfo().username;
|
||||
const shell = shellEnv.SHELL?.trim() || process.env.SHELL?.trim() || '/bin/zsh';
|
||||
const xdgConfigHome =
|
||||
|
|
|
|||
|
|
@ -228,24 +228,24 @@ export const KanbanTaskCard = ({
|
|||
#{task.id}
|
||||
</Badge>
|
||||
{task.owner ? <MemberBadge name={task.owner} color={colorMap.get(task.owner)} /> : null}
|
||||
{task.needsClarification ? (
|
||||
<span
|
||||
className={`inline-flex shrink-0 items-center gap-1 rounded-full px-1.5 py-0.5 text-[10px] font-medium ${
|
||||
task.needsClarification === 'user'
|
||||
? 'bg-red-500/15 text-red-400'
|
||||
: 'bg-blue-500/15 text-blue-400'
|
||||
}`}
|
||||
>
|
||||
<HelpCircle size={10} />
|
||||
{task.needsClarification === 'user' ? 'Awaiting user' : 'Awaiting lead'}
|
||||
</span>
|
||||
) : null}
|
||||
{!compact && (
|
||||
<h5 className="min-w-0 truncate text-sm font-medium text-[var(--color-text)]">
|
||||
{task.subject}
|
||||
</h5>
|
||||
)}
|
||||
</div>
|
||||
{task.needsClarification ? (
|
||||
<span
|
||||
className={`mt-1 inline-flex items-center gap-1 rounded-full px-1.5 py-0.5 text-[10px] font-medium ${
|
||||
task.needsClarification === 'user'
|
||||
? 'bg-red-500/15 text-red-400'
|
||||
: 'bg-blue-500/15 text-blue-400'
|
||||
}`}
|
||||
>
|
||||
<HelpCircle size={10} />
|
||||
{task.needsClarification === 'user' ? 'Awaiting user' : 'Awaiting lead'}
|
||||
</span>
|
||||
) : null}
|
||||
{compact && (
|
||||
<h5 className="mt-1 truncate text-sm font-medium text-[var(--color-text)]">
|
||||
{task.subject}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import * as os from 'os';
|
|||
import * as path from 'path';
|
||||
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
||||
|
||||
import { setClaudeBasePathOverride } from '../../../src/main/utils/pathDecoder';
|
||||
import { getHomeDir, setClaudeBasePathOverride } from '../../../src/main/utils/pathDecoder';
|
||||
|
||||
import {
|
||||
isPathWithinAllowedDirectories,
|
||||
|
|
@ -17,7 +17,7 @@ import {
|
|||
} from '../../../src/main/utils/pathValidation';
|
||||
|
||||
describe('pathValidation', () => {
|
||||
const homeDir = os.homedir();
|
||||
const homeDir = getHomeDir();
|
||||
const claudeDir = path.join(homeDir, '.claude');
|
||||
const testProjectPath = path.resolve('/home/user/my-project');
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue