Skip to content

Commit 7482607

Browse files
VicVic
authored andcommitted
pyob-bot+action
1 parent ccf52ea commit 7482607

4 files changed

Lines changed: 176 additions & 123 deletions

File tree

.github/workflows/pyob_service.yml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: PyOB Autonomous Service
2+
3+
on:
4+
schedule:
5+
- cron: '0 * * * *' # Runs every hour
6+
workflow_dispatch: # Allows you to start it manually from your phone
7+
8+
jobs:
9+
evolve:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- name: Checkout Code
13+
uses: actions/checkout@v4
14+
with:
15+
token: ${{ secrets.BOT_GITHUB_TOKEN }}
16+
17+
- name: Set up Python
18+
uses: actions/setup-python@v5
19+
with:
20+
python-version: "3.12"
21+
22+
- name: Install PyOB
23+
run: |
24+
pip install -e .
25+
pip install ruff mypy pytest types-requests types-chardet
26+
27+
- name: Run PyOB Service
28+
env:
29+
PYOB_GEMINI_KEYS: ${{ secrets.PYOB_GEMINI_KEYS }}
30+
GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }}
31+
run: |
32+
# Run for 5 iterations then stop (to avoid timeout)
33+
pyob . --iterations 5

Dockerfile

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
FROM python:3.12-slim
2+
3+
# Install system dependencies (Git and GitHub CLI are required for the Librarian)
4+
RUN apt-get update && apt-get install -y \
5+
git \
6+
curl \
7+
&& curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \
8+
&& chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \
9+
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | tee /etc/apt/sources.list.d/github-cli.list > /dev/null \
10+
&& apt-get update && apt-get install -y gh \
11+
&& rm -rf /var/lib/apt/lists/*
12+
13+
WORKDIR /app
14+
15+
# Copy the project files
16+
COPY . .
17+
18+
# Install PyOB as a package
19+
RUN pip install --upgrade pip
20+
RUN pip install .
21+
RUN pip install ruff mypy pytest types-requests types-chardet
22+
23+
# Set the entrypoint to run our launcher
24+
ENTRYPOINT ["pyob"]

action.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: 'PyOB Autonomous Architect'
2+
description: 'Autonomous code review, surgical patching, and symbolic dependency tracking.'
3+
author: 'vicsanity623'
4+
inputs:
5+
gemini_keys:
6+
description: 'Comma-separated list of Gemini API keys'
7+
required: true
8+
target_dir:
9+
description: 'The directory to analyze'
10+
required: false
11+
default: '.'
12+
iterations:
13+
description: 'Number of autonomous loops to run'
14+
required: false
15+
default: '1'
16+
17+
runs:
18+
using: 'docker'
19+
image: 'Dockerfile'
20+
env:
21+
PYOB_GEMINI_KEYS: ${{ inputs.gemini_keys }}

src/pyob/pyob_dashboard.py

Lines changed: 98 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -8,145 +8,120 @@
88
<html lang="en">
99
<head>
1010
<meta charset="UTF-8">
11-
<title>PyOB // OBSERVER</title>
11+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
12+
<title>PyOB // ARCHITECT HUD</title>
13+
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&family=Inter:wght@300;600&display=swap" rel="stylesheet">
1214
<style>
13-
body { background: #050505; color: #00FF41; font-family: 'Menlo', monospace; margin: 0; padding: 20px; overflow-x: hidden; }
14-
.glow { text-shadow: 0 0 10px #00FF41, 0 0 20px #00FF41; }
15-
.border { border: 1px solid #00FF41; box-shadow: 0 0 15px rgba(0, 255, 65, 0.2); padding: 20px; margin-bottom: 20px; }
16-
h1 { font-size: 2em; border-bottom: 2px solid #00FF41; padding-bottom: 10px; }
17-
.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
18-
.card { background: rgba(0, 255, 65, 0.05); }
19-
.label { color: #008F11; font-weight: bold; margin-bottom: 5px; }
20-
.data { font-size: 0.9em; white-space: pre-wrap; height: 300px; overflow-y: auto; border: 1px solid #004411; padding: 10px; background: #000; }
21-
.stat-bar { display: flex; justify-content: space-between; font-size: 1.2em; margin-bottom: 20px; }
22-
.queue-item { background: #00FF41; color: #000; padding: 2px 5px; margin: 2px; display: inline-block; font-size: 0.8em; }
23-
#iteration { font-size: 1.5em; color: #fff; }
24-
/* New styles for patch review */
25-
.patch-card { background: rgba(0, 255, 65, 0.1); border: 1px solid #008F11; padding: 10px; margin-bottom: 10px; }
26-
.patch-content { font-family: 'Courier New', monospace; font-size: 0.8em; background: #000; border: 1px solid #004411; padding: 5px; margin-top: 5px; max-height: 200px; overflow-y: auto; }
27-
.patch-actions button { padding: 5px 10px; margin-right: 5px; cursor: pointer; border: none; }
28-
.approve-btn { background: #00FF41; color: #000; }
29-
.reject-btn { background: #FF0000; color: #fff; }
15+
:root { --bg: #0a0a0c; --card: #141417; --accent: #00ffa3; --text: #e0e0e6; --dim: #88888e; --err: #ff4d4d; }
16+
* { box-sizing: border-box; }
17+
body { background: var(--bg); color: var(--text); font-family: 'Inter', sans-serif; margin: 0; padding: 15px; line-height: 1.5; }
18+
.hud-container { max-width: 1200px; margin: 0 auto; display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
19+
20+
/* Typography & Glow */
21+
h1 { grid-column: span 2; font-family: 'JetBrains Mono'; font-size: 1.2rem; letter-spacing: 2px; color: var(--accent); text-transform: uppercase; margin: 10px 0; display: flex; justify-content: space-between; }
22+
.glow { text-shadow: 0 0 15px var(--accent); }
23+
24+
/* Component Cards */
25+
.card { background: var(--card); border: 1px solid #2a2a30; border-radius: 8px; padding: 20px; overflow: hidden; position: relative; }
26+
.card::before { content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 2px; background: linear-gradient(90deg, transparent, var(--accent), transparent); opacity: 0.3; }
27+
.label { font-size: 0.7rem; font-weight: 600; color: var(--dim); text-transform: uppercase; margin-bottom: 12px; letter-spacing: 1px; display: flex; align-items: center; gap: 8px; }
28+
.label::before { content: ''; width: 6px; height: 6px; background: var(--accent); border-radius: 50%; box-shadow: 0 0 8px var(--accent); }
29+
30+
/* Data Displays */
31+
.data-box { font-family: 'JetBrains Mono', monospace; font-size: 0.85rem; height: 250px; overflow-y: auto; background: #00000044; border-radius: 4px; padding: 12px; color: #ced4e0; scrollbar-width: thin; }
32+
.stat-grid { grid-column: span 2; display: flex; gap: 40px; background: var(--card); padding: 15px 25px; border-radius: 8px; border: 1px solid #2a2a30; }
33+
.stat-item { display: flex; flex-direction: column; }
34+
.stat-val { font-size: 1.5rem; font-weight: 700; font-family: 'JetBrains Mono'; color: #fff; }
35+
.stat-lbl { font-size: 0.6rem; color: var(--dim); }
36+
37+
/* Mobile Specifics */
38+
@media (max-width: 768px) {
39+
.hud-container { grid-template-columns: 1fr; }
40+
h1, .stat-grid { grid-column: 1; }
41+
.stat-grid { flex-wrap: wrap; gap: 20px; }
42+
}
43+
44+
.status-pill { padding: 4px 12px; border-radius: 20px; font-size: 0.7rem; font-weight: 800; background: #222; }
45+
.evolving { color: var(--accent); border: 1px solid var(--accent); box-shadow: 0 0 10px #00ffa344; }
46+
47+
input { background: #000; border: 1px solid #2a2a30; color: var(--accent); padding: 10px; border-radius: 4px; width: 100%; font-family: 'JetBrains Mono'; margin-bottom: 10px; }
48+
button { width: 100%; padding: 12px; background: var(--accent); color: #000; border: none; border-radius: 4px; font-weight: 700; cursor: pointer; transition: 0.2s; }
49+
button:hover { filter: brightness(1.2); }
3050
</style>
3151
</head>
3252
<body>
33-
<h1 class="glow">PYOB_OS // OBSERVER_DASHBOARD</h1>
34-
<div class="stat-bar border card">
35-
<div>ITERATION: <span id="iteration" class="glow">--</span></div>
36-
<div>LEDGER: <span id="ledger">--</span> symbols</div>
37-
<div>STATUS: <span id="status" style="color: #fff">SCANNING...</span></div>
38-
</div>
39-
<div class="border card"><div class="label">SYMBOLIC CASCADE QUEUE:</div><div id="queue">--</div></div>
40-
<div class="grid">
41-
<div class="border card"><div class="label">LIVE MEMORY (MEMORY.md):</div><div id="memory" class="data">--</div></div>
42-
<div class="border card"><div class="label">RECENT HISTORY (HISTORY.md):</div><div id="history" class="data">--</div></div>
43-
</div>
44-
<div class="border card"><div class="label">LATEST ARCHITECTURAL ANALYSIS:</div><div id="analysis" class="data">--</div></div>
45-
<!-- New section for Pending Architectural Patches -->
46-
<div class="border card">
47-
<div class="label">PENDING ARCHITECTURAL PATCHES:</div>
48-
<div id="pendingPatches" class="data" style="height: auto; min-height: 150px;">
49-
<!-- Patches will be loaded here -->
50-
No pending patches.
53+
<h1>
54+
<span>PyOB // Evolution Engine</span>
55+
<span id="status-pill" class="status-pill">READY</span>
56+
</h1>
57+
58+
<div class="hud-container">
59+
<div class="stat-grid">
60+
<div class="stat-item"><span class="stat-lbl">Iteration</span><span id="iteration" class="stat-val">--</span></div>
61+
<div class="stat-item"><span class="stat-lbl">Symbolic Ledger</span><span id="ledger" class="stat-val">--</span></div>
62+
<div class="stat-item"><span class="stat-lbl">Pending Cascades</span><span id="queue-count" class="stat-val">--</span></div>
63+
</div>
64+
65+
<div class="card">
66+
<div class="label">Logic Memory (MEMORY.md)</div>
67+
<div id="memory" class="data-box">Initializing brain...</div>
68+
</div>
69+
70+
<div class="card">
71+
<div class="label">System Logs (HISTORY.md)</div>
72+
<div id="history" class="data-box">No history yet.</div>
73+
</div>
74+
75+
<div class="card" style="grid-column: span 2;">
76+
<div class="label">Architectural Analysis</div>
77+
<div id="analysis" class="data-box" style="height: 350px;">Scanning structure...</div>
78+
</div>
79+
80+
<div class="card">
81+
<div class="label">Manual Override</div>
82+
<input type="text" id="manualTargetFile" placeholder="src/pyob/target.py">
83+
<button onclick="setManualTarget()">FORCE TARGET</button>
84+
</div>
85+
86+
<div class="card">
87+
<div class="label">Queue Status</div>
88+
<div id="queue" class="data-box" style="height: 100px;">IDLE</div>
5189
</div>
5290
</div>
53-
<div class="border card">
54-
<div class="label">MANUAL TARGET OVERRIDE:</div>
55-
<input type="text" id="manualTargetFile" placeholder="e.g., src/pyob/new_feature.py" style="width: calc(100% - 120px); padding: 8px; margin-right: 10px; background: #000; border: 1px solid #00FF41; color: #00FF41;">
56-
<button onclick="setManualTarget()" style="padding: 8px 15px; background: #00FF41; color: #000; border: none; cursor: pointer;">Set Next Target</button>
57-
<div id="targetMessage" style="margin-top: 10px; font-size: 0.9em;"></div>
58-
</div>
91+
5992
<script>
6093
async function updateStats() {
6194
try {
6295
const response = await fetch('/api/status');
6396
const data = await response.json();
64-
document.getElementById('iteration').innerText = data.iteration;
65-
document.getElementById('ledger').innerText = data.ledger_stats.definitions;
66-
document.getElementById('memory').innerText = data.memory || "Initializing brain...";
67-
document.getElementById('history').innerText = data.history || "No history recorded yet.";
68-
document.getElementById('analysis').innerText = data.analysis || "Parsing directory structure...";
69-
const queueDiv = document.getElementById('queue');
70-
queueDiv.innerHTML = data.cascade_queue.length > 0 ? data.cascade_queue.map(f => `<span class='queue-item'>${f}</span>`).join('') : "IDLE // NO PENDING CASCADES";
71-
document.getElementById('status').innerText = data.cascade_queue.length > 0 ? "EVOLVING" : "READY";
97+
98+
document.getElementById('iteration').innerText = data.iteration || "0";
99+
document.getElementById('ledger').innerText = (data.ledger_stats?.definitions || 0) + " SYM";
100+
document.getElementById('queue-count').innerText = data.cascade_queue?.length || "0";
101+
102+
const pill = document.getElementById('status-pill');
103+
const isEvolving = data.cascade_queue?.length > 0 || data.patches_count > 0;
104+
pill.innerText = isEvolving ? "EVOLVING" : "STABLE";
105+
pill.className = isEvolving ? "status-pill evolving" : "status-pill";
72106
73-
// Fetch and render pending patches
74-
const patchesResponse = await fetch('/api/pending_patches');
75-
const patchesData = await patchesResponse.json();
76-
renderPatches(patchesData.patches);
77-
78-
} catch (e) {
79-
console.error("Error updating stats:", e);
80-
document.getElementById('status').innerText = "OFFLINE";
81-
}
107+
document.getElementById('memory').innerText = data.memory || "Brain empty.";
108+
document.getElementById('history').innerText = data.history || "No logs.";
109+
document.getElementById('analysis').innerText = data.analysis || "Parsing...";
110+
111+
const queueDiv = document.getElementById('queue');
112+
queueDiv.innerText = data.cascade_queue?.length > 0 ? data.cascade_queue.join('\\n') : "EMPTY";
113+
} catch (e) { document.getElementById('status-pill').innerText = "OFFLINE"; }
82114
}
83115
84116
async function setManualTarget() {
85117
const targetFile = document.getElementById('manualTargetFile').value;
86-
const targetMessageDiv = document.getElementById('targetMessage');
87-
if (!targetFile) {
88-
targetMessageDiv.innerText = "Please enter a file path.";
89-
targetMessageDiv.style.color = 'red';
90-
return;
91-
}
92-
try {
93-
const response = await fetch('/api/set_target_file', {
94-
method: 'POST',
95-
headers: { 'Content-Type': 'application/json' },
96-
body: JSON.stringify({ target_file: targetFile })
97-
});
98-
const data = await response.json();
99-
if (response.ok) {
100-
targetMessageDiv.innerText = `Target set: ${data.target_file}`;
101-
targetMessageDiv.style.color = '#00FF41';
102-
document.getElementById('manualTargetFile').value = ''; // Clear input
103-
} else {
104-
targetMessageDiv.innerText = `Error: ${data.error || 'Failed to set target.'}`;
105-
targetMessageDiv.style.color = 'red';
106-
}
107-
} catch (e) {
108-
targetMessageDiv.innerText = `Network error: ${e.message}`;
109-
targetMessageDiv.style.color = 'red';
110-
}
111-
}
112-
113-
// New function to render patches
114-
function renderPatches(patches) {
115-
const patchesDiv = document.getElementById('pendingPatches');
116-
if (patches.length === 0) {
117-
patchesDiv.innerHTML = "No pending patches.";
118-
return;
119-
}
120-
patchesDiv.innerHTML = patches.map(patch => `
121-
<div class="patch-card">
122-
<div class="label">Patch ID: ${patch.id}</div>
123-
<pre class="patch-content">${patch.content}</pre>
124-
<div class="patch-actions" style="margin-top: 10px;">
125-
<button class="approve-btn" onclick="reviewPatch('${patch.id}', 'approve')">Approve</button>
126-
<button class="reject-btn" onclick="reviewPatch('${patch.id}', 'reject')">Reject</button>
127-
</div>
128-
</div>
129-
`).join('');
130-
}
131-
132-
// New function to send patch review action
133-
async function reviewPatch(patchId, action) {
134-
try {
135-
const response = await fetch('/api/review_patch', {
136-
method: 'POST',
137-
headers: { 'Content-Type': 'application/json' },
138-
body: JSON.stringify({ patch_id: patchId, action: action })
139-
});
140-
const data = await response.json();
141-
if (response.ok) {
142-
alert(`Patch ${patchId} ${action}d successfully.`);
143-
updateStats(); // Refresh dashboard to reflect changes
144-
} else {
145-
alert(`Error ${action}ing patch ${patchId}: ${data.error || 'Unknown error'}`);
146-
}
147-
} catch (e) {
148-
alert(`Network error while ${action}ing patch ${patchId}: ${e.message}`);
149-
}
118+
if (!targetFile) return;
119+
await fetch('/api/set_target_file', {
120+
method: 'POST',
121+
headers: { 'Content-Type': 'application/json' },
122+
body: JSON.stringify({ target_file: targetFile })
123+
});
124+
document.getElementById('manualTargetFile').value = '';
150125
}
151126
152127
setInterval(updateStats, 3000);

0 commit comments

Comments
 (0)