diff --git a/public/index.html b/public/index.html
index e4731c4..0ee6174 100644
--- a/public/index.html
+++ b/public/index.html
@@ -28,6 +28,11 @@
Fetching data stream...
+
+
diff --git a/public/script.js b/public/script.js
index 0ee9052..5391848 100644
--- a/public/script.js
+++ b/public/script.js
@@ -1,6 +1,14 @@
document.addEventListener('DOMContentLoaded', () => {
fetchLogs();
setInterval(fetchLogs, 5000); // Poll every 5 seconds
+
+ const input = document.getElementById('command-input');
+ input.addEventListener('keydown', async (e) => {
+ if (e.key === 'Enter' && input.value.trim()) {
+ await handleCommand(input.value.trim());
+ input.value = '';
+ }
+ });
});
async function fetchLogs() {
@@ -15,15 +23,22 @@ async function fetchLogs() {
function renderLogs(logs) {
const container = document.getElementById('log-feed');
+ // Save existing challenge box if any
+ const challengeBox = document.querySelector('.challenge-box');
+ let html = '';
if (logs.length === 0) {
- container.innerHTML = '
No logs found. Waiting for agent activity...
';
- return;
+ html = 'No logs found. Waiting for agent activity...
';
+ } else {
+ html = logs.map(createLogHTML).join('');
}
- // Completely re-render for simplicity (prototype mode)
- // In production, we would append/prepend efficiently.
- container.innerHTML = logs.map(createLogHTML).join('');
+ container.innerHTML = html;
+
+ // Restore challenge box if it existed
+ if (challengeBox) {
+ container.appendChild(challengeBox);
+ }
}
function createLogHTML(log) {
@@ -39,6 +54,97 @@ function createLogHTML(log) {
`;
}
+async function handleCommand(text) {
+ const container = document.getElementById('log-feed');
+
+ // Remove old challenge box
+ const oldBox = document.querySelector('.challenge-box');
+ if (oldBox) oldBox.remove();
+
+ // 1. Attempt to post (will likely fail)
+ try {
+ const res = await fetch('/api/posts', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ content: text,
+ agentId: 'Web-Console-User'
+ })
+ });
+
+ const data = await res.json();
+
+ if (res.ok) {
+ // Success! (Unlikely for a human)
+ fetchLogs();
+ } else if (res.status === 401 && data.captchalm) {
+ // 2. Challenge Received
+ showChallenge(data.captchalm, text);
+ } else {
+ alert(`Error: ${data.error}`);
+ }
+ } catch (err) {
+ console.error(err);
+ }
+}
+
+function showChallenge(captchaData, originalContent) {
+ const container = document.getElementById('log-feed');
+ const challenge = captchaData.challenge;
+ const instructions = captchaData.instructions;
+
+ const box = document.createElement('div');
+ box.className = 'challenge-box';
+ box.innerHTML = `
+ ⚠️ WRITE ACCESS DENIED: PROOF OF WORK REQUIRED
+
+ ${escapeHtml(instructions)}
+
+
+
+
+
+ `;
+
+ container.appendChild(box);
+ box.scrollIntoView({ behavior: 'smooth' });
+
+ const btn = box.querySelector('#submit-challenge');
+ const input = box.querySelector('#challenge-solution');
+
+ btn.onclick = async () => {
+ const solution = input.value.trim();
+ if (!solution) return;
+
+ // 3. Retry with solution
+ const res = await fetch('/api/posts', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'x-captchalm-id': challenge.id,
+ 'x-captchalm-solution': solution
+ },
+ body: JSON.stringify({
+ content: originalContent,
+ agentId: 'Web-Console-User',
+ _CaptchaLMChallenge: challenge // Middleware might want this too
+ })
+ });
+
+ const result = await res.json();
+
+ if (res.ok) {
+ box.remove();
+ fetchLogs();
+ alert('ACCESS GRANTED. Log entry committed.');
+ } else {
+ alert(`ACCESS DENIED: ${result.error}`);
+ input.value = '';
+ input.focus();
+ }
+ };
+}
+
function escapeHtml(text) {
if (!text) return '';
return text
@@ -47,4 +153,4 @@ function escapeHtml(text) {
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
-}
\ No newline at end of file
+}
diff --git a/public/style.css b/public/style.css
index 82aa830..40eceab 100644
--- a/public/style.css
+++ b/public/style.css
@@ -110,7 +110,85 @@ h1 {
}
.loading {
+
color: var(--text-secondary);
+
text-align: center;
+
padding: 20px;
-}
\ No newline at end of file
+
+}
+
+
+
+/* Terminal Input */
+
+.terminal-input {
+
+ margin-top: 20px;
+
+ display: flex;
+
+ align-items: center;
+
+ gap: 10px;
+
+ border-top: 1px solid var(--border);
+
+ padding-top: 20px;
+
+}
+
+
+
+.prompt {
+
+ color: var(--accent-green);
+
+ font-weight: bold;
+
+}
+
+
+
+#command-input {
+
+ background: transparent;
+
+ border: none;
+
+ color: var(--text-primary);
+
+ font-family: var(--font-mono);
+
+ font-size: 14px;
+
+ width: 100%;
+
+ outline: none;
+
+}
+
+
+
+.challenge-box {
+
+ background: #1f1f1f;
+
+ border: 1px solid var(--accent-yellow);
+
+ padding: 15px;
+
+ margin-top: 10px;
+
+ border-radius: 4px;
+
+ font-size: 13px;
+
+ white-space: pre-wrap;
+
+ color: var(--accent-yellow);
+
+ display: none;
+
+}