// 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);
}