omnimem 0.1.6 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/omnimem/__init__.py +1 -1
- package/omnimem/webui.py +99 -0
- package/package.json +1 -1
package/omnimem/__init__.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
__all__ = ["__version__"]
|
|
2
|
-
__version__ = "0.1.
|
|
2
|
+
__version__ = "0.1.7"
|
package/omnimem/webui.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
+
import os
|
|
4
5
|
import sqlite3
|
|
5
6
|
import threading
|
|
6
7
|
import time
|
|
@@ -127,6 +128,20 @@ HTML_PAGE = """<!doctype html>
|
|
|
127
128
|
<div class=\"card wide\">
|
|
128
129
|
<h3 data-i18n=\"project_title\">Project Integration</h3>
|
|
129
130
|
<label><span data-i18n=\"project_path\">Project Path</span><input id=\"projectPath\" placeholder=\"/path/to/your/project\" /></label>
|
|
131
|
+
<div class=\"row-btn\">
|
|
132
|
+
<button id=\"btnBrowseProject\" data-i18n=\"btn_browse_project\">Browse Directory</button>
|
|
133
|
+
<button id=\"btnUseCwd\" data-i18n=\"btn_use_cwd\">Use Server CWD</button>
|
|
134
|
+
</div>
|
|
135
|
+
<div id=\"browserPanel\" class=\"card\" style=\"margin-top:10px; display:none;\">
|
|
136
|
+
<div class=\"small\" data-i18n=\"browser_title\">Directory Browser</div>
|
|
137
|
+
<div id=\"browserPath\" class=\"small\" style=\"margin:8px 0\"></div>
|
|
138
|
+
<div class=\"row-btn\">
|
|
139
|
+
<button id=\"btnBrowserUp\" data-i18n=\"btn_browser_up\">Up</button>
|
|
140
|
+
<button id=\"btnBrowserSelect\" data-i18n=\"btn_browser_select\">Select This Directory</button>
|
|
141
|
+
<button id=\"btnBrowserClose\" data-i18n=\"btn_browser_close\">Close</button>
|
|
142
|
+
</div>
|
|
143
|
+
<div id=\"browserList\" class=\"small\" style=\"margin-top:8px\"></div>
|
|
144
|
+
</div>
|
|
130
145
|
<label><span data-i18n=\"project_id\">Project ID</span><input id=\"projectId\" placeholder=\"my-project\" /></label>
|
|
131
146
|
<div class=\"row-btn\">
|
|
132
147
|
<button id=\"btnProjectAttach\" data-i18n=\"btn_project_attach\">Attach Project + Install Agent Rules</button>
|
|
@@ -176,6 +191,8 @@ HTML_PAGE = """<!doctype html>
|
|
|
176
191
|
mem_recent: 'Recent Memories', mem_hint: 'Click an ID to open full content', mem_content: 'Memory Content',
|
|
177
192
|
th_id: 'ID', th_layer: 'Layer', th_kind: 'Kind', th_summary: 'Summary', th_updated: 'Updated At',
|
|
178
193
|
project_title: 'Project Integration', project_path: 'Project Path', project_id: 'Project ID',
|
|
194
|
+
btn_browse_project: 'Browse Directory', btn_use_cwd: 'Use Server CWD',
|
|
195
|
+
browser_title: 'Directory Browser', btn_browser_up: 'Up', btn_browser_select: 'Select This Directory', btn_browser_close: 'Close',
|
|
179
196
|
btn_project_attach: 'Attach Project + Install Agent Rules', btn_project_detach: 'Detach Project',
|
|
180
197
|
project_hint: 'Attach will create .omnimem files and inject managed memory protocol blocks into AGENTS.md / CLAUDE.md / .cursorrules.',
|
|
181
198
|
cfg_saved: 'Configuration saved', cfg_failed: 'Save failed',
|
|
@@ -293,6 +310,7 @@ HTML_PAGE = """<!doctype html>
|
|
|
293
310
|
let currentLang = safeGetLang();
|
|
294
311
|
if (!I18N[currentLang]) currentLang = 'en';
|
|
295
312
|
let daemonCache = { running:false, enabled:false, initialized:false };
|
|
313
|
+
let browserPath = '';
|
|
296
314
|
|
|
297
315
|
function t(key) {
|
|
298
316
|
const dict = I18N[currentLang] || I18N.en;
|
|
@@ -401,6 +419,30 @@ HTML_PAGE = """<!doctype html>
|
|
|
401
419
|
document.getElementById('status').innerHTML = d.ok ? `<span class=\"ok\">${t('project_detach_ok')}</span>` : `<span class=\"err\">${t('project_failed')}</span>`;
|
|
402
420
|
}
|
|
403
421
|
|
|
422
|
+
function escHtml(v) {
|
|
423
|
+
return String(v).replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>').replaceAll('\"', '"');
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
async function listDirs(path) {
|
|
427
|
+
const d = await jget('/api/fs/list?path=' + encodeURIComponent(path || ''));
|
|
428
|
+
if (!d.ok) {
|
|
429
|
+
document.getElementById('browserList').innerHTML = `<span class=\"err\">${escHtml(d.error || 'list failed')}</span>`;
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
browserPath = d.path || '';
|
|
433
|
+
document.getElementById('browserPath').textContent = browserPath;
|
|
434
|
+
const rows = (d.items || [])
|
|
435
|
+
.map(x => `<div><a href=\"#\" data-path=\"${escHtml(x.path)}\">${escHtml(x.name)}/</a></div>`)
|
|
436
|
+
.join('');
|
|
437
|
+
document.getElementById('browserList').innerHTML = rows || '<span class=\"small\">(empty)</span>';
|
|
438
|
+
document.querySelectorAll('#browserList a').forEach(a => {
|
|
439
|
+
a.onclick = (e) => {
|
|
440
|
+
e.preventDefault();
|
|
441
|
+
listDirs(a.dataset.path || '');
|
|
442
|
+
};
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
|
|
404
446
|
function bindTabs() {
|
|
405
447
|
const btns = document.querySelectorAll('.tab-btn');
|
|
406
448
|
btns.forEach(btn => {
|
|
@@ -429,6 +471,39 @@ HTML_PAGE = """<!doctype html>
|
|
|
429
471
|
document.getElementById('btnDaemonOff').onclick = () => toggleDaemon(false);
|
|
430
472
|
document.getElementById('btnProjectAttach').onclick = () => attachProject();
|
|
431
473
|
document.getElementById('btnProjectDetach').onclick = () => detachProject();
|
|
474
|
+
document.getElementById('btnBrowseProject').onclick = async () => {
|
|
475
|
+
document.getElementById('browserPanel').style.display = 'block';
|
|
476
|
+
await listDirs(document.getElementById('projectPath').value.trim() || '');
|
|
477
|
+
};
|
|
478
|
+
document.getElementById('btnBrowserUp').onclick = async () => {
|
|
479
|
+
if (!browserPath) return;
|
|
480
|
+
const p = browserPath.replace(/\/+$/, '');
|
|
481
|
+
const i = p.lastIndexOf('/');
|
|
482
|
+
const up = i > 0 ? p.slice(0, i) : '/';
|
|
483
|
+
await listDirs(up);
|
|
484
|
+
};
|
|
485
|
+
document.getElementById('btnBrowserSelect').onclick = () => {
|
|
486
|
+
document.getElementById('projectPath').value = browserPath;
|
|
487
|
+
const pid = document.getElementById('projectId');
|
|
488
|
+
if (!pid.value.trim() && browserPath) {
|
|
489
|
+
const s = browserPath.replace(/\/+$/, '').split('/');
|
|
490
|
+
pid.value = s[s.length - 1] || 'project';
|
|
491
|
+
}
|
|
492
|
+
};
|
|
493
|
+
document.getElementById('btnBrowserClose').onclick = () => {
|
|
494
|
+
document.getElementById('browserPanel').style.display = 'none';
|
|
495
|
+
};
|
|
496
|
+
document.getElementById('btnUseCwd').onclick = async () => {
|
|
497
|
+
const d = await jget('/api/fs/cwd');
|
|
498
|
+
if (d.ok) {
|
|
499
|
+
document.getElementById('projectPath').value = d.cwd;
|
|
500
|
+
const pid = document.getElementById('projectId');
|
|
501
|
+
if (!pid.value.trim()) {
|
|
502
|
+
const s = d.cwd.replace(/\/+$/, '').split('/');
|
|
503
|
+
pid.value = s[s.length - 1] || 'project';
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
};
|
|
432
507
|
}
|
|
433
508
|
|
|
434
509
|
window.addEventListener('error', (e) => {
|
|
@@ -687,6 +762,30 @@ def run_webui(
|
|
|
687
762
|
self._send_json({"ok": True, **daemon_state})
|
|
688
763
|
return
|
|
689
764
|
|
|
765
|
+
if parsed.path == "/api/fs/cwd":
|
|
766
|
+
self._send_json({"ok": True, "cwd": str(Path.cwd())})
|
|
767
|
+
return
|
|
768
|
+
|
|
769
|
+
if parsed.path == "/api/fs/list":
|
|
770
|
+
q = parse_qs(parsed.query)
|
|
771
|
+
raw_path = q.get("path", [""])[0].strip()
|
|
772
|
+
base = Path(raw_path).expanduser() if raw_path else Path.home()
|
|
773
|
+
try:
|
|
774
|
+
p = base.resolve()
|
|
775
|
+
if not p.exists() or not p.is_dir():
|
|
776
|
+
self._send_json({"ok": False, "error": f"not a directory: {p}"}, 400)
|
|
777
|
+
return
|
|
778
|
+
items = []
|
|
779
|
+
for child in sorted(p.iterdir(), key=lambda x: x.name.lower()):
|
|
780
|
+
if child.is_dir() and not child.name.startswith("."):
|
|
781
|
+
items.append({"name": child.name, "path": str(child)})
|
|
782
|
+
if len(items) >= 200:
|
|
783
|
+
break
|
|
784
|
+
self._send_json({"ok": True, "path": str(p), "items": items})
|
|
785
|
+
except Exception as exc: # pragma: no cover
|
|
786
|
+
self._send_json({"ok": False, "error": str(exc)}, 500)
|
|
787
|
+
return
|
|
788
|
+
|
|
690
789
|
if parsed.path == "/api/project/defaults":
|
|
691
790
|
self._send_json(
|
|
692
791
|
{
|