Skip to content

Feature/env management - #132

Merged
laopo001 merged 17 commits into
dev2from
feature/env-management
Jul 23, 2025
Merged

Feature/env management#132
laopo001 merged 17 commits into
dev2from
feature/env-management

Conversation

@laopo001

Copy link
Copy Markdown
Contributor

No description provided.

laopo001 and others added 17 commits July 23, 2025 15:35
- 删除原有英文 README.md,将中文版重命名为主 README
- 重新组织文档结构,突出 CLI 作为首要平台的地位
- 新增环境变量配置章节,详细介绍 5 层优先级系统
- 完善 CLI 功能介绍,增加使用场景和特性说明
- 调整多平台支持描述,体现 CLI → Web → Electron 的开发优先级
- 更新技术架构说明,强调 CLI First 开发理念
- 移除冗余的 AI 能力矩阵章节,精简文档结构
- 根据最新环境变量功能更新近期目标完成状态

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
## 主要修复
- 🔧 修复 --data-dir 等CLI参数解析时机问题,确保在模块加载前设置环境变量
- 🚀 重构CLI入口文件,将参数解析移至最前面,避免模块加载顺序问题
- 🔄 添加EnvManager全局默认实例机制,支持CLI参数覆盖

## 核心改进
- 📦 **模块加载优化**: CLI参数解析在任何其他模块导入之前执行
- 🎯 **动态路径获取**: 将静态路径常量改为动态getter,支持运行时配置变更
- 🔧 **环境管理器增强**: 添加setGlobalDefault()方法,确保全局配置一致性
- 📋 **CLI帮助完善**: 添加完整的环境变量配置选项和5层优先级说明

## 技术细节
- EnvManager支持setGlobalDefault()设置CLI实例为全局默认
- CONSTANTS.GLOBAL_PATH改为动态getter,实时获取appDataDir
- WorkspaceManager.GLOBAL_HYPERCHAT_DIR改为动态getter
- CLI参数清理逻辑完善,支持所有环境变量相关选项

## 验证结果
- ✅ --data-dir ~/Documents/HyperChat2 正确设置全局数据目录
- ✅ getAppDataDir()输出正确的CLI指定路径
- ✅ 工作区管理器使用指定的全局配置目录
- ✅ 5层环境变量优先级系统完全正常工作

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
## 问题
AI配置管理器(AppSettingsManager)使用静态的appDataDir路径,
导致--data-dir参数无法生效,AI模型配置仍从旧位置加载。

## 修复内容
- 🔄 **appSettingsService.mts**: 使用getAppDataDir()替代静态appDataDir
- 🔄 **appSettingsManager.mts**: 构造函数使用动态getAppDataDir()
- 📍 确保AI配置文件(app-settings.jsonc)使用CLI指定的数据目录

## 技术改进
- initAppSettingsManager()使用动态路径初始化
- AppSettingsManager构造函数中的appDataDir字段使用实时获取的路径
- 移除对静态appDataDir常量的依赖

## 验证结果
- ✅ Agent配置文件正确创建在新数据目录
- 🔄 AI配置加载路径已修复(需要重启进程生效)

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
## 核心问题
AI配置管理器在模块加载时立即初始化,导致CLI参数--data-dir无法生效,
AI配置仍从旧位置加载,聊天功能异常成功而不是预期失败。

## 解决方案
1. **移除立即执行代码**: 删除appSettingsService.mts底部的模块加载时初始化代码
2. **实现懒加载模式**: AI配置管理器改为按需初始化,支持动态appDataDir
3. **添加同步初始化**: 新增initSync()和saveSync()方法,兼容现有同步调用
4. **自动初始化**: getAppSettingsManager()支持自动初始化,向后兼容

## 技术改进
- ✅ **appSettingsService.mts**: 移除模块加载时的立即初始化,改为懒加载
- ✅ **appSettingsManager.mts**: 添加initSync()和loadSync()、saveSync()同步方法
- ✅ **settingsCommands.mts**: 移除手动初始化检查,依赖自动初始化
- 🔄 **AI_MODELS**: 改为懒加载实例,支持向后兼容的API

## 验证结果
- ✅ CLI参数--data-dir ~/Documents/HyperChat2完全正常工作
- ✅ AI配置文件正确创建在指定目录: ~/Documents/HyperChat2/app-settings.jsonc
- ✅ appDataDir正确设置: "/home/laop/Documents/HyperChat2"
- ✅ 空AI配置时聊天正确失败: "未找到可用的AI模型配置"
- ✅ 应用行为完全符合预期,配置隔离正常工作

## 架构提升
实现了真正的CLI参数优先级系统,所有配置组件都支持动态路径,
为多环境部署和用户自定义数据目录提供了完整支持。

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
## 问题
环境管理器在解析系统环境变量时,只检查`key in defaultValues`,
导致HyperChat_API_KEY和HyperChat_API_URL等可选环境变量无法被读取。

## 根本原因
DEFAULT_ENV_CONFIG通过空对象{}生成,可选字段不在默认配置中,
因此系统环境变量中的这些值被忽略。

## 解决方案
- 🔧 **环境变量读取逻辑改进**: 检查`EnvSchema.shape`中的所有定义key
- ✅ **支持所有schema定义的环境变量**: 包括可选的API配置环境变量
- 🔍 **完整环境变量支持**: HyperChat_API_KEY、HyperChat_API_URL等现在能正确读取

## 技术改进
```typescript
// 修复前: 只检查默认配置中的key
if (key in defaultValues && value \!== undefined)

// 修复后: 检查schema中定义的所有key
const schemaKeys = Object.keys(EnvSchema.shape);
if (schemaKeys.includes(key) && value \!== undefined)
```

## 验证结果
- ✅ HyperChat_API_KEY和HyperChat_API_URL环境变量被正确读取
- ✅ 环境变量在系统中正确添加到配置中
- 🔄 为后续AI配置使用这些环境变量提供了基础

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
## 核心改进
- 🔧 **可选modelKey参数**: getAIOptions(modelKey?: string) 支持不传入模型key
- 🚀 **智能模型选择**: 优先级:传入参数 > 环境变量HyperChat_AI_Model > 默认值
- ⚡ **移除冗余验证**: 删除AiProviderFactory.validateModelConfig()调用,简化流程

## 功能优化
- 📝 **更好的参数处理**:
  ```typescript
  // 修改前: 必须传入modelKey
  async getAIOptions(modelKey: string)

  // 修改后: modelKey可选,支持环境变量回退
  async getAIOptions(modelKey?: string)
  const finalModelKey = modelKey || envModel || 'default-model';
  ```

- 🔍 **错误消息更新**: 使用finalModelKey提供更准确的错误信息
- 🗑️ **简化验证逻辑**: 移除不必要的validateModelConfig调用

## 验证结果
- ✅ 环境变量AI配置完全正常工作
- ✅ HyperChat_API_KEY + HyperChat_API_URL + HyperChat_AI_Model 成功创建AI会话
- ✅ 聊天功能正常:"你好!有什么可以帮助你的吗?"
- ✅ 向后兼容:现有传入modelKey的调用依然正常工作

## 架构提升
现在AI系统更加灵活,支持完全基于环境变量的配置,
为无配置文件的部署场景提供了更好的支持。

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- 消除重复的配置构建函数,统一使用 buildEffectiveConfig
- 修正模型选择优先级:默认第一个可用模型 > 环境变量
- 重构 ai.mts 中的 getAIOptions 方法,复用配置合并逻辑
- 删除 chatCommands.mts 中重复的 getEffectiveConfig 函数
- 清理未使用的导入,保持代码整洁
- 确保所有AI配置在系统中保持一致的优先级顺序

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- 为DataList类添加getStats方法,支持轻量级统计(只读取文件数量和最后修改时间)
- 修改AgentInstance.getSummary方法,使用轻量级统计替代全量加载聊天记录
- 解决工作区初始化时不必要的聊天日志YAML解析导致的性能问题和错误
- 实现按需加载策略:统计信息使用文件系统操作,具体内容仅在实际使用时加载

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
🔧 refactor: 注释掉Claude自动评论工作流中的触发条件
🔧 fix: 更新默认代理配置,移除不必要的MCP选项
Copilot AI review requested due to automatic review settings July 23, 2025 11:40
@claude

claude Bot commented Jul 23, 2025

Copy link
Copy Markdown
Contributor

Claude finished @laopo001's task —— View job


PR Review: Feature/env-management ✅

I've completed a comprehensive review of this PR focusing on code quality, potential bugs, performance, security, and test coverage.

Todo List:

  • Read repository guidelines from CLAUDE.md
  • Analyze core environment management files
  • Review CLI argument parsing changes
  • Check workspace integration
  • Assess security concerns
  • Evaluate TypeScript usage and type safety
  • Check for potential bugs and edge cases
  • Provide comprehensive feedback

Architecture Overview:

This PR implements a sophisticated 5-layer environment variable management system:

  1. Default values (lowest priority)
  2. System environment variables (process.env)
  3. Global .env file (~/.hyperchat/.env)
  4. Workspace .env file (./.hyperchat/.env)
  5. CLI arguments (highest priority)

Core Components:

  • EnvSchema (shared): Zod-based schema definitions with validation
  • EnvManager (core): Singleton pattern manager with layered configuration loading
  • DotenvLoader (core): Custom .env file parser without external dependencies
  • CliArgsParser (core): CLI argument to environment variable mapping
  • Integration: CLI, logging, AI configuration systems updated to use new env management

✅ Positive Aspects

1. Excellent Architecture & Design

  • Clean separation of concerns: Schema definitions in shared package, implementation in core
  • Type-safe throughout: Leverages Zod for runtime validation and TypeScript for compile-time safety
  • Follows project conventions: Matches the established pattern of Schema → Manager → Integration
  • Singleton pattern: Well-implemented with proper instance management and cleanup

2. Comprehensive Feature Implementation

  • Complete priority system: All 5 layers properly implemented with clear precedence
  • Rich CLI integration: Extensive command-line argument support with aliases
  • Flexible configuration: Supports both programmatic and file-based configuration
  • Good error handling: Graceful fallbacks when configuration files are missing or invalid

3. Security Considerations

  • API key masking: Sensitive information properly hidden in logs (HyperChat_API_KEY***)
  • Input validation: Zod schemas validate all environment variables with proper constraints
  • Safe file operations: Proper error handling for file system operations

⚠️ Areas for Improvement

1. Type Safety & Code Quality

Issue: Missing type safety in CLI argument processing

// packages/core/src/utils/cliArgsParser.mts:58
(result as any)[envKey] = 'debug';  // Using 'any' type assertion

Recommendation: Use proper type assertion or create a helper function:

const setEnvValue = <K extends keyof EnvConfig>(
  result: Partial<EnvConfig>, 
  key: K, 
  value: EnvConfig[K]
) => {
  result[key] = value;
};

Issue: Potential undefined access in EnvManager

// packages/core/src/data/managers/envManager.mts:146
const filteredCliArgs = Object.fromEntries(
  cliKeys.map(key => [key, (this.cliArgs as any)![key]])  // Using 'any'
);

2. Error Handling & Edge Cases

Issue: Silent failures in environment parsing

// packages/core/src/data/managers/envManager.mts:166-169
Logger.error("Environment validation failed:", result.error.format());
Logger.warn("Using default environment configuration");
return DEFAULT_ENV_CONFIG;  // Silently falls back

Recommendation: Consider throwing an error for critical configuration failures, or provide more detailed error reporting.

Issue: File system race conditions

// packages/core/src/utils/dotenvLoader.mts:51-53
if (!fs.existsSync(filePath)) {
  return {};
}
const content = fs.readFileSync(filePath, 'utf8');  // File could be deleted between checks

Recommendation: Use try-catch around readFileSync instead of separate existence check.

3. Performance Considerations

Issue: Potentially expensive operations on every getInstance call

// packages/core/src/data/managers/envManager.mts:89-170
private parseEnvConfig(): EnvConfig {
  // Complex parsing happens every time, even for cached instances
}

Recommendation: Consider caching parsed results and implementing a way to invalidate cache when files change.

Issue: Synchronous file operations
The DotenvLoader uses synchronous file operations which could block the event loop.
Recommendation: Consider providing async alternatives for non-CLI usage.

4. Testing & Documentation

Missing: Unit tests for the new environment management system
Recommendation: Add comprehensive tests covering:

  • Priority resolution edge cases
  • Invalid configuration handling
  • File system error scenarios
  • CLI argument parsing edge cases

Missing: Integration tests
Recommendation: Test the full chain from CLI args → EnvManager → Application components

5. Security Enhancements

Issue: Path traversal potential

// packages/core/src/data/managers/envManager.mts:194
return path.join(customAppDataDir, '.hyperchat', '.env');

Recommendation: Validate and sanitize customAppDataDir to prevent path traversal attacks.

Enhancement: Consider encrypting sensitive values in .env files
For production environments, API keys in plain text files pose a security risk.


🐛 Potential Bugs

1. Race Condition in Singleton Management

// packages/core/src/data/managers/envManager.mts:64-82
public static getInstance(workspacePath?: string, cliArgs?: Partial<EnvConfig>): EnvManager {
  if (cliArgs && Object.keys(cliArgs).length > 0) {
    return new EnvManager(workspacePath, cliArgs);  // Creates new instance
  }
  // ... but doesn't update global default
}

Issue: CLI arguments might not persist to subsequent getInstance calls without cliArgs.

2. Inconsistent Environment Variable Names

// packages/core/src/log.mts:37
const isDevMode = process.env.myEnv === 'dev';  // Should be HyperChat_MY_ENV

Issue: Still using old environment variable naming convention.

3. Memory Leak Potential

// packages/core/src/data/managers/envManager.mts:35
private static instances: Map<string, EnvManager> = new Map();

Issue: No cleanup mechanism for cached instances.


🚀 Performance Impact

Positive:

  • Caching reduces repeated file system operations
  • Lazy loading of environment configuration
  • Efficient priority resolution algorithm

Concerns:

  • Synchronous file I/O could impact startup time
  • Multiple Zod validations on each configuration load
  • No mechanism to reload configuration without creating new instances

📋 Recommendations Summary

High Priority:

  1. Add comprehensive unit tests for all new components
  2. Fix type safety issues - eliminate any type assertions
  3. Improve error handling - don't silently fall back to defaults for critical errors
  4. Add input validation for file paths to prevent security issues

Medium Priority:

  1. Consider async alternatives for file operations
  2. Add configuration caching with proper invalidation
  3. Implement cleanup mechanisms for singleton instances
  4. Update legacy environment variable references

Low Priority:

  1. Add integration tests for full environment management flow
  2. Consider encryption for sensitive configuration values
  3. Add configuration file watching for hot-reload capabilities

🎯 Overall Assessment

This is a well-architected and thoughtfully implemented feature that significantly improves HyperChat's configuration management. The code follows established patterns, provides comprehensive functionality, and integrates cleanly with existing systems.

Strengths: Excellent design, comprehensive feature set, good security practices, follows project conventions

Areas for improvement: Type safety, error handling, testing coverage, and some performance optimizations

Recommendation: Approve with minor revisions - address the type safety issues and add basic unit tests before merging.

Impact: This feature provides a solid foundation for configuration management and will improve both developer and user experience significantly.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements a comprehensive environment variable management system for HyperChat, introducing a 5-layer priority configuration system that enables flexible deployment and configuration management across different environments.

Key changes include:

  • Implementation of a 5-layer priority environment variable system (default < process.env < global .env < workspace .env < CLI args)
  • Migration of language and password settings from AppSettings to environment variables
  • Enhanced CLI argument parsing with comprehensive environment variable mapping
  • Removal of unused configuration files and cleanup of legacy code

Reviewed Changes

Copilot reviewed 42 out of 43 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
packages/core/src/data/managers/envManager.mts New environment variable manager with 5-layer priority system
packages/core/src/utils/cliArgsParser.mts CLI argument parser with environment variable mapping
packages/core/src/utils/dotenvLoader.mts Custom .env file parser without external dependencies
packages/web/src/i18n.ts Migrated language settings from AppSettings to environment variables
packages/shared/src/zodSchemas/envSchema.mts Comprehensive environment variable schema definitions
packages/core/src/cli/index.mts Enhanced CLI with environment variable integration
Comments suppressed due to low confidence (2)

Comment on lines +56 to +57
firstAvailableModel, // 默认第一个可用模型
envModel // 环境变量(最低优先级)

Copilot AI Jul 23, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment indicates environment variables have lowest priority, but the actual implementation places environment variables as the last fallback in the candidates array. This creates inconsistency between the comment and the actual priority order. Environment variables should have higher priority than 'firstAvailableModel' according to typical configuration hierarchies.

Suggested change
firstAvailableModel, // 默认第一个可用模型
envModel // 环境变量(最低优先级)
envModel, // 环境变量
firstAvailableModel // 默认第一个可用模型

Copilot uses AI. Check for mistakes.
Comment on lines +92 to +94
const rootEnvPath = path.resolve(process.cwd(), '.env');
if (fs.existsSync(rootEnvPath)) {
paths.push(rootEnvPath);

Copilot AI Jul 23, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method checks for .env file existence but doesn't validate if process.cwd() is accessible or valid. Consider wrapping in try-catch to handle potential permission errors when accessing the current working directory.

Suggested change
const rootEnvPath = path.resolve(process.cwd(), '.env');
if (fs.existsSync(rootEnvPath)) {
paths.push(rootEnvPath);
try {
const rootEnvPath = path.resolve(process.cwd(), '.env');
if (fs.existsSync(rootEnvPath)) {
paths.push(rootEnvPath);
}
} catch (error) {
Logger.warn("Failed to access the current working directory for .env file resolution.", error);

Copilot uses AI. Check for mistakes.
const optionsWithValues = [
'--workspace', '--host', '--port', '--password', '--language', '--lang', '--ui',
'--data-dir', '--app-data-dir', '--api-key', '--api-url', '--ai-provider', '--ai-model',
'--log-level', '--web-password', '--env', '--my-env', '-p'

Copilot AI Jul 23, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hardcoded list of CLI options with values should be kept in sync with the CLI_ARG_MAPPING in CliArgsParser. Consider extracting this to a shared constant or deriving it programmatically to avoid maintenance issues.

Copilot uses AI. Check for mistakes.
if (!modelConfig) {
// 检查是否有足够的环境变量来创建基础配置
if (!envApiKey || !envApiUrl) {
throw new Error(`Model not found: ${finalModelKey}. Please configure it in app settings or provide HyperChat_API_KEY and HyperChat_API_URL environment variables.`);

Copilot AI Jul 23, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message mentions 'HyperChat_API_KEY and HyperChat_API_URL environment variables' but should be more specific about what constitutes 'sufficient' environment variables. Consider providing examples of valid configurations.

Suggested change
throw new Error(`Model not found: ${finalModelKey}. Please configure it in app settings or provide HyperChat_API_KEY and HyperChat_API_URL environment variables.`);
throw new Error(`Model not found: ${finalModelKey}. Please configure it in app settings or provide the following environment variables:
- HyperChat_API_KEY: Your API key (e.g., "abc123xyz").
- HyperChat_API_URL: The base URL of the API (e.g., "https://api.hyperchat.com").
Ensure both variables are set and valid.`);

Copilot uses AI. Check for mistakes.

// 为了向后兼容,提供常量形式的导出
// 但实际上每次调用都会动态获取最新值
export const appDataDir = getAppDataDir();

Copilot AI Jul 23, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The appDataDir constant is computed at module load time and may not reflect runtime changes to environment variables. Consider making this a getter function or lazy evaluation to ensure it always returns the current environment-aware path.

Copilot uses AI. Check for mistakes.
@laopo001
laopo001 merged commit 8902dd2 into dev2 Jul 23, 2025
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants