Files
self-healing-autopoietic-shell/index.html
T

251 lines
8.4 KiB
HTML
Raw Normal View History

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Self-Healing Autopoietic Shell</title>
<style>
:root{
--bg:#0b0d10; --panel:#12151a; --ink:#e6e9ef; --dim:#8b93a1;
--accent:#7c9eff; --ok:#5fd88f; --warn:#f2b64b; --err:#ff6b6b;
--border:#232833;
}
*{box-sizing:border-box;}
body{
margin:0; background:var(--bg); color:var(--ink);
font:15px/1.55 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
header{
padding:2.5rem 1.5rem 1.25rem; max-width:900px; margin:0 auto;
}
h1{font-size:1.4rem; margin:0 0 .4rem; font-weight:600;}
header p{color:var(--dim); margin:.2rem 0; font-size:.92rem;}
main{max-width:900px; margin:0 auto; padding:0 1.5rem 3rem; display:grid; gap:1.25rem; grid-template-columns:1fr; }
@media (min-width:760px){ main{grid-template-columns: 1.3fr .9fr;} }
.panel{
background:var(--panel); border:1px solid var(--border); border-radius:10px;
overflow:hidden;
}
.panel h2{
font-size:.78rem; text-transform:uppercase; letter-spacing:.06em; color:var(--dim);
margin:0; padding:.7rem .9rem; border-bottom:1px solid var(--border);
}
#term{
padding:.9rem; height:360px; overflow-y:auto; white-space:pre-wrap; word-break:break-word;
font-size:.88rem;
}
#term .line{margin:0 0 .15rem;}
.prompt{color:var(--accent);}
.learn{color:var(--warn);}
.ok{color:var(--ok);}
.err{color:var(--err);}
.dim{color:var(--dim);}
form{display:flex; border-top:1px solid var(--border);}
form span{padding:.6rem .9rem; color:var(--accent);}
input{
flex:1; background:transparent; border:0; color:var(--ink); font:inherit;
padding:.6rem .9rem .6rem 0; outline:none;
}
#pfile{padding:.9rem; min-height:120px; font-size:.85rem;}
#pfile .row{color:var(--ok); margin:0 0 .3rem;}
#pfile .empty{color:var(--dim);}
.try{padding:.9rem; border-top:1px solid var(--border); display:flex; flex-wrap:wrap; gap:.4rem;}
.try button{
background:#171b22; border:1px solid var(--border); color:var(--ink);
font:inherit; font-size:.78rem; padding:.35rem .6rem; border-radius:6px; cursor:pointer;
}
.try button:hover{border-color:var(--accent); color:var(--accent);}
code{background:#171b22; padding:.1rem .35rem; border-radius:4px; font-size:.85em;}
pre{
background:#171b22; padding:.9rem; border-radius:8px; overflow-x:auto;
font-size:.82rem; border:1px solid var(--border); margin:.8rem 1.5rem 0; max-width:900px;
}
footer{max-width:900px; margin:0 auto; padding:0 1.5rem 3rem; color:var(--dim); font-size:.82rem;}
footer a{color:var(--accent);}
.reset{color:var(--dim); font-size:.78rem; background:none; border:0; cursor:pointer; text-decoration:underline; padding:0;}
</style>
</head>
<body>
<header>
<h1>🔁 Self-Healing Autopoietic Shell</h1>
<p>A shell function that learns from its own failures — run a command, teach it a fix when it fails, and it never fails that way again.</p>
<p class="dim">This is a live simulation of <code>f()</code> running entirely in your browser. Try a command below.</p>
</header>
<main>
<div class="panel">
<h2>Simulated shell</h2>
<div id="term"></div>
<form id="form">
<span>$ f</span>
<input id="cmdInput" autocomplete="off" placeholder="cat needs-fix.txt" autofocus>
</form>
<div class="try">
<button data-cmd="cat needs-fix.txt">cat needs-fix.txt</button>
<button data-cmd="curl api.example.com">curl api.example.com</button>
<button data-cmd="echo already-fine">echo already-fine</button>
</div>
</div>
<div class="panel">
<h2>p &nbsp;<span class="dim" style="text-transform:none;">(learned fixes)</span></h2>
<div id="pfile"><div class="empty">// empty — nothing learned yet</div></div>
<div class="try">
<button class="reset" id="resetBtn">reset memory</button>
</div>
</div>
</main>
<pre>f() {
FIX=""
if [ -f p ]; then
while IFS= read -r line; do
case "$line" in
"$*="*) FIX="${line#"$*="}" ;;
esac
done < p
fi
until { eval "$FIX" 2>/dev/null; "$@"; }; do
printf '\n[LEARN] "%s" failed. Enter a fix (blank to give up): ' "$*" >&2
read -r n
[ -n "$n" ] || return 1
echo "$*=$n" >> p
FIX="$n"
done
}</pre>
<footer>
Real source, tests, and design notes are in the repo alongside this page —
see <code>src/f.sh</code>, <code>test/test_f.sh</code>, and <code>docs/DESIGN.md</code>.
</footer>
<script>
(function(){
const term = document.getElementById('term');
const form = document.getElementById('form');
const input = document.getElementById('cmdInput');
const pfileEl = document.getElementById('pfile');
const resetBtn = document.getElementById('resetBtn');
// "p" — the learned-fixes store. Simulated filesystem state lives here.
let p = {}; // command -> fix
let fs = {}; // simulated tiny filesystem for the demo commands
let awaitingFix = null; // command string we're currently learning a fix for
function print(text, cls){
const div = document.createElement('div');
div.className = 'line' + (cls ? ' ' + cls : '');
div.textContent = text;
term.appendChild(div);
term.scrollTop = term.scrollHeight;
}
function renderP(){
const keys = Object.keys(p);
if(!keys.length){
pfileEl.innerHTML = '<div class="empty">// empty — nothing learned yet</div>';
return;
}
pfileEl.innerHTML = keys.map(k =>
`<div class="row">${escapeHtml(k)}=${escapeHtml(p[k])}</div>`
).join('');
}
function escapeHtml(s){
return s.replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
}
// Applies a learned fix (or a freshly supplied one) to the tiny simulated fs/state.
function applyFix(fix){
const m = fix.match(/^echo\s+(.*?)\s*>\s*(\S+)$/);
if(m){
let val = m[1].trim();
if((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))){
val = val.slice(1,-1);
}
fs[m[2]] = val;
return;
}
if(/^(export\s+)?API_KEY=/.test(fix)){ fs['__api_key'] = true; return; }
}
// Runs one of the demo commands against simulated state. Returns {ok, out}.
function runCommand(cmd){
if(cmd === 'cat needs-fix.txt'){
if(fs['needs-fix.txt'] !== undefined) return {ok:true, out: fs['needs-fix.txt']};
return {ok:false, out: 'cat: needs-fix.txt: No such file or directory'};
}
if(cmd === 'curl api.example.com'){
if(fs['__api_key']) return {ok:true, out: '{"status":"ok"}'};
return {ok:false, out: 'curl: (401) Unauthorized — missing API_KEY'};
}
if(cmd.startsWith('echo ')){
return {ok:true, out: cmd.slice(5)};
}
return {ok:false, out: cmd + ': command not found'};
}
function tryCommand(cmd){
if(p[cmd]) applyFix(p[cmd]);
const res = runCommand(cmd);
if(res.ok){
print(res.out, 'ok');
return;
}
print(res.out, 'err');
print(`[LEARN] "${cmd}" failed. Enter a fix (blank to give up):`, 'learn');
awaitingFix = cmd;
}
form.addEventListener('submit', function(e){
e.preventDefault();
const val = input.value;
input.value = '';
if(awaitingFix){
print('> ' + (val || '(blank)'), 'dim');
if(!val.trim()){
print(`f: giving up on "${awaitingFix}"`, 'err');
awaitingFix = null;
return;
}
p[awaitingFix] = val.trim();
renderP();
applyFix(val.trim());
const cmd = awaitingFix;
awaitingFix = null;
const res = runCommand(cmd);
print(res.ok ? res.out : res.out, res.ok ? 'ok' : 'err');
if(!res.ok){
print(`[LEARN] "${cmd}" failed. Enter a fix (blank to give up):`, 'learn');
awaitingFix = cmd;
}
return;
}
if(!val.trim()) return;
print('$ f ' + val, 'prompt');
tryCommand(val.trim());
});
document.querySelectorAll('.try button[data-cmd]').forEach(btn => {
btn.addEventListener('click', () => {
input.value = btn.dataset.cmd;
input.focus();
});
});
resetBtn.addEventListener('click', () => {
p = {}; fs = {}; awaitingFix = null;
renderP();
term.innerHTML = '';
print('// memory reset — p is empty again', 'dim');
});
print('// try: cat needs-fix.txt', 'dim');
print('// it will fail once — teach it a fix like: echo hello > needs-fix.txt', 'dim');
})();
</script>
</body>
</html>