// This is free software for the public good of a permacomputer hosted at // permacomputer.com, an always-on computer by the people, for the people. // One which is durable, easy to repair, & distributed like tap water // for machine learning intelligence. // // The permacomputer is community-owned infrastructure optimized around // four values: // // TRUTH First principles, math & science, open source code freely distributed // FREEDOM Voluntary partnerships, freedom from tyranny & corporate control // HARMONY Minimal waste, self-renewing systems with diverse thriving connections // LOVE Be yourself without hurting others, cooperation through natural law // // This software contributes to that vision by making machine learning // accessible to everyone through a free, open, embeddable chat interface. // Code is seeds to sprout on any abandoned technology. import { CODE_EXEC_URL } from "./config.js"; import * as un from "./un.js"; // Helper function to sleep/wait function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } // Helper function to escape HTML for safe display export function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } // Function to execute code in sandbox export async function executeCode(code, language, resultsContainer, playButton) { // Update button state playButton.textContent = 'Running...'; playButton.disabled = true; // Clear previous results resultsContainer.innerHTML = '
Executing code...
'; try { // If no language specified, default to Python if (!language) { language = 'python'; } // Check if un.com API is configured if (un.isConfigured()) { await executeWithUnsandbox(code, language, resultsContainer); } else { await executeWithFallback(code, language, resultsContainer); } } catch (error) { console.error('Error executing code:', error); resultsContainer.innerHTML = `
Error: ${escapeHtml(error.message)}
`; } finally { // Reset button state playButton.textContent = '▶ Run'; playButton.disabled = false; } } // Execute using un.com SDK (authenticated) async function executeWithUnsandbox(code, language, resultsContainer) { let pollCount = 0; let cancelButton = null; let currentJobId = null; try { // Start async execution to get job_id for cancel button currentJobId = await un.executeAsync(language, code); // Create cancel button (shown after 3 seconds) cancelButton = createCancelButton(resultsContainer, async () => { if (currentJobId) { try { await un.cancelJob(currentJobId); cancelButton.disabled = true; cancelButton.textContent = 'Cancelling...'; } catch (error) { console.error('Error cancelling job:', error); } } }); // Wait for job with progress callback const result = await un.waitForJob(currentJobId, null, null, (count) => { pollCount = count; // Show cancel button after poll #5 (around 3 seconds) if (count >= 5 && cancelButton) { cancelButton.style.display = 'inline-block'; } }); // Hide cancel button if (cancelButton) { cancelButton.style.display = 'none'; } // Display result if (result.status === 'completed') { displayExecutionResults(result, resultsContainer, language); } else { displayErrorResult(result, resultsContainer); } } catch (error) { if (cancelButton) { cancelButton.style.display = 'none'; } throw error; } } // Execute using fallback endpoint (unauthenticated) async function executeWithFallback(code, language, resultsContainer) { // Use /execute/async endpoint with polling const asyncResponse = await fetch(`${CODE_EXEC_URL}/execute/async`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ language: language, code: code }) }); if (!asyncResponse.ok) { throw new Error(`HTTP error! status: ${asyncResponse.status}`); } const { job_id } = await asyncResponse.json(); // Poll for results using same delays as SDK const delays = [300, 450, 700, 900, 650, 1600, 2000]; let pollCount = 0; // Create cancel button const cancelButton = createCancelButton(resultsContainer, async () => { try { await fetch(`${CODE_EXEC_URL}/jobs/${job_id}`, { method: 'DELETE' }); cancelButton.disabled = true; cancelButton.textContent = 'Cancelling...'; } catch (error) { console.error('Error cancelling job:', error); } }); while (true) { await sleep(delays[Math.min(pollCount, delays.length - 1)]); pollCount++; const jobResponse = await fetch(`${CODE_EXEC_URL}/jobs/${job_id}`); if (!jobResponse.ok) { throw new Error(`Failed to fetch job status: ${jobResponse.status}`); } const job = await jobResponse.json(); if (job.status !== 'pending' && job.status !== 'running') { // Job finished - hide cancel button cancelButton.style.display = 'none'; if (job.status === 'completed') { const result = job.result || job; displayExecutionResults(result, resultsContainer, language); } else { displayErrorResult(job.result || job, resultsContainer); } break; } // Show cancel button after poll #5 (around 3 seconds) if (pollCount === 5) { cancelButton.style.display = 'inline-block'; } } } // Create cancel button helper function createCancelButton(resultsContainer, onCancel) { let cancelButton = resultsContainer.querySelector('.uncloseai-cancel-execution-btn'); if (!cancelButton) { cancelButton = document.createElement('button'); cancelButton.textContent = 'Cancel'; cancelButton.className = 'uncloseai-cancel-execution-btn uncloseai-btn-small'; cancelButton.style.display = 'none'; cancelButton.style.marginTop = '8px'; cancelButton.onclick = onCancel; resultsContainer.appendChild(cancelButton); } else { cancelButton.disabled = false; cancelButton.textContent = 'Cancel'; cancelButton.style.display = 'none'; cancelButton.onclick = onCancel; } return cancelButton; } // Display error result function displayErrorResult(result, resultsContainer) { const errorMsg = result.error || result.stderr || result.message || `Execution ${result.status || 'failed'}`; const partialOutput = result.stdout || result.partial_output; let outputHtml = `
${escapeHtml(errorMsg)}
`; if (partialOutput) { outputHtml += '
Partial output:
'; outputHtml += `
${escapeHtml(partialOutput)}
`; } resultsContainer.innerHTML = outputHtml; } // Helper function to display execution results export function displayExecutionResults(result, resultsContainer, language) { // Format and display results let outputHtml = ''; // Handle nested response structure - check if stdout is an object with nested data let actualStdout = result.stdout; let actualStderr = result.stderr; // If stdout is an object (nested response), extract the actual stdout/stderr if (typeof result.stdout === 'object' && result.stdout !== null) { actualStdout = result.stdout.stdout || ''; actualStderr = result.stdout.stderr || ''; } // Show language if (language) { outputHtml += `
Language: ${language}
`; } // Show stdout if (actualStdout) { outputHtml += '
Output:
'; outputHtml += `
${escapeHtml(actualStdout)}
`; } // Show stderr if present if (actualStderr) { outputHtml += '
Errors/Warnings:
'; outputHtml += `
${escapeHtml(actualStderr)}
`; } // If no output at all if (!actualStdout && !actualStderr) { outputHtml += '
(No output produced)
'; } resultsContainer.innerHTML = outputHtml; } // Function to add code execution buttons to code blocks export function addCodeExecutionButton(codeBlock) { // Check if we already have an execution button const pre = codeBlock.parentElement; if (!pre || pre.tagName.toLowerCase() !== 'pre') return; if (pre.querySelector('.uncloseai-run-code-btn')) return; // Already has button // Try to detect language from the code block's class let language = null; const classes = codeBlock.className.split(' '); for (const cls of classes) { if (cls.startsWith('language-')) { language = cls.replace('language-', ''); break; } } // Default to Python if no language specified if (!language) { language = 'python'; } // Get the code content const code = codeBlock.dataset.fullContent || codeBlock.textContent; // Create run button const runButton = document.createElement('button'); runButton.textContent = '▶ Run'; runButton.className = 'uncloseai-run-code-btn uncloseai-code-copy-btn'; // Reuse copy button styling runButton.title = `Run ${language} code in sandbox`; // Create results container let resultsContainer = pre.parentElement.querySelector('.uncloseai-code-execution-results'); if (!resultsContainer) { resultsContainer = document.createElement('div'); resultsContainer.className = 'uncloseai-code-execution-results'; resultsContainer.style.marginTop = '10px'; resultsContainer.style.padding = '10px'; resultsContainer.style.backgroundColor = 'var(--uncloseai-modal-background)'; resultsContainer.style.borderRadius = '5px'; resultsContainer.style.fontFamily = 'monospace'; resultsContainer.style.fontSize = '14px'; resultsContainer.style.whiteSpace = 'pre-wrap'; resultsContainer.style.wordWrap = 'break-word'; resultsContainer.style.display = 'none'; // Hidden initially // Insert after the pre element pre.parentElement.insertBefore(resultsContainer, pre.nextSibling); } runButton.onclick = async () => { // Show results container resultsContainer.style.display = 'block'; await executeCode(code, language, resultsContainer, runButton); }; // Find existing copy button container or create one let buttonContainer = pre.querySelector('.uncloseai-code-copy-btn')?.parentElement; if (!buttonContainer) { // Create button container if it doesn't exist buttonContainer = document.createElement('div'); buttonContainer.style.position = 'absolute'; buttonContainer.style.top = '8px'; buttonContainer.style.right = '8px'; buttonContainer.style.display = 'flex'; buttonContainer.style.gap = '4px'; pre.style.position = 'relative'; pre.appendChild(buttonContainer); } // Add run button to container buttonContainer.appendChild(runButton); }