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.
- package/.claude/settings.local.json +7 -0
- package/.dockerignore +21 -0
- package/.env.example +31 -0
- package/.eslintrc.json +26 -0
- package/.prettierrc.json +9 -0
- package/CHANGELOG.md +93 -0
- package/Dockerfile +47 -0
- package/README.md +408 -0
- package/build.sh +33 -0
- package/dist/ai/ai-orchestrator.d.ts +11 -0
- package/dist/ai/ai-orchestrator.d.ts.map +1 -0
- package/dist/ai/ai-orchestrator.js +85 -0
- package/dist/ai/ai-orchestrator.js.map +1 -0
- package/dist/ai/cli-client.d.ts +17 -0
- package/dist/ai/cli-client.d.ts.map +1 -0
- package/dist/ai/cli-client.js +239 -0
- package/dist/ai/cli-client.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +33 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/config.d.ts +7 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +52 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/init.d.ts +6 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +205 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/start.d.ts +6 -0
- package/dist/commands/start.d.ts.map +1 -0
- package/dist/commands/start.js +49 -0
- package/dist/commands/start.js.map +1 -0
- package/dist/commands/status.d.ts +2 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +48 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/config/default.d.ts +5 -0
- package/dist/config/default.d.ts.map +1 -0
- package/dist/config/default.js +22 -0
- package/dist/config/default.js.map +1 -0
- package/dist/healthcheck/gateway-monitor.d.ts +19 -0
- package/dist/healthcheck/gateway-monitor.d.ts.map +1 -0
- package/dist/healthcheck/gateway-monitor.js +116 -0
- package/dist/healthcheck/gateway-monitor.js.map +1 -0
- package/dist/healthcheck/health-checker.d.ts +11 -0
- package/dist/healthcheck/health-checker.d.ts.map +1 -0
- package/dist/healthcheck/health-checker.js +60 -0
- package/dist/healthcheck/health-checker.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +39 -0
- package/dist/index.js.map +1 -0
- package/dist/recovery/auto-fixer.d.ts +16 -0
- package/dist/recovery/auto-fixer.d.ts.map +1 -0
- package/dist/recovery/auto-fixer.js +162 -0
- package/dist/recovery/auto-fixer.js.map +1 -0
- package/dist/recovery/change-recorder.d.ts +8 -0
- package/dist/recovery/change-recorder.d.ts.map +1 -0
- package/dist/recovery/change-recorder.js +41 -0
- package/dist/recovery/change-recorder.js.map +1 -0
- package/dist/setup/config-initializer.d.ts +13 -0
- package/dist/setup/config-initializer.d.ts.map +1 -0
- package/dist/setup/config-initializer.js +46 -0
- package/dist/setup/config-initializer.js.map +1 -0
- package/dist/setup/config-loader.d.ts +9 -0
- package/dist/setup/config-loader.d.ts.map +1 -0
- package/dist/setup/config-loader.js +17 -0
- package/dist/setup/config-loader.js.map +1 -0
- package/dist/setup/git-initializer.d.ts +15 -0
- package/dist/setup/git-initializer.d.ts.map +1 -0
- package/dist/setup/git-initializer.js +189 -0
- package/dist/setup/git-initializer.js.map +1 -0
- package/dist/setup/safe-config-generator.d.ts +9 -0
- package/dist/setup/safe-config-generator.d.ts.map +1 -0
- package/dist/setup/safe-config-generator.js +85 -0
- package/dist/setup/safe-config-generator.js.map +1 -0
- package/dist/types/index.d.ts +60 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/executor.d.ts +17 -0
- package/dist/utils/executor.d.ts.map +1 -0
- package/dist/utils/executor.js +57 -0
- package/dist/utils/executor.js.map +1 -0
- package/dist/utils/git-manager.d.ts +14 -0
- package/dist/utils/git-manager.d.ts.map +1 -0
- package/dist/utils/git-manager.js +116 -0
- package/dist/utils/git-manager.js.map +1 -0
- package/dist/utils/github-cli.d.ts +9 -0
- package/dist/utils/github-cli.d.ts.map +1 -0
- package/dist/utils/github-cli.js +31 -0
- package/dist/utils/github-cli.js.map +1 -0
- package/dist/utils/logger.d.ts +4 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +26 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/paths.d.ts +6 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +19 -0
- package/dist/utils/paths.js.map +1 -0
- package/docker-compose.yml +43 -0
- package/nodemon.json +9 -0
- package/package.json +59 -0
- package/prompts/fix-openclaw.md +202 -0
- package/scripts/setup.sh +105 -0
- package/src/ai/ai-orchestrator.ts +95 -0
- package/src/ai/cli-client.ts +296 -0
- package/src/cli.ts +40 -0
- package/src/commands/config.ts +57 -0
- package/src/commands/init.ts +239 -0
- package/src/commands/start.ts +75 -0
- package/src/commands/status.ts +79 -0
- package/src/config/default.ts +25 -0
- package/src/healthcheck/gateway-monitor.ts +137 -0
- package/src/healthcheck/health-checker.ts +71 -0
- package/src/index.ts +48 -0
- package/src/recovery/auto-fixer.ts +184 -0
- package/src/recovery/change-recorder.ts +46 -0
- package/src/setup/config-initializer.ts +63 -0
- package/src/setup/config-loader.ts +25 -0
- package/src/setup/git-initializer.ts +203 -0
- package/src/setup/safe-config-generator.ts +100 -0
- package/src/types/index.ts +67 -0
- package/src/utils/executor.ts +75 -0
- package/src/utils/git-manager.ts +121 -0
- package/src/utils/github-cli.ts +37 -0
- package/src/utils/logger.ts +39 -0
- package/src/utils/paths.ts +25 -0
- package/tsconfig.json +29 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import logger from '@/utils/logger.js';
|
|
4
|
+
|
|
5
|
+
export class SafeConfigGenerator {
|
|
6
|
+
private configPath: string;
|
|
7
|
+
|
|
8
|
+
constructor(configPath: string) {
|
|
9
|
+
this.configPath = configPath;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async generate(): Promise<void> {
|
|
13
|
+
const openclawJsonPath = path.join(this.configPath, 'openclaw.json');
|
|
14
|
+
const safeJsonPath = path.join(this.configPath, 'openclaw.safe.json');
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
// Read original config
|
|
18
|
+
const content = await fs.readFile(openclawJsonPath, 'utf-8');
|
|
19
|
+
const config = JSON.parse(content);
|
|
20
|
+
|
|
21
|
+
// Redact sensitive data
|
|
22
|
+
const safeConfig = this.redactSensitiveData(config);
|
|
23
|
+
|
|
24
|
+
// Write safe config
|
|
25
|
+
await fs.writeFile(safeJsonPath, JSON.stringify(safeConfig, null, 2), 'utf-8');
|
|
26
|
+
|
|
27
|
+
logger.info('Safe configuration generated', { path: safeJsonPath });
|
|
28
|
+
} catch (error: any) {
|
|
29
|
+
logger.error('Failed to generate safe configuration', { error: error.message });
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async update(): Promise<void> {
|
|
35
|
+
// Same as generate - always regenerate from openclaw.json
|
|
36
|
+
await this.generate();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
private redactSensitiveData(obj: any): any {
|
|
40
|
+
if (typeof obj !== 'object' || obj === null) {
|
|
41
|
+
return obj;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (Array.isArray(obj)) {
|
|
45
|
+
return obj.map((item) => this.redactSensitiveData(item));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const result: any = {};
|
|
49
|
+
|
|
50
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
51
|
+
const lowerKey = key.toLowerCase();
|
|
52
|
+
|
|
53
|
+
// Check if key indicates sensitive data
|
|
54
|
+
if (
|
|
55
|
+
lowerKey.includes('key') ||
|
|
56
|
+
lowerKey.includes('token') ||
|
|
57
|
+
lowerKey.includes('secret') ||
|
|
58
|
+
lowerKey.includes('password') ||
|
|
59
|
+
lowerKey.includes('apikey') ||
|
|
60
|
+
lowerKey.includes('api_key') ||
|
|
61
|
+
lowerKey === 'bearer'
|
|
62
|
+
) {
|
|
63
|
+
// Replace with placeholder
|
|
64
|
+
result[key] = this.getPlaceholder(lowerKey, value);
|
|
65
|
+
} else if (typeof value === 'object' && value !== null) {
|
|
66
|
+
// Recursively process nested objects
|
|
67
|
+
result[key] = this.redactSensitiveData(value);
|
|
68
|
+
} else {
|
|
69
|
+
// Keep non-sensitive values
|
|
70
|
+
result[key] = value;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
private getPlaceholder(key: string, value: any): string {
|
|
78
|
+
if (typeof value !== 'string') {
|
|
79
|
+
return '<REDACTED>';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (key.includes('apikey') || key.includes('api_key')) {
|
|
83
|
+
return '<YOUR_API_KEY_HERE>';
|
|
84
|
+
}
|
|
85
|
+
if (key.includes('token')) {
|
|
86
|
+
return '<YOUR_TOKEN_HERE>';
|
|
87
|
+
}
|
|
88
|
+
if (key.includes('secret')) {
|
|
89
|
+
return '<YOUR_SECRET_HERE>';
|
|
90
|
+
}
|
|
91
|
+
if (key.includes('password')) {
|
|
92
|
+
return '<YOUR_PASSWORD_HERE>';
|
|
93
|
+
}
|
|
94
|
+
if (key.includes('key')) {
|
|
95
|
+
return '<YOUR_KEY_HERE>';
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return '<REDACTED>';
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
export interface HealthCheckResult {
|
|
2
|
+
healthy: boolean;
|
|
3
|
+
timestamp: Date;
|
|
4
|
+
endpoint: string;
|
|
5
|
+
responseTime?: number;
|
|
6
|
+
error?: string;
|
|
7
|
+
statusCode?: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface DiagnosisResult {
|
|
11
|
+
issue: string;
|
|
12
|
+
rootCause: string;
|
|
13
|
+
suggestedFix: string;
|
|
14
|
+
commands: string[];
|
|
15
|
+
configChanges?: ConfigChange[];
|
|
16
|
+
confidence: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ConfigChange {
|
|
20
|
+
file: string;
|
|
21
|
+
path: string;
|
|
22
|
+
oldValue: unknown;
|
|
23
|
+
newValue: unknown;
|
|
24
|
+
reason: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface RecoveryResult {
|
|
28
|
+
success: boolean;
|
|
29
|
+
actions: string[];
|
|
30
|
+
errors?: string[];
|
|
31
|
+
configChanges?: ConfigChange[];
|
|
32
|
+
restartRequired: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface ChangeRecord {
|
|
36
|
+
timestamp: Date;
|
|
37
|
+
trigger: string;
|
|
38
|
+
diagnosis: DiagnosisResult;
|
|
39
|
+
recovery: RecoveryResult;
|
|
40
|
+
gitCommit?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type AIProvider = 'claude' | 'kimi';
|
|
44
|
+
|
|
45
|
+
export interface AIClientConfig {
|
|
46
|
+
provider: AIProvider;
|
|
47
|
+
timeout?: number;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface MonitorConfig {
|
|
51
|
+
gatewayUrl: string;
|
|
52
|
+
healthEndpoint: string;
|
|
53
|
+
checkInterval: number;
|
|
54
|
+
timeout: number;
|
|
55
|
+
maxRetries: number;
|
|
56
|
+
failureThreshold: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface RecoveryConfig {
|
|
60
|
+
useGitTracking: boolean;
|
|
61
|
+
useGitHubCli: boolean;
|
|
62
|
+
gitCommitPrefix: string;
|
|
63
|
+
backupBeforeChange: boolean;
|
|
64
|
+
openclawConfigPath: string;
|
|
65
|
+
maxRecoveryRetries: number;
|
|
66
|
+
recoveryCooldownMs: number;
|
|
67
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { exec } from 'child_process';
|
|
2
|
+
import { promisify } from 'util';
|
|
3
|
+
import logger from './logger.js';
|
|
4
|
+
|
|
5
|
+
const execAsync = promisify(exec);
|
|
6
|
+
|
|
7
|
+
export interface CommandResult {
|
|
8
|
+
stdout: string;
|
|
9
|
+
stderr: string;
|
|
10
|
+
exitCode: number;
|
|
11
|
+
success: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class CommandExecutor {
|
|
15
|
+
private timeout: number;
|
|
16
|
+
|
|
17
|
+
constructor(timeout: number = 30000) {
|
|
18
|
+
this.timeout = timeout;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async execute(command: string, cwd?: string): Promise<CommandResult> {
|
|
22
|
+
logger.info('Executing command', { command, cwd });
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const { stdout, stderr } = await execAsync(command, {
|
|
26
|
+
timeout: this.timeout,
|
|
27
|
+
cwd,
|
|
28
|
+
shell: '/bin/bash',
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
logger.info('Command executed successfully', { command, stdout, stderr });
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
stdout: stdout.trim(),
|
|
35
|
+
stderr: stderr.trim(),
|
|
36
|
+
exitCode: 0,
|
|
37
|
+
success: true,
|
|
38
|
+
};
|
|
39
|
+
} catch (error: any) {
|
|
40
|
+
logger.error('Command execution failed', {
|
|
41
|
+
command,
|
|
42
|
+
error: error.message,
|
|
43
|
+
stdout: error.stdout,
|
|
44
|
+
stderr: error.stderr,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
stdout: error.stdout?.trim() || '',
|
|
49
|
+
stderr: error.stderr?.trim() || error.message,
|
|
50
|
+
exitCode: error.code || 1,
|
|
51
|
+
success: false,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async executeOpenClawCommand(subcommand: string): Promise<CommandResult> {
|
|
57
|
+
const command = `openclaw ${subcommand}`;
|
|
58
|
+
return this.execute(command);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async restartGateway(): Promise<CommandResult> {
|
|
62
|
+
logger.info('Attempting to restart OpenClaw gateway');
|
|
63
|
+
return this.executeOpenClawCommand('gateway restart');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async getGatewayStatus(): Promise<CommandResult> {
|
|
67
|
+
return this.executeOpenClawCommand('gateway status');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async getGatewayLogs(lines: number = 100): Promise<CommandResult> {
|
|
71
|
+
return this.executeOpenClawCommand(`gateway logs --lines ${lines}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export const executor = new CommandExecutor();
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import simpleGit, { SimpleGit } from 'simple-git';
|
|
2
|
+
import logger from './logger.js';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import fs from 'fs/promises';
|
|
5
|
+
|
|
6
|
+
export class GitManager {
|
|
7
|
+
private git: SimpleGit;
|
|
8
|
+
private repoPath: string;
|
|
9
|
+
|
|
10
|
+
constructor(repoPath: string) {
|
|
11
|
+
this.repoPath = repoPath;
|
|
12
|
+
this.git = simpleGit(repoPath);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async init(): Promise<void> {
|
|
16
|
+
try {
|
|
17
|
+
const isRepo = await this.git.checkIsRepo();
|
|
18
|
+
if (!isRepo) {
|
|
19
|
+
logger.info('Initializing git repository', { path: this.repoPath });
|
|
20
|
+
await this.git.init();
|
|
21
|
+
await this.createGitignore();
|
|
22
|
+
await this.git.add('.gitignore');
|
|
23
|
+
await this.git.commit('Initial commit: Setup config tracking');
|
|
24
|
+
}
|
|
25
|
+
} catch (error: any) {
|
|
26
|
+
logger.error('Failed to initialize git repository', { error: error.message });
|
|
27
|
+
throw error;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
private async createGitignore(): Promise<void> {
|
|
32
|
+
const gitignorePath = path.join(this.repoPath, '.gitignore');
|
|
33
|
+
const content = `# Ignore sensitive files
|
|
34
|
+
*.key
|
|
35
|
+
*.pem
|
|
36
|
+
*.secret
|
|
37
|
+
secrets/
|
|
38
|
+
*.env
|
|
39
|
+
`;
|
|
40
|
+
await fs.writeFile(gitignorePath, content, 'utf-8');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async commit(message: string, files?: string[]): Promise<string> {
|
|
44
|
+
try {
|
|
45
|
+
if (files && files.length > 0) {
|
|
46
|
+
await this.git.add(files);
|
|
47
|
+
} else {
|
|
48
|
+
await this.git.add('.');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const status = await this.git.status();
|
|
52
|
+
if (status.files.length === 0) {
|
|
53
|
+
logger.info('No changes to commit');
|
|
54
|
+
return '';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const result = await this.git.commit(message);
|
|
58
|
+
logger.info('Changes committed', { message, hash: result.commit });
|
|
59
|
+
return result.commit;
|
|
60
|
+
} catch (error: any) {
|
|
61
|
+
logger.error('Failed to commit changes', { error: error.message });
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async createBackup(files: string[]): Promise<void> {
|
|
67
|
+
try {
|
|
68
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
69
|
+
const backupBranch = `backup-${timestamp}`;
|
|
70
|
+
|
|
71
|
+
const currentBranch = await this.git.revparse(['--abbrev-ref', 'HEAD']);
|
|
72
|
+
await this.git.checkoutBranch(backupBranch, currentBranch.trim());
|
|
73
|
+
await this.git.checkout(currentBranch.trim());
|
|
74
|
+
|
|
75
|
+
logger.info('Backup created', { branch: backupBranch, files });
|
|
76
|
+
} catch (error: any) {
|
|
77
|
+
logger.error('Failed to create backup', { error: error.message });
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async getDiff(file?: string): Promise<string> {
|
|
83
|
+
try {
|
|
84
|
+
const diff = file ? await this.git.diff([file]) : await this.git.diff();
|
|
85
|
+
return diff;
|
|
86
|
+
} catch (error: any) {
|
|
87
|
+
logger.error('Failed to get diff', { error: error.message });
|
|
88
|
+
return '';
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async hasRemote(): Promise<boolean> {
|
|
93
|
+
try {
|
|
94
|
+
const remotes = await this.git.getRemotes();
|
|
95
|
+
return remotes.some((r) => r.name === 'origin');
|
|
96
|
+
} catch {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async push(): Promise<boolean> {
|
|
102
|
+
try {
|
|
103
|
+
await this.git.push('origin', 'HEAD');
|
|
104
|
+
logger.info('Pushed to remote');
|
|
105
|
+
return true;
|
|
106
|
+
} catch (error: any) {
|
|
107
|
+
logger.warn('Failed to push to remote', { error: error.message });
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async getLog(maxCount: number = 10): Promise<string> {
|
|
113
|
+
try {
|
|
114
|
+
const log = await this.git.log({ maxCount });
|
|
115
|
+
return log.all.map((commit) => `${commit.hash.substring(0, 7)} - ${commit.message}`).join('\n');
|
|
116
|
+
} catch (error: any) {
|
|
117
|
+
logger.error('Failed to get log', { error: error.message });
|
|
118
|
+
return '';
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { CommandExecutor, CommandResult } from './executor.js';
|
|
2
|
+
import logger from './logger.js';
|
|
3
|
+
|
|
4
|
+
export class GitHubCli {
|
|
5
|
+
private executor: CommandExecutor;
|
|
6
|
+
|
|
7
|
+
constructor() {
|
|
8
|
+
this.executor = new CommandExecutor(60000);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async isInstalled(): Promise<boolean> {
|
|
12
|
+
const result = await this.executor.execute('gh --version');
|
|
13
|
+
return result.success;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async isAuthenticated(): Promise<boolean> {
|
|
17
|
+
const result = await this.executor.execute('gh auth status');
|
|
18
|
+
return result.success;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async createRepoAndPush(
|
|
22
|
+
repoName: string,
|
|
23
|
+
localPath: string
|
|
24
|
+
): Promise<CommandResult> {
|
|
25
|
+
const command = `gh repo create ${repoName} --private --source="${localPath}" --remote=origin --push`;
|
|
26
|
+
const result = await this.executor.execute(command, localPath);
|
|
27
|
+
if (result.success) {
|
|
28
|
+
logger.info('GitHub repository created and pushed', { repoName });
|
|
29
|
+
} else {
|
|
30
|
+
logger.error('Failed to create GitHub repository', {
|
|
31
|
+
repoName,
|
|
32
|
+
stderr: result.stderr,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import winston from 'winston';
|
|
2
|
+
import { getLogFilePath } from '@/utils/paths.js';
|
|
3
|
+
|
|
4
|
+
const logLevel = process.env.LOG_LEVEL || 'info';
|
|
5
|
+
const logFilePath = process.env.LOG_FILE_PATH || getLogFilePath();
|
|
6
|
+
|
|
7
|
+
const logger = winston.createLogger({
|
|
8
|
+
level: logLevel,
|
|
9
|
+
format: winston.format.combine(
|
|
10
|
+
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
|
|
11
|
+
winston.format.errors({ stack: true }),
|
|
12
|
+
winston.format.splat(),
|
|
13
|
+
winston.format.json()
|
|
14
|
+
),
|
|
15
|
+
defaultMeta: { service: 'openclaw-healthcheck' },
|
|
16
|
+
transports: [
|
|
17
|
+
new winston.transports.File({
|
|
18
|
+
filename: logFilePath.replace('.log', '-error.log'),
|
|
19
|
+
level: 'error',
|
|
20
|
+
}),
|
|
21
|
+
new winston.transports.File({ filename: logFilePath }),
|
|
22
|
+
],
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
26
|
+
logger.add(
|
|
27
|
+
new winston.transports.Console({
|
|
28
|
+
format: winston.format.combine(
|
|
29
|
+
winston.format.colorize(),
|
|
30
|
+
winston.format.printf(({ level, message, timestamp, ...meta }) => {
|
|
31
|
+
const metaStr = Object.keys(meta).length ? JSON.stringify(meta, null, 2) : '';
|
|
32
|
+
return `${timestamp} [${level}]: ${message} ${metaStr}`;
|
|
33
|
+
})
|
|
34
|
+
),
|
|
35
|
+
})
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export default logger;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
|
|
4
|
+
const WATCHER_HOME =
|
|
5
|
+
process.env.WATCHER_HOME || path.join(os.homedir(), '.openclaw-watcher');
|
|
6
|
+
|
|
7
|
+
export function getWatcherHome(): string {
|
|
8
|
+
return WATCHER_HOME;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function getConfigPath(): string {
|
|
12
|
+
return path.join(WATCHER_HOME, 'config.json');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function getLogsDir(): string {
|
|
16
|
+
return path.join(WATCHER_HOME, 'logs');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function getLogFilePath(): string {
|
|
20
|
+
return path.join(WATCHER_HOME, 'logs', 'healthcheck.log');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function getChangesFilePath(): string {
|
|
24
|
+
return path.join(WATCHER_HOME, 'logs', 'changes.json');
|
|
25
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"lib": ["ES2022"],
|
|
6
|
+
"moduleResolution": "bundler",
|
|
7
|
+
"resolveJsonModule": true,
|
|
8
|
+
"allowJs": true,
|
|
9
|
+
"outDir": "./dist",
|
|
10
|
+
"rootDir": "./src",
|
|
11
|
+
"strict": true,
|
|
12
|
+
"esModuleInterop": true,
|
|
13
|
+
"skipLibCheck": true,
|
|
14
|
+
"forceConsistentCasingInFileNames": true,
|
|
15
|
+
"declaration": true,
|
|
16
|
+
"declarationMap": true,
|
|
17
|
+
"sourceMap": true,
|
|
18
|
+
"noUnusedLocals": true,
|
|
19
|
+
"noUnusedParameters": true,
|
|
20
|
+
"noImplicitReturns": true,
|
|
21
|
+
"noFallthroughCasesInSwitch": true,
|
|
22
|
+
"allowSyntheticDefaultImports": true,
|
|
23
|
+
"paths": {
|
|
24
|
+
"@/*": ["./src/*"]
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"include": ["src/**/*"],
|
|
28
|
+
"exclude": ["node_modules", "dist"]
|
|
29
|
+
}
|