diff --git a/Makefile b/Makefile index 3fb13b2..285526d 100644 --- a/Makefile +++ b/Makefile @@ -242,4 +242,41 @@ test-info: @echo "" @echo "🎯 Key Test Commands:" @echo " make test - Run all tests" - @echo " make validate-yaml - Validate all YAML files" \ No newline at end of file + @echo " make validate-yaml - Validate all YAML files" +# ============================================================================ +# CODE EXECUTOR API TESTING +# ============================================================================ + +# Test artifact retrieval - compile C code, get base64 binary, decode and test execution +# URL can be overridden: make test-artifact URL=https://code.ai.unturf.com +.PHONY: test-artifact +test-artifact: + $(eval URL ?= http://127.0.0.1:8080) + @echo "==========================================" + @echo "Testing Binary Artifact Retrieval" + @echo "==========================================" + @echo "API: $(URL)" + @echo "" + @echo "Step 1: Compiling C code and retrieving base64 binary..." + @curl -s -X POST $(URL)/execute \ + -H "Content-Type: application/json" \ + -d '{"language": "c", "code": "#include \nint main() { printf(\"Hello from artifact!\\n\"); return 0; }", "return_artifact": true}' \ + | jq -r '.stdout.artifact.data' > /tmp/artifact.b64 + @echo "✓ Base64 artifact saved to /tmp/artifact.b64" + @echo " Size: $$(wc -c < /tmp/artifact.b64) bytes (base64)" + @echo "" + @echo "Step 2: Decoding base64 to binary..." + @base64 -d /tmp/artifact.b64 > /tmp/artifact_binary + @chmod +x /tmp/artifact_binary + @echo "✓ Binary decoded to /tmp/artifact_binary" + @echo " Size: $$(wc -c < /tmp/artifact_binary) bytes (ELF binary)" + @echo "" + @echo "Step 3: Verifying ELF binary..." + @file /tmp/artifact_binary + @echo "" + @echo "Step 4: Executing binary..." + @/tmp/artifact_binary + @echo "" + @echo "✓ Artifact test complete!" + @echo "" + @echo "Cleanup: rm /tmp/artifact.b64 /tmp/artifact_binary" diff --git a/templates/chat.html b/templates/chat.html index 13a6eb2..b2b32a7 100644 --- a/templates/chat.html +++ b/templates/chat.html @@ -1492,16 +1492,50 @@ async function executeCodeBlock(code, blockElement, playButton) { break; } - // timeout or cancelled + // timeout or cancelled - display results and artifact if available const errorMsg = job.result?.error || 'Execution failed'; const partialOutput = job.result?.partial_output; - let outputHtml = `
${escapeHtml(errorMsg)}
`; - if (partialOutput) { - outputHtml += '
Partial output before timeout:
'; - outputHtml += `
${escapeHtml(partialOutput)}
`; + // CONFIRMED via testing (make test-artifact): Executor service does NOT + // include artifact field in GET /jobs/{id} response for cancelled jobs + // (exit_code 137 = SIGKILL), even when return_artifact=true was requested. + // Artifacts only included for fully completed jobs (exit_code 0). + // + // The code below checks for artifact anyway in case this gets fixed in the + // future, but currently it will always be undefined for cancelled/timeout. + const artifact = job.artifact || job.result?.artifact; + + // Build result object that displayExecutionResults can understand + // For timeout/cancelled, partial_output contains the output before timeout + if (job.result) { + const resultForDisplay = { + stdout: partialOutput || job.result.stdout || '', + stderr: job.result.stderr || '', + artifact: artifact + }; + displayExecutionResults(resultForDisplay, resultsContainer, language); + + // Prepend error message to the results + const errorDiv = document.createElement('div'); + errorDiv.style.color = 'var(--text-error)'; + errorDiv.style.fontWeight = 'bold'; + errorDiv.style.marginBottom = '8px'; + errorDiv.textContent = errorMsg; + resultsContainer.insertBefore(errorDiv, resultsContainer.firstChild); + + // Add note if there was partial output + if (partialOutput) { + const partialNote = document.createElement('div'); + partialNote.style.color = 'var(--text-muted)'; + partialNote.style.fontSize = '12px'; + partialNote.style.marginBottom = '8px'; + partialNote.textContent = '(Output before timeout/cancellation)'; + resultsContainer.insertBefore(partialNote, resultsContainer.children[1]); + } + } else { + // No result object at all, just show error + resultsContainer.innerHTML = `
${escapeHtml(errorMsg)}
`; } - resultsContainer.innerHTML = outputHtml; break; }