Update agents, dashboard, kb +2 more (+6 ~11)
This commit is contained in:
63
TOOLS.md
63
TOOLS.md
@@ -51,6 +51,69 @@ python3 tools/email_send.py "dest@email.com" "Subiect" "Corp mesaj"
|
|||||||
- **Repo:** ~/clawd → gitea.romfast.ro/romfast/clawd
|
- **Repo:** ~/clawd → gitea.romfast.ro/romfast/clawd
|
||||||
- **Commit script:** `python3 tools/git_commit.py --push`
|
- **Commit script:** `python3 tools/git_commit.py --push`
|
||||||
|
|
||||||
|
### Docker LXC (portainer)
|
||||||
|
- **Host:** 10.0.20.170 (LXC 100 pe pvemini)
|
||||||
|
- **User:** echo
|
||||||
|
- **SSH:** `ssh echo@10.0.20.170`
|
||||||
|
- **Portainer:** https://10.0.20.170:9443
|
||||||
|
- **Docker:** v28.3.3 + Compose v2.39.1
|
||||||
|
- **Resurse:** 1GB RAM, 20GB disk (4.1GB folosit)
|
||||||
|
- **Proiecte:** `/opt/docker/`
|
||||||
|
|
||||||
|
**Containere:**
|
||||||
|
| Container | Port | Status | Descriere |
|
||||||
|
|-----------|------|--------|-----------|
|
||||||
|
| portainer | 9443 | ✅ | Management Docker |
|
||||||
|
| nginx | 443, 8080 | ✅ | Reverse proxy |
|
||||||
|
| roa-efactura | 5003 | ⚠️ unhealthy | E-Factura ANAF |
|
||||||
|
| pdf-qr-app | 5002 | ✅ | QR facturi |
|
||||||
|
| flask_app | 5001 | ✅ | ROA Flask |
|
||||||
|
| bt-web-automation | 5000, 8081 | ✅ | BT automation |
|
||||||
|
| pulse | 7655 | ✅ | Monitoring Proxmox |
|
||||||
|
| wol-manager | - | ✅ | Wake-on-LAN |
|
||||||
|
| rustdesk (hbbs+hbbr) | 21115-21119 | ✅ | Remote desktop server |
|
||||||
|
|
||||||
|
**Proiecte docker-compose:**
|
||||||
|
- `/opt/docker/docker-compose.yaml` - stack principal (nginx, flask, efactura, qr)
|
||||||
|
- `/opt/docker/wol/docker-compose.yml` - Wake-on-LAN
|
||||||
|
- `/opt/docker/qrinvoice/docker-compose.yml` - QR Invoice app
|
||||||
|
|
||||||
|
### Proxmox Cluster (3 noduri)
|
||||||
|
**User:** echo | **Restricție:** doar din 10.0.20.173 (moltbot) | **Sudo:** qm, pct, pvesh
|
||||||
|
|
||||||
|
#### pveelite (10.0.20.202)
|
||||||
|
- **Resurse:** 16GB RAM, 557GB disk
|
||||||
|
- **SSH:** `ssh echo@10.0.20.202`
|
||||||
|
|
||||||
|
| VMID | Tip | Nume | Status |
|
||||||
|
|------|-----|------|--------|
|
||||||
|
| 109 | VM | oracle-dr-windows | stopped |
|
||||||
|
| 101 | LXC | minecraft | stopped |
|
||||||
|
| 110 | LXC | moltbot | running |
|
||||||
|
| 301 | LXC | docker-portainer-template | stopped |
|
||||||
|
|
||||||
|
#### pvemini (10.0.20.201)
|
||||||
|
- **Resurse:** 64GB RAM, 1.4TB disk
|
||||||
|
- **SSH:** `ssh echo@10.0.20.201`
|
||||||
|
|
||||||
|
| VMID | Tip | Nume | Status |
|
||||||
|
|------|-----|------|--------|
|
||||||
|
| 201 | VM | roacentral | running |
|
||||||
|
| 300 | VM | Win11-Template | stopped |
|
||||||
|
| 302 | VM | oracle-test-302 | stopped |
|
||||||
|
| 100 | LXC | portainer | running |
|
||||||
|
| 103 | LXC | dokploy | running |
|
||||||
|
| 104 | LXC | flowise | running |
|
||||||
|
| 105 | LXC | test | stopped |
|
||||||
|
| 106 | LXC | gitea | running |
|
||||||
|
| 108 | LXC | central-oracle | running |
|
||||||
|
| 171 | LXC | claude-agent | running |
|
||||||
|
|
||||||
|
#### pve1 (10.0.20.200)
|
||||||
|
- **Resurse:** 32GB RAM, 1.3TB disk
|
||||||
|
- **SSH:** `ssh echo@10.0.20.200`
|
||||||
|
- **Status:** Gol (fără VM/LXC)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ⚡ Echo Work - Unelte specifice
|
## ⚡ Echo Work - Unelte specifice
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
TIP: Mișcare fizică / Pattern Interrupt
|
TIP: Recunoștință / Priming seară
|
||||||
PROVOCARE: Ridică-te, fă 5 respirații adânci (inspiră 4 sec, expiră 6 sec), întinde-te (ridică brațele, deschide pieptul), mergi 2 minute oriunde. E resetare de stare prin corp - corpul nu știe să mintă.
|
PROVOCARE: 3 minute înainte de somn - găsește 3 momente bune din zi și SIMTE-le (nu doar gândește). Unde în corp? Ce senzație? Antrenament de sistem nervos.
|
||||||
|
|||||||
8
agents/echo-work/memory/approved-tasks.md
Normal file
8
agents/echo-work/memory/approved-tasks.md
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Task-uri aprobate pentru execuție
|
||||||
|
|
||||||
|
Acest fișier e populat de raportul de seară când Marius aprobă task-uri.
|
||||||
|
Job-ul night-execute (23:00) le execută și golește fișierul.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<!-- Task-uri aprobate se adaugă aici -->
|
||||||
@@ -9,5 +9,20 @@ Când primesc mesaj de la alt agent (via sessions_send):
|
|||||||
|
|
||||||
Marius nu vede mesajele interne - trebuie să comunic transparent ce se întâmplă.
|
Marius nu vede mesajele interne - trebuie să comunic transparent ce se întâmplă.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Execuție task-uri
|
||||||
|
|
||||||
|
**Din raportul de seară (job evening-report):**
|
||||||
|
- Aprobări ("ok X") → notez în `approved-tasks.md`
|
||||||
|
- Confirm: "✅ Notat pentru 23:00: [task-uri]"
|
||||||
|
- Job-ul `night-execute` (23:00) execută
|
||||||
|
|
||||||
|
**Din conversație directă separată:**
|
||||||
|
- Dacă Marius cere explicit → execut imediat
|
||||||
|
- Dacă e continuare din raport → respectă fluxul 23:00
|
||||||
|
|
||||||
|
**NU amesteca contextele!**
|
||||||
|
|
||||||
---
|
---
|
||||||
*Adăugat: 2026-01-31*
|
*Adăugat: 2026-01-31*
|
||||||
|
|||||||
100
dashboard/api.py
100
dashboard/api.py
@@ -108,6 +108,8 @@ class TaskBoardHandler(SimpleHTTPRequestHandler):
|
|||||||
self.handle_activity()
|
self.handle_activity()
|
||||||
elif self.path.startswith('/api/files'):
|
elif self.path.startswith('/api/files'):
|
||||||
self.handle_files_get()
|
self.handle_files_get()
|
||||||
|
elif self.path.startswith('/api/diff'):
|
||||||
|
self.handle_git_diff()
|
||||||
elif self.path.startswith('/api/'):
|
elif self.path.startswith('/api/'):
|
||||||
self.send_error(404)
|
self.send_error(404)
|
||||||
else:
|
else:
|
||||||
@@ -155,6 +157,14 @@ class TaskBoardHandler(SimpleHTTPRequestHandler):
|
|||||||
cwd=workspace, capture_output=True, text=True, timeout=5
|
cwd=workspace, capture_output=True, text=True, timeout=5
|
||||||
).stdout.strip()
|
).stdout.strip()
|
||||||
|
|
||||||
|
# Parse uncommitted into structured format
|
||||||
|
uncommitted_parsed = []
|
||||||
|
for line in uncommitted:
|
||||||
|
if len(line) >= 3:
|
||||||
|
status = line[:2].strip()
|
||||||
|
filepath = line[3:].strip()
|
||||||
|
uncommitted_parsed.append({'status': status, 'path': filepath})
|
||||||
|
|
||||||
self.send_json({
|
self.send_json({
|
||||||
'branch': branch,
|
'branch': branch,
|
||||||
'lastCommit': {
|
'lastCommit': {
|
||||||
@@ -163,6 +173,7 @@ class TaskBoardHandler(SimpleHTTPRequestHandler):
|
|||||||
'time': commit_parts[2] if len(commit_parts) > 2 else ''
|
'time': commit_parts[2] if len(commit_parts) > 2 else ''
|
||||||
},
|
},
|
||||||
'uncommitted': uncommitted,
|
'uncommitted': uncommitted,
|
||||||
|
'uncommittedParsed': uncommitted_parsed,
|
||||||
'uncommittedCount': len(uncommitted),
|
'uncommittedCount': len(uncommitted),
|
||||||
'diffStat': diff_stat,
|
'diffStat': diff_stat,
|
||||||
'clean': len(uncommitted) == 0
|
'clean': len(uncommitted) == 0
|
||||||
@@ -170,6 +181,60 @@ class TaskBoardHandler(SimpleHTTPRequestHandler):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.send_json({'error': str(e)}, 500)
|
self.send_json({'error': str(e)}, 500)
|
||||||
|
|
||||||
|
def handle_git_diff(self):
|
||||||
|
"""Get git diff for a specific file."""
|
||||||
|
from urllib.parse import urlparse, parse_qs
|
||||||
|
parsed = urlparse(self.path)
|
||||||
|
params = parse_qs(parsed.query)
|
||||||
|
|
||||||
|
filepath = params.get('path', [''])[0]
|
||||||
|
|
||||||
|
if not filepath:
|
||||||
|
self.send_json({'error': 'path required'}, 400)
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
workspace = Path('/home/moltbot/clawd')
|
||||||
|
|
||||||
|
# Security check
|
||||||
|
target = (workspace / filepath).resolve()
|
||||||
|
if not str(target).startswith(str(workspace)):
|
||||||
|
self.send_json({'error': 'Access denied'}, 403)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get diff (try staged first, then unstaged)
|
||||||
|
diff = subprocess.run(
|
||||||
|
['git', 'diff', '--cached', '--', filepath],
|
||||||
|
cwd=workspace, capture_output=True, text=True, timeout=10
|
||||||
|
).stdout
|
||||||
|
|
||||||
|
if not diff:
|
||||||
|
diff = subprocess.run(
|
||||||
|
['git', 'diff', '--', filepath],
|
||||||
|
cwd=workspace, capture_output=True, text=True, timeout=10
|
||||||
|
).stdout
|
||||||
|
|
||||||
|
# If still no diff, file might be untracked - show full content
|
||||||
|
if not diff:
|
||||||
|
status = subprocess.run(
|
||||||
|
['git', 'status', '--short', '--', filepath],
|
||||||
|
cwd=workspace, capture_output=True, text=True, timeout=5
|
||||||
|
).stdout.strip()
|
||||||
|
|
||||||
|
if status.startswith('??'):
|
||||||
|
# Untracked file - show as new
|
||||||
|
if target.exists():
|
||||||
|
content = target.read_text(encoding='utf-8', errors='replace')[:50000]
|
||||||
|
diff = f"+++ b/{filepath}\n" + '\n'.join(f'+{line}' for line in content.split('\n'))
|
||||||
|
|
||||||
|
self.send_json({
|
||||||
|
'path': filepath,
|
||||||
|
'diff': diff or 'No changes',
|
||||||
|
'hasDiff': bool(diff)
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
self.send_json({'error': str(e)}, 500)
|
||||||
|
|
||||||
def handle_agents_status(self):
|
def handle_agents_status(self):
|
||||||
"""Get agents status - fast version reading session files directly."""
|
"""Get agents status - fast version reading session files directly."""
|
||||||
try:
|
try:
|
||||||
@@ -361,7 +426,40 @@ class TaskBoardHandler(SimpleHTTPRequestHandler):
|
|||||||
'text': message[:60] + ('...' if len(message) > 60 else ''),
|
'text': message[:60] + ('...' if len(message) > 60 else ''),
|
||||||
'agent': 'git',
|
'agent': 'git',
|
||||||
'time': local_time.strftime('%H:%M'),
|
'time': local_time.strftime('%H:%M'),
|
||||||
'timestamp': timestamp * 1000
|
'timestamp': timestamp * 1000,
|
||||||
|
'commitHash': commit_hash[:8]
|
||||||
|
})
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 2b. Git uncommitted files
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
['git', 'status', '--short'],
|
||||||
|
cwd=workspace, capture_output=True, text=True, timeout=10
|
||||||
|
)
|
||||||
|
if result.returncode == 0 and result.stdout.strip():
|
||||||
|
for line in result.stdout.strip().split('\n'):
|
||||||
|
if len(line) >= 4:
|
||||||
|
# Git status format: XY filename (XY = 2 chars status)
|
||||||
|
# Handle both "M " and " M" formats
|
||||||
|
status = line[:2]
|
||||||
|
# Find filepath - skip status chars and any spaces
|
||||||
|
filepath = line[2:].lstrip()
|
||||||
|
if not filepath:
|
||||||
|
continue
|
||||||
|
status_clean = status.strip()
|
||||||
|
status_labels = {'M': 'modificat', 'A': 'adăugat', 'D': 'șters', '??': 'nou', 'R': 'redenumit'}
|
||||||
|
status_label = status_labels.get(status_clean, status_clean)
|
||||||
|
activities.append({
|
||||||
|
'type': 'git-file',
|
||||||
|
'icon': 'file-diff',
|
||||||
|
'text': f"{filepath}",
|
||||||
|
'agent': f"git ({status_label})",
|
||||||
|
'time': 'acum',
|
||||||
|
'timestamp': int(datetime.now().timestamp() * 1000),
|
||||||
|
'path': filepath,
|
||||||
|
'gitStatus': status_clean
|
||||||
})
|
})
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|||||||
4
dashboard/favicon.svg
Normal file
4
dashboard/favicon.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||||
|
<circle cx="16" cy="16" r="14" fill="none" stroke="#3b82f6" stroke-width="2.5"/>
|
||||||
|
<circle cx="16" cy="16" r="3" fill="#3b82f6"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 200 B |
@@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="icon" type="image/svg+xml" href="favicon.svg">
|
||||||
<title>Echo · Files</title>
|
<title>Echo · Files</title>
|
||||||
<link rel="stylesheet" href="common.css">
|
<link rel="stylesheet" href="common.css">
|
||||||
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script>
|
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script>
|
||||||
@@ -294,6 +295,41 @@
|
|||||||
background: var(--accent-subtle);
|
background: var(--accent-subtle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.file-item.git-changed {
|
||||||
|
border-left: 3px solid var(--warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
.git-badge {
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 700;
|
||||||
|
padding: 1px 4px;
|
||||||
|
border-radius: 3px;
|
||||||
|
margin-right: 4px;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
}
|
||||||
|
|
||||||
|
.git-modified { background: #3b82f620; color: #3b82f6; }
|
||||||
|
.git-added { background: #22c55e20; color: #22c55e; }
|
||||||
|
.git-deleted { background: #ef444420; color: #ef4444; }
|
||||||
|
.git-untracked { background: #f59e0b20; color: #f59e0b; }
|
||||||
|
.git-renamed { background: #8b5cf620; color: #8b5cf6; }
|
||||||
|
|
||||||
|
.diff-btn {
|
||||||
|
background: var(--accent);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 11px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-btn:hover {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
.file-icon {
|
.file-icon {
|
||||||
width: 40px;
|
width: 40px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
@@ -419,6 +455,9 @@
|
|||||||
.preview-active #markdownPreview { display: block; }
|
.preview-active #markdownPreview { display: block; }
|
||||||
|
|
||||||
.btn-preview.active { background: var(--accent); color: white; }
|
.btn-preview.active { background: var(--accent); color: white; }
|
||||||
|
.btn-diff.active { background: var(--warning); color: white; }
|
||||||
|
|
||||||
|
#gitFilterBtn.active { background: var(--warning); color: white; }
|
||||||
|
|
||||||
.editor-footer {
|
.editor-footer {
|
||||||
padding: var(--space-2) var(--space-5);
|
padding: var(--space-2) var(--space-5);
|
||||||
@@ -493,6 +532,13 @@
|
|||||||
<span class="breadcrumb-item current" onclick="loadPath('')">~/clawd</span>
|
<span class="breadcrumb-item current" onclick="loadPath('')">~/clawd</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="toolbar-actions">
|
<div class="toolbar-actions">
|
||||||
|
<!-- Git Filter Toggle -->
|
||||||
|
<div class="view-toggle">
|
||||||
|
<button class="view-btn" id="gitFilterBtn" onclick="toggleGitFilter()" title="Git Changes">
|
||||||
|
<i data-lucide="git-branch"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- View Mode Toggle -->
|
<!-- View Mode Toggle -->
|
||||||
<div class="view-toggle" id="viewModeToggle">
|
<div class="view-toggle" id="viewModeToggle">
|
||||||
<button class="view-btn" data-view="list" onclick="setViewMode('list')" title="Listă">
|
<button class="view-btn" data-view="list" onclick="setViewMode('list')" title="Listă">
|
||||||
@@ -554,6 +600,9 @@
|
|||||||
<button class="btn btn-ghost btn-preview" onclick="togglePreview()" id="previewBtn" style="display:none;" title="Preview Markdown">
|
<button class="btn btn-ghost btn-preview" onclick="togglePreview()" id="previewBtn" style="display:none;" title="Preview Markdown">
|
||||||
<i data-lucide="eye"></i>
|
<i data-lucide="eye"></i>
|
||||||
</button>
|
</button>
|
||||||
|
<button class="btn btn-ghost btn-diff" onclick="toggleDiff()" id="diffBtn" style="display:none;" title="Git Diff">
|
||||||
|
<i data-lucide="git-compare"></i>
|
||||||
|
</button>
|
||||||
<button class="btn btn-ghost" onclick="reloadFile()" id="reloadBtn" disabled title="Reload">
|
<button class="btn btn-ghost" onclick="reloadFile()" id="reloadBtn" disabled title="Reload">
|
||||||
<i data-lucide="refresh-cw"></i>
|
<i data-lucide="refresh-cw"></i>
|
||||||
</button>
|
</button>
|
||||||
@@ -611,6 +660,8 @@
|
|||||||
let currentSortBy = localStorage.getItem('filesSortBy') || 'name';
|
let currentSortBy = localStorage.getItem('filesSortBy') || 'name';
|
||||||
let currentSortDir = localStorage.getItem('filesSortDir') || 'asc';
|
let currentSortDir = localStorage.getItem('filesSortDir') || 'asc';
|
||||||
let currentItems = [];
|
let currentItems = [];
|
||||||
|
let gitStatus = {}; // Map of filepath -> status (M, A, D, ??)
|
||||||
|
let gitOnlyMode = false; // Show only git-changed files
|
||||||
|
|
||||||
// Initialize view mode
|
// Initialize view mode
|
||||||
function initViewMode() {
|
function initViewMode() {
|
||||||
@@ -690,6 +741,63 @@
|
|||||||
document.getElementById('editorBtn').classList.add('active');
|
document.getElementById('editorBtn').classList.add('active');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function loadGitStatus() {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${API_BASE}/api/git?` + Date.now());
|
||||||
|
const data = await response.json();
|
||||||
|
gitStatus = {};
|
||||||
|
if (data.uncommittedParsed) {
|
||||||
|
data.uncommittedParsed.forEach(item => {
|
||||||
|
gitStatus[item.path] = item.status;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to load git status:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function showDiff(filepath, event) {
|
||||||
|
if (event) event.stopPropagation();
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${API_BASE}/api/diff?path=${encodeURIComponent(filepath)}`);
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
// Show in a modal or the editor
|
||||||
|
const diffHtml = data.diff
|
||||||
|
.split('\n')
|
||||||
|
.map(line => {
|
||||||
|
if (line.startsWith('+') && !line.startsWith('+++')) {
|
||||||
|
return `<span style="color:var(--success)">${escapeHtml(line)}</span>`;
|
||||||
|
} else if (line.startsWith('-') && !line.startsWith('---')) {
|
||||||
|
return `<span style="color:var(--error)">${escapeHtml(line)}</span>`;
|
||||||
|
} else if (line.startsWith('@@')) {
|
||||||
|
return `<span style="color:var(--accent)">${escapeHtml(line)}</span>`;
|
||||||
|
}
|
||||||
|
return escapeHtml(line);
|
||||||
|
})
|
||||||
|
.join('\n');
|
||||||
|
|
||||||
|
// Open file in editor with diff view
|
||||||
|
document.getElementById('editorFileName').textContent = `DIFF: ${filepath}`;
|
||||||
|
document.getElementById('codeEditor').value = data.diff;
|
||||||
|
document.getElementById('markdownPreview').innerHTML = `<pre style="font-family:var(--font-mono);font-size:13px;line-height:1.5">${diffHtml}</pre>`;
|
||||||
|
document.getElementById('editorBody').classList.add('preview-active');
|
||||||
|
document.getElementById('previewBtn').style.display = 'flex';
|
||||||
|
document.getElementById('previewBtn').classList.add('active');
|
||||||
|
document.getElementById('saveBtn').disabled = true;
|
||||||
|
document.getElementById('reloadBtn').disabled = true;
|
||||||
|
currentFile = null;
|
||||||
|
setStatus('Diff view', 'saved');
|
||||||
|
showEditor();
|
||||||
|
} catch (e) {
|
||||||
|
alert('Eroare la încărcare diff: ' + e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeHtml(text) {
|
||||||
|
return text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||||
|
}
|
||||||
|
|
||||||
async function loadPath(path = '') {
|
async function loadPath(path = '') {
|
||||||
currentPath = path;
|
currentPath = path;
|
||||||
updateBreadcrumb();
|
updateBreadcrumb();
|
||||||
@@ -704,6 +812,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (data.type === 'dir') {
|
if (data.type === 'dir') {
|
||||||
|
await loadGitStatus(); // Refresh git status
|
||||||
renderFileGrid(data.items);
|
renderFileGrid(data.items);
|
||||||
updateURL(path);
|
updateURL(path);
|
||||||
} else if (data.type === 'file') {
|
} else if (data.type === 'file') {
|
||||||
@@ -796,37 +905,42 @@
|
|||||||
const fileType = item.type === 'dir' ? 'Folder' : getFileType(item.name);
|
const fileType = item.type === 'dir' ? 'Folder' : getFileType(item.name);
|
||||||
const sizeStr = item.size !== undefined ? formatSize(item.size) : '-';
|
const sizeStr = item.size !== undefined ? formatSize(item.size) : '-';
|
||||||
|
|
||||||
|
// Git status
|
||||||
|
const gStatus = gitStatus[item.path] || '';
|
||||||
|
const gitBadge = gStatus ? getGitBadge(gStatus) : '';
|
||||||
|
const hasGitChange = !!gStatus;
|
||||||
|
|
||||||
if (currentViewMode === 'details') {
|
if (currentViewMode === 'details') {
|
||||||
return `
|
return `
|
||||||
<div class="file-item ${currentFile === item.path ? 'active' : ''}" onclick="handleClick('${item.path}', '${item.type}')">
|
<div class="file-item ${currentFile === item.path ? 'active' : ''} ${hasGitChange ? 'git-changed' : ''}" onclick="handleClick('${item.path}', '${item.type}')">
|
||||||
<div class="file-icon ${item.type === 'dir' ? 'folder' : ''}">
|
<div class="file-icon ${item.type === 'dir' ? 'folder' : ''}">
|
||||||
<i data-lucide="${item.type === 'dir' ? 'folder' : getFileIcon(item.name)}"></i>
|
<i data-lucide="${item.type === 'dir' ? 'folder' : getFileIcon(item.name)}"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="file-name">${item.name}</div>
|
<div class="file-name">${gitBadge}${item.name}</div>
|
||||||
<div class="file-meta">
|
<div class="file-meta">
|
||||||
<span class="file-type">${fileType}</span>
|
<span class="file-type">${fileType}</span>
|
||||||
<span class="file-size">${sizeStr}</span>
|
<span class="file-size">${sizeStr}</span>
|
||||||
<span class="file-date">${dateStr || '-'}</span>
|
<span class="file-date">${hasGitChange ? `<button class="diff-btn" onclick="showDiff('${item.path}', event)" title="Vezi diff">diff</button>` : (dateStr || '-')}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
} else if (currentViewMode === 'list') {
|
} else if (currentViewMode === 'list') {
|
||||||
return `
|
return `
|
||||||
<div class="file-item ${currentFile === item.path ? 'active' : ''}" onclick="handleClick('${item.path}', '${item.type}')">
|
<div class="file-item ${currentFile === item.path ? 'active' : ''} ${hasGitChange ? 'git-changed' : ''}" onclick="handleClick('${item.path}', '${item.type}')">
|
||||||
<div class="file-icon ${item.type === 'dir' ? 'folder' : ''}">
|
<div class="file-icon ${item.type === 'dir' ? 'folder' : ''}">
|
||||||
<i data-lucide="${item.type === 'dir' ? 'folder' : getFileIcon(item.name)}"></i>
|
<i data-lucide="${item.type === 'dir' ? 'folder' : getFileIcon(item.name)}"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="file-name">${item.name}</div>
|
<div class="file-name">${gitBadge}${item.name}</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
} else {
|
} else {
|
||||||
// Tiles view - original style
|
// Tiles view - original style
|
||||||
return `
|
return `
|
||||||
<div class="file-item ${currentFile === item.path ? 'active' : ''}" onclick="handleClick('${item.path}', '${item.type}')">
|
<div class="file-item ${currentFile === item.path ? 'active' : ''} ${hasGitChange ? 'git-changed' : ''}" onclick="handleClick('${item.path}', '${item.type}')">
|
||||||
<div class="file-icon ${item.type === 'dir' ? 'folder' : ''}">
|
<div class="file-icon ${item.type === 'dir' ? 'folder' : ''}">
|
||||||
<i data-lucide="${item.type === 'dir' ? 'folder' : getFileIcon(item.name)}"></i>
|
<i data-lucide="${item.type === 'dir' ? 'folder' : getFileIcon(item.name)}"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="file-name">${item.name}</div>
|
<div class="file-name">${gitBadge}${item.name}</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@@ -881,6 +995,17 @@
|
|||||||
return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
|
return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getGitBadge(status) {
|
||||||
|
const badges = {
|
||||||
|
'M': '<span class="git-badge git-modified" title="Modificat">M</span>',
|
||||||
|
'A': '<span class="git-badge git-added" title="Adăugat">A</span>',
|
||||||
|
'D': '<span class="git-badge git-deleted" title="Șters">D</span>',
|
||||||
|
'??': '<span class="git-badge git-untracked" title="Nou (untracked)">+</span>',
|
||||||
|
'R': '<span class="git-badge git-renamed" title="Redenumit">R</span>',
|
||||||
|
};
|
||||||
|
return badges[status] || `<span class="git-badge">${status}</span>`;
|
||||||
|
}
|
||||||
|
|
||||||
function handleClick(path, type) {
|
function handleClick(path, type) {
|
||||||
if (type === 'dir') {
|
if (type === 'dir') {
|
||||||
loadPath(path);
|
loadPath(path);
|
||||||
@@ -905,6 +1030,10 @@
|
|||||||
const isMarkdown = path.endsWith('.md');
|
const isMarkdown = path.endsWith('.md');
|
||||||
document.getElementById('previewBtn').style.display = isMarkdown ? 'flex' : 'none';
|
document.getElementById('previewBtn').style.display = isMarkdown ? 'flex' : 'none';
|
||||||
|
|
||||||
|
// Always show diff button - let user check if file has changes
|
||||||
|
document.getElementById('diffBtn').style.display = 'flex';
|
||||||
|
document.getElementById('diffBtn').classList.remove('active');
|
||||||
|
|
||||||
// Auto-activate preview for markdown files
|
// Auto-activate preview for markdown files
|
||||||
if (isMarkdown) {
|
if (isMarkdown) {
|
||||||
const preview = document.getElementById('markdownPreview');
|
const preview = document.getElementById('markdownPreview');
|
||||||
@@ -929,6 +1058,7 @@
|
|||||||
function togglePreview() {
|
function togglePreview() {
|
||||||
const editorBody = document.getElementById('editorBody');
|
const editorBody = document.getElementById('editorBody');
|
||||||
const previewBtn = document.getElementById('previewBtn');
|
const previewBtn = document.getElementById('previewBtn');
|
||||||
|
const diffBtn = document.getElementById('diffBtn');
|
||||||
const preview = document.getElementById('markdownPreview');
|
const preview = document.getElementById('markdownPreview');
|
||||||
const content = document.getElementById('codeEditor').value;
|
const content = document.getElementById('codeEditor').value;
|
||||||
|
|
||||||
@@ -936,16 +1066,69 @@
|
|||||||
// Switch to edit mode
|
// Switch to edit mode
|
||||||
editorBody.classList.remove('preview-active');
|
editorBody.classList.remove('preview-active');
|
||||||
previewBtn.classList.remove('active');
|
previewBtn.classList.remove('active');
|
||||||
|
if (diffBtn) diffBtn.classList.remove('active');
|
||||||
setStatus('Edit mode', 'saved');
|
setStatus('Edit mode', 'saved');
|
||||||
} else {
|
} else {
|
||||||
// Switch to preview mode
|
// Switch to preview mode
|
||||||
preview.innerHTML = marked.parse(content);
|
preview.innerHTML = marked.parse(content);
|
||||||
editorBody.classList.add('preview-active');
|
editorBody.classList.add('preview-active');
|
||||||
previewBtn.classList.add('active');
|
previewBtn.classList.add('active');
|
||||||
|
if (diffBtn) diffBtn.classList.remove('active');
|
||||||
setStatus('Preview mode', 'saved');
|
setStatus('Preview mode', 'saved');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function toggleDiff() {
|
||||||
|
if (!currentFile) return;
|
||||||
|
|
||||||
|
const editorBody = document.getElementById('editorBody');
|
||||||
|
const diffBtn = document.getElementById('diffBtn');
|
||||||
|
const previewBtn = document.getElementById('previewBtn');
|
||||||
|
const preview = document.getElementById('markdownPreview');
|
||||||
|
|
||||||
|
// If already showing diff, switch back to edit
|
||||||
|
if (diffBtn.classList.contains('active')) {
|
||||||
|
editorBody.classList.remove('preview-active');
|
||||||
|
diffBtn.classList.remove('active');
|
||||||
|
setStatus('Edit mode', 'saved');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
setStatus('Se încarcă diff...', 'modified');
|
||||||
|
const response = await fetch(`${API_BASE}/api/diff?path=${encodeURIComponent(currentFile)}`);
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (data.error) {
|
||||||
|
setStatus('Eroare: ' + data.error, 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format diff with colors
|
||||||
|
const diffHtml = data.diff
|
||||||
|
.split('\n')
|
||||||
|
.map(line => {
|
||||||
|
if (line.startsWith('+') && !line.startsWith('+++')) {
|
||||||
|
return `<span style="color:var(--success)">${escapeHtml(line)}</span>`;
|
||||||
|
} else if (line.startsWith('-') && !line.startsWith('---')) {
|
||||||
|
return `<span style="color:var(--error)">${escapeHtml(line)}</span>`;
|
||||||
|
} else if (line.startsWith('@@')) {
|
||||||
|
return `<span style="color:var(--accent)">${escapeHtml(line)}</span>`;
|
||||||
|
}
|
||||||
|
return escapeHtml(line);
|
||||||
|
})
|
||||||
|
.join('\n');
|
||||||
|
|
||||||
|
preview.innerHTML = `<pre style="font-family:var(--font-mono);font-size:13px;line-height:1.5;white-space:pre-wrap">${diffHtml || 'Nicio modificare față de ultima versiune comisă.'}</pre>`;
|
||||||
|
editorBody.classList.add('preview-active');
|
||||||
|
diffBtn.classList.add('active');
|
||||||
|
if (previewBtn) previewBtn.classList.remove('active');
|
||||||
|
setStatus('Diff view', 'saved');
|
||||||
|
} catch (e) {
|
||||||
|
setStatus('Eroare: ' + e.message, 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function saveFile() {
|
async function saveFile() {
|
||||||
if (!currentFile) return;
|
if (!currentFile) return;
|
||||||
|
|
||||||
@@ -1028,7 +1211,68 @@
|
|||||||
|
|
||||||
// Init
|
// Init
|
||||||
initViewMode();
|
initViewMode();
|
||||||
|
|
||||||
|
// Check for git mode
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
if (urlParams.get('git') === '1') {
|
||||||
|
gitOnlyMode = true;
|
||||||
|
loadGitChangedFiles();
|
||||||
|
} else {
|
||||||
loadPath(getPathFromURL());
|
loadPath(getPathFromURL());
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleGitFilter() {
|
||||||
|
gitOnlyMode = !gitOnlyMode;
|
||||||
|
const btn = document.getElementById('gitFilterBtn');
|
||||||
|
btn.classList.toggle('active', gitOnlyMode);
|
||||||
|
|
||||||
|
if (gitOnlyMode) {
|
||||||
|
loadGitChangedFiles();
|
||||||
|
} else {
|
||||||
|
// Return to normal browse
|
||||||
|
window.history.replaceState(null, '', 'files.html');
|
||||||
|
loadPath('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadGitChangedFiles() {
|
||||||
|
await loadGitStatus();
|
||||||
|
const changedPaths = Object.keys(gitStatus);
|
||||||
|
|
||||||
|
// Update button state
|
||||||
|
document.getElementById('gitFilterBtn').classList.add('active');
|
||||||
|
|
||||||
|
if (changedPaths.length === 0) {
|
||||||
|
document.getElementById('breadcrumb').innerHTML = `
|
||||||
|
<span class="breadcrumb-item" onclick="toggleGitFilter()">~/clawd</span>
|
||||||
|
<span class="breadcrumb-sep"><i data-lucide="chevron-right"></i></span>
|
||||||
|
<span class="breadcrumb-item current" style="color:var(--success)">✓ Git curat</span>
|
||||||
|
`;
|
||||||
|
lucide.createIcons();
|
||||||
|
showError('Nicio modificare git - totul e comis!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update breadcrumb
|
||||||
|
document.getElementById('breadcrumb').innerHTML = `
|
||||||
|
<span class="breadcrumb-item" onclick="toggleGitFilter()">~/clawd</span>
|
||||||
|
<span class="breadcrumb-sep"><i data-lucide="chevron-right"></i></span>
|
||||||
|
<span class="breadcrumb-item current" style="color:var(--warning)">🔸 Git Changes (${changedPaths.length})</span>
|
||||||
|
`;
|
||||||
|
lucide.createIcons();
|
||||||
|
|
||||||
|
// Create virtual items for changed files
|
||||||
|
const items = changedPaths.map(path => ({
|
||||||
|
name: path,
|
||||||
|
path: path,
|
||||||
|
type: 'file',
|
||||||
|
size: null,
|
||||||
|
mtime: null
|
||||||
|
}));
|
||||||
|
|
||||||
|
currentItems = items;
|
||||||
|
renderFileGrid(items);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="icon" type="image/svg+xml" href="favicon.svg">
|
||||||
<title>Echo · Dashboard</title>
|
<title>Echo · Dashboard</title>
|
||||||
<link rel="stylesheet" href="common.css">
|
<link rel="stylesheet" href="common.css">
|
||||||
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script>
|
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script>
|
||||||
@@ -14,61 +15,6 @@
|
|||||||
padding: var(--space-5);
|
padding: var(--space-5);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Stats Summary */
|
|
||||||
.stats-summary {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(4, 1fr);
|
|
||||||
gap: var(--space-3);
|
|
||||||
margin-bottom: var(--space-4);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.stats-summary {
|
|
||||||
grid-template-columns: repeat(2, 1fr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-card {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--space-3);
|
|
||||||
padding: var(--space-4);
|
|
||||||
background: var(--bg-surface);
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
border-radius: var(--radius-lg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-icon {
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
background: var(--accent-subtle);
|
|
||||||
border-radius: var(--radius-md);
|
|
||||||
color: var(--accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-icon svg {
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-content {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-value {
|
|
||||||
font-size: var(--text-xl);
|
|
||||||
font-weight: 700;
|
|
||||||
color: var(--text-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-label {
|
|
||||||
font-size: var(--text-xs);
|
|
||||||
color: var(--text-muted);
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-header {
|
.page-header {
|
||||||
margin-bottom: var(--space-4);
|
margin-bottom: var(--space-4);
|
||||||
}
|
}
|
||||||
@@ -518,6 +464,11 @@
|
|||||||
color: #f97316;
|
color: #f97316;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.activity-icon.git-file {
|
||||||
|
background: rgba(234, 179, 8, 0.2);
|
||||||
|
color: #eab308;
|
||||||
|
}
|
||||||
|
|
||||||
.activity-icon.file {
|
.activity-icon.file {
|
||||||
background: rgba(20, 184, 166, 0.2);
|
background: rgba(20, 184, 166, 0.2);
|
||||||
color: #14b8a6;
|
color: #14b8a6;
|
||||||
@@ -1000,38 +951,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Stats Summary -->
|
|
||||||
<div class="stats-summary" id="statsSummary">
|
|
||||||
<div class="stat-card" title="Task-uri completate azi">
|
|
||||||
<div class="stat-icon"><i data-lucide="check-circle"></i></div>
|
|
||||||
<div class="stat-content">
|
|
||||||
<div class="stat-value" id="statToday">0</div>
|
|
||||||
<div class="stat-label">Tasks azi</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="stat-card" title="Task-uri completate săptămâna aceasta">
|
|
||||||
<div class="stat-icon"><i data-lucide="calendar"></i></div>
|
|
||||||
<div class="stat-content">
|
|
||||||
<div class="stat-value" id="statWeek">0</div>
|
|
||||||
<div class="stat-label">Tasks săpt.</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="stat-card" title="Task-uri completate luna aceasta">
|
|
||||||
<div class="stat-icon"><i data-lucide="trending-up"></i></div>
|
|
||||||
<div class="stat-content">
|
|
||||||
<div class="stat-value" id="statMonth">0</div>
|
|
||||||
<div class="stat-label">Tasks lună</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="stat-card" title="Număr de fișiere insights">
|
|
||||||
<div class="stat-icon"><i data-lucide="lightbulb"></i></div>
|
|
||||||
<div class="stat-content">
|
|
||||||
<div class="stat-value" id="statInsights">0</div>
|
|
||||||
<div class="stat-label">Insights</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="dashboard-grid">
|
<div class="dashboard-grid">
|
||||||
<!-- Activity Panel -->
|
<!-- Activity Panel -->
|
||||||
<div class="panel activity-panel" id="activityPanel">
|
<div class="panel activity-panel" id="activityPanel">
|
||||||
@@ -1297,39 +1216,27 @@
|
|||||||
|
|
||||||
// Update details
|
// Update details
|
||||||
const details = document.getElementById('gitDetails');
|
const details = document.getElementById('gitDetails');
|
||||||
|
const GITEA_URL = 'https://gitea.romfast.ro/romfast/clawd';
|
||||||
let html = `
|
let html = `
|
||||||
<div class="status-detail-item">
|
|
||||||
<i data-lucide="git-branch"></i>
|
|
||||||
<span>Branch: <strong>${git.branch}</strong></span>
|
|
||||||
</div>
|
|
||||||
<div class="status-detail-item">
|
<div class="status-detail-item">
|
||||||
<i data-lucide="git-commit"></i>
|
<i data-lucide="git-commit"></i>
|
||||||
<span>Last: <code>${git.lastCommit.hash}</code> ${git.lastCommit.message} (${git.lastCommit.time})</span>
|
<span><a href="${GITEA_URL}/commit/${git.lastCommit.hash}" target="_blank" style="color:var(--accent)">${git.lastCommit.hash}</a> ${git.lastCommit.message.substring(0, 40)}${git.lastCommit.message.length > 40 ? '...' : ''} <small>(${git.lastCommit.time})</small></span>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
if (git.uncommittedCount > 0) {
|
if (git.uncommittedCount > 0) {
|
||||||
|
const files = git.uncommitted.slice(0, 3).map(f => f.trim().split(' ').pop()).join(', ');
|
||||||
|
const more = git.uncommittedCount > 3 ? ` +${git.uncommittedCount - 3}` : '';
|
||||||
html += `<div class="status-detail-item uncommitted">
|
html += `<div class="status-detail-item uncommitted">
|
||||||
<i data-lucide="alert-circle"></i>
|
<i data-lucide="alert-circle"></i>
|
||||||
<span><strong>${git.uncommittedCount}</strong> fișiere necomise:</span>
|
<span><a href="files.html?git=1" style="color:var(--warning)"><strong>${git.uncommittedCount}</strong> necomise</a>: <small>${files}${more}</small></span>
|
||||||
</div>`;
|
|
||||||
git.uncommitted.slice(0, 5).forEach(file => {
|
|
||||||
html += `<div class="status-detail-item uncommitted">
|
|
||||||
<i data-lucide="file"></i>
|
|
||||||
<code>${file}</code>
|
|
||||||
</div>`;
|
|
||||||
});
|
|
||||||
if (git.uncommittedCount > 5) {
|
|
||||||
html += `<div class="status-detail-item uncommitted">
|
|
||||||
<span>... și încă ${git.uncommittedCount - 5}</span>
|
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
html += `<div class="status-detail-item">
|
html += `<div class="status-detail-item">
|
||||||
<i data-lucide="check-circle"></i>
|
<i data-lucide="external-link"></i>
|
||||||
<span>Totul comis ✓</span>
|
<a href="${GITEA_URL}" target="_blank" style="color:var(--accent);font-size:var(--text-xs)">gitea.romfast.ro/romfast/clawd</a>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
|
||||||
|
|
||||||
details.innerHTML = html;
|
details.innerHTML = html;
|
||||||
lucide.createIcons();
|
lucide.createIcons();
|
||||||
@@ -1354,7 +1261,9 @@
|
|||||||
badge.className = 'status-badge ' + (status.anaf.ok !== false ? 'ok' : 'warning');
|
badge.className = 'status-badge ' + (status.anaf.ok !== false ? 'ok' : 'warning');
|
||||||
|
|
||||||
const subtitle = document.getElementById('anafSubtitle');
|
const subtitle = document.getElementById('anafSubtitle');
|
||||||
subtitle.textContent = status.anaf.message || 'Nicio modificare detectată';
|
const lastCheck = status.anaf.lastCheck || '-';
|
||||||
|
const msg = status.anaf.ok !== false ? 'Nicio modificare' : (status.anaf.message || 'Modificări!');
|
||||||
|
subtitle.textContent = `${msg} · ${lastCheck}`;
|
||||||
|
|
||||||
if (status.anaf.lastCheck) {
|
if (status.anaf.lastCheck) {
|
||||||
document.getElementById('anafLastCheck').textContent =
|
document.getElementById('anafLastCheck').textContent =
|
||||||
@@ -1542,51 +1451,9 @@
|
|||||||
|
|
||||||
function refreshActivity() {
|
function refreshActivity() {
|
||||||
loadActivity();
|
loadActivity();
|
||||||
loadStats();
|
|
||||||
showToast('Activitate reîmprospătată');
|
showToast('Activitate reîmprospătată');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadStats() {
|
|
||||||
try {
|
|
||||||
// Load tasks
|
|
||||||
const tasksRes = await fetch('tasks.json');
|
|
||||||
const tasksData = await tasksRes.json();
|
|
||||||
|
|
||||||
// Get done tasks
|
|
||||||
const doneColumn = tasksData.columns.find(c => c.id === 'done');
|
|
||||||
const doneTasks = doneColumn ? doneColumn.tasks : [];
|
|
||||||
|
|
||||||
const now = new Date();
|
|
||||||
const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
||||||
const weekStart = new Date(todayStart);
|
|
||||||
weekStart.setDate(weekStart.getDate() - weekStart.getDay() + 1); // Monday
|
|
||||||
const monthStart = new Date(now.getFullYear(), now.getMonth(), 1);
|
|
||||||
|
|
||||||
let today = 0, week = 0, month = 0;
|
|
||||||
|
|
||||||
doneTasks.forEach(task => {
|
|
||||||
if (!task.completed) return;
|
|
||||||
const completed = new Date(task.completed);
|
|
||||||
if (completed >= todayStart) today++;
|
|
||||||
if (completed >= weekStart) week++;
|
|
||||||
if (completed >= monthStart) month++;
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById('statToday').textContent = today;
|
|
||||||
document.getElementById('statWeek').textContent = week;
|
|
||||||
document.getElementById('statMonth').textContent = month;
|
|
||||||
|
|
||||||
// Count insights
|
|
||||||
const insightsRes = await fetch('/echo/api/files?path=kb/insights');
|
|
||||||
if (insightsRes.ok) {
|
|
||||||
const insightsData = await insightsRes.json();
|
|
||||||
document.getElementById('statInsights').textContent = insightsData.files ? insightsData.files.length : 0;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.log('Stats load error:', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderActivity() {
|
function renderActivity() {
|
||||||
const body = document.getElementById('activityBody');
|
const body = document.getElementById('activityBody');
|
||||||
|
|
||||||
@@ -1607,6 +1474,7 @@
|
|||||||
const typeLabels = {
|
const typeLabels = {
|
||||||
'cron': '⏰ Cron Jobs',
|
'cron': '⏰ Cron Jobs',
|
||||||
'git': '📦 Git Commits',
|
'git': '📦 Git Commits',
|
||||||
|
'git-file': '🔸 Git Changes',
|
||||||
'file': '📄 Fișiere',
|
'file': '📄 Fișiere',
|
||||||
'task': '✅ Task-uri'
|
'task': '✅ Task-uri'
|
||||||
};
|
};
|
||||||
@@ -1617,13 +1485,22 @@
|
|||||||
<i data-lucide="activity"></i>
|
<i data-lucide="activity"></i>
|
||||||
Ultimele 24h
|
Ultimele 24h
|
||||||
</div>
|
</div>
|
||||||
${activityData.map(item => `
|
${activityData.map(item => {
|
||||||
<div class="activity-item" ${item.path ? `onclick="window.open('files.html#${item.path}', '_blank')" style="cursor:pointer"` : ''}>
|
let clickAttr = '';
|
||||||
|
if (item.type === 'git-file' && item.path) {
|
||||||
|
clickAttr = `onclick="window.open('files.html#${item.path}', '_blank')" style="cursor:pointer"`;
|
||||||
|
} else if (item.path) {
|
||||||
|
clickAttr = `onclick="window.open('files.html#${item.path}', '_blank')" style="cursor:pointer"`;
|
||||||
|
} else if (item.type === 'git' && item.commitHash) {
|
||||||
|
clickAttr = `onclick="window.open('https://gitea.romfast.ro/romfast/clawd/commit/${item.commitHash}', '_blank')" style="cursor:pointer"`;
|
||||||
|
}
|
||||||
|
return `
|
||||||
|
<div class="activity-item" ${clickAttr}>
|
||||||
<div class="activity-icon ${item.type}">
|
<div class="activity-icon ${item.type}">
|
||||||
<i data-lucide="${item.icon || 'activity'}"></i>
|
<i data-lucide="${item.icon || 'activity'}"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="activity-content">
|
<div class="activity-content">
|
||||||
<div class="activity-text">${item.text}</div>
|
<div class="activity-text">${item.type === 'git' && item.commitHash ? `<code style="font-size:10px;margin-right:4px">${item.commitHash}</code>` : ''}${item.text}</div>
|
||||||
<div class="activity-meta">
|
<div class="activity-meta">
|
||||||
<span class="activity-type">${typeLabels[item.type] || item.type}</span>
|
<span class="activity-type">${typeLabels[item.type] || item.type}</span>
|
||||||
<span class="activity-agent">${item.agent}</span>
|
<span class="activity-agent">${item.agent}</span>
|
||||||
@@ -1631,7 +1508,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`).join('')}
|
`}).join('')}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
lucide.createIcons();
|
lucide.createIcons();
|
||||||
@@ -1867,7 +1744,6 @@
|
|||||||
loadStatus();
|
loadStatus();
|
||||||
loadIssues();
|
loadIssues();
|
||||||
loadActivity();
|
loadActivity();
|
||||||
loadStats();
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="icon" type="image/svg+xml" href="favicon.svg">
|
||||||
<title>Echo · KB</title>
|
<title>Echo · KB</title>
|
||||||
<link rel="stylesheet" href="common.css">
|
<link rel="stylesheet" href="common.css">
|
||||||
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script>
|
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script>
|
||||||
@@ -1203,7 +1204,7 @@
|
|||||||
try {
|
try {
|
||||||
let content = notesCache[file];
|
let content = notesCache[file];
|
||||||
if (!content) {
|
if (!content) {
|
||||||
const response = await fetch(file);
|
const response = await fetch(file + '?t=' + Date.now());
|
||||||
content = await response.text();
|
content = await response.text();
|
||||||
notesCache[file] = content;
|
notesCache[file] = content;
|
||||||
}
|
}
|
||||||
@@ -1222,7 +1223,7 @@
|
|||||||
async function preloadNotes() {
|
async function preloadNotes() {
|
||||||
for (const note of notesIndex) {
|
for (const note of notesIndex) {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(note.file);
|
const response = await fetch(note.file + '?t=' + Date.now());
|
||||||
notesCache[note.file] = await response.text();
|
notesCache[note.file] = await response.text();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
notesCache[note.file] = '';
|
notesCache[note.file] = '';
|
||||||
|
|||||||
@@ -1,4 +1,19 @@
|
|||||||
{
|
{
|
||||||
"git": {"status": "4 fișiere", "clean": false, "files": 4},
|
"git": {
|
||||||
"lastReport": {"type": "evening", "summary": "notes.html îmbunătățit (filtre colorate), rețetă salvată", "time": "30 Jan 2026, 22:00"}
|
"status": "4 fișiere",
|
||||||
|
"clean": false,
|
||||||
|
"files": 4
|
||||||
|
},
|
||||||
|
"lastReport": {
|
||||||
|
"type": "evening",
|
||||||
|
"summary": "notes.html îmbunătățit (filtre colorate), rețetă salvată",
|
||||||
|
"time": "30 Jan 2026, 22:00"
|
||||||
|
},
|
||||||
|
"anaf": {
|
||||||
|
"ok": true,
|
||||||
|
"status": "OK",
|
||||||
|
"message": "Nicio modificare detectată",
|
||||||
|
"lastCheck": "31 Jan 2026, 13:43",
|
||||||
|
"changesCount": 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
98
kb/PROCES-INSIGHTS.md
Normal file
98
kb/PROCES-INSIGHTS.md
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
# Proces Extragere Insights
|
||||||
|
|
||||||
|
**Scop:** Extrag TOATE ideile acționabile din notele YouTube, nu doar 1-2.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Când se rulează
|
||||||
|
|
||||||
|
- **Morning report** (08:30) - scanează note noi din ultimele 48h
|
||||||
|
- **Evening report** (20:00) - scanează note noi din ultimele 48h
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pași extragere
|
||||||
|
|
||||||
|
### 1. Identifică notele noi
|
||||||
|
```bash
|
||||||
|
find /home/moltbot/clawd/kb/youtube/ -mtime -2 -name "*.md"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Citește COMPLET fiecare notă
|
||||||
|
|
||||||
|
**NU doar TL;DR!** Verifică TOATE secțiunile:
|
||||||
|
- [ ] **TL;DR** - rezumat general
|
||||||
|
- [ ] **Puncte Cheie** - concepte principale
|
||||||
|
- [ ] **Acțiuni Practice** - ce poți face concret
|
||||||
|
- [ ] **Citate** - fraze memorabile care pot deveni provocări
|
||||||
|
- [ ] **Resurse** - linkuri, cărți, tool-uri menționate
|
||||||
|
|
||||||
|
### 3. Pentru fiecare idee acționabilă
|
||||||
|
|
||||||
|
Întreabă-te:
|
||||||
|
- **Este acționabil?** (pot face ceva concret cu asta?)
|
||||||
|
- **Pentru cine?** (stabilește tag-ul)
|
||||||
|
- **De ce contează?** (ce problemă rezolvă?)
|
||||||
|
|
||||||
|
### 4. Stabilește tag-ul
|
||||||
|
|
||||||
|
| Tag | Domeniu | Exemple |
|
||||||
|
|-----|---------|---------|
|
||||||
|
| @work | Productivitate, cod, automatizări | tool-uri, patterns, workflows |
|
||||||
|
| @health | Sănătate, corp, energie | exerciții, nutriție, somn |
|
||||||
|
| @growth | Dezvoltare personală, mindset | tehnici mentale, obiceiuri |
|
||||||
|
| @sprijin | Relații, emoții, grup sprijin | comunicare, conflicte |
|
||||||
|
| @scout | Cercetași, activități | jocuri, tabere, proiecte |
|
||||||
|
|
||||||
|
### 5. Format insight
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
- [ ] 📌 **Titlu scurt și clar** - [Sursa](link)
|
||||||
|
*Context: Ce e, de ce e util, ce problemă rezolvă, cum se aplică*
|
||||||
|
```
|
||||||
|
|
||||||
|
**Prioritate emoji:**
|
||||||
|
- ⚡ Urgent + Important (fă acum)
|
||||||
|
- 📌 Important dar nu urgent (planifică)
|
||||||
|
- 💡 Nice to have (backlog)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Checklist calitate
|
||||||
|
|
||||||
|
Înainte de a termina scanarea, verifică:
|
||||||
|
|
||||||
|
- [ ] Am citit nota COMPLETĂ, nu doar TL;DR?
|
||||||
|
- [ ] Am verificat TOATE secțiunile (Puncte Cheie, Acțiuni, Citate)?
|
||||||
|
- [ ] Fiecare insight are CONTEXT (nu doar titlu)?
|
||||||
|
- [ ] Am stabilit tag-ul corect pentru fiecare?
|
||||||
|
- [ ] Am extras TOATE ideile acționabile, nu doar cele evidente?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Exemple bune vs rele
|
||||||
|
|
||||||
|
❌ **Rău:**
|
||||||
|
```
|
||||||
|
- [ ] 💡 Activitate hero's journey pentru cercetași
|
||||||
|
```
|
||||||
|
(Lipsă context - ce e hero's journey? cum se aplică?)
|
||||||
|
|
||||||
|
✅ **Bun:**
|
||||||
|
```
|
||||||
|
- [ ] 📌 **Activitate hero's journey pentru cercetași** - [Tony Robbins](link)
|
||||||
|
*Context: Viața pare OK → ceva se întâmplă → "call to adventure" (pare sfârșit dar e început).
|
||||||
|
Exercițiu: cercetașii identifică un moment greu din viață care s-a dovedit a fi un dar.*
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Fișiere relevante
|
||||||
|
|
||||||
|
- **Note YouTube:** `/home/moltbot/clawd/kb/youtube/`
|
||||||
|
- **Insights zilnice:** `/home/moltbot/clawd/kb/insights/YYYY-MM-DD.md`
|
||||||
|
- **Backlog:** `/home/moltbot/clawd/kb/backlog.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Creat: 2026-01-31 | Actualizat de Echo Work*
|
||||||
69
kb/coaching/2026-01-31-seara.md
Normal file
69
kb/coaching/2026-01-31-seara.md
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
# Gândul de seară - 2026-01-31
|
||||||
|
|
||||||
|
**Tags:** @health @coaching #tony-robbins #recunostinta #priming
|
||||||
|
|
||||||
|
## Mesaj trimis
|
||||||
|
|
||||||
|
[❤️ Echo] **GÂNDUL DE SEARĂ** 🌙
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Mai întâi: cum a fost provocarea de azi?**
|
||||||
|
|
||||||
|
Dimineață ți-am propus pattern interrupt-ul: ridică-te, 5 respirații, întinde-te, mergi 2 minute.
|
||||||
|
|
||||||
|
Ai încercat? Când te-ai ridicat azi doar ca să te miști, fără scop? Dacă da - ce ai observat?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Acum, pentru seară:
|
||||||
|
|
||||||
|
> *"Work on gratitude and happiness is automatic."*
|
||||||
|
> — Tony Robbins
|
||||||
|
|
||||||
|
**Recunoștința nu se gândește. Se simte.**
|
||||||
|
|
||||||
|
Tony Robbins are un ritual de 10 minute în fiecare dimineață - dar funcționează și seara, înainte de somn. Îl numește **Priming**:
|
||||||
|
|
||||||
|
Nu *gândești* 3 lucruri pentru care ești recunoscător.
|
||||||
|
Le **simți**. Le treci prin corp.
|
||||||
|
|
||||||
|
De ce contează asta seara?
|
||||||
|
|
||||||
|
Pentru că închei ziua nu cu lista de ce ai greșit, ce n-ai terminat, ce te îngrijorează.
|
||||||
|
Ci cu ce a mers. Ce a fost acolo pentru tine. Ce ți-a ieșit.
|
||||||
|
|
||||||
|
> *"What's wrong is always available. So is what's right."*
|
||||||
|
|
||||||
|
Ai de ales ce focusezi.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**🌙 PROVOCAREA DE SEARĂ** *(3 minute, înainte de somn)*
|
||||||
|
|
||||||
|
1. Stai jos, relaxat, ochii închiși
|
||||||
|
2. Gândește-te la un moment de azi care a fost bun
|
||||||
|
3. **Simte-l** - nu doar "a fost ok", ci: unde în corp simți recunoștința? Ce senzație are? Cât de mare poți să o faci?
|
||||||
|
4. Repetă cu încă 2 momente (pot fi mici - un mesaj, o cafea, un râs)
|
||||||
|
|
||||||
|
Nu e exercițiu de gândire pozitivă. E **antrenament de sistem nervos**.
|
||||||
|
|
||||||
|
Adormi cu totul altceva în corp.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Noapte liniștită!* ✨
|
||||||
|
|
||||||
|
## Surse folosite
|
||||||
|
|
||||||
|
- [Tony Robbins - Extraordinary Life](http://100.120.119.70:8080/files.html#kb/youtube/2026-01-31_tony-robbins-secret-extraordinary-life.md) - Priming ritual, recunoștința simțită (nu gândită), citate despre recunoștință și focus
|
||||||
|
- [Insights 2026-01-31](http://100.120.119.70:8080/files.html#kb/insights/2026-01-31.md) - Exercițiu Priming marcat ca integrat
|
||||||
|
|
||||||
|
## Provocarea zilei
|
||||||
|
|
||||||
|
TIP: Recunoștință / Priming seară
|
||||||
|
PROVOCARE: 3 minute înainte de somn - găsește 3 momente bune din zi și SIMTE-le (nu doar gândește). Unde în corp? Ce senzație? Antrenament de sistem nervos.
|
||||||
|
|
||||||
|
## Follow-up provocare dimineață
|
||||||
|
|
||||||
|
Am întrebat dacă a încercat pattern interrupt-ul (ridicat, 5 respirații, întins, mers 2 min) și ce a observat.
|
||||||
51
kb/health/checklist-post-apa.md
Normal file
51
kb/health/checklist-post-apa.md
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# Checklist Post cu Apă
|
||||||
|
|
||||||
|
**Sursă:** [Greșeli frecvente în timpul postului](https://youtu.be/4QjkI0sf64M) - Cristina și alimentația naturală
|
||||||
|
**Tags:** @health #post #water-fasting #detox
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
Postul terapeutic trebuie să fie un **post al stării de bine**. Dacă experimentezi stări de rău, greață sau dureri (altele decât cele de "lucru" localizate) - ceva nu e în regulă.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Checklist
|
||||||
|
|
||||||
|
### Înainte de post (tranziție)
|
||||||
|
- [ ] 1 săptămână tranziție pe vegetale/lichide
|
||||||
|
- [ ] Elimină produsele animale gradual
|
||||||
|
- [ ] Ideal: treci prin sucuri/ciorbe înainte de post complet
|
||||||
|
|
||||||
|
### În timpul postului
|
||||||
|
- [ ] **Clisme zilnice** (sau cel mult la 2 zile) - CRUCIAL!
|
||||||
|
- [ ] Apă de calitate cu minerale (magneziu, calciu, potasiu)
|
||||||
|
- [ ] Evită: apă demineralizată, Bucovina, apă distilată
|
||||||
|
- [ ] Suplimente Ca/Mg efervescente dacă apa e săracă
|
||||||
|
- [ ] Curăță limba cu bicarbonat (limba albă = toxine)
|
||||||
|
- [ ] Monitorizează simptomele - nu le ignora!
|
||||||
|
|
||||||
|
### Mental/Spiritual
|
||||||
|
- [ ] Scop care depășește propria persoană
|
||||||
|
- [ ] Nu doar pentru slăbit - pentru vindecare, rugăciune, alții
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚠️ Semne de alarmă
|
||||||
|
|
||||||
|
**Oprește postul sau fă clismă URGENT dacă:**
|
||||||
|
- Stări de leșin (= intoxicație colon, NU "ispită")
|
||||||
|
- Greață persistentă
|
||||||
|
- Dureri de cap intense
|
||||||
|
- Apăsare, angoasă, anxietate
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 De citit
|
||||||
|
|
||||||
|
- "Jurnalul unui post de 40 de zile cu apă" - Cristina (Meridiane Publishing, Iași)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Notă: Acest checklist e pentru referință. Consultă un specialist înainte de posturi lungi.*
|
||||||
@@ -21,6 +21,47 @@
|
|||||||
"video": "",
|
"video": "",
|
||||||
"tldr": "PROVOCARE: Ridică-te, fă 5 respirații adânci (4-6), întinde-te, mergi 2 minute. Resetare de stare prin corp."
|
"tldr": "PROVOCARE: Ridică-te, fă 5 respirații adânci (4-6), întinde-te, mergi 2 minute. Resetare de stare prin corp."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"file": "notes-data/coaching/2026-01-31-seara.md",
|
||||||
|
"title": "Gândul de seară - 2026-01-31",
|
||||||
|
"date": "2026-01-31",
|
||||||
|
"tags": [
|
||||||
|
"tony-robbins",
|
||||||
|
"recunostinta",
|
||||||
|
"priming"
|
||||||
|
],
|
||||||
|
"domains": [
|
||||||
|
"health"
|
||||||
|
],
|
||||||
|
"types": [
|
||||||
|
"coaching",
|
||||||
|
"reflectie"
|
||||||
|
],
|
||||||
|
"category": "coaching",
|
||||||
|
"project": null,
|
||||||
|
"subdir": null,
|
||||||
|
"video": "",
|
||||||
|
"tldr": "Am întrebat dacă a încercat pattern interrupt-ul (ridicat, 5 respirații, întins, mers 2 min) și ce a observat."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "notes-data/health/checklist-post-apa.md",
|
||||||
|
"title": "Checklist Post cu Apă",
|
||||||
|
"date": "2026-01-31",
|
||||||
|
"tags": [
|
||||||
|
"post",
|
||||||
|
"water-fasting",
|
||||||
|
"detox"
|
||||||
|
],
|
||||||
|
"domains": [
|
||||||
|
"health"
|
||||||
|
],
|
||||||
|
"types": [],
|
||||||
|
"category": "health",
|
||||||
|
"project": null,
|
||||||
|
"subdir": null,
|
||||||
|
"video": "",
|
||||||
|
"tldr": "*Notă: Acest checklist e pentru referință. Consultă un specialist înainte de posturi lungi.*"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"file": "notes-data/insights/2026-01-31.md",
|
"file": "notes-data/insights/2026-01-31.md",
|
||||||
"title": "Insights 2026-01-31",
|
"title": "Insights 2026-01-31",
|
||||||
@@ -56,6 +97,19 @@
|
|||||||
"video": "",
|
"video": "",
|
||||||
"tldr": "*Acest document se actualizează când se modifică fluxul joburilor.*"
|
"tldr": "*Acest document se actualizează când se modifică fluxul joburilor.*"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"file": "notes-data/projects/ssh-access-echo.md",
|
||||||
|
"title": "Acces SSH pentru Echo",
|
||||||
|
"date": "2026-01-31",
|
||||||
|
"tags": [],
|
||||||
|
"domains": [],
|
||||||
|
"types": [],
|
||||||
|
"category": "projects",
|
||||||
|
"project": null,
|
||||||
|
"subdir": null,
|
||||||
|
"video": "",
|
||||||
|
"tldr": "*Actualizat: 2026-01-31*"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"file": "notes-data/projects/grup-sprijin/README.md",
|
"file": "notes-data/projects/grup-sprijin/README.md",
|
||||||
"title": "Grup de Sprijin - Lideri Cercetași",
|
"title": "Grup de Sprijin - Lideri Cercetași",
|
||||||
@@ -188,15 +242,15 @@
|
|||||||
"project": null,
|
"project": null,
|
||||||
"subdir": null,
|
"subdir": null,
|
||||||
"video": "",
|
"video": "",
|
||||||
"tldr": "- Insights trebuie să aibă status clar ca să nu se repete propunerile"
|
"tldr": "- Files: view modes ca Windows Explorer (List/Details/Tiles)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"file": "notes-data/projects/vending-master/README.md",
|
"file": "notes-data/projects/vending-master/README.md",
|
||||||
"title": "Proiect: Vending Master - Integrare Website → ROA",
|
"title": "Proiect: Vending Master - Integrare Website → ROA",
|
||||||
"date": "2026-01-30",
|
"date": "2026-01-30",
|
||||||
"tags": [
|
"tags": [
|
||||||
"integrare",
|
"vending-master",
|
||||||
"vending-master"
|
"integrare"
|
||||||
],
|
],
|
||||||
"domains": [
|
"domains": [
|
||||||
"work"
|
"work"
|
||||||
@@ -661,18 +715,19 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"stats": {
|
"stats": {
|
||||||
"total": 33,
|
"total": 36,
|
||||||
"by_domain": {
|
"by_domain": {
|
||||||
"work": 9,
|
"work": 9,
|
||||||
"health": 5,
|
"health": 7,
|
||||||
"growth": 3,
|
"growth": 3,
|
||||||
"sprijin": 16,
|
"sprijin": 16,
|
||||||
"scout": 1
|
"scout": 1
|
||||||
},
|
},
|
||||||
"by_category": {
|
"by_category": {
|
||||||
"coaching": 1,
|
"coaching": 2,
|
||||||
|
"health": 1,
|
||||||
"insights": 1,
|
"insights": 1,
|
||||||
"projects": 17,
|
"projects": 18,
|
||||||
"retete": 1,
|
"retete": 1,
|
||||||
"youtube": 9,
|
"youtube": 9,
|
||||||
"memory": 3,
|
"memory": 3,
|
||||||
@@ -699,6 +754,7 @@
|
|||||||
],
|
],
|
||||||
"categories": [
|
"categories": [
|
||||||
"coaching",
|
"coaching",
|
||||||
|
"health",
|
||||||
"insights",
|
"insights",
|
||||||
"projects",
|
"projects",
|
||||||
"retete",
|
"retete",
|
||||||
|
|||||||
@@ -10,16 +10,18 @@ Idei extrase din note YouTube. Format: `[ ]` neprocesat, `[x]` făcut, `[→]` b
|
|||||||
|
|
||||||
- [x] ⚡ Verificare securitate Clawdbot (port, trustedProxies) - FĂCUT 2026-01-31 de Echo Work
|
- [x] ⚡ Verificare securitate Clawdbot (port, trustedProxies) - FĂCUT 2026-01-31 de Echo Work
|
||||||
- [x] ⚡ Git commit + push restructurare - FĂCUT 2026-01-31 de Echo Work (99 fișiere)
|
- [x] ⚡ Git commit + push restructurare - FĂCUT 2026-01-31 de Echo Work (99 fișiere)
|
||||||
- [ ] 📌 Template spec-driven development în kb/projects/ - [GSD](https://moltbot.tailf7372d.ts.net/echo/files.html#kb/youtube/2026-01-29_gsd-framework-claude-code.md)
|
- [—] 📌 Template spec-driven development în kb/projects/ - [GSD](https://moltbot.tailf7372d.ts.net/echo/files.html#kb/youtube/2026-01-29_gsd-framework-claude-code.md) - SKIP 2026-01-31
|
||||||
- [ ] 💡 Job proactive coding noaptea (ora 23) - [5 Use Cases](https://moltbot.tailf7372d.ts.net/echo/files.html#kb/youtube/2026-01-30_clawdbot-5-use-cases.md)
|
- [—] 💡 Job proactive coding noaptea (ora 23) - [5 Use Cases](https://moltbot.tailf7372d.ts.net/echo/files.html#kb/youtube/2026-01-30_clawdbot-5-use-cases.md) - SKIP 2026-01-31 (există deja night-execute)
|
||||||
- [ ] 💡 Spellbook - prompt templates cu variabile - [Kitze](https://moltbot.tailf7372d.ts.net/echo/files.html#kb/youtube/2026-01-30_clawdbot-personal-os-kitze.md)
|
- [—] 💡 Spellbook - prompt templates cu variabile - [Kitze](https://moltbot.tailf7372d.ts.net/echo/files.html#kb/youtube/2026-01-30_clawdbot-personal-os-kitze.md) - SKIP 2026-01-31
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## @health - Sănătate
|
## @health - Sănătate
|
||||||
|
|
||||||
- [ ] 📌 Reminder "ridică-te, mergi 2 min" în respirații - [Tony Robbins](https://moltbot.tailf7372d.ts.net/echo/files.html#kb/youtube/2026-01-31_tony-robbins-secret-extraordinary-life.md)
|
- [x] 📌 Reminder "ridică-te, mergi 2 min" în respirații - [Tony Robbins](https://moltbot.tailf7372d.ts.net/echo/files.html#kb/youtube/2026-01-31_tony-robbins-secret-extraordinary-life.md) - FĂCUT 2026-01-31 (actualizat job respiratie-orar cu context Pattern Interrupt)
|
||||||
- [ ] 💡 Checklist post negru (tranziție, clisme, apă) - [Post Apă](https://moltbot.tailf7372d.ts.net/echo/files.html#kb/youtube/2026-01-29_greseli-post-apa.md)
|
- [x] 💡 Checklist post negru (tranziție, clisme, apă) - [Post Apă](https://moltbot.tailf7372d.ts.net/echo/files.html#kb/youtube/2026-01-29_greseli-post-apa.md) - FĂCUT 2026-01-31 (creat kb/health/checklist-post-apa.md)
|
||||||
|
- [ ] 📌 **Apă rece/duș rece pentru deblocare rapidă** - [Tony Robbins](https://moltbot.tailf7372d.ts.net/echo/files.html#kb/youtube/2026-01-31_tony-robbins-secret-extraordinary-life.md)
|
||||||
|
*Context: Schimbarea de temperatură resetează instant starea. Când ești blocat/stresat, duș rece sau apă pe față.*
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -27,8 +29,16 @@ Idei extrase din note YouTube. Format: `[ ]` neprocesat, `[x]` făcut, `[→]` b
|
|||||||
|
|
||||||
- [x] ⚡ Exercițiu Priming în morning-coaching - INTEGRAT în job morning-coaching (citește din insights)
|
- [x] ⚡ Exercițiu Priming în morning-coaching - INTEGRAT în job morning-coaching (citește din insights)
|
||||||
- [x] 📌 Pattern Interrupt tehnica - INTEGRAT în job morning-coaching ca provocare practică
|
- [x] 📌 Pattern Interrupt tehnica - INTEGRAT în job morning-coaching ca provocare practică
|
||||||
- [ ] 💡 Dezvoltă personalitățile agenților (stil, nu doar funcțional) - [Kitze](https://moltbot.tailf7372d.ts.net/echo/files.html#kb/youtube/2026-01-30_clawdbot-personal-os-kitze.md)
|
- [—] 💡 Dezvoltă personalitățile agenților (stil, nu doar funcțional) - [Kitze](https://moltbot.tailf7372d.ts.net/echo/files.html#kb/youtube/2026-01-30_clawdbot-personal-os-kitze.md) - SKIP 2026-01-31
|
||||||
- [x] 💡 "Pentru cine altcineva faci?" - INTEGRAT în coaching dimineață/seară
|
- [x] 💡 "Pentru cine altcineva faci?" - INTEGRAT în coaching dimineață/seară
|
||||||
|
- [ ] 📌 **Tehnica focus pentru anxietate** - [Tony Robbins](https://moltbot.tailf7372d.ts.net/echo/files.html#kb/youtube/2026-01-31_tony-robbins-secret-extraordinary-life.md)
|
||||||
|
*Context: Nu doar CE vezi, ci CUM. Anxietate = imagine mare, aproape, se îndepărtează. Încredere = imagine se apropie. Schimbă perspectiva mental.*
|
||||||
|
- [ ] 📌 **"Can we begin again?"** - [Tony Robbins](https://moltbot.tailf7372d.ts.net/echo/files.html#kb/youtube/2026-01-31_tony-robbins-secret-extraordinary-life.md)
|
||||||
|
*Context: Tehnică de reset în relații/conflicte. În loc să escaladezi, propui reset.*
|
||||||
|
- [ ] 💡 **Cele 5 arii de stăpânit** - [Tony Robbins](https://moltbot.tailf7372d.ts.net/echo/files.html#kb/youtube/2026-01-31_tony-robbins-secret-extraordinary-life.md)
|
||||||
|
*Context: 1.Corp 2.Emoții 3.Relații 4.Timp 5.Finanțe+Spiritualitate. Framework pentru audit personal.*
|
||||||
|
- [ ] 💡 **Obiective nerealiste, timeline strâns** - [Tony Robbins](https://moltbot.tailf7372d.ts.net/echo/files.html#kb/youtube/2026-01-31_tony-robbins-secret-extraordinary-life.md)
|
||||||
|
*Context: Goals mari cu deadline 12-36 luni max. "Unrealistic goals with realistic timeline" forțează acțiune.*
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -37,12 +47,19 @@ Idei extrase din note YouTube. Format: `[ ]` neprocesat, `[x]` făcut, `[→]` b
|
|||||||
- [x] ⚡ Fișă "blocare vs deblocare" - CREAT `kb/projects/grup-sprijin/biblioteca/fisa-blocare-vs-deblocare.md`
|
- [x] ⚡ Fișă "blocare vs deblocare" - CREAT `kb/projects/grup-sprijin/biblioteca/fisa-blocare-vs-deblocare.md`
|
||||||
- [x] 📌 Întrebare "Ce moment greu s-a dovedit cadou?" - CREAT `kb/projects/grup-sprijin/biblioteca/intrebare-moment-greu-cadou.md`
|
- [x] 📌 Întrebare "Ce moment greu s-a dovedit cadou?" - CREAT `kb/projects/grup-sprijin/biblioteca/intrebare-moment-greu-cadou.md`
|
||||||
- [x] 💡 "Pentru cine altcineva faci?" - CREAT `kb/projects/grup-sprijin/biblioteca/intrebare-pentru-cine-altcineva.md`
|
- [x] 💡 "Pentru cine altcineva faci?" - CREAT `kb/projects/grup-sprijin/biblioteca/intrebare-pentru-cine-altcineva.md`
|
||||||
|
- [ ] 📌 **"I love you too much to go this place"** - [Tony Robbins](https://moltbot.tailf7372d.ts.net/echo/files.html#kb/youtube/2026-01-31_tony-robbins-secret-extraordinary-life.md)
|
||||||
|
*Context: Frază pentru a opri escaladarea în conflicte. Recunoaște că mergi într-o direcție proastă și propune oprire.*
|
||||||
|
- [ ] 💡 **Ritualuri de conectare** - [Tony Robbins](https://moltbot.tailf7372d.ts.net/echo/files.html#kb/youtube/2026-01-31_tony-robbins-secret-extraordinary-life.md)
|
||||||
|
*Context: Cina fără telefoane, hot tub catch-up, priviri, atingeri. Fă momentele să conteze.*
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## @scout - Cercetași
|
## @scout - Cercetași
|
||||||
|
|
||||||
- [ ] 💡 Activitate hero's journey pentru cercetași - [Tony Robbins](https://moltbot.tailf7372d.ts.net/echo/files.html#kb/youtube/2026-01-31_tony-robbins-secret-extraordinary-life.md)
|
- [ ] 📌 **Activitate hero's journey pentru cercetași** - [Tony Robbins](https://moltbot.tailf7372d.ts.net/echo/files.html#kb/youtube/2026-01-31_tony-robbins-secret-extraordinary-life.md)
|
||||||
|
*Context: Viața pare OK → ceva se întâmplă → "call to adventure" (pare sfârșit dar e început). Exercițiu: cercetașii identifică un moment greu din viață care s-a dovedit a fi un dar. Conexiune cu povestea lui Tony: la 11 ani era sărac, un străin a adus mâncare → a învățat "strangers care" → acum hrănește milioane.*
|
||||||
|
- [ ] 💡 **"Începe de unde ești"** - [Tony Robbins](https://moltbot.tailf7372d.ts.net/echo/files.html#kb/youtube/2026-01-31_tony-robbins-secret-extraordinary-life.md)
|
||||||
|
*Context: Povestea lui Tony - la 17 ani a hrănit 2 familii, apoi 4, 8, acum 42 milioane. "If I won't give a dime out of a dollar, how will I ever give a million out of 10?" Aplicabil pentru proiecte cercetași de ajutor comunitar.*
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
78
kb/projects/ssh-access-echo.md
Normal file
78
kb/projects/ssh-access-echo.md
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
# Acces SSH pentru Echo
|
||||||
|
|
||||||
|
Instrucțiuni pentru a da acces SSH lui Echo pe un server nou.
|
||||||
|
|
||||||
|
## Cheia SSH publică
|
||||||
|
|
||||||
|
```
|
||||||
|
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO8ULMGdacgLrW3YhNb3+bsUsP60LdEohwNPgpiRAdSE echo@moltbot
|
||||||
|
```
|
||||||
|
|
||||||
|
## Comenzi (rulează ca root)
|
||||||
|
|
||||||
|
### 1. Creează user echo
|
||||||
|
```bash
|
||||||
|
adduser echo
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Adaugă în grupuri necesare (opțional)
|
||||||
|
```bash
|
||||||
|
# Pentru Docker:
|
||||||
|
usermod -aG docker echo
|
||||||
|
|
||||||
|
# Pentru sudo (dacă e nevoie):
|
||||||
|
usermod -aG sudo echo
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Setup SSH key
|
||||||
|
```bash
|
||||||
|
mkdir -p /home/echo/.ssh
|
||||||
|
echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO8ULMGdacgLrW3YhNb3+bsUsP60LdEohwNPgpiRAdSE echo@moltbot" > /home/echo/.ssh/authorized_keys
|
||||||
|
chown -R echo:echo /home/echo/.ssh
|
||||||
|
chmod 700 /home/echo/.ssh
|
||||||
|
chmod 600 /home/echo/.ssh/authorized_keys
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Securizare (blochează parola)
|
||||||
|
```bash
|
||||||
|
passwd -l echo
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Verifică SSH activ
|
||||||
|
```bash
|
||||||
|
systemctl status sshd || apt install -y openssh-server
|
||||||
|
```
|
||||||
|
|
||||||
|
## Comandă rapidă (all-in-one)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
adduser echo --disabled-password --gecos "" && \
|
||||||
|
mkdir -p /home/echo/.ssh && \
|
||||||
|
echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO8ULMGdacgLrW3YhNb3+bsUsP60LdEohwNPgpiRAdSE echo@moltbot" > /home/echo/.ssh/authorized_keys && \
|
||||||
|
chown -R echo:echo /home/echo/.ssh && \
|
||||||
|
chmod 700 /home/echo/.ssh && \
|
||||||
|
chmod 600 /home/echo/.ssh/authorized_keys && \
|
||||||
|
echo "Done! Echo poate accesa: ssh echo@$(hostname -I | awk '{print $1}')"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Pentru LXC Proxmox
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# De pe Proxmox host:
|
||||||
|
pct enter <CTID>
|
||||||
|
|
||||||
|
# Apoi rulează comenzile de mai sus
|
||||||
|
```
|
||||||
|
|
||||||
|
## Servere cu acces configurat
|
||||||
|
|
||||||
|
| Server | IP | User | Acces | Note |
|
||||||
|
|--------|-----|------|-------|------|
|
||||||
|
| portainer | 10.0.20.170 | echo | ✅ | Docker, LXC 100 pe pvemini |
|
||||||
|
| pveelite | 10.0.20.202 | echo | ✅ | Proxmox node, sudo: qm/pct/pvesh |
|
||||||
|
| pvemini | 10.0.20.201 | echo | ✅ | Proxmox node, sudo: qm/pct/pvesh |
|
||||||
|
| pve1 | 10.0.20.200 | echo | ✅ | Proxmox node, sudo: qm/pct/pvesh |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Actualizat: 2026-01-31*
|
||||||
@@ -14,6 +14,7 @@ SCRIPT_DIR = Path(__file__).parent
|
|||||||
CONFIG_FILE = SCRIPT_DIR / "config.json"
|
CONFIG_FILE = SCRIPT_DIR / "config.json"
|
||||||
VERSIONS_FILE = SCRIPT_DIR / "versions.json"
|
VERSIONS_FILE = SCRIPT_DIR / "versions.json"
|
||||||
LOG_FILE = SCRIPT_DIR / "monitor.log"
|
LOG_FILE = SCRIPT_DIR / "monitor.log"
|
||||||
|
DASHBOARD_STATUS = SCRIPT_DIR.parent.parent / "dashboard" / "status.json"
|
||||||
|
|
||||||
SSL_CTX = ssl.create_default_context()
|
SSL_CTX = ssl.create_default_context()
|
||||||
SSL_CTX.check_hostname = False
|
SSL_CTX.check_hostname = False
|
||||||
@@ -171,6 +172,21 @@ def check_page(page, saved_versions):
|
|||||||
log(f"OK: {page_id}")
|
log(f"OK: {page_id}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def update_dashboard_status(has_changes, changes_count):
|
||||||
|
"""Actualizează status.json pentru dashboard"""
|
||||||
|
try:
|
||||||
|
status = load_json(DASHBOARD_STATUS, {})
|
||||||
|
status['anaf'] = {
|
||||||
|
'ok': not has_changes,
|
||||||
|
'status': 'MODIFICĂRI' if has_changes else 'OK',
|
||||||
|
'message': f'{changes_count} modificări detectate' if has_changes else 'Nicio modificare detectată',
|
||||||
|
'lastCheck': datetime.now().strftime('%d %b %Y, %H:%M'),
|
||||||
|
'changesCount': changes_count
|
||||||
|
}
|
||||||
|
save_json(DASHBOARD_STATUS, status)
|
||||||
|
except Exception as e:
|
||||||
|
log(f"ERROR updating dashboard status: {e}")
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
log("=== Starting ANAF monitor v2 ===")
|
log("=== Starting ANAF monitor v2 ===")
|
||||||
|
|
||||||
@@ -184,6 +200,10 @@ def main():
|
|||||||
all_changes.append(result)
|
all_changes.append(result)
|
||||||
|
|
||||||
save_json(VERSIONS_FILE, saved_versions)
|
save_json(VERSIONS_FILE, saved_versions)
|
||||||
|
|
||||||
|
# Update dashboard status
|
||||||
|
update_dashboard_status(len(all_changes) > 0, len(all_changes))
|
||||||
|
|
||||||
log("=== Monitor complete ===")
|
log("=== Monitor complete ===")
|
||||||
|
|
||||||
print(json.dumps({"changes": all_changes}, ensure_ascii=False, indent=2))
|
print(json.dumps({"changes": all_changes}, ensure_ascii=False, indent=2))
|
||||||
|
|||||||
Reference in New Issue
Block a user