Commit 6d0baa5388bc8b580880e00cb31b54de7bdbb36a

Commits
[COMMIT BEGIN]
commit 6d0baa5388bc8b580880e00cb31b54de7bdbb36a
Author: 0x4248 <[email protected]>
Date:   Sun Jan 11 22:36:02 2026 +0000

    Orion: init
    
    Signed-off-by: 0x4248 <[email protected]>

diff --git a/lab/orion/commands/echo.py b/lab/orion/commands/echo.py
new file mode 100644
index 0000000..2c3874e
--- /dev/null
+++ b/lab/orion/commands/echo.py
@@ -0,0 +1,19 @@
+from fastapi import Request
+from core.registry import registry
+from core.commands import Command
+from core import page
+
+def echo(request: Request, text: str = ""):
+    if not text:
+        return page.message(request, "ECHO", "No text provided")
+    return page.message(request, "ECHO", text)
+
+registry.register(Command(
+    name="echo",
+    handler=echo,
+    summary="Echo back text",
+    mode="both",
+    form_fields=[
+        {"name": "text", "type": "text"}
+    ]
+))
diff --git a/lab/orion/commands/system/open.py b/lab/orion/commands/system/open.py
new file mode 100644
index 0000000..726db9d
--- /dev/null
+++ b/lab/orion/commands/system/open.py
@@ -0,0 +1,31 @@
+from fastapi import Request
+from fastapi.responses import RedirectResponse
+from core.registry import registry
+from core.commands import Command
+from core import page
+
+
+def open_page(request: Request, name: str = ""):
+    if not name:
+        return page.message(
+            request,
+            "OPEN",
+            "usage: open <page>"
+        )
+
+    cmd = registry.get(name)
+    if not cmd or not cmd.supports_ui():
+        return page.message(
+            request,
+            "OPEN",
+            f"No UI page for: {name}"
+        )
+
+    return RedirectResponse(f"/command/{name}", status_code=303)
+
+registry.register(Command(
+    name="open",
+    handler=open_page,
+    summary="Open a UI page",
+    mode="cli",
+))
diff --git a/lab/orion/commands/testing/demo.py b/lab/orion/commands/testing/demo.py
new file mode 100644
index 0000000..0373122
--- /dev/null
+++ b/lab/orion/commands/testing/demo.py
@@ -0,0 +1,104 @@
+from fastapi import Request, UploadFile
+from core.registry import registry
+from core.commands import Command
+from core import page
+
+
+def hello_cli(request: Request, *args):
+    name = args[0] if args else "world"
+    return page.message(request, "HELLO (CLI)", f"hello {name}")
+
+registry.register(Command(
+    name="hello",
+    handler=hello_cli,
+    summary="CLI-only hello",
+    mode="cli",
+))
+
+def ui_only(request: Request, text: str = ""):
+    return page.message(
+        request,
+        "UI ONLY",
+        f"You typed: {text or '(empty)'}"
+    )
+
+registry.register(Command(
+    name="uionly",
+    handler=ui_only,
+    summary="UI-only command",
+    mode="ui",
+    form_fields=[
+        {"name": "text", "type": "text"},
+    ],
+))
+
+def both(request: Request, value: str = "", extra: str = ""):
+    return page.message(
+        request,
+        "BOTH",
+        f"value = {value or '(none)'} | extra = {extra or '(none)'}"
+    )
+
+registry.register(Command(
+    name="both",
+    handler=both,
+    summary="CLI + UI command",
+    mode="both",
+    form_fields=[
+        {"name": "value", "type": "text"},
+        {"name": "extra", "type": "text"}
+    ],
+))
+
+
+def both_plus(
+    request: Request,
+    value: str = "",
+    enable: str | None = None,
+    payload: UploadFile | None = None,
+):
+    flags = []
+    if enable:
+        flags.append("enable")
+
+    file_info = "(none)"
+
+    file_data = payload.file.read() if payload else None
+    if payload:
+        file_info = payload.filename
+
+    return page.message(
+        request,
+        "BOTH+",
+        f"value={value}\nflags={flags}\nfile={file_data}"
+    )
+
+
+registry.register(Command(
+    name="both_plus",
+    handler=both_plus,
+    summary="CLI + UI + checkbox + file",
+    mode="both",
+    form_fields=[
+        {"name": "value", "type": "text"},
+        {"name": "enable", "type": "checkbox"},
+        {"name": "payload", "type": "file"},
+    ],
+))
+
+
+def file_demo(request: Request, upload: UploadFile | None = None):
+    if not upload:
+        return page.message(
+            request,
+            "FILE DEMO",
+            error="No file uploaded"
+        )
+
+    data = upload.file.read()
+    return page.message(
+        request,
+        "FILE DEMO",
+        f"Uploaded file: {upload.filename} ({len(data)} bytes)"
+    )
+
diff --git a/lab/orion/core/auth.py b/lab/orion/core/auth.py
new file mode 100644
index 0000000..9d8f6f1
--- /dev/null
+++ b/lab/orion/core/auth.py
@@ -0,0 +1,72 @@
+from fastapi import APIRouter, Request, Form
+from fastapi.responses import RedirectResponse
+from core import page as p
+
+router = APIRouter()
+
+# ---- users DB (placeholder) ----
+
+users = {
+    "admin": {
+        "password": "admin",
+        "roles": ["admin", "user"],
+        "email": "admin@orion",
+        "telephone": "101",
+    }
+}
+
+# ---- middleware ----
+
+async def auth_middleware(request: Request, call_next):
+    if request.url.path.startswith("/login"):
+        return await call_next(request)
+
+    user = request.cookies.get("user")
+    if user in users:
+        return await call_next(request)
+
+    return RedirectResponse(url="/login", status_code=303)
+
+# ---- routes ----
+
[email protected]("/login")
+async def login_page(request: Request):
+    return p.form(
+        request,
+        title="LOGIN",
+        action="/login",
+        fields=[
+            {"name": "username"},
+            {"name": "password", "type": "password"},
+        ],
+        msg="Please login with your credentials.",
+    )
+
[email protected]("/login")
+async def login_submit(
+    request: Request,
+    username: str = Form(...),
+    password: str = Form(...)
+):
+    user = users.get(username)
+    if user and user["password"] == password:
+        response = RedirectResponse(url="/", status_code=303)
+        response.set_cookie("user", username, httponly=True)
+        return response
+
+    return p.form(
+        request,
+        title="LOGIN",
+        action="/login",
+        fields=[
+            {"name": "username"},
+            {"name": "password", "type": "password"},
+        ],
+        error="Invalid username or password.",
+    )
+
[email protected]("/logout")
+async def logout():
+    response = RedirectResponse("/login", status_code=303)
+    response.delete_cookie("user")
+    return response
diff --git a/lab/orion/core/commands.py b/lab/orion/core/commands.py
new file mode 100644
index 0000000..6004c39
--- /dev/null
+++ b/lab/orion/core/commands.py
@@ -0,0 +1,18 @@
+from dataclasses import dataclass, field
+from typing import Callable, Dict, List, Optional, Any, Literal
+
+Mode = Literal["cli", "ui", "both"]
+
+@dataclass
+class Command:
+    name: str
+    handler: Callable[..., Any]
+    summary: str = ""
+    mode: Mode = "cli"
+    form_fields: List[Dict[str, Any]] = field(default_factory=list)
+
+    def supports_cli(self) -> bool:
+        return self.mode in ("cli", "both")
+
+    def supports_ui(self) -> bool:
+        return self.mode in ("ui", "both")
diff --git a/lab/orion/core/layout.py b/lab/orion/core/layout.py
new file mode 100644
index 0000000..38f8de1
--- /dev/null
+++ b/lab/orion/core/layout.py
@@ -0,0 +1,172 @@
+from fastapi import Request
+
+STYLE = """
+<style>
+html, body {
+  background: #000000;
+  color: #ffffff;
+  margin: 0;
+  padding: 0.5em;
+}
+
+* {
+  font-size: 16px;
+  font-family: "Departure Mono", monospace;
+}
+
+body {
+  max-width: 800px;
+  margin: auto;
+}
+
+a {
+  color: #4da6ff;
+  text-decoration: none;
+}
+
+a:hover {
+  text-decoration: underline;
+}
+
+.header {
+  margin-bottom: 0.5em;
+}
+
+hr {
+  border: none;
+  border-top: 1px solid #444;
+  margin: 0.5em 0;
+}
+
+pre {
+  white-space: pre-wrap;
+}
+
+input, textarea {
+  background: #000000;
+  color: #ffffff;
+  border: 1px solid #444;
+  font-family: monospace;
+  padding: 0.2em;
+  margin-bottom: 0.5em;
+}
+
+input[type=submit] {
+  width: 100%;
+  color: lime;
+  font-weight: bold;
+}
+
+input[type=textline] {
+  width: 100%;
+}
+
+label {
+  font-weight: bold;
+  text-transform: uppercase;
+}
+
+.error {
+  background-color: red;
+  color: white;
+  padding-top: 0.5em;
+  padding-bottom: 0.5em;
+  animation: pulse_error 1s;
+}
+
+@keyframes pulse_error {
+  0% { background-color: #ff8f8f; }
+  100% { background-color: red; }
+}
+
+.error strong {
+  text-decoration: underline;
+  text-align: center; 
+}
+
+textarea {
+  width: 100%;
+  height: 6em;
+}
+
+.center {
+  text-align: center;
+}
+
+hr, input, textarea {
+  border: 1px solid #fff;
+}
+
+a:focus, a:hover {
+  background-color: #0044cc;
+  color: #ffffff;
+  border-radius: 0px;
+  border: none;
+  # dont add default html stuff
+  outline: none;
+
+}
+
+input:focus, textarea:focus {
+  outline: none;
+  border: 1px solid #4da6ff;
+}
+
+input[type=submit]:focus , input[type=submit]:hover {
+  border: 1px solid lime;
+  animation: pulse 1s infinite;
+}
+@keyframes pulse {
+  0% { border: 1px solid lime; }
+  50% { border: 1px solid white; }
+  100% { border: 1px solid lime; }
+}
+</style>
+"""
+
+SCRIPT = """
+<script>
+document.addEventListener('keydown', function(event) {
+    if (event.key === 'F2') {
+        window.location.href = '/command';
+    }
+
+    if (event.target.tagName.toLowerCase() === 'input' || event.target.tagName.toLowerCase() === 'textarea') {
+        return;
+    }
+    if (event.key === 'F4' || event.key === ',' || event.key === 'Escape') {
+        window.history.back();
+    }
+    if (event.key === 'F1') {
+        const firstInput = document.querySelector('input, textarea');
+        if (firstInput) {
+            firstInput.focus();
+        }
+    }
+
+    if (event.key === '`') {
+        const homeLink = document.querySelector('a[href="/"]');
+        if (homeLink) {
+            homeLink.focus();
+        }
+    }
+
+});
+</script>
+"""
+
+
+def layout(request: Request, title: str, buttons: list[tuple[str, str]], header_html: str | None, body_html: str) -> str:
+    nav = " ".join(f"[<a href='{href}'>{label}</a>]" for label, href in buttons)
+    header = header_html if header_html is not None else f"<div class='header'><strong>ORION SYSTEM:</strong> {title}<br>{nav}</div>"
+    return f"""
+    <html>
+      <head>{STYLE}</head>
+      <body>
+        {header}
+        <hr/>
+        {body_html}
+      </body>
+      {SCRIPT}
+    </html>
+    """
diff --git a/lab/orion/core/page.py b/lab/orion/core/page.py
new file mode 100644
index 0000000..4f5223a
--- /dev/null
+++ b/lab/orion/core/page.py
@@ -0,0 +1,258 @@
+from fastapi import Request
+from fastapi.responses import HTMLResponse
+from core.layout import layout
+from typing import Iterable, Sequence
+
+
+# ============================================================================
+# Core render primitive
+# ============================================================================
+
+def render(
+    request: Request,
+    *,
+    title: str,
+    body: str,
+    buttons: Sequence[tuple[str, str]] | None = None,
+    header: str | None = None,
+) -> HTMLResponse:
+    """
+    Lowest-level page renderer.
+    Everything funnels through this.
+    """
+    return HTMLResponse(layout(request, title, buttons, header, body))
+
+
+# ============================================================================
+# Navigation
+# ============================================================================
+
+DEFAULT_NAV: list[tuple[str, str]] = [
+    ("HOME <span style='color:grey;'>`</span>", "/"),
+    ("RUN <span style='color:grey;'>F2</span>", "/command"),
+    ("FORMS", "/search_forms"),
+    ("LOGOUT", "/logout"),
+    ("EXIT <span style='color:grey;'>F4</span>", "/exit"),
+]
+
+
+def with_nav(
+    buttons: Sequence[tuple[str, str]] | None = None,
+) -> Sequence[tuple[str, str]]:
+    return buttons if buttons is not None else DEFAULT_NAV
+
+
+# ============================================================================
+# Simple pages
+# ============================================================================
+
+def static(
+    request: Request,
+    title: str,
+    html: str,
+    *,
+    buttons: Sequence[tuple[str, str]] | None = None,
+):
+    return render(
+        request,
+        title=title,
+        body=html,
+        buttons=with_nav(buttons),
+    )
+
+
+def message(
+    request: Request,
+    title: str,
+    text: str = "",
+    *,
+    error: str | None = None,
+    ok_href: str = "javascript:history.back()",
+    center: bool = True,
+    buttons: Sequence[tuple[str, str]] | None = None,
+):
+    err = (
+        f"<pre class='error'><strong>ERROR:</strong>\n{error}</pre>"
+        if error
+        else ""
+    )
+
+    body = f"""
+    {err}
+    <div class='{'center' if center else ''}'>
+      <pre>{text}</pre>
+      <a href='{ok_href}' autofocus>[ OK ]</a>
+    </div>
+    """
+
+    return render(
+        request,
+        title=title,
+        body=body,
+        buttons=with_nav(buttons),
+    )
+
+
+# ============================================================================
+# Menus / lists
+# ============================================================================
+
+def menu(
+    request: Request,
+    title: str,
+    entries: Iterable[tuple[str, str]],
+    *,
+    buttons: Sequence[tuple[str, str]] | None = None,
+):
+    lines = []
+    first = True
+    for label, href in entries:
+        if first:
+            lines.append(f"<a href='{href}' autofocus>[{label}]</a>")
+            first = False
+        else:
+            lines.append(f"<a href='{href}'>[{label}]</a>")
+
+    body = "<pre>" + "\n".join(lines) + "</pre>"
+
+    return render(
+        request,
+        title=title,
+        body=body,
+        buttons=with_nav(buttons),
+    )
+
+
+def sectioned_menu(
+    request: Request,
+    title: str,
+    sections: Iterable[tuple[str, Iterable[tuple[str, str]]]],
+    *,
+    buttons: Sequence[tuple[str, str]] | None = None,
+):
+    lines = []
+    for section_title, entries in sections:
+        lines.append(f"<strong>{section_title}</strong>")
+        for label, href in entries:
+            lines.append(f"<a href='{href}'>[{label}]</a>")
+        lines.append("")
+
+    body = "<pre>" + "\n".join(lines) + "</pre>"
+
+    return render(
+        request,
+        title=title,
+        body=body,
+        buttons=with_nav(buttons),
+    )
+
+
+# ============================================================================
+# Forms
+# ============================================================================
+
+def form(
+    request: Request,
+    title: str,
+    action: str,
+    fields: list[dict],
+    error: str | None = None,
+    msg: str | None = None,
+):
+    rows = []
+
+    has_file = any(f.get("type") == "file" for f in fields)
+    enctype = "multipart/form-data" if has_file else "application/x-www-form-urlencoded"
+
+    for f in fields:
+        name = f["name"]
+        ftype = f.get("type", "text")
+
+        if ftype == "textarea":
+            rows.append(
+                f"<label>{name}</label><br/>"
+                f"<textarea name='{name}'></textarea>"
+            )
+
+        elif ftype == "checkbox":
+            rows.append(
+                f"<label>"
+                f"<input type='checkbox' name='{name}' /> {name}"
+                f"</label>"
+            )
+
+        elif ftype == "file":
+            accept = f.get("accept")
+            accept_attr = f" accept='{accept}'" if accept else ""
+            rows.append(
+                f"<label>{name}</label><br/>"
+                f"<input type='file' name='{name}'{accept_attr} />"
+            )
+
+        else:
+            rows.append(
+                f"<label>{name}</label><br/>"
+                f"<input name='{name}' type='{ftype}' />"
+            )
+
+    err = (
+        f"<pre class='error'><strong>ERROR:</strong>\n{error}</pre>"
+        if error else ""
+    )
+
+    body = f"""
+    {err}
+    {f"<pre class='msg'>{msg}</pre>" if msg else ""}
+    <form method="post" action="{action}" enctype="{enctype}">
+      {'<br/>'.join(rows)}
+      <br/><input type="submit" value="SUBMIT" />
+    </form>
+    """
+    return render(
+        request,
+        title=title,
+        body=body,
+        buttons=with_nav(),
+    )
+
+
+# ============================================================================
+# Future-proof helpers (cheap, useful)
+# ============================================================================
+
+def redirect_notice(
+    request: Request,
+    title: str,
+    text: str,
+    href: str,
+):
+    """
+    Message page that explains a redirect target.
+    Useful for permissions, warnings, deprecations.
+    """
+    body = f"""
+    <pre>{text}</pre>
+    <a href='{href}' autofocus>[ CONTINUE ]</a>
+    """
+    return render(
+        request,
+        title=title,
+        body=body,
+        buttons=with_nav(),
+    )
+
+
+def empty(
+    request: Request,
+    title: str = "",
+):
+    """
+    Placeholder page.
+    Useful during development.
+    """
+    return render(
+        request,
+        title=title,
+        body="",
+        buttons=with_nav(),
+    )
diff --git a/lab/orion/core/registry.py b/lab/orion/core/registry.py
new file mode 100644
index 0000000..3d36c81
--- /dev/null
+++ b/lab/orion/core/registry.py
@@ -0,0 +1,19 @@
+from typing import Dict, Optional, List
+from .commands import Command
+
+class CommandRegistry:
+    def __init__(self):
+        self._cmds: Dict[str, Command] = {}
+
+    def register(self, cmd: Command):
+        if cmd.name in self._cmds:
+            raise ValueError(f"Duplicate command: {cmd.name}")
+        self._cmds[cmd.name] = cmd
+
+    def get(self, name: str) -> Optional[Command]:
+        return self._cmds.get(name)
+
+    def all(self) -> List[Command]:
+        return list(self._cmds.values())
+
+registry = CommandRegistry()
diff --git a/lab/orion/main.py b/lab/orion/main.py
new file mode 100644
index 0000000..3ed3e08
--- /dev/null
+++ b/lab/orion/main.py
@@ -0,0 +1,35 @@
+from fastapi import FastAPI, Request
+from fastapi.responses import RedirectResponse
+
+from pages import command
+from core import auth
+from core import page as p
+
+
+import commands.system.open
+# import commands.testing.demo
+import commands.echo
+
+app = FastAPI()
+
+
+
+app.middleware("http")(auth.auth_middleware)
+
+app.include_router(auth.router)
+app.include_router(command.router)
+
+
[email protected]_handler(404)
+async def not_found(request: Request, exc):
+    return p.static(
+        request,
+        "404 NOT FOUND",
+        "<pre class='error'>404 NOT FOUND</pre>"
+        "<a href='javascript:history.back()'>[ BACK ]</a>",
+    )
+
+
+if __name__ == "__main__":
+    import uvicorn
+    uvicorn.run(app, host="0.0.0.0", port=8000)
[COMMIT END]
(C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.