Done—Step Two is now real: dynamic plugins + scaffolder + admin refresh.

Download (fresh):

  • solveforce_phone_two.pyDownload
  • SHA‑256: 745183884701b5c7c7dcc4b8d77abcd50ebcb1776ad67455eef26a9767031c79

What Step Two adds

  1. Dynamic plugin auto‑loader
    • --plugins-dir <dir>: load every *.py plugin at boot.
    • /plugins: lists all plugins (builtin + dynamic), their origin, file path, and any loader errors.
    • No overrides: dynamic plugins can’t shadow built‑ins; collisions are reported.
  2. Hot reload (opt‑in admin)
    • --allow-admin --admin-token "<secret>" to enable admin routes.
    • /admin/refresh?token=<secret>: rescans --plugins-dir and reloads plugins safely.
    • /admin/scaffold?name=<plugin>&token=<secret>: writes a new stub plugin file (see safety below).
  3. Scaffolder (CLI)
    • Create a plugin file from your phone without opening an editor: python solveforce_phone_two.py --plugins-dir plugins --scaffold hello # -> creates plugins/hello.py (read-only stub)
    • Safe names only: [A-Za-z0-9_]. If the file exists, it won’t overwrite.
  4. Dynamic UI
    • /ui now fetches /plugins and renders buttons for every plugin—no code edits required to expose new ones.
  5. Everything from Step One remains
    • /health, /state, /read, /translate, /metrics, JSONL exports (--export, --export-dir), optional poller (--poll N).

Phone commands (Termux)

Local only:

python solveforce_phone_two.py --host 127.0.0.1 --port 8080 --poll 0

LAN + exports + dynamic plugins:

mkdir -p /sdcard/solveforce/exports ~/solveforce/plugins
python solveforce_phone_two.py \
  --host 0.0.0.0 --port 8080 --poll 5 \
  --export --export-dir /sdcard/solveforce/exports \
  --plugins-dir ~/solveforce/plugins

Enable admin hot‑reload + scaffolding (optional, guarded):

python solveforce_phone_two.py \
  --host 0.0.0.0 --port 8080 \
  --plugins-dir ~/solveforce/plugins \
  --allow-admin --admin-token S3cRet123

Now you can hit:

  • /admin/refresh?token=S3cRet123
  • /admin/scaffold?name=hello&token=S3cRet123

Default is safe: admin routes are disabled unless you pass --allow-admin. A token is required if set.


How dynamic plugins work

File: ~/solveforce/plugins/hello.py

from solveforce_phone_two import Plugin

class HelloPlugin(Plugin):
    NAME = "hello"
    ORIGIN = "dynamic"
    def read(self):
        from datetime import datetime
        return {"hello": "world", "ts": datetime.utcnow().isoformat()+"Z"}

# explicit form also supported:
# PLUGIN = HelloPlugin
  • The loader accepts either PLUGIN = <subclass or instance> or the first Plugin subclass it finds.
  • On /plugins, you’ll see: { "plugins": {"hello": {"origin": "dynamic", "path": "/.../plugins/hello.py"}, ...}, "dyn_errors": [] }
  • Press the hello button in /ui to read it.
  • Exports: if --export is on, each read writes exports/hello.jsonl.

Safety & Transparency (short, stern, honest)

  • Read‑only by design: plugins implement read() only. No control paths are shipped.
  • Admin endpoints are OFF unless you turn them on explicitly.
  • Scaffolder sanitizes names and never overwrites existing files.
  • No implicit network exposure: we bind to 127.0.0.1 by default; 0.0.0.0 is your active decision.

Suggestion for your public page

Add a new section:

Step Two — Dynamic Plugins (Auto‑Loader + Scaffolder)

  • --plugins-dir to point at a folder on the phone.
  • /plugins lists runtime plugins, builtin vs. dynamic.
  • /admin/refresh (with token) reloads without restarts.
  • --scaffold NAME to generate a safe stub: python solveforce_phone_two.py --plugins-dir plugins --scaffold meter
  • Minimal stub template (published inline) so the community can see exactly what’s generated.

If you want Step Three (richer metrics + live /events stream for the UI), say the word and I’ll ship it fully wired.


Step Three shipped—live events + richer metrics on top of the dynamic plugin core. – SolveForce Communications