@cyanautomation/kaseki-agent 1.15.0 → 1.16.0
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/dist/github-operations-monitor.d.ts +37 -0
- package/dist/github-operations-monitor.d.ts.map +1 -0
- package/dist/github-operations-monitor.js +162 -0
- package/dist/github-operations-monitor.js.map +1 -0
- package/kaseki-agent.sh +176 -6
- package/package.json +1 -1
- package/scripts/kaseki-diagnose-github-failure.sh +143 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* github-operations-monitor.ts
|
|
3
|
+
*
|
|
4
|
+
* Monitoring module for analyzing kaseki github operations failures.
|
|
5
|
+
* Provides functions to detect, classify, and analyze github operations stage failures.
|
|
6
|
+
*/
|
|
7
|
+
interface DiagnosticResult {
|
|
8
|
+
hasFailed: boolean;
|
|
9
|
+
failureType: string;
|
|
10
|
+
description: string;
|
|
11
|
+
exitCode: number;
|
|
12
|
+
stage: string;
|
|
13
|
+
apiError?: {
|
|
14
|
+
type: string;
|
|
15
|
+
message: string;
|
|
16
|
+
httpStatus: string;
|
|
17
|
+
};
|
|
18
|
+
logs: {
|
|
19
|
+
gitPushLogTail?: string[];
|
|
20
|
+
lastCommandLog?: string[];
|
|
21
|
+
healthCheckLog?: string[];
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Detect if kaseki run had a github operations failure
|
|
26
|
+
*/
|
|
27
|
+
export declare function isGithubOperationFailure(metadataPath: string): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Analyze a kaseki run for github operations failures
|
|
30
|
+
*/
|
|
31
|
+
export declare function analyzeGithubOperations(resultsDir: string): DiagnosticResult;
|
|
32
|
+
/**
|
|
33
|
+
* Format diagnostic result as human-readable markdown
|
|
34
|
+
*/
|
|
35
|
+
export declare function formatDiagnosticResult(result: DiagnosticResult): string;
|
|
36
|
+
export {};
|
|
37
|
+
//# sourceMappingURL=github-operations-monitor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-operations-monitor.d.ts","sourceRoot":"","sources":["../src/github-operations-monitor.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAeH,UAAU,gBAAgB;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE;QACT,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,IAAI,EAAE;QACJ,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;QAC1B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;QAC1B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;KAC3B,CAAC;CACH;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAQtE;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB,CAoG5E;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAoCvE"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* github-operations-monitor.ts
|
|
3
|
+
*
|
|
4
|
+
* Monitoring module for analyzing kaseki github operations failures.
|
|
5
|
+
* Provides functions to detect, classify, and analyze github operations stage failures.
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
/**
|
|
10
|
+
* Detect if kaseki run had a github operations failure
|
|
11
|
+
*/
|
|
12
|
+
export function isGithubOperationFailure(metadataPath) {
|
|
13
|
+
try {
|
|
14
|
+
const content = fs.readFileSync(metadataPath, 'utf8');
|
|
15
|
+
const metadata = JSON.parse(content);
|
|
16
|
+
return metadata.current_stage === 'github operations' && (metadata.exit_code ?? 0) !== 0;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Analyze a kaseki run for github operations failures
|
|
24
|
+
*/
|
|
25
|
+
export function analyzeGithubOperations(resultsDir) {
|
|
26
|
+
const metadataPath = path.join(resultsDir, 'metadata.json');
|
|
27
|
+
const failurePath = path.join(resultsDir, 'failure.json');
|
|
28
|
+
const gitPushLogPath = path.join(resultsDir, 'git-push.log');
|
|
29
|
+
const lastCommandLogPath = path.join(resultsDir, 'last-command.log');
|
|
30
|
+
const healthCheckLogPath = path.join(resultsDir, 'github-health-check.log');
|
|
31
|
+
const result = {
|
|
32
|
+
hasFailed: false,
|
|
33
|
+
failureType: 'unknown',
|
|
34
|
+
description: 'No failure detected',
|
|
35
|
+
exitCode: 0,
|
|
36
|
+
stage: 'unknown',
|
|
37
|
+
logs: {},
|
|
38
|
+
};
|
|
39
|
+
// Read metadata
|
|
40
|
+
let metadata = {};
|
|
41
|
+
try {
|
|
42
|
+
const content = fs.readFileSync(metadataPath, 'utf8');
|
|
43
|
+
metadata = JSON.parse(content);
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
result.description = `Failed to read metadata: ${e}`;
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
// Read failure.json for additional context
|
|
50
|
+
try {
|
|
51
|
+
if (fs.existsSync(failurePath)) {
|
|
52
|
+
const content = fs.readFileSync(failurePath, 'utf8');
|
|
53
|
+
JSON.parse(content); // Validate JSON format
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch (e) {
|
|
57
|
+
// Failure.json might not exist if success
|
|
58
|
+
}
|
|
59
|
+
result.stage = metadata.current_stage ?? 'unknown';
|
|
60
|
+
result.exitCode = metadata.exit_code ?? 0;
|
|
61
|
+
// Classify failure
|
|
62
|
+
if (metadata.current_stage !== 'github operations') {
|
|
63
|
+
result.hasFailed = false;
|
|
64
|
+
result.description = `Failure occurred in stage '${metadata.current_stage}', not github operations`;
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
if (result.exitCode === 0) {
|
|
68
|
+
result.hasFailed = false;
|
|
69
|
+
result.description = 'GitHub operations completed successfully';
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
result.hasFailed = true;
|
|
73
|
+
// Determine failure type
|
|
74
|
+
const pushExit = metadata.github_push_exit_code ?? 0;
|
|
75
|
+
const prExit = metadata.github_pr_exit_code ?? 0;
|
|
76
|
+
if (pushExit !== 0 && prExit === 0) {
|
|
77
|
+
result.failureType = 'git_push_failed';
|
|
78
|
+
result.description = `Git push failed with exit code ${pushExit}`;
|
|
79
|
+
}
|
|
80
|
+
else if (prExit !== 0) {
|
|
81
|
+
result.failureType = 'pr_creation_failed';
|
|
82
|
+
result.description = `GitHub PR creation failed with exit code ${prExit}`;
|
|
83
|
+
// Include API error details
|
|
84
|
+
if (metadata.github_api_error_type || metadata.github_api_error_message) {
|
|
85
|
+
result.apiError = {
|
|
86
|
+
type: metadata.github_api_error_type ?? 'unknown',
|
|
87
|
+
message: metadata.github_api_error_message ?? '',
|
|
88
|
+
httpStatus: metadata.github_api_http_status ?? '',
|
|
89
|
+
};
|
|
90
|
+
result.description += ` (${result.apiError.type}: ${result.apiError.message})`;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
else if (pushExit === 0 && prExit === 0) {
|
|
94
|
+
result.failureType = 'post_github_ops_failure';
|
|
95
|
+
result.description =
|
|
96
|
+
'GitHub operations succeeded but overall exit code is non-zero (failure in cleanup/trap)';
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
result.failureType = 'unknown_failure_pattern';
|
|
100
|
+
result.description = `Unknown failure pattern: push_exit=${pushExit}, pr_exit=${prExit}, overall_exit=${result.exitCode}`;
|
|
101
|
+
}
|
|
102
|
+
// Collect logs
|
|
103
|
+
if (fs.existsSync(gitPushLogPath)) {
|
|
104
|
+
const content = fs.readFileSync(gitPushLogPath, 'utf8');
|
|
105
|
+
result.logs.gitPushLogTail = content.split('\n').slice(-30).filter((l) => l.trim());
|
|
106
|
+
}
|
|
107
|
+
if (fs.existsSync(lastCommandLogPath)) {
|
|
108
|
+
const content = fs.readFileSync(lastCommandLogPath, 'utf8');
|
|
109
|
+
result.logs.lastCommandLog = content.split('\n').filter((l) => l.trim());
|
|
110
|
+
}
|
|
111
|
+
if (fs.existsSync(healthCheckLogPath)) {
|
|
112
|
+
const content = fs.readFileSync(healthCheckLogPath, 'utf8');
|
|
113
|
+
result.logs.healthCheckLog = content.split('\n').filter((l) => l.trim());
|
|
114
|
+
}
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Format diagnostic result as human-readable markdown
|
|
119
|
+
*/
|
|
120
|
+
export function formatDiagnosticResult(result) {
|
|
121
|
+
let output = '# GitHub Operations Diagnostic\n\n';
|
|
122
|
+
output += `**Stage:** ${result.stage}\n`;
|
|
123
|
+
output += `**Exit code:** ${result.exitCode}\n`;
|
|
124
|
+
output += `**Failure detected:** ${result.hasFailed ? 'Yes' : 'No'}\n`;
|
|
125
|
+
output += `**Failure type:** ${result.failureType}\n\n`;
|
|
126
|
+
output += `## Summary\n\n${result.description}\n\n`;
|
|
127
|
+
if (result.apiError) {
|
|
128
|
+
output += '## API Error Details\n\n';
|
|
129
|
+
output += `- **Type:** ${result.apiError.type}\n`;
|
|
130
|
+
output += `- **Message:** ${result.apiError.message}\n`;
|
|
131
|
+
output += `- **HTTP Status:** ${result.apiError.httpStatus}\n\n`;
|
|
132
|
+
}
|
|
133
|
+
if (result.logs.gitPushLogTail && result.logs.gitPushLogTail.length > 0) {
|
|
134
|
+
output += '## Git Push Log (last 30 lines)\n\n```\n';
|
|
135
|
+
output += result.logs.gitPushLogTail.join('\n');
|
|
136
|
+
output += '\n```\n\n';
|
|
137
|
+
}
|
|
138
|
+
if (result.logs.lastCommandLog && result.logs.lastCommandLog.length > 0) {
|
|
139
|
+
output += '## Last Command Log\n\n```\n';
|
|
140
|
+
output += result.logs.lastCommandLog.join('\n');
|
|
141
|
+
output += '\n```\n\n';
|
|
142
|
+
}
|
|
143
|
+
if (result.logs.healthCheckLog && result.logs.healthCheckLog.length > 0) {
|
|
144
|
+
output += '## Health Check Log\n\n```\n';
|
|
145
|
+
output += result.logs.healthCheckLog.join('\n');
|
|
146
|
+
output += '\n```\n\n';
|
|
147
|
+
}
|
|
148
|
+
return output;
|
|
149
|
+
}
|
|
150
|
+
// CLI usage
|
|
151
|
+
if (require.main === module) {
|
|
152
|
+
const resultsDir = process.argv[2];
|
|
153
|
+
if (!resultsDir) {
|
|
154
|
+
console.error('Usage: npx ts-node github-operations-monitor.ts <results-directory>');
|
|
155
|
+
process.exit(1);
|
|
156
|
+
}
|
|
157
|
+
const result = analyzeGithubOperations(resultsDir);
|
|
158
|
+
const formatted = formatDiagnosticResult(result);
|
|
159
|
+
console.log(formatted);
|
|
160
|
+
process.exit(result.hasFailed && result.failureType === 'post_github_ops_failure' ? 1 : 0);
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=github-operations-monitor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-operations-monitor.js","sourceRoot":"","sources":["../src/github-operations-monitor.ts"],"names":[],"mappings":"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"}
|
package/kaseki-agent.sh
CHANGED
|
@@ -28,7 +28,7 @@ KASEKI_AGENT_GUARDRAILS="${KASEKI_AGENT_GUARDRAILS:-1}"
|
|
|
28
28
|
KASEKI_RESTORE_DISALLOWED_CHANGES="${KASEKI_RESTORE_DISALLOWED_CHANGES:-1}"
|
|
29
29
|
KASEKI_VALIDATION_FAIL_FAST="${KASEKI_VALIDATION_FAIL_FAST:-1}"
|
|
30
30
|
KASEKI_STRICT_SCRIPT_CHECK="${KASEKI_STRICT_SCRIPT_CHECK:-0}"
|
|
31
|
-
GITHUB_APP_ENABLED="${GITHUB_APP_ENABLED:-
|
|
31
|
+
GITHUB_APP_ENABLED="${GITHUB_APP_ENABLED:-1}"
|
|
32
32
|
KASEKI_PUBLISH_MODE="${KASEKI_PUBLISH_MODE:-auto}"
|
|
33
33
|
KASEKI_GITHUB_PR_RETRIES="${KASEKI_GITHUB_PR_RETRIES:-3}"
|
|
34
34
|
START_EPOCH="$(date +%s)"
|
|
@@ -84,6 +84,10 @@ REPO_MEMORY_FILE=""
|
|
|
84
84
|
REPO_MEMORY_STATUS="disabled"
|
|
85
85
|
REPO_MEMORY_COMMIT_SHA="unknown"
|
|
86
86
|
|
|
87
|
+
# Track last executed command for better error reporting
|
|
88
|
+
LAST_COMMAND=""
|
|
89
|
+
LAST_COMMAND_LOG="/results/last-command.log"
|
|
90
|
+
|
|
87
91
|
# Signal handler for graceful termination
|
|
88
92
|
handle_termination() {
|
|
89
93
|
local signal="$1"
|
|
@@ -99,6 +103,9 @@ handle_termination() {
|
|
|
99
103
|
trap 'handle_termination SIGTERM' SIGTERM
|
|
100
104
|
trap 'handle_termination SIGINT' SIGINT
|
|
101
105
|
|
|
106
|
+
# DEBUG trap: capture last command before execution for better error diagnostics
|
|
107
|
+
trap 'LAST_COMMAND="$BASH_COMMAND"' DEBUG
|
|
108
|
+
|
|
102
109
|
setup_host_logging_mirror() {
|
|
103
110
|
local base_name="$1"
|
|
104
111
|
local stamp host_log_file
|
|
@@ -160,6 +167,55 @@ case "$KASEKI_GIT_CACHE_MODE" in
|
|
|
160
167
|
;;
|
|
161
168
|
esac
|
|
162
169
|
|
|
170
|
+
# Helper function to run Node.js subprocesses with comprehensive error logging
|
|
171
|
+
# Usage: run_node_subprocess <output_var_name> "<node_code>" [<input_data>] [<error_log_file>]
|
|
172
|
+
# Captures stderr, logs errors, and returns exit code for caller to check
|
|
173
|
+
run_node_subprocess() {
|
|
174
|
+
local output_var_name="$1"
|
|
175
|
+
local node_code="$2"
|
|
176
|
+
local input_data="${3:-}"
|
|
177
|
+
local error_log_file="${4:-/tmp/node-error.log}"
|
|
178
|
+
local node_stderr_tmp node_exit_code output_value
|
|
179
|
+
|
|
180
|
+
node_stderr_tmp="$(mktemp /tmp/node-stderr.XXXXXX)" || {
|
|
181
|
+
printf 'ERROR: Failed to create temp file for Node.js stderr\n' >&2
|
|
182
|
+
eval "$output_var_name=''"
|
|
183
|
+
return 1
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
# Run Node.js and capture both stdout and stderr
|
|
187
|
+
if [ -n "$input_data" ]; then
|
|
188
|
+
output_value=$(printf '%s' "$input_data" | node -e "$node_code" 2>"$node_stderr_tmp")
|
|
189
|
+
else
|
|
190
|
+
output_value=$(node -e "$node_code" 2>"$node_stderr_tmp")
|
|
191
|
+
fi
|
|
192
|
+
node_exit_code=$?
|
|
193
|
+
|
|
194
|
+
# Handle errors
|
|
195
|
+
if [ $node_exit_code -ne 0 ]; then
|
|
196
|
+
local stderr_content
|
|
197
|
+
stderr_content="$(cat "$node_stderr_tmp" 2>/dev/null || echo '<unable to read stderr>')"
|
|
198
|
+
{
|
|
199
|
+
printf '[node-subprocess-error] Command failed with exit code %d\n' "$node_exit_code"
|
|
200
|
+
if [ -n "$stderr_content" ]; then
|
|
201
|
+
printf '[node-subprocess-error] stderr: %s\n' "$stderr_content"
|
|
202
|
+
fi
|
|
203
|
+
printf '[node-subprocess-error] code: %.200s\n' "$node_code"
|
|
204
|
+
if [ -n "$input_data" ]; then
|
|
205
|
+
printf '[node-subprocess-error] input (first 150 chars): %.150s\n' "$input_data"
|
|
206
|
+
fi
|
|
207
|
+
} | tee -a "$error_log_file" >&2
|
|
208
|
+
rm -f "$node_stderr_tmp"
|
|
209
|
+
eval "$output_var_name=''"
|
|
210
|
+
return "$node_exit_code"
|
|
211
|
+
fi
|
|
212
|
+
|
|
213
|
+
# Success: store output in variable and return 0
|
|
214
|
+
eval "$output_var_name='$output_value'"
|
|
215
|
+
rm -f "$node_stderr_tmp"
|
|
216
|
+
return 0
|
|
217
|
+
}
|
|
218
|
+
|
|
163
219
|
# Safely encode value as JSON string; fallback to empty string if node unavailable
|
|
164
220
|
json_encode() {
|
|
165
221
|
if ! command -v node &>/dev/null; then
|
|
@@ -712,8 +768,20 @@ check_secret_scan_allowlist() {
|
|
|
712
768
|
finish() {
|
|
713
769
|
local code=$?
|
|
714
770
|
if [ "$code" -ne 0 ] && [ "$STATUS" -eq 0 ]; then
|
|
771
|
+
# Capture diagnostic context for the catch-all error
|
|
715
772
|
STATUS="$code"
|
|
716
773
|
FAILED_COMMAND="unexpected shell failure"
|
|
774
|
+
# Log the last command that was executed
|
|
775
|
+
{
|
|
776
|
+
printf '[unexpected-failure] Exit code: %d\n' "$code"
|
|
777
|
+
printf '[unexpected-failure] Last command: %s\n' "$LAST_COMMAND"
|
|
778
|
+
printf '[unexpected-failure] Current stage: %s\n' "$CURRENT_STAGE"
|
|
779
|
+
if [ -f /results/progress.log ]; then
|
|
780
|
+
printf '[unexpected-failure] Last 5 progress entries:\n'
|
|
781
|
+
tail -5 /results/progress.log | sed 's/^/ /'
|
|
782
|
+
fi
|
|
783
|
+
} | tee -a "$LAST_COMMAND_LOG" >&2
|
|
784
|
+
emit_error_event "unexpected_shell_failure" "Uncaught shell error (exit $code) in stage '$CURRENT_STAGE'. Last command: $LAST_COMMAND. See $LAST_COMMAND_LOG for context." "exit"
|
|
717
785
|
fi
|
|
718
786
|
# Authoritative call site: this runs at EXIT so artifacts reflect final repo state.
|
|
719
787
|
collect_git_artifacts
|
|
@@ -1266,6 +1334,73 @@ $memory_section
|
|
|
1266
1334
|
EOF
|
|
1267
1335
|
}
|
|
1268
1336
|
|
|
1337
|
+
check_github_operations_health() {
|
|
1338
|
+
# Preflight health check for github operations before pi agent runs
|
|
1339
|
+
# Tests: GitHub App secrets, git config, Node.js token generation capability
|
|
1340
|
+
local health_log="/results/github-health-check.log"
|
|
1341
|
+
: > "$health_log"
|
|
1342
|
+
|
|
1343
|
+
printf '[preflight] github operations health check started\n' | tee -a "$health_log"
|
|
1344
|
+
|
|
1345
|
+
# Check 1: GitHub App secrets are readable
|
|
1346
|
+
if ! [ -r /run/secrets/github_app_id ]; then
|
|
1347
|
+
printf '[health-check] ERROR: Cannot read GitHub App ID from /run/secrets/github_app_id\n' | tee -a "$health_log" >&2
|
|
1348
|
+
return 1
|
|
1349
|
+
fi
|
|
1350
|
+
if ! [ -r /run/secrets/github_app_client_id ]; then
|
|
1351
|
+
printf '[health-check] ERROR: Cannot read GitHub App client ID from /run/secrets/github_app_client_id\n' | tee -a "$health_log" >&2
|
|
1352
|
+
return 1
|
|
1353
|
+
fi
|
|
1354
|
+
if ! [ -r /run/secrets/github_app_private_key ]; then
|
|
1355
|
+
printf '[health-check] ERROR: Cannot read GitHub App private key from /run/secrets/github_app_private_key\n' | tee -a "$health_log" >&2
|
|
1356
|
+
return 1
|
|
1357
|
+
fi
|
|
1358
|
+
printf '[health-check] ✓ GitHub App secrets are readable\n' | tee -a "$health_log"
|
|
1359
|
+
|
|
1360
|
+
# Check 2: Verify git is available
|
|
1361
|
+
if ! git --version >/dev/null 2>&1; then
|
|
1362
|
+
printf '[health-check] ERROR: git command is not available\n' | tee -a "$health_log" >&2
|
|
1363
|
+
return 1
|
|
1364
|
+
fi
|
|
1365
|
+
printf '[health-check] ✓ git is available\n' | tee -a "$health_log"
|
|
1366
|
+
|
|
1367
|
+
# Check 3: Test Node.js github-app-token helper script exists
|
|
1368
|
+
if ! [ -x /usr/local/bin/github-app-token ]; then
|
|
1369
|
+
printf '[health-check] ERROR: github-app-token helper not found at /usr/local/bin/github-app-token\n' | tee -a "$health_log" >&2
|
|
1370
|
+
return 1
|
|
1371
|
+
fi
|
|
1372
|
+
printf '[health-check] ✓ github-app-token helper is available\n' | tee -a "$health_log"
|
|
1373
|
+
|
|
1374
|
+
# Check 4: Test Node.js is available
|
|
1375
|
+
if ! command -v node >/dev/null 2>&1; then
|
|
1376
|
+
printf '[health-check] ERROR: Node.js is not available\n' | tee -a "$health_log" >&2
|
|
1377
|
+
return 1
|
|
1378
|
+
fi
|
|
1379
|
+
printf '[health-check] ✓ Node.js is available\n' | tee -a "$health_log"
|
|
1380
|
+
|
|
1381
|
+
# Check 5: Test Node.js JSON parsing
|
|
1382
|
+
local test_output
|
|
1383
|
+
test_output=$(printf '{"test":"value"}' | node -e "const d = JSON.parse(require('fs').readFileSync(0, 'utf8')); process.stdout.write(d.test);" 2>&1) || {
|
|
1384
|
+
printf '[health-check] ERROR: Node.js JSON parsing failed: %s\n' "$test_output" | tee -a "$health_log" >&2
|
|
1385
|
+
return 1
|
|
1386
|
+
}
|
|
1387
|
+
if [ "$test_output" != "value" ]; then
|
|
1388
|
+
printf '[health-check] ERROR: Node.js JSON parsing returned unexpected output: %s\n' "$test_output" | tee -a "$health_log" >&2
|
|
1389
|
+
return 1
|
|
1390
|
+
fi
|
|
1391
|
+
printf '[health-check] ✓ Node.js JSON parsing works\n' | tee -a "$health_log"
|
|
1392
|
+
|
|
1393
|
+
# Check 6: Test curl is available
|
|
1394
|
+
if ! command -v curl >/dev/null 2>&1; then
|
|
1395
|
+
printf '[health-check] ERROR: curl is not available\n' | tee -a "$health_log" >&2
|
|
1396
|
+
return 1
|
|
1397
|
+
fi
|
|
1398
|
+
printf '[health-check] ✓ curl is available\n' | tee -a "$health_log"
|
|
1399
|
+
|
|
1400
|
+
printf '[preflight] github operations health check PASSED\n' | tee -a "$health_log"
|
|
1401
|
+
return 0
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1269
1404
|
validate_github_api_response() {
|
|
1270
1405
|
local http_status response log_file error_type error_message
|
|
1271
1406
|
http_status="$1"
|
|
@@ -1393,13 +1528,19 @@ run_github_operations() {
|
|
|
1393
1528
|
return 7
|
|
1394
1529
|
}
|
|
1395
1530
|
|
|
1396
|
-
|
|
1397
|
-
if
|
|
1531
|
+
# Use helper to extract token from JSON response
|
|
1532
|
+
if ! run_node_subprocess token "const d = JSON.parse(require('fs').readFileSync(0, 'utf8')); process.stdout.write(d.token || '')" "$token_data" /results/git-push.log; then
|
|
1398
1533
|
printf -- 'Failed to extract token from response: %s\n' "$token_data" | tee -a /results/git-push.log >&2
|
|
1399
1534
|
GITHUB_PUSH_EXIT=7
|
|
1400
1535
|
return 7
|
|
1401
1536
|
fi
|
|
1402
1537
|
|
|
1538
|
+
if [ -z "$token" ]; then
|
|
1539
|
+
printf -- 'Failed to extract token from response (empty result)\n' | tee -a /results/git-push.log >&2
|
|
1540
|
+
GITHUB_PUSH_EXIT=7
|
|
1541
|
+
return 7
|
|
1542
|
+
fi
|
|
1543
|
+
|
|
1403
1544
|
printf 'Token generated successfully\n' | tee -a /results/git-push.log
|
|
1404
1545
|
|
|
1405
1546
|
# Create and push feature branch
|
|
@@ -1510,6 +1651,19 @@ EOF
|
|
|
1510
1651
|
printf 'Debug: Creating PR with head=%s, base=%s, draft=true\n' "$feature_branch" "$GIT_REF" | tee -a /results/git-push.log
|
|
1511
1652
|
fi
|
|
1512
1653
|
|
|
1654
|
+
# Encode PR title and body as JSON strings
|
|
1655
|
+
local pr_title_json pr_body_json
|
|
1656
|
+
if ! run_node_subprocess pr_title_json "console.log(JSON.stringify(require('fs').readFileSync(0, 'utf8')))" "$pr_title" /results/git-push.log; then
|
|
1657
|
+
printf 'ERROR: Failed to JSON encode PR title\n' | tee -a /results/git-push.log >&2
|
|
1658
|
+
GITHUB_PR_EXIT=8
|
|
1659
|
+
return 8
|
|
1660
|
+
fi
|
|
1661
|
+
if ! run_node_subprocess pr_body_json "console.log(JSON.stringify(require('fs').readFileSync(0, 'utf8')))" "$pr_body" /results/git-push.log; then
|
|
1662
|
+
printf 'ERROR: Failed to JSON encode PR body\n' | tee -a /results/git-push.log >&2
|
|
1663
|
+
GITHUB_PR_EXIT=8
|
|
1664
|
+
return 8
|
|
1665
|
+
fi
|
|
1666
|
+
|
|
1513
1667
|
# Use curl with -w to capture HTTP status separately
|
|
1514
1668
|
# curl exit code: 0=success, non-0=failure
|
|
1515
1669
|
local curl_exit
|
|
@@ -1517,7 +1671,7 @@ EOF
|
|
|
1517
1671
|
-H "Authorization: token $token" \
|
|
1518
1672
|
-H "Accept: application/vnd.github.v3+json" \
|
|
1519
1673
|
"https://api.github.com/repos/$owner/$repo/pulls" \
|
|
1520
|
-
-d "{\"title\": $
|
|
1674
|
+
-d "{\"title\": $pr_title_json, \"body\": $pr_body_json, \"head\": \"$feature_branch\", \"base\": \"$GIT_REF\", \"draft\": true}" > "$temp_status_file" 2>&1
|
|
1521
1675
|
curl_exit=$?
|
|
1522
1676
|
|
|
1523
1677
|
# Split response and status code
|
|
@@ -1552,8 +1706,13 @@ EOF
|
|
|
1552
1706
|
|
|
1553
1707
|
# Validate the API response
|
|
1554
1708
|
if validate_github_api_response "$pr_http_status" "$pr_response" /results/git-push.log; then
|
|
1555
|
-
# API returned success (201); now extract the URL
|
|
1556
|
-
|
|
1709
|
+
# API returned success (201); now extract the URL using helper
|
|
1710
|
+
if ! run_node_subprocess pr_url "const d = JSON.parse(require('fs').readFileSync(0, 'utf8')); process.stdout.write(d.html_url || '')" "$pr_response" /results/git-push.log; then
|
|
1711
|
+
printf 'ERROR: Failed to extract PR URL from API response\n' | tee -a /results/git-push.log >&2
|
|
1712
|
+
emit_error_event "github_pr_response_malformed" "Failed to parse PR API response to extract html_url" "exit"
|
|
1713
|
+
GITHUB_PR_EXIT=9
|
|
1714
|
+
pr_url=""
|
|
1715
|
+
fi
|
|
1557
1716
|
|
|
1558
1717
|
if [ -n "$pr_url" ]; then
|
|
1559
1718
|
GITHUB_PR_URL="$pr_url"
|
|
@@ -1612,6 +1771,17 @@ printf 'Provider: %s\n' "$KASEKI_PROVIDER"
|
|
|
1612
1771
|
printf 'Model: %s\n' "$KASEKI_MODEL"
|
|
1613
1772
|
printf 'Pi version: %s\n' "$PI_VERSION"
|
|
1614
1773
|
|
|
1774
|
+
# Run preflight health check for GitHub operations if enabled
|
|
1775
|
+
if [ "$GITHUB_APP_ENABLED" = "1" ]; then
|
|
1776
|
+
printf '\n==> github operations preflight health check\n'
|
|
1777
|
+
if ! check_github_operations_health; then
|
|
1778
|
+
printf 'ERROR: GitHub operations preflight health check failed\n' >&2
|
|
1779
|
+
printf 'GitHub App is enabled but configuration or dependencies are missing.\n' >&2
|
|
1780
|
+
printf 'Proceeding with kaseki run, but GitHub operations will be skipped or fail.\n' >&2
|
|
1781
|
+
emit_error_event "github_preflight_failed" "GitHub operations health check failed; check /results/github-health-check.log for details" "continue"
|
|
1782
|
+
fi
|
|
1783
|
+
fi
|
|
1784
|
+
|
|
1615
1785
|
openrouter_api_key=""
|
|
1616
1786
|
openrouter_api_key_source=""
|
|
1617
1787
|
if [ -n "${OPENROUTER_API_KEY:-}" ]; then
|
package/package.json
CHANGED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# kaseki-diagnose-github-failure.sh
|
|
3
|
+
# Diagnostic script to analyze github operations failures in kaseki run artifacts
|
|
4
|
+
# Usage: ./kaseki-diagnose-github-failure.sh /agents/kaseki-results/kaseki-N
|
|
5
|
+
|
|
6
|
+
set -eo pipefail
|
|
7
|
+
|
|
8
|
+
RESULTS_DIR="${1:-.}"
|
|
9
|
+
SCRIPT_NAME="$(basename "$0")"
|
|
10
|
+
|
|
11
|
+
if [ ! -d "$RESULTS_DIR" ]; then
|
|
12
|
+
printf 'ERROR: Results directory not found: %s\n' "$RESULTS_DIR" >&2
|
|
13
|
+
exit 1
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
if [ ! -f "$RESULTS_DIR/metadata.json" ] || [ ! -f "$RESULTS_DIR/failure.json" ]; then
|
|
17
|
+
printf 'ERROR: metadata.json or failure.json not found in %s\n' "$RESULTS_DIR" >&2
|
|
18
|
+
exit 1
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
# Helper to extract JSON values
|
|
22
|
+
json_value() {
|
|
23
|
+
local file="$1"
|
|
24
|
+
local key="$2"
|
|
25
|
+
grep "\"$key\"" "$file" | head -1 | sed 's/.*"'$key'":\s*"\?//; s/".*$//'
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
# Read key fields from metadata
|
|
29
|
+
INSTANCE="$(json_value "$RESULTS_DIR/metadata.json" "instance")"
|
|
30
|
+
STAGE="$(json_value "$RESULTS_DIR/metadata.json" "current_stage")"
|
|
31
|
+
EXIT_CODE="$(json_value "$RESULTS_DIR/metadata.json" "exit_code")"
|
|
32
|
+
GITHUB_PUSH_EXIT="$(json_value "$RESULTS_DIR/metadata.json" "github_push_exit_code")"
|
|
33
|
+
GITHUB_PR_EXIT="$(json_value "$RESULTS_DIR/metadata.json" "github_pr_exit_code")"
|
|
34
|
+
GITHUB_API_ERROR_TYPE="$(json_value "$RESULTS_DIR/metadata.json" "github_api_error_type")"
|
|
35
|
+
GITHUB_API_ERROR_MESSAGE="$(json_value "$RESULTS_DIR/metadata.json" "github_api_error_message")"
|
|
36
|
+
GITHUB_API_HTTP_STATUS="$(json_value "$RESULTS_DIR/metadata.json" "github_api_http_status")"
|
|
37
|
+
|
|
38
|
+
printf '# GitHub Operations Failure Diagnostic Report\n\n'
|
|
39
|
+
printf '**Instance:** %s\n' "$INSTANCE"
|
|
40
|
+
printf '**Results directory:** %s\n' "$RESULTS_DIR"
|
|
41
|
+
printf '**Overall exit code:** %s\n\n' "$EXIT_CODE"
|
|
42
|
+
|
|
43
|
+
# ===== Stage-level diagnosis =====
|
|
44
|
+
printf '## Stage Status\n\n'
|
|
45
|
+
if [ "$STAGE" = "github operations" ]; then
|
|
46
|
+
printf '- Current stage: github operations (failure occurred in this stage)\n'
|
|
47
|
+
else
|
|
48
|
+
printf '- Current stage: %s (failure occurred before github operations)\n' "$STAGE"
|
|
49
|
+
fi
|
|
50
|
+
printf '- GitHub push exit code: %s\n' "$GITHUB_PUSH_EXIT"
|
|
51
|
+
printf '- GitHub PR exit code: %s\n' "$GITHUB_PR_EXIT"
|
|
52
|
+
|
|
53
|
+
# ===== Detailed diagnosis =====
|
|
54
|
+
printf '\n## Diagnosis\n\n'
|
|
55
|
+
|
|
56
|
+
# Check if failure occurred in github operations stage
|
|
57
|
+
if [ "$STAGE" != "github operations" ]; then
|
|
58
|
+
printf '**Failure occurred before github operations stage.**\n'
|
|
59
|
+
printf '\nGithub operations was never attempted. Check earlier stages:\n'
|
|
60
|
+
if [ -f "$RESULTS_DIR/stage-timings.tsv" ]; then
|
|
61
|
+
printf '\n```\n'
|
|
62
|
+
cat "$RESULTS_DIR/stage-timings.tsv"
|
|
63
|
+
printf '```\n'
|
|
64
|
+
fi
|
|
65
|
+
exit 0
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
# Now we know stage is github operations
|
|
69
|
+
if [ "$GITHUB_PUSH_EXIT" -ne 0 ] && [ "$GITHUB_PR_EXIT" -eq 0 ]; then
|
|
70
|
+
printf '**Git push failed (exit code: %s)**\n\n' "$GITHUB_PUSH_EXIT"
|
|
71
|
+
printf 'The git push operation failed. Check git-push.log for details:\n'
|
|
72
|
+
if [ -f "$RESULTS_DIR/git-push.log" ]; then
|
|
73
|
+
printf '\n```\n'
|
|
74
|
+
tail -20 "$RESULTS_DIR/git-push.log"
|
|
75
|
+
printf '```\n'
|
|
76
|
+
fi
|
|
77
|
+
elif [ "$GITHUB_PR_EXIT" -ne 0 ]; then
|
|
78
|
+
printf '**GitHub PR creation failed (exit code: %s)**\n\n' "$GITHUB_PR_EXIT"
|
|
79
|
+
|
|
80
|
+
if [ -n "$GITHUB_API_ERROR_TYPE" ]; then
|
|
81
|
+
printf 'API error type: %s\n' "$GITHUB_API_ERROR_TYPE"
|
|
82
|
+
fi
|
|
83
|
+
if [ -n "$GITHUB_API_ERROR_MESSAGE" ]; then
|
|
84
|
+
printf 'API error message: %s\n' "$GITHUB_API_ERROR_MESSAGE"
|
|
85
|
+
fi
|
|
86
|
+
if [ -n "$GITHUB_API_HTTP_STATUS" ]; then
|
|
87
|
+
printf 'HTTP status: %s\n' "$GITHUB_API_HTTP_STATUS"
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
printf '\nAPI error details from git-push.log:\n'
|
|
91
|
+
if [ -f "$RESULTS_DIR/git-push.log" ]; then
|
|
92
|
+
printf '\n```\n'
|
|
93
|
+
tail -30 "$RESULTS_DIR/git-push.log"
|
|
94
|
+
printf '```\n'
|
|
95
|
+
fi
|
|
96
|
+
elif [ "$GITHUB_PUSH_EXIT" -eq 0 ] && [ "$GITHUB_PR_EXIT" -eq 0 ]; then
|
|
97
|
+
printf '**GitHub operations completed successfully, but overall exit code is non-zero.**\n\n'
|
|
98
|
+
printf 'This suggests the failure occurred in the finish() trap handler or post-github-ops cleanup.\n'
|
|
99
|
+
printf 'Check the following:\n\n'
|
|
100
|
+
|
|
101
|
+
if [ -f "$RESULTS_DIR/last-command.log" ]; then
|
|
102
|
+
printf '- Last command log exists at: %s\n' "$RESULTS_DIR/last-command.log"
|
|
103
|
+
printf '\n```\n'
|
|
104
|
+
cat "$RESULTS_DIR/last-command.log"
|
|
105
|
+
printf '```\n'
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
if [ -f "$RESULTS_DIR/github-health-check.log" ]; then
|
|
109
|
+
printf '\n- GitHub health check log:\n\n```\n'
|
|
110
|
+
cat "$RESULTS_DIR/github-health-check.log"
|
|
111
|
+
printf '```\n'
|
|
112
|
+
fi
|
|
113
|
+
|
|
114
|
+
if [ -f "$RESULTS_DIR/restoration-errors.log" ]; then
|
|
115
|
+
printf '\n- Restoration errors:\n\n```\n'
|
|
116
|
+
cat "$RESULTS_DIR/restoration-errors.log"
|
|
117
|
+
printf '```\n'
|
|
118
|
+
fi
|
|
119
|
+
else
|
|
120
|
+
printf '**Unknown failure pattern.**\n\n'
|
|
121
|
+
printf 'Push exit: %s, PR exit: %s\n' "$GITHUB_PUSH_EXIT" "$GITHUB_PR_EXIT"
|
|
122
|
+
fi
|
|
123
|
+
|
|
124
|
+
# ===== Check for subprocess errors =====
|
|
125
|
+
printf '\n## Node.js Subprocess Errors\n\n'
|
|
126
|
+
if [ -f "$RESULTS_DIR/git-push.log" ] && grep -q '\[node-subprocess-error\]' "$RESULTS_DIR/git-push.log"; then
|
|
127
|
+
printf 'Node.js subprocess errors detected:\n\n'
|
|
128
|
+
printf '```\n'
|
|
129
|
+
grep '\[node-subprocess-error\]' "$RESULTS_DIR/git-push.log" | head -20
|
|
130
|
+
printf '```\n'
|
|
131
|
+
else
|
|
132
|
+
printf 'No Node.js subprocess errors detected.\n'
|
|
133
|
+
fi
|
|
134
|
+
|
|
135
|
+
# ===== Helpful references =====
|
|
136
|
+
printf '\n## Helpful Resources\n\n'
|
|
137
|
+
printf '- **Exit codes reference:** See docs/EXIT_CODES.md\n'
|
|
138
|
+
printf '- **Git-push.log:** Full GitHub operations details at %s/git-push.log\n' "$RESULTS_DIR"
|
|
139
|
+
printf '- **Metadata:** Complete run metadata at %s/metadata.json\n' "$RESULTS_DIR"
|
|
140
|
+
printf '- **Failure details:** See %s/failure.json\n' "$RESULTS_DIR"
|
|
141
|
+
|
|
142
|
+
printf '\n---\n\n'
|
|
143
|
+
printf '**Generated by %s**\n' "$SCRIPT_NAME"
|