@agentikos/omega-os 0.19.23 → 0.19.25
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/bootstrap/lib/steps.sh +9 -0
- package/omega/Agentik_Engine/omega_engine/__init__.py +1 -1
- package/omega/Agentik_Engine/omega_engine/__pycache__/__init__.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/cli.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/tmux.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/cli.py +301 -139
- package/omega/Agentik_Engine/omega_engine/tmux.py +35 -0
- package/omega/Agentik_Engine/pyproject.toml +1 -1
- package/omega/Agentik_SSOT/VERSION +1 -1
- package/omega/Agentik_SSOT/clis/clis-catalog.yaml +13 -0
- package/package.json +1 -1
package/bootstrap/lib/steps.sh
CHANGED
|
@@ -564,6 +564,15 @@ for entry in missing:
|
|
|
564
564
|
if shutil.which(binary):
|
|
565
565
|
print(f" ok {cli_id:<14} (installed via {attempt})")
|
|
566
566
|
installed_now.append(cli_id)
|
|
567
|
+
# Optional post-install command (e.g. `scrapling install`
|
|
568
|
+
# downloads its browser deps after pip install).
|
|
569
|
+
post = entry.get("post_install") or []
|
|
570
|
+
if post and shutil.which(post[0]):
|
|
571
|
+
rc2, _ = _run(post, timeout=600)
|
|
572
|
+
if rc2 == 0:
|
|
573
|
+
print(f" post-install: {' '.join(post)} ok")
|
|
574
|
+
else:
|
|
575
|
+
print(f" post-install: {' '.join(post)} rc={rc2} (non-fatal)")
|
|
567
576
|
else:
|
|
568
577
|
print(f" fail {cli_id}: {attempt} returned rc={rc}")
|
|
569
578
|
failed_now.append(cli_id)
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -2798,145 +2798,249 @@ def _session_age(unix_ts: int) -> str:
|
|
|
2798
2798
|
|
|
2799
2799
|
|
|
2800
2800
|
def cmd_menu(_args: argparse.Namespace) -> int:
|
|
2801
|
-
"""`omega` (no args) —
|
|
2802
|
-
|
|
2803
|
-
v0.19.
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
*
|
|
2820
|
-
|
|
2821
|
-
|
|
2801
|
+
"""`omega` (no args) — attach to the persistent Omega master tmux session.
|
|
2802
|
+
|
|
2803
|
+
v0.19.25 redesign per the user's spec: bare `omega` should feel like
|
|
2804
|
+
typing `claude` (opens the Claude Code TUI) or `hermes` (opens
|
|
2805
|
+
Hermès). It should TAKE OVER your terminal as the OmegaOS home.
|
|
2806
|
+
|
|
2807
|
+
Mechanism (tmux-based, since OmegaOS is multi-session by design):
|
|
2808
|
+
1. Ensure the `Omega` tmux session exists (spawn if missing).
|
|
2809
|
+
The session's window 0 runs `omega menu-tui` — the fzf-driven
|
|
2810
|
+
action picker.
|
|
2811
|
+
2. `os.execvp("tmux attach -t Omega")` — your terminal now IS
|
|
2812
|
+
the Omega session. Detach with Ctrl-b d, attach again with
|
|
2813
|
+
`omega`.
|
|
2814
|
+
|
|
2815
|
+
Inside the Omega session you have:
|
|
2816
|
+
* window 0 (menu) — the action picker
|
|
2817
|
+
* spawned on demand: AISB-chat, Hermes-chat, projects, scratch shells
|
|
2818
|
+
* Ctrl-b w — tmux's own window picker
|
|
2819
|
+
* Ctrl-b z — tmux-claude session manager (if installed)
|
|
2820
|
+
|
|
2821
|
+
If you're already inside tmux (e.g. iTerm tmux integration), we
|
|
2822
|
+
don't take over the current session — we tell you how to switch
|
|
2823
|
+
to the Omega session manually.
|
|
2822
2824
|
"""
|
|
2823
2825
|
import os
|
|
2824
2826
|
import shutil
|
|
2825
|
-
import subprocess
|
|
2826
2827
|
from omega_engine import tmux
|
|
2827
2828
|
|
|
2828
|
-
|
|
2829
|
-
# take over the current pane from a subprocess, but `omega aisb` /
|
|
2830
|
-
# `omega hermes` from inside tmux still print switch-client instructions.
|
|
2831
|
-
if os.environ.get("TMUX"):
|
|
2832
|
-
print(" You're already inside tmux. Use Ctrl+b z (tmux-claude")
|
|
2833
|
-
print(" session manager) or one of:")
|
|
2834
|
-
print(" tmux switch-client -t AISB-chat")
|
|
2835
|
-
print(" tmux switch-client -t Hermes-chat")
|
|
2836
|
-
return 0
|
|
2829
|
+
OMEGA_SESSION = "Omega"
|
|
2837
2830
|
|
|
2838
2831
|
if not shutil.which("tmux"):
|
|
2839
2832
|
print(" tmux not installed — `brew install tmux` (macOS) or "
|
|
2840
2833
|
"`apt-get install tmux` (Linux)")
|
|
2841
2834
|
return 2
|
|
2842
2835
|
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
for
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2836
|
+
if os.environ.get("TMUX"):
|
|
2837
|
+
# We're already in a tmux session. Don't try to attach (tmux
|
|
2838
|
+
# refuses nested attach). Tell the user how to switch.
|
|
2839
|
+
if not tmux.is_alive(OMEGA_SESSION):
|
|
2840
|
+
tmux.spawn_omega_master(_omega_home())
|
|
2841
|
+
print(f" Omega session is running. Switch to it with:")
|
|
2842
|
+
print(f" tmux switch-client -t {OMEGA_SESSION}")
|
|
2843
|
+
print(f" Or use Ctrl-b s to pick from the session list,")
|
|
2844
|
+
print(f" or Ctrl-b z for the tmux-claude session manager.")
|
|
2845
|
+
return 0
|
|
2846
|
+
|
|
2847
|
+
if not tmux.is_alive(OMEGA_SESSION):
|
|
2848
|
+
tmux.spawn_omega_master(_omega_home())
|
|
2849
|
+
|
|
2850
|
+
try:
|
|
2851
|
+
os.execvp("tmux", ["tmux", "attach", "-t", OMEGA_SESSION])
|
|
2852
|
+
except FileNotFoundError:
|
|
2853
|
+
print(" tmux not on PATH")
|
|
2854
|
+
return 2
|
|
2855
|
+
return 0
|
|
2856
|
+
|
|
2857
|
+
|
|
2858
|
+
def cmd_menu_tui(_args: argparse.Namespace) -> int:
|
|
2859
|
+
"""`omega menu-tui` — the fzf action picker that runs INSIDE the
|
|
2860
|
+
Omega master tmux session. Don't invoke this directly; use bare
|
|
2861
|
+
`omega` which spawns the Omega session and lands you here.
|
|
2862
|
+
|
|
2863
|
+
Loops until you pick Quit or hit Esc. Each action either:
|
|
2864
|
+
- Spawns a new tmux window in the Omega session (AISB chat,
|
|
2865
|
+
Hermes chat, scratch shell)
|
|
2866
|
+
- Runs a sub-command inline (status, doctor, vault list)
|
|
2867
|
+
- Opens a sub-menu (switch provider, manage account)
|
|
2868
|
+
"""
|
|
2869
|
+
import os
|
|
2870
|
+
import shlex
|
|
2871
|
+
import shutil
|
|
2872
|
+
import subprocess
|
|
2873
|
+
from omega_engine import tmux
|
|
2874
|
+
from omega_engine import __version__
|
|
2875
|
+
|
|
2876
|
+
HOME = _omega_home()
|
|
2877
|
+
OMEGA_BIN = str(HOME / "Agentik_Tools" / "bin" / "omega")
|
|
2878
|
+
|
|
2879
|
+
if not shutil.which("fzf"):
|
|
2880
|
+
print(" fzf not installed — install with `brew install fzf` "
|
|
2881
|
+
"(macOS) or `apt install fzf` (Linux)")
|
|
2882
|
+
return 2
|
|
2883
|
+
|
|
2884
|
+
# The menu actions. Each entry: (label, action_key).
|
|
2885
|
+
# Actions are dispatched in the if/elif block below.
|
|
2886
|
+
def _build_menu() -> list[tuple[str, str]]:
|
|
2887
|
+
active = _active_provider()
|
|
2888
|
+
sessions = tmux.list_sessions(HOME)
|
|
2889
|
+
items: list[tuple[str, str]] = [
|
|
2890
|
+
("CHAT", "__separator__"),
|
|
2891
|
+
(f" AISB master chat (Claude Max OAuth)", "open:aisb"),
|
|
2892
|
+
(f" Hermès meta-companion (Anthropic API)", "open:hermes"),
|
|
2893
|
+
(f" Switch LLM provider ({active})", "switch:provider"),
|
|
2894
|
+
("PROJECTS", "__separator__"),
|
|
2895
|
+
(f" New project (Genesis pipeline)", "genesis:new"),
|
|
2896
|
+
(f" Open project shell", "project:open"),
|
|
2897
|
+
("AUDITS & MISSIONS", "__separator__"),
|
|
2898
|
+
(f" Run a mission ({active})", "run:mission"),
|
|
2899
|
+
(f" Quality Arsenal — pick an audit", "audit:menu"),
|
|
2900
|
+
("INFRASTRUCTURE", "__separator__"),
|
|
2901
|
+
(f" Sessions ({len(sessions)} active) — pick to attach", "sessions:list"),
|
|
2902
|
+
(f" Accounts — Claude Max + per-provider auth", "accounts:menu"),
|
|
2903
|
+
(f" Vault — secrets", "vault:menu"),
|
|
2904
|
+
("HEALTH", "__separator__"),
|
|
2905
|
+
(f" omega doctor", "cmd:doctor"),
|
|
2906
|
+
(f" omega status", "cmd:status"),
|
|
2907
|
+
("SCRAPE & SEARCH", "__separator__"),
|
|
2908
|
+
(f" Stealth scrape a URL (CloakBrowser)", "scrape:cloak"),
|
|
2909
|
+
(f" Fast scrape a URL (Scrapling)", "scrape:scrapling"),
|
|
2910
|
+
("", "__separator__"),
|
|
2911
|
+
(f" Detach (Ctrl-b d also works)", "detach"),
|
|
2912
|
+
(f" Quit Omega session (kills the tmux session)", "quit:kill"),
|
|
2913
|
+
]
|
|
2914
|
+
return items
|
|
2915
|
+
|
|
2916
|
+
def _send_in_window(window: str, command: str) -> None:
|
|
2917
|
+
"""Open (or focus) a window in the Omega session and run command."""
|
|
2918
|
+
_, list_out = tmux._tmux("list-windows", "-t", "Omega", "-F", "#W")
|
|
2919
|
+
windows = list_out.splitlines() if list_out else []
|
|
2920
|
+
if window not in windows:
|
|
2921
|
+
tmux._tmux("new-window", "-t", "Omega", "-n", window,
|
|
2922
|
+
"-c", str(Path.home()))
|
|
2923
|
+
tmux._tmux("send-keys", "-t", f"Omega:{window}", command, "Enter")
|
|
2924
|
+
else:
|
|
2925
|
+
tmux._tmux("send-keys", "-t", f"Omega:{window}", command, "Enter")
|
|
2926
|
+
tmux._tmux("select-window", "-t", f"Omega:{window}")
|
|
2927
|
+
|
|
2928
|
+
print(f"\n Omega v{__version__} — type a number to refresh, Ctrl-c to exit.\n")
|
|
2929
|
+
while True:
|
|
2930
|
+
items = _build_menu()
|
|
2931
|
+
# Build fzf input — separators are unselectable category headers.
|
|
2932
|
+
lines = []
|
|
2933
|
+
for label, key in items:
|
|
2934
|
+
if key == "__separator__":
|
|
2935
|
+
# Visual section header line.
|
|
2936
|
+
lines.append(f"── {label} " + "─" * max(0, 50 - len(label)))
|
|
2937
|
+
else:
|
|
2938
|
+
lines.append(label)
|
|
2939
|
+
fzf_input = "\n".join(lines)
|
|
2865
2940
|
try:
|
|
2866
2941
|
proc = subprocess.run(
|
|
2867
2942
|
["fzf",
|
|
2868
|
-
"--prompt
|
|
2869
|
-
"--header=Omega Session Manager — Enter to
|
|
2943
|
+
"--prompt=Ω › ",
|
|
2944
|
+
"--header=Omega Session Manager — Enter to pick, Esc to refresh",
|
|
2870
2945
|
"--layout=reverse",
|
|
2871
|
-
"--height=
|
|
2872
|
-
"--border",
|
|
2873
|
-
"--no-multi"
|
|
2946
|
+
"--height=100%",
|
|
2947
|
+
"--border=rounded",
|
|
2948
|
+
"--no-multi",
|
|
2949
|
+
"--info=inline"],
|
|
2874
2950
|
input=fzf_input, capture_output=True, text=True,
|
|
2875
2951
|
)
|
|
2876
|
-
|
|
2877
|
-
pick = proc.stdout.strip()
|
|
2878
|
-
for disp, key in lines:
|
|
2879
|
-
if disp == pick and key != "__separator__":
|
|
2880
|
-
chosen_key = key
|
|
2881
|
-
break
|
|
2882
|
-
except Exception as exc: # noqa: BLE001
|
|
2883
|
-
print(f" fzf failed: {exc}")
|
|
2884
|
-
return 2
|
|
2885
|
-
else:
|
|
2886
|
-
# Plain stdin fallback — numbered list.
|
|
2887
|
-
print()
|
|
2888
|
-
print(" Omega Session Manager")
|
|
2889
|
-
for i, (disp, _) in enumerate(lines, 1):
|
|
2890
|
-
print(f" {i:2}) {disp}")
|
|
2891
|
-
print()
|
|
2892
|
-
try:
|
|
2893
|
-
raw = input(" pick a number (Enter to quit): ").strip()
|
|
2894
|
-
except (EOFError, KeyboardInterrupt):
|
|
2952
|
+
except KeyboardInterrupt:
|
|
2895
2953
|
return 0
|
|
2896
|
-
if
|
|
2954
|
+
if proc.returncode != 0:
|
|
2955
|
+
# Esc / Ctrl-c → refresh the menu rather than exit immediately.
|
|
2956
|
+
# User picks "Quit" or "Detach" explicitly.
|
|
2957
|
+
try:
|
|
2958
|
+
input("\n (Esc pressed — press Enter to refresh, Ctrl-c to quit)\n > ")
|
|
2959
|
+
continue
|
|
2960
|
+
except (EOFError, KeyboardInterrupt):
|
|
2961
|
+
return 0
|
|
2962
|
+
pick = proc.stdout.strip()
|
|
2963
|
+
action = None
|
|
2964
|
+
for label, key in items:
|
|
2965
|
+
display = (f"── {label} " + "─" * max(0, 50 - len(label))
|
|
2966
|
+
if key == "__separator__" else label)
|
|
2967
|
+
if display == pick and key != "__separator__":
|
|
2968
|
+
action = key
|
|
2969
|
+
break
|
|
2970
|
+
if action is None:
|
|
2971
|
+
continue
|
|
2972
|
+
|
|
2973
|
+
# Dispatch.
|
|
2974
|
+
if action == "detach":
|
|
2975
|
+
subprocess.run(["tmux", "detach-client"])
|
|
2897
2976
|
return 0
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
except ValueError:
|
|
2901
|
-
print(f" not a number: {raw!r}")
|
|
2977
|
+
if action == "quit:kill":
|
|
2978
|
+
subprocess.run(["tmux", "kill-session", "-t", "Omega"])
|
|
2902
2979
|
return 0
|
|
2903
|
-
if
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
"
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2980
|
+
if action == "open:aisb":
|
|
2981
|
+
tmux.spawn_aisb_chat(HOME, force_replace=False)
|
|
2982
|
+
subprocess.run(["tmux", "switch-client", "-t", "AISB-chat"])
|
|
2983
|
+
continue
|
|
2984
|
+
if action == "open:hermes":
|
|
2985
|
+
tmux.spawn_hermes_chat(HOME, force_replace=False)
|
|
2986
|
+
subprocess.run(["tmux", "switch-client", "-t", "Hermes-chat"])
|
|
2987
|
+
continue
|
|
2988
|
+
if action == "switch:provider":
|
|
2989
|
+
_send_in_window("switch", f"{shlex.quote(OMEGA_BIN)} switch")
|
|
2990
|
+
continue
|
|
2991
|
+
if action == "genesis:new":
|
|
2992
|
+
_send_in_window("genesis",
|
|
2993
|
+
f"{shlex.quote(OMEGA_BIN)} genesis questions")
|
|
2994
|
+
continue
|
|
2995
|
+
if action == "project:open":
|
|
2996
|
+
_send_in_window("project", "ls -la ~/Omega/Agentik_Coding/projects/")
|
|
2997
|
+
continue
|
|
2998
|
+
if action == "run:mission":
|
|
2999
|
+
_send_in_window("mission",
|
|
3000
|
+
f"echo 'usage: omega run \"<intent>\"' && "
|
|
3001
|
+
f"echo 'examples: omega run \"fix all linear feedbacks\"'")
|
|
3002
|
+
continue
|
|
3003
|
+
if action == "audit:menu":
|
|
3004
|
+
_send_in_window("audit",
|
|
3005
|
+
f"echo 'Quality Arsenal — invoke an audit:' && "
|
|
3006
|
+
f"echo ' /codeaudit /uiuxaudit /flowaudit /debugaudit' && "
|
|
3007
|
+
f"echo ' /featureaudit /perfaudit /secaudit /a11yaudit' && "
|
|
3008
|
+
f"echo ' /seoaudit /dataaudit /apiaudit /copyaudit' && "
|
|
3009
|
+
f"echo ' /dxaudit /motionaudit /automationaudit' && "
|
|
3010
|
+
f"echo ' /logicaudit /retentionaudit /refontaudit' && "
|
|
3011
|
+
f"echo 'or: omega audit <id>'")
|
|
3012
|
+
continue
|
|
3013
|
+
if action == "sessions:list":
|
|
3014
|
+
_send_in_window("sessions", "tmux ls")
|
|
3015
|
+
continue
|
|
3016
|
+
if action == "accounts:menu":
|
|
3017
|
+
_send_in_window("accounts",
|
|
3018
|
+
f"{shlex.quote(OMEGA_BIN)} account list")
|
|
3019
|
+
continue
|
|
3020
|
+
if action == "vault:menu":
|
|
3021
|
+
_send_in_window("vault",
|
|
3022
|
+
f"{shlex.quote(OMEGA_BIN)} vault status")
|
|
3023
|
+
continue
|
|
3024
|
+
if action == "cmd:doctor":
|
|
3025
|
+
_send_in_window("doctor", f"{shlex.quote(OMEGA_BIN)} doctor")
|
|
3026
|
+
continue
|
|
3027
|
+
if action == "cmd:status":
|
|
3028
|
+
_send_in_window("status", f"{shlex.quote(OMEGA_BIN)} status")
|
|
3029
|
+
continue
|
|
3030
|
+
if action == "scrape:cloak":
|
|
3031
|
+
_send_in_window("scrape",
|
|
3032
|
+
"read -p 'URL: ' u && "
|
|
3033
|
+
f"{shlex.quote(OMEGA_BIN)} scrape \"$u\"")
|
|
3034
|
+
continue
|
|
3035
|
+
if action == "scrape:scrapling":
|
|
3036
|
+
_send_in_window("scrape",
|
|
3037
|
+
"read -p 'URL: ' u && "
|
|
3038
|
+
f"{shlex.quote(OMEGA_BIN)} scrape \"$u\" --engine scrapling")
|
|
3039
|
+
continue
|
|
2933
3040
|
|
|
2934
3041
|
|
|
2935
3042
|
def cmd_menu_whiptail(_args: argparse.Namespace) -> int:
|
|
2936
|
-
"""`omega menu` — the explicit whiptail picker.
|
|
2937
|
-
|
|
2938
|
-
The old bare-omega behaviour, moved behind an explicit subcommand
|
|
2939
|
-
because the default is now the AISB chat (v0.19.15)."""
|
|
3043
|
+
"""`omega menu` — the explicit whiptail picker (legacy)."""
|
|
2940
3044
|
from omega_engine.menu import run_menu
|
|
2941
3045
|
return run_menu()
|
|
2942
3046
|
|
|
@@ -2966,28 +3070,74 @@ def _set_active_provider(provider_id: str) -> None:
|
|
|
2966
3070
|
|
|
2967
3071
|
|
|
2968
3072
|
def cmd_scrape(args: argparse.Namespace) -> int:
|
|
2969
|
-
"""`omega scrape <url> [--
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
3073
|
+
"""`omega scrape <url> [--engine cloak|scrapling] [--out file]` — scraper.
|
|
3074
|
+
|
|
3075
|
+
Two engines available since v0.19.24:
|
|
3076
|
+
* ``cloak`` (default) — CloakBrowser, heavy stealth Chromium, 58
|
|
3077
|
+
C++ patches, passes Cloudflare/Turnstile/FingerprintJS. Slow
|
|
3078
|
+
but invincible.
|
|
3079
|
+
* ``scrapling`` — D4Vinci/Scrapling, HTTP-first with optional
|
|
3080
|
+
browser, adaptive element tracking, concurrent crawl with
|
|
3081
|
+
pause/resume. Fast for non-protected sites.
|
|
3082
|
+
|
|
3083
|
+
Output: cleaned text to stdout (or --out FILE). Wraps both engines
|
|
3084
|
+
behind a consistent CLI so any LLM (Claude/Gemini/Codex/OpenCode/
|
|
3085
|
+
Aider) can `omega scrape <url>` via Bash without caring which
|
|
3086
|
+
Python library is doing the work.
|
|
3087
|
+
|
|
3088
|
+
Examples:
|
|
3089
|
+
omega scrape https://example.com # CloakBrowser
|
|
3090
|
+
omega scrape https://example.com --engine scrapling # Scrapling
|
|
3091
|
+
omega scrape https://news.ycombinator.com --engine scrapling --css ".titleline"
|
|
2985
3092
|
"""
|
|
2986
3093
|
import shutil
|
|
2987
3094
|
import subprocess
|
|
2988
3095
|
if not args.url:
|
|
2989
|
-
print("usage: omega scrape <url>
|
|
3096
|
+
print("usage: omega scrape <url> "
|
|
3097
|
+
"[--engine cloak|scrapling] [--out FILE] [--humanize] "
|
|
3098
|
+
"[--proxy URL] [--css SELECTOR]")
|
|
2990
3099
|
return 2
|
|
3100
|
+
engine = getattr(args, "engine", None) or "cloak"
|
|
3101
|
+
if engine == "scrapling":
|
|
3102
|
+
# Scrapling path — uses its own CLI `scrapling extract`.
|
|
3103
|
+
if not shutil.which("scrapling"):
|
|
3104
|
+
print(" scrapling not installed — "
|
|
3105
|
+
"run `omega tool install scrapling` or "
|
|
3106
|
+
"`uv tool install 'scrapling[fetchers]' && scrapling install`")
|
|
3107
|
+
return 2
|
|
3108
|
+
out_file = getattr(args, "out", None)
|
|
3109
|
+
# Scrapling writes to a file (it doesn't print to stdout by
|
|
3110
|
+
# default). If user didn't specify --out, use a tmpfile and
|
|
3111
|
+
# cat it after.
|
|
3112
|
+
import tempfile
|
|
3113
|
+
from pathlib import Path
|
|
3114
|
+
tmp_out = out_file or tempfile.mktemp(suffix=".md")
|
|
3115
|
+
scrapling_cmd = ["scrapling", "extract",
|
|
3116
|
+
"stealthy-fetch" if getattr(args, "humanize", False)
|
|
3117
|
+
else "fetch",
|
|
3118
|
+
args.url, tmp_out]
|
|
3119
|
+
if getattr(args, "css", None):
|
|
3120
|
+
scrapling_cmd += ["--css-selector", args.css]
|
|
3121
|
+
try:
|
|
3122
|
+
proc = subprocess.run(scrapling_cmd, capture_output=True,
|
|
3123
|
+
text=True, timeout=120)
|
|
3124
|
+
except subprocess.TimeoutExpired:
|
|
3125
|
+
print(" scrapling timed out after 120s")
|
|
3126
|
+
return 2
|
|
3127
|
+
if proc.returncode != 0:
|
|
3128
|
+
print(f" scrapling error: {proc.stderr[:400]}")
|
|
3129
|
+
return proc.returncode
|
|
3130
|
+
if not out_file:
|
|
3131
|
+
try:
|
|
3132
|
+
print(Path(tmp_out).read_text())
|
|
3133
|
+
Path(tmp_out).unlink(missing_ok=True)
|
|
3134
|
+
except OSError as exc:
|
|
3135
|
+
print(f" read scrapling output failed: {exc}")
|
|
3136
|
+
return 2
|
|
3137
|
+
else:
|
|
3138
|
+
print(f" wrote → {out_file}")
|
|
3139
|
+
return 0
|
|
3140
|
+
# Default engine = CloakBrowser (kept verbatim from v0.19.23).
|
|
2991
3141
|
# CloakBrowser is installed as a uv tool (`cloakbrowser`) but we need
|
|
2992
3142
|
# to drive it from Python — the canonical path is `python -m
|
|
2993
3143
|
# cloakbrowser` after `pip install cloakbrowser`, OR we invoke the
|
|
@@ -4175,8 +4325,12 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
4175
4325
|
parser.set_defaults(fn=cmd_menu)
|
|
4176
4326
|
sub = parser.add_subparsers(dest="cmd")
|
|
4177
4327
|
sub.add_parser("menu",
|
|
4178
|
-
help="open the interactive whiptail menu (
|
|
4328
|
+
help="open the interactive whiptail menu (legacy)"
|
|
4179
4329
|
).set_defaults(fn=cmd_menu_whiptail)
|
|
4330
|
+
sub.add_parser("menu-tui",
|
|
4331
|
+
help="the fzf action picker that runs INSIDE the Omega tmux "
|
|
4332
|
+
"session (don't call directly — bare `omega` lands you here)"
|
|
4333
|
+
).set_defaults(fn=cmd_menu_tui)
|
|
4180
4334
|
sub.add_parser("version", help="print the engine version").set_defaults(fn=cmd_version)
|
|
4181
4335
|
|
|
4182
4336
|
# `omega switch <provider>` — hot-swap the LLM CLI used for new chats.
|
|
@@ -4190,20 +4344,28 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
4190
4344
|
help="provider id (omit to print current + available)")
|
|
4191
4345
|
p_sw.set_defaults(fn=cmd_switch)
|
|
4192
4346
|
|
|
4193
|
-
# `omega scrape <url>` — official OmegaOS web scraper (CloakBrowser
|
|
4347
|
+
# `omega scrape <url>` — official OmegaOS web scraper (CloakBrowser
|
|
4348
|
+
# default, Scrapling optional). Both engines pre-installed at step 40
|
|
4349
|
+
# when their catalog entry is `recommended: true`.
|
|
4194
4350
|
p_sc = sub.add_parser(
|
|
4195
4351
|
"scrape",
|
|
4196
|
-
help="
|
|
4197
|
-
"
|
|
4352
|
+
help="scrape a URL — CloakBrowser (default, stealth) or Scrapling "
|
|
4353
|
+
"(--engine scrapling, fast). Output: stdout or --out FILE",
|
|
4198
4354
|
)
|
|
4199
4355
|
p_sc.add_argument("url", nargs="?", default=None,
|
|
4200
4356
|
help="URL to fetch")
|
|
4357
|
+
p_sc.add_argument("--engine", default="cloak",
|
|
4358
|
+
choices=["cloak", "scrapling"],
|
|
4359
|
+
help="which scraper to use (default: cloak = CloakBrowser)")
|
|
4201
4360
|
p_sc.add_argument("--out", default=None,
|
|
4202
4361
|
help="write result to file instead of stdout")
|
|
4203
4362
|
p_sc.add_argument("--humanize", action="store_true",
|
|
4204
|
-
help="mouse curves +
|
|
4363
|
+
help="mouse curves + scroll patterns (cloak) "
|
|
4364
|
+
"or stealthy-fetch mode (scrapling)")
|
|
4205
4365
|
p_sc.add_argument("--proxy", default=None,
|
|
4206
|
-
help="HTTP or SOCKS5 proxy (
|
|
4366
|
+
help="HTTP or SOCKS5 proxy (cloak engine only)")
|
|
4367
|
+
p_sc.add_argument("--css", default=None,
|
|
4368
|
+
help="CSS selector to scope output (scrapling engine only)")
|
|
4207
4369
|
p_sc.set_defaults(fn=cmd_scrape)
|
|
4208
4370
|
p_doc = sub.add_parser("doctor", help="validate the deployment")
|
|
4209
4371
|
p_doc.add_argument("--json", action="store_true",
|
|
@@ -353,6 +353,41 @@ def spawn_aisb_chat(omega_home: str | Path | None = None,
|
|
|
353
353
|
)
|
|
354
354
|
|
|
355
355
|
|
|
356
|
+
def spawn_omega_master(omega_home: str | Path | None = None) -> str:
|
|
357
|
+
"""Spawn the master Omega tmux session — your persistent home.
|
|
358
|
+
|
|
359
|
+
v0.19.25 redesign: when you type `omega`, you ENTER this session.
|
|
360
|
+
Like `claude` opens the Claude Code TUI in your terminal, `omega`
|
|
361
|
+
attaches to a persistent tmux session called `Omega` which acts as
|
|
362
|
+
your OS-level dashboard. Inside, window 0 runs the Omega menu TUI
|
|
363
|
+
(`omega menu-tui` — the fzf-driven action picker). Other windows
|
|
364
|
+
open on demand:
|
|
365
|
+
- window 1: AISB-chat (spawned when user picks it)
|
|
366
|
+
- window 2: Hermes-chat
|
|
367
|
+
- window 3+: per-project oracles / workers / shells
|
|
368
|
+
|
|
369
|
+
Survives across terminal restarts. `tmux kill-server` is the only
|
|
370
|
+
way to nuke it.
|
|
371
|
+
"""
|
|
372
|
+
name = "Omega"
|
|
373
|
+
home = Path(omega_home or os.environ.get("OMEGA_HOME")
|
|
374
|
+
or Path.home() / "Omega")
|
|
375
|
+
if is_alive(name):
|
|
376
|
+
return name
|
|
377
|
+
omega_bin = home / "Agentik_Tools" / "bin" / "omega"
|
|
378
|
+
# Window 0 = menu TUI. We launch the user's shell first then send
|
|
379
|
+
# the menu command — so if the TUI exits, the shell stays alive
|
|
380
|
+
# and the user can manually `omega menu-tui` again or run anything
|
|
381
|
+
# else. Same shell-stays-alive pattern as spawn_aisb_chat.
|
|
382
|
+
rc, _ = _tmux("new-session", "-d", "-s", name,
|
|
383
|
+
"-n", "menu", "-c", str(Path.home()))
|
|
384
|
+
if rc != 0:
|
|
385
|
+
return name
|
|
386
|
+
_tmux("send-keys", "-t", f"{name}:menu",
|
|
387
|
+
f"{omega_bin} menu-tui", "Enter")
|
|
388
|
+
return name
|
|
389
|
+
|
|
390
|
+
|
|
356
391
|
def spawn_hermes_chat(omega_home: str | Path | None = None,
|
|
357
392
|
force_replace: bool = False) -> str:
|
|
358
393
|
"""Spawn the Hermès chat tmux session — REAL Claude Code TUI, but on
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.19.
|
|
1
|
+
0.19.25
|
|
@@ -74,6 +74,19 @@ native:
|
|
|
74
74
|
secrets: []
|
|
75
75
|
recommended: true
|
|
76
76
|
|
|
77
|
+
- id: scrapling
|
|
78
|
+
name: Scrapling (optional fast scraper — HTTP-first, adaptive elements)
|
|
79
|
+
# Complementary to CloakBrowser. Scrapling shines for non-protected
|
|
80
|
+
# sites + multi-page crawls (concurrent + pause/resume + adaptive
|
|
81
|
+
# element tracking that survives page redesigns). CloakBrowser stays
|
|
82
|
+
# the default for hard bot-walls; Scrapling for everything else.
|
|
83
|
+
# `omega scrape --engine scrapling` selects it explicitly.
|
|
84
|
+
install: { uv_tool: "scrapling[fetchers]" }
|
|
85
|
+
binary: scrapling
|
|
86
|
+
secrets: []
|
|
87
|
+
recommended: false
|
|
88
|
+
post_install: ["scrapling", "install"] # downloads browser deps once
|
|
89
|
+
|
|
77
90
|
# --- 2. Printing Press CLIs --------------------------------------------
|
|
78
91
|
# Installed via `npx -y @mvanhorn/printing-press-library install <name>`.
|
|
79
92
|
# Each ships with a local SQLite mirror + Claude Code skill.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentikos/omega-os",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.25",
|
|
4
4
|
"description": "Omega OS — installable agentic operating system with verified-completion orchestration. Event-sourced engine, 8-block rack, autonomous agents, MCP.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"omega-os": "bin/omega-os.js"
|