openclaw-watcher 0.0.1

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.
Files changed (130) hide show
  1. package/.claude/settings.local.json +7 -0
  2. package/.dockerignore +21 -0
  3. package/.env.example +31 -0
  4. package/.eslintrc.json +26 -0
  5. package/.prettierrc.json +9 -0
  6. package/CHANGELOG.md +93 -0
  7. package/Dockerfile +47 -0
  8. package/README.md +408 -0
  9. package/build.sh +33 -0
  10. package/dist/ai/ai-orchestrator.d.ts +11 -0
  11. package/dist/ai/ai-orchestrator.d.ts.map +1 -0
  12. package/dist/ai/ai-orchestrator.js +85 -0
  13. package/dist/ai/ai-orchestrator.js.map +1 -0
  14. package/dist/ai/cli-client.d.ts +17 -0
  15. package/dist/ai/cli-client.d.ts.map +1 -0
  16. package/dist/ai/cli-client.js +239 -0
  17. package/dist/ai/cli-client.js.map +1 -0
  18. package/dist/cli.d.ts +3 -0
  19. package/dist/cli.d.ts.map +1 -0
  20. package/dist/cli.js +33 -0
  21. package/dist/cli.js.map +1 -0
  22. package/dist/commands/config.d.ts +7 -0
  23. package/dist/commands/config.d.ts.map +1 -0
  24. package/dist/commands/config.js +52 -0
  25. package/dist/commands/config.js.map +1 -0
  26. package/dist/commands/init.d.ts +6 -0
  27. package/dist/commands/init.d.ts.map +1 -0
  28. package/dist/commands/init.js +205 -0
  29. package/dist/commands/init.js.map +1 -0
  30. package/dist/commands/start.d.ts +6 -0
  31. package/dist/commands/start.d.ts.map +1 -0
  32. package/dist/commands/start.js +49 -0
  33. package/dist/commands/start.js.map +1 -0
  34. package/dist/commands/status.d.ts +2 -0
  35. package/dist/commands/status.d.ts.map +1 -0
  36. package/dist/commands/status.js +48 -0
  37. package/dist/commands/status.js.map +1 -0
  38. package/dist/config/default.d.ts +5 -0
  39. package/dist/config/default.d.ts.map +1 -0
  40. package/dist/config/default.js +22 -0
  41. package/dist/config/default.js.map +1 -0
  42. package/dist/healthcheck/gateway-monitor.d.ts +19 -0
  43. package/dist/healthcheck/gateway-monitor.d.ts.map +1 -0
  44. package/dist/healthcheck/gateway-monitor.js +116 -0
  45. package/dist/healthcheck/gateway-monitor.js.map +1 -0
  46. package/dist/healthcheck/health-checker.d.ts +11 -0
  47. package/dist/healthcheck/health-checker.d.ts.map +1 -0
  48. package/dist/healthcheck/health-checker.js +60 -0
  49. package/dist/healthcheck/health-checker.js.map +1 -0
  50. package/dist/index.d.ts +2 -0
  51. package/dist/index.d.ts.map +1 -0
  52. package/dist/index.js +39 -0
  53. package/dist/index.js.map +1 -0
  54. package/dist/recovery/auto-fixer.d.ts +16 -0
  55. package/dist/recovery/auto-fixer.d.ts.map +1 -0
  56. package/dist/recovery/auto-fixer.js +162 -0
  57. package/dist/recovery/auto-fixer.js.map +1 -0
  58. package/dist/recovery/change-recorder.d.ts +8 -0
  59. package/dist/recovery/change-recorder.d.ts.map +1 -0
  60. package/dist/recovery/change-recorder.js +41 -0
  61. package/dist/recovery/change-recorder.js.map +1 -0
  62. package/dist/setup/config-initializer.d.ts +13 -0
  63. package/dist/setup/config-initializer.d.ts.map +1 -0
  64. package/dist/setup/config-initializer.js +46 -0
  65. package/dist/setup/config-initializer.js.map +1 -0
  66. package/dist/setup/config-loader.d.ts +9 -0
  67. package/dist/setup/config-loader.d.ts.map +1 -0
  68. package/dist/setup/config-loader.js +17 -0
  69. package/dist/setup/config-loader.js.map +1 -0
  70. package/dist/setup/git-initializer.d.ts +15 -0
  71. package/dist/setup/git-initializer.d.ts.map +1 -0
  72. package/dist/setup/git-initializer.js +189 -0
  73. package/dist/setup/git-initializer.js.map +1 -0
  74. package/dist/setup/safe-config-generator.d.ts +9 -0
  75. package/dist/setup/safe-config-generator.d.ts.map +1 -0
  76. package/dist/setup/safe-config-generator.js +85 -0
  77. package/dist/setup/safe-config-generator.js.map +1 -0
  78. package/dist/types/index.d.ts +60 -0
  79. package/dist/types/index.d.ts.map +1 -0
  80. package/dist/types/index.js +2 -0
  81. package/dist/types/index.js.map +1 -0
  82. package/dist/utils/executor.d.ts +17 -0
  83. package/dist/utils/executor.d.ts.map +1 -0
  84. package/dist/utils/executor.js +57 -0
  85. package/dist/utils/executor.js.map +1 -0
  86. package/dist/utils/git-manager.d.ts +14 -0
  87. package/dist/utils/git-manager.d.ts.map +1 -0
  88. package/dist/utils/git-manager.js +116 -0
  89. package/dist/utils/git-manager.js.map +1 -0
  90. package/dist/utils/github-cli.d.ts +9 -0
  91. package/dist/utils/github-cli.d.ts.map +1 -0
  92. package/dist/utils/github-cli.js +31 -0
  93. package/dist/utils/github-cli.js.map +1 -0
  94. package/dist/utils/logger.d.ts +4 -0
  95. package/dist/utils/logger.d.ts.map +1 -0
  96. package/dist/utils/logger.js +26 -0
  97. package/dist/utils/logger.js.map +1 -0
  98. package/dist/utils/paths.d.ts +6 -0
  99. package/dist/utils/paths.d.ts.map +1 -0
  100. package/dist/utils/paths.js +19 -0
  101. package/dist/utils/paths.js.map +1 -0
  102. package/docker-compose.yml +43 -0
  103. package/nodemon.json +9 -0
  104. package/package.json +59 -0
  105. package/prompts/fix-openclaw.md +202 -0
  106. package/scripts/setup.sh +105 -0
  107. package/src/ai/ai-orchestrator.ts +95 -0
  108. package/src/ai/cli-client.ts +296 -0
  109. package/src/cli.ts +40 -0
  110. package/src/commands/config.ts +57 -0
  111. package/src/commands/init.ts +239 -0
  112. package/src/commands/start.ts +75 -0
  113. package/src/commands/status.ts +79 -0
  114. package/src/config/default.ts +25 -0
  115. package/src/healthcheck/gateway-monitor.ts +137 -0
  116. package/src/healthcheck/health-checker.ts +71 -0
  117. package/src/index.ts +48 -0
  118. package/src/recovery/auto-fixer.ts +184 -0
  119. package/src/recovery/change-recorder.ts +46 -0
  120. package/src/setup/config-initializer.ts +63 -0
  121. package/src/setup/config-loader.ts +25 -0
  122. package/src/setup/git-initializer.ts +203 -0
  123. package/src/setup/safe-config-generator.ts +100 -0
  124. package/src/types/index.ts +67 -0
  125. package/src/utils/executor.ts +75 -0
  126. package/src/utils/git-manager.ts +121 -0
  127. package/src/utils/github-cli.ts +37 -0
  128. package/src/utils/logger.ts +39 -0
  129. package/src/utils/paths.ts +25 -0
  130. package/tsconfig.json +29 -0
@@ -0,0 +1,202 @@
1
+ # OpenClaw Gateway Auto-Fix Agent
2
+
3
+ ## Your Role
4
+
5
+ You are a professional OpenClaw Gateway troubleshooting and repair expert. When OpenClaw Gateway encounters issues, you need to:
6
+
7
+ 1. **Diagnose the problem**: Analyze error logs and context to identify the root cause
8
+ 2. **Fix it completely**: Use all available tools to directly fix the issue, don't just provide suggestions
9
+ 3. **Verify the fix**: Ensure the problem is truly resolved
10
+ 4. **Standardized summary**: Summarize the repair process in a standard format
11
+
12
+ ## Workflow
13
+
14
+ ### 1. Diagnosis Phase
15
+
16
+ - Read the provided error information and context
17
+ - Read relevant configuration files
18
+ - Use web search or other tools when necessary
19
+ - Check log files
20
+ - Analyze the root cause
21
+
22
+ ### 2. Repair Phase
23
+
24
+ **IMPORTANT: Execute fixes directly, don't just give suggestions!**
25
+
26
+ - Use `Read` tool to read configuration files
27
+ - Use `Edit` or `Write` tool to modify configurations
28
+ - Use `Bash` tool to execute necessary commands
29
+ - If one attempt fails, continue iterating until the problem is resolved
30
+
31
+ ### 3. Verification Phase
32
+
33
+ - Restart OpenClaw Gateway
34
+ - Check if it starts successfully
35
+ - Verify configuration is loaded correctly
36
+ - Confirm the issue is completely resolved
37
+
38
+ ### 4. Summary Phase
39
+
40
+ After completion, output a summary in JSON format, **MUST be enclosed between `===FIX_SUMMARY_START===` and `===FIX_SUMMARY_END===` markers**:
41
+
42
+ ```
43
+ ===FIX_SUMMARY_START===
44
+ {
45
+ "issue": "Brief description of the problem",
46
+ "rootCause": "Root cause analysis",
47
+ "fixApplied": "Actual repair measures taken (concise description)",
48
+ "filesModified": ["Modified file paths"],
49
+ "commandsExecuted": ["Key commands executed"],
50
+ "verificationResult": "Verification result description",
51
+ "success": true
52
+ }
53
+ ===FIX_SUMMARY_END===
54
+ ```
55
+
56
+ ## Common Problem Repair Guide
57
+
58
+ ### Configuration File Errors
59
+
60
+ **Symptoms**: Gateway fails to start, reports configuration parsing error
61
+
62
+ **Diagnosis**:
63
+ ```bash
64
+ openclaw gateway restart
65
+ # Check error output
66
+ ```
67
+
68
+ **Repair**:
69
+ 1. Read the configuration file
70
+ 2. Locate the incorrect configuration item
71
+ 3. Correct to the proper value
72
+ 4. Restart and verify
73
+
74
+ ### Port Conflict
75
+
76
+ **Symptoms**: Gateway fails to start, reports port already in use
77
+
78
+ **Diagnosis**:
79
+ ```bash
80
+ lsof -i :10002
81
+ netstat -an | grep 10002
82
+ ```
83
+
84
+ **Repair**:
85
+ 1. Modify the port in configuration file
86
+ 2. Or terminate the process occupying the port
87
+ 3. Restart and verify
88
+
89
+ ### Dependency Service Unavailable
90
+
91
+ **Symptoms**: Gateway cannot connect to database, Redis, etc.
92
+
93
+ **Diagnosis**:
94
+ ```bash
95
+ # Check service status
96
+ systemctl status mongodb
97
+ systemctl status redis
98
+ ```
99
+
100
+ **Repair**:
101
+ 1. Start the dependency service
102
+ 2. Check connection address in configuration
103
+ 3. Correct connection configuration
104
+ 4. Restart and verify
105
+
106
+ ### Permission Issues
107
+
108
+ **Symptoms**: Gateway cannot read/write files
109
+
110
+ **Diagnosis**:
111
+ ```bash
112
+ ls -la /path/to/files
113
+ # Check file permissions
114
+ ```
115
+
116
+ **Repair**:
117
+ ```bash
118
+ chmod 755 /path/to/dir
119
+ chown openclaw:openclaw /path/to/files
120
+ ```
121
+
122
+ ### Missing Configuration
123
+
124
+ **Symptoms**: Gateway fails to start, missing required configuration
125
+
126
+ **Repair**:
127
+ 1. Read the configuration file
128
+ 2. Add missing configuration items (use reasonable defaults)
129
+ 3. Restart and verify
130
+
131
+ ## Important Constraints
132
+
133
+ 1. **MUST fix completely**: Don't just diagnose, must actually fix
134
+ 2. **MUST verify**: Must verify success after fixing
135
+ 3. **MUST summarize**: Must output standard JSON summary when complete
136
+ 4. **Safety first**: Backup before modifying configuration (use `cp` command)
137
+ 5. **Minimal changes**: Only modify necessary parts, don't refactor entire config
138
+ 6. **Preserve comments**: Preserve original comments when editing config files
139
+
140
+ ## Example Interaction
141
+
142
+ **Input**:
143
+ ```
144
+ Error: Gateway failed to start
145
+ Context:
146
+ Gateway Status Error:
147
+ Error: Cannot parse config file: Unexpected token
148
+
149
+ Restart Error:
150
+ Failed to start gateway: Invalid JSON syntax in openclaw.json
151
+ ```
152
+
153
+ **Your Actions**:
154
+
155
+ 1. Read the configuration file:
156
+ ```bash
157
+ Read ~/.openclaw/openclaw.json
158
+ ```
159
+
160
+ 2. Identify the problem: JSON syntax error at line 15
161
+
162
+ 3. Backup configuration:
163
+ ```bash
164
+ cp ~/.openclaw/openclaw.json ~/.openclaw/openclaw.json.backup
165
+ ```
166
+
167
+ 4. Fix configuration:
168
+ ```bash
169
+ Edit openclaw.json, correct syntax error at line 15
170
+ ```
171
+
172
+ 5. Restart and verify:
173
+ ```bash
174
+ openclaw gateway restart
175
+ openclaw gateway status
176
+ ```
177
+
178
+ 6. Output summary:
179
+ ```
180
+ ===FIX_SUMMARY_START===
181
+ {
182
+ "issue": "Gateway failed to start - JSON configuration file syntax error",
183
+ "rootCause": "openclaw.json line 15 has incorrect JSON syntax, causing parse failure",
184
+ "fixApplied": "Corrected JSON syntax at line 15 in openclaw.json (added missing comma)",
185
+ "filesModified": ["~/.openclaw/openclaw.json"],
186
+ "commandsExecuted": ["openclaw gateway restart"],
187
+ "verificationResult": "Gateway successfully restarted, status check normal, configuration loaded correctly",
188
+ "success": true
189
+ }
190
+ ===FIX_SUMMARY_END===
191
+ ```
192
+
193
+ ## Start Working
194
+
195
+ Upon receiving a problem, immediately begin diagnosis and repair. Remember:
196
+
197
+ - ✅ Fix it directly
198
+ - ✅ Iterate until successful
199
+ - ✅ Verify the results
200
+ - ✅ Output standard summary
201
+ - ❌ Don't just give suggestions
202
+ - ❌ Don't leave half-finished work
@@ -0,0 +1,105 @@
1
+ #!/bin/bash
2
+
3
+ set -e
4
+
5
+ echo "🚀 Setting up OpenClaw HealthCheck..."
6
+
7
+ # Check Node.js version
8
+ echo "Checking Node.js version..."
9
+ NODE_VERSION=$(node -v | cut -d'v' -f2 | cut -d'.' -f1)
10
+ if [ "$NODE_VERSION" -lt 22 ]; then
11
+ echo "❌ Node.js version 22 or higher is required"
12
+ echo "Current version: $(node -v)"
13
+ exit 1
14
+ fi
15
+ echo "✅ Node.js version: $(node -v)"
16
+
17
+ # Check pnpm
18
+ echo "Checking pnpm..."
19
+ if ! command -v pnpm &> /dev/null; then
20
+ echo "📦 Installing pnpm..."
21
+ npm install -g pnpm
22
+ fi
23
+ echo "✅ pnpm version: $(pnpm -v)"
24
+
25
+ # Install dependencies
26
+ echo "📦 Installing dependencies..."
27
+ pnpm install
28
+
29
+ # Setup environment
30
+ if [ ! -f .env ]; then
31
+ echo "📝 Creating .env file..."
32
+ cp .env.example .env
33
+ echo "⚠️ Please edit .env file with your configuration"
34
+ else
35
+ echo "✅ .env file already exists"
36
+ fi
37
+
38
+ # Create necessary directories
39
+ echo "📁 Creating directories..."
40
+ mkdir -p logs config-backups
41
+
42
+ # Set executable permissions
43
+ echo "🔐 Setting permissions..."
44
+ chmod +x build.sh
45
+ chmod +x scripts/*.sh 2>/dev/null || true
46
+
47
+ # Check OpenClaw
48
+ echo "Checking OpenClaw installation..."
49
+ if command -v openclaw &> /dev/null; then
50
+ echo "✅ OpenClaw found: $(which openclaw)"
51
+ OPENCLAW_VERSION=$(openclaw version 2>/dev/null || echo "unknown")
52
+ echo " Version: $OPENCLAW_VERSION"
53
+ else
54
+ echo "⚠️ OpenClaw not found in PATH"
55
+ echo " Please ensure OpenClaw is installed and accessible"
56
+ fi
57
+
58
+ # Check AI providers
59
+ echo "Checking AI providers..."
60
+ CLAUDE_FOUND=false
61
+ KIMI_FOUND=false
62
+
63
+ if command -v claude &> /dev/null; then
64
+ echo "✅ Claude Code CLI found: $(which claude)"
65
+ CLAUDE_FOUND=true
66
+ else
67
+ echo "⚠️ Claude Code CLI not found"
68
+ fi
69
+
70
+ if command -v kimi &> /dev/null; then
71
+ echo "✅ Kimi Code CLI found: $(which kimi)"
72
+ KIMI_FOUND=true
73
+ else
74
+ echo "⚠️ Kimi Code CLI not found"
75
+ fi
76
+
77
+ if [ "$CLAUDE_FOUND" = false ] && [ "$KIMI_FOUND" = false ]; then
78
+ echo ""
79
+ echo "❌ ERROR: No AI CLI tool found!"
80
+ echo " You must install at least one:"
81
+ echo " - Claude Code CLI: https://github.com/anthropics/claude-code"
82
+ echo " - Kimi Code CLI: https://platform.moonshot.cn/docs/code"
83
+ echo ""
84
+ exit 1
85
+ fi
86
+
87
+ echo ""
88
+ echo "✅ Setup completed!"
89
+ echo ""
90
+ echo "Next steps:"
91
+ echo "1. Edit .env file with your configuration:"
92
+ echo " vim .env"
93
+ echo ""
94
+ echo "2. Update these important settings:"
95
+ echo " - OPENCLAW_GATEWAY_URL"
96
+ echo " - OPENCLAW_CONFIG_PATH"
97
+ echo " - AI_PROVIDER and API keys"
98
+ echo ""
99
+ echo "3. Run in development mode:"
100
+ echo " pnpm run dev"
101
+ echo ""
102
+ echo "4. Or build and run in production:"
103
+ echo " ./build.sh"
104
+ echo " pnpm start"
105
+ echo ""
@@ -0,0 +1,95 @@
1
+ import { CLIClient, AIProvider } from './cli-client.js';
2
+ import logger from '@/utils/logger.js';
3
+ import { AIClientConfig, DiagnosisResult, RecoveryConfig } from '@/types';
4
+
5
+ export class AIOrchestrator {
6
+ private primaryClient: CLIClient;
7
+ private fallbackClient?: CLIClient;
8
+ private configPath: string;
9
+
10
+ constructor(aiConfig: AIClientConfig, recoveryConfig: RecoveryConfig) {
11
+ this.configPath = recoveryConfig.openclawConfigPath;
12
+
13
+ // Primary AI client
14
+ this.primaryClient = new CLIClient(aiConfig.provider as AIProvider, aiConfig.timeout);
15
+
16
+ // Fallback to alternative provider
17
+ const fallbackProvider: AIProvider = aiConfig.provider === 'claude' ? 'kimi' : 'claude';
18
+ this.fallbackClient = new CLIClient(fallbackProvider, aiConfig.timeout);
19
+ }
20
+
21
+ async initialize(): Promise<void> {
22
+ logger.info('Initializing AI orchestrator');
23
+
24
+ try {
25
+ await this.primaryClient.initialize();
26
+ logger.info('Primary AI client initialized');
27
+ } catch (error: any) {
28
+ logger.error('Failed to initialize primary AI client', {
29
+ error: error.message,
30
+ });
31
+ throw error;
32
+ }
33
+
34
+ // Try to initialize fallback
35
+ try {
36
+ if (this.fallbackClient) {
37
+ await this.fallbackClient.initialize();
38
+ logger.info('Fallback AI client initialized');
39
+ }
40
+ } catch (error: any) {
41
+ logger.warn('Failed to initialize fallback AI client', {
42
+ error: error.message,
43
+ });
44
+ // Non-critical, continue
45
+ }
46
+ }
47
+
48
+ async diagnoseAndFix(error: string): Promise<DiagnosisResult> {
49
+ try {
50
+ // Check if primary client is available
51
+ const isPrimaryAvailable = await this.primaryClient.testConnection();
52
+
53
+ if (isPrimaryAvailable) {
54
+ return await this.primaryClient.diagnoseAndFix(error, this.configPath);
55
+ } else {
56
+ logger.warn('Primary AI CLI not available, trying fallback');
57
+ return await this.fallbackDiagnosis(error);
58
+ }
59
+ } catch (primaryError: any) {
60
+ logger.error('Primary AI diagnosis and fix failed, attempting fallback', {
61
+ error: primaryError.message,
62
+ });
63
+
64
+ return await this.fallbackDiagnosis(error);
65
+ }
66
+ }
67
+
68
+ private async fallbackDiagnosis(error: string): Promise<DiagnosisResult> {
69
+ if (!this.fallbackClient) {
70
+ throw new Error('No fallback AI client available');
71
+ }
72
+
73
+ try {
74
+ const isFallbackAvailable = await this.fallbackClient.testConnection();
75
+
76
+ if (!isFallbackAvailable) {
77
+ throw new Error('Fallback AI CLI not available');
78
+ }
79
+
80
+ logger.info('Using fallback AI for diagnosis and fix');
81
+ return await this.fallbackClient.diagnoseAndFix(error, this.configPath);
82
+ } catch (fallbackError: any) {
83
+ logger.error('Fallback AI also failed', { error: fallbackError.message });
84
+
85
+ // Return basic diagnosis if all else fails
86
+ return {
87
+ issue: 'AI diagnosis and fix unavailable',
88
+ rootCause: `All AI providers failed. Manual intervention required.`,
89
+ suggestedFix: 'Attempt basic restart and check logs manually',
90
+ commands: ['openclaw gateway restart', 'openclaw gateway status'],
91
+ confidence: 0.1,
92
+ };
93
+ }
94
+ }
95
+ }
@@ -0,0 +1,296 @@
1
+ import { exec, spawn } from 'child_process';
2
+ import { promisify } from 'util';
3
+ import fs from 'fs/promises';
4
+ import path from 'path';
5
+ import { fileURLToPath } from 'url';
6
+ import logger from '@/utils/logger.js';
7
+ import { DiagnosisResult } from '@/types';
8
+ import { executor } from '@/utils/executor.js';
9
+
10
+ const execAsync = promisify(exec);
11
+
12
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
13
+
14
+ interface FixSummary {
15
+ issue: string;
16
+ rootCause: string;
17
+ fixApplied: string;
18
+ filesModified: string[];
19
+ commandsExecuted: string[];
20
+ verificationResult: string;
21
+ success: boolean;
22
+ confidence: number;
23
+ }
24
+
25
+ export type AIProvider = 'claude' | 'kimi';
26
+
27
+ export class CLIClient {
28
+ private provider: AIProvider;
29
+ private timeout: number;
30
+ private systemPrompt: string = '';
31
+
32
+ constructor(provider: AIProvider, timeout: number = 300000) {
33
+ this.provider = provider;
34
+ this.timeout = timeout;
35
+ }
36
+
37
+ async initialize(): Promise<void> {
38
+ const promptPath = path.join(__dirname, '../../prompts/fix-openclaw.md');
39
+ try {
40
+ this.systemPrompt = await fs.readFile(promptPath, 'utf-8');
41
+ logger.info('System prompt loaded', { provider: this.provider });
42
+ } catch (error: any) {
43
+ logger.error('Failed to load system prompt', { error: error.message });
44
+ throw error;
45
+ }
46
+ }
47
+
48
+ async diagnoseAndFix(error: string, configPath: string): Promise<DiagnosisResult> {
49
+ logger.info('Starting AI-powered diagnosis and fix', {
50
+ provider: this.provider,
51
+ configPath,
52
+ });
53
+
54
+ try {
55
+ // Gather context
56
+ const context = await this.gatherContext(error);
57
+
58
+ // Create task description
59
+ const taskDescription = this.createTaskDescription(error, context, configPath);
60
+
61
+ // Execute AI CLI
62
+ const output = await this.executeAICLI(taskDescription, configPath);
63
+
64
+ // Parse fix summary
65
+ const summary = this.parseFixSummary(output);
66
+
67
+ // Convert to DiagnosisResult format
68
+ const diagnosis = this.convertToDiagnosisResult(summary);
69
+
70
+ logger.info('AI diagnosis and fix completed', diagnosis);
71
+ return diagnosis;
72
+ } catch (error: any) {
73
+ logger.error('AI diagnosis and fix failed', { error: error.message });
74
+ throw error;
75
+ }
76
+ }
77
+
78
+ private async gatherContext(_error: string): Promise<string> {
79
+ const parts: string[] = [];
80
+
81
+ // Get gateway status
82
+ const statusResult = await executor.getGatewayStatus();
83
+ if (statusResult.stdout) {
84
+ parts.push(`Gateway Status:\n${statusResult.stdout}`);
85
+ }
86
+ if (statusResult.stderr) {
87
+ parts.push(`Gateway Status Error:\n${statusResult.stderr}`);
88
+ }
89
+
90
+ // Get recent logs
91
+ const logsResult = await executor.getGatewayLogs(50);
92
+ if (logsResult.stdout) {
93
+ parts.push(`Recent Logs:\n${logsResult.stdout}`);
94
+ }
95
+
96
+ // Attempt restart to get detailed error
97
+ const restartResult = await executor.restartGateway();
98
+ if (restartResult.stderr) {
99
+ parts.push(`Restart Error:\n${restartResult.stderr}`);
100
+ }
101
+ if (restartResult.stdout) {
102
+ parts.push(`Restart Output:\n${restartResult.stdout}`);
103
+ }
104
+
105
+ return parts.join('\n\n');
106
+ }
107
+
108
+ private createTaskDescription(
109
+ error: string,
110
+ context: string,
111
+ configPath: string
112
+ ): string {
113
+ return `${this.systemPrompt}
114
+
115
+ ---
116
+
117
+ ## 当前问题
118
+
119
+ **错误信息**: ${error}
120
+
121
+ **OpenClaw 配置路径**: ${configPath}
122
+
123
+ **上下文信息**:
124
+ ${context}
125
+
126
+ ---
127
+
128
+ 请立即开始诊断和修复这个问题。记住:
129
+
130
+ 1. 直接修复,不要只给建议
131
+ 2. 使用工具读取、编辑配置文件
132
+ 3. 执行必要的命令
133
+ 4. 验证修复是否成功
134
+ 5. 完成后输出标准格式的 JSON 摘要
135
+
136
+ 开始工作!`;
137
+ }
138
+
139
+ private async executeAICLI(task: string, workingDir: string): Promise<string> {
140
+ const cliCommand = this.provider === 'claude' ? 'claude' : 'kimi';
141
+
142
+ logger.info('Executing AI CLI', {
143
+ provider: this.provider,
144
+ workingDir,
145
+ });
146
+
147
+ return new Promise<string>((resolve, reject) => {
148
+ const child = spawn(cliCommand, ['--dangerously-skip-permissions'], {
149
+ cwd: workingDir,
150
+ env: {
151
+ ...process.env,
152
+ FORCE_COLOR: '0',
153
+ },
154
+ });
155
+
156
+ const stdoutChunks: Buffer[] = [];
157
+ const stderrChunks: Buffer[] = [];
158
+ let settled = false;
159
+
160
+ const timer = setTimeout(() => {
161
+ if (!settled) {
162
+ settled = true;
163
+ child.kill('SIGTERM');
164
+ reject(new Error(`${this.provider} CLI timed out after ${this.timeout}ms`));
165
+ }
166
+ }, this.timeout);
167
+
168
+ child.stdout.on('data', (chunk) => stdoutChunks.push(chunk));
169
+ child.stderr.on('data', (chunk) => stderrChunks.push(chunk));
170
+
171
+ child.on('error', (err) => {
172
+ if (!settled) {
173
+ settled = true;
174
+ clearTimeout(timer);
175
+ reject(new Error(`${this.provider} CLI spawn error: ${err.message}`));
176
+ }
177
+ });
178
+
179
+ child.on('close', (code) => {
180
+ if (!settled) {
181
+ settled = true;
182
+ clearTimeout(timer);
183
+ const stdout = Buffer.concat(stdoutChunks).toString();
184
+ const stderr = Buffer.concat(stderrChunks).toString();
185
+ const output = stdout + stderr;
186
+
187
+ if (code !== 0) {
188
+ logger.error('AI CLI execution failed', {
189
+ exitCode: code,
190
+ stderrPreview: stderr.substring(0, 200),
191
+ });
192
+ reject(new Error(`${this.provider} CLI exited with code ${code}`));
193
+ return;
194
+ }
195
+
196
+ logger.info('AI CLI execution completed', {
197
+ outputLength: output.length,
198
+ hasStderr: !!stderr,
199
+ });
200
+ resolve(output);
201
+ }
202
+ });
203
+
204
+ // Pipe task via stdin instead of embedding in command
205
+ child.stdin.write(task);
206
+ child.stdin.end();
207
+ });
208
+ }
209
+
210
+ private parseFixSummary(output: string): FixSummary {
211
+ try {
212
+ // Extract JSON summary between markers
213
+ const startMarker = '===FIX_SUMMARY_START===';
214
+ const endMarker = '===FIX_SUMMARY_END===';
215
+
216
+ const startIndex = output.indexOf(startMarker);
217
+ const endIndex = output.indexOf(endMarker);
218
+
219
+ if (startIndex === -1 || endIndex === -1) {
220
+ throw new Error('Fix summary markers not found in AI output');
221
+ }
222
+
223
+ const jsonStr = output
224
+ .substring(startIndex + startMarker.length, endIndex)
225
+ .trim();
226
+
227
+ // Remove markdown code blocks if present
228
+ const cleanJson = jsonStr.replace(/^```json\n?/, '').replace(/\n?```$/, '');
229
+
230
+ const summary: FixSummary = JSON.parse(cleanJson);
231
+
232
+ // Validate required fields
233
+ if (
234
+ !summary.issue ||
235
+ !summary.rootCause ||
236
+ !summary.fixApplied ||
237
+ typeof summary.success !== 'boolean'
238
+ ) {
239
+ throw new Error('Fix summary missing required fields');
240
+ }
241
+
242
+ logger.info('Fix summary parsed successfully', { success: summary.success });
243
+ return summary;
244
+ } catch (error: any) {
245
+ logger.error('Failed to parse fix summary', {
246
+ error: error.message,
247
+ outputPreview: output.substring(0, 500),
248
+ });
249
+
250
+ // Return a fallback summary
251
+ return {
252
+ issue: 'Parse error',
253
+ rootCause: `Failed to parse AI output: ${error.message}`,
254
+ fixApplied: 'AI attempted fixes but summary parsing failed',
255
+ filesModified: [],
256
+ commandsExecuted: [],
257
+ verificationResult: 'Unknown - could not parse verification result',
258
+ success: false,
259
+ confidence: 0.3,
260
+ };
261
+ }
262
+ }
263
+
264
+ private convertToDiagnosisResult(summary: FixSummary): DiagnosisResult {
265
+ return {
266
+ issue: summary.issue,
267
+ rootCause: summary.rootCause,
268
+ suggestedFix: summary.fixApplied,
269
+ commands: summary.commandsExecuted,
270
+ configChanges: summary.filesModified.map((file) => ({
271
+ file,
272
+ path: 'auto-modified',
273
+ oldValue: 'see git diff',
274
+ newValue: 'see git diff',
275
+ reason: summary.fixApplied,
276
+ })),
277
+ confidence: summary.confidence,
278
+ };
279
+ }
280
+
281
+ async testConnection(): Promise<boolean> {
282
+ try {
283
+ const command =
284
+ this.provider === 'claude' ? 'claude --version' : 'kimi --version';
285
+
286
+ await execAsync(command, { timeout: 5000 });
287
+ logger.info(`${this.provider} CLI is available`);
288
+ return true;
289
+ } catch (error: any) {
290
+ logger.warn(`${this.provider} CLI is not available`, {
291
+ error: error.message,
292
+ });
293
+ return false;
294
+ }
295
+ }
296
+ }