mtrx-cli 0.1.10 → 0.1.13
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/package.json +6 -2
- package/src/matrx/__init__.py +1 -1
- package/src/matrx/cli/cursor_ca.py +275 -0
- package/src/matrx/cli/cursor_config.py +262 -74
- package/src/matrx/cli/cursor_daemon.py +64 -0
- package/src/matrx/cli/cursor_proxy.py +459 -261
- package/src/matrx/cli/cursor_service.py +343 -0
- package/src/matrx/cli/launcher.py +47 -27
- package/src/matrx/cli/main.py +150 -69
package/src/matrx/cli/main.py
CHANGED
|
@@ -32,14 +32,17 @@ from matrx.cli.launcher import (
|
|
|
32
32
|
)
|
|
33
33
|
from matrx.cli.cursor_config import (
|
|
34
34
|
configure_cursor_for_proxy,
|
|
35
|
+
configure_cursor_proxy_settings,
|
|
35
36
|
cursor_is_running,
|
|
37
|
+
cursor_settings_json_path,
|
|
36
38
|
cursor_state_db_path,
|
|
37
39
|
print_manual_setup_instructions,
|
|
38
40
|
read_cursor_settings,
|
|
41
|
+
restore_cursor_proxy_settings,
|
|
39
42
|
restore_cursor_settings,
|
|
40
43
|
)
|
|
41
|
-
from matrx.cli.cursor_proxy import CursorProxyServer
|
|
42
44
|
from matrx.cli.state import (
|
|
45
|
+
config_dir,
|
|
43
46
|
ensure_app_url,
|
|
44
47
|
ensure_root_url,
|
|
45
48
|
ensure_v1_url,
|
|
@@ -74,7 +77,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
74
77
|
if args.command in {"codex", "claude"}:
|
|
75
78
|
return _cmd_launch(args.command, args.route, remainder)
|
|
76
79
|
if args.command == "cursor":
|
|
77
|
-
return _cmd_cursor(args
|
|
80
|
+
return _cmd_cursor(args)
|
|
78
81
|
|
|
79
82
|
parser.print_help()
|
|
80
83
|
return 1
|
|
@@ -115,7 +118,8 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
115
118
|
|
|
116
119
|
cursor = subparsers.add_parser("cursor")
|
|
117
120
|
cursor.add_argument("--route", choices=["direct", "matrx"])
|
|
118
|
-
cursor.add_argument("--
|
|
121
|
+
cursor.add_argument("--status", action="store_true", help="Check proxy status")
|
|
122
|
+
cursor.add_argument("--stop", action="store_true", help="Stop the proxy service")
|
|
119
123
|
|
|
120
124
|
return parser
|
|
121
125
|
|
|
@@ -489,9 +493,50 @@ def _cmd_use(args) -> int:
|
|
|
489
493
|
print(f"Saved to {path}")
|
|
490
494
|
if args.tool == "claude" and args.route == "direct":
|
|
491
495
|
_print_claude_shell_proxy_hint()
|
|
496
|
+
if args.tool == "cursor" and args.route == "direct":
|
|
497
|
+
_restore_cursor_if_needed()
|
|
492
498
|
return 0
|
|
493
499
|
|
|
494
500
|
|
|
501
|
+
def _restore_cursor_if_needed() -> None:
|
|
502
|
+
import json as _json
|
|
503
|
+
|
|
504
|
+
from matrx.cli.cursor_service import is_proxy_running, uninstall_service
|
|
505
|
+
|
|
506
|
+
# Stop the MITM proxy service if it's running
|
|
507
|
+
if is_proxy_running():
|
|
508
|
+
uninstall_service()
|
|
509
|
+
print("MTRX Cursor proxy service stopped.")
|
|
510
|
+
|
|
511
|
+
# Restore settings.json (MITM proxy settings)
|
|
512
|
+
proxy_prev_path = config_dir() / "cursor-proxy-previous-settings.json"
|
|
513
|
+
if proxy_prev_path.exists():
|
|
514
|
+
try:
|
|
515
|
+
previous = _json.loads(proxy_prev_path.read_text(encoding="utf-8"))
|
|
516
|
+
if restore_cursor_proxy_settings(previous):
|
|
517
|
+
proxy_prev_path.unlink(missing_ok=True)
|
|
518
|
+
print("Cursor proxy settings restored.")
|
|
519
|
+
except Exception:
|
|
520
|
+
pass
|
|
521
|
+
|
|
522
|
+
# Restore state.vscdb (legacy Override Base URL settings)
|
|
523
|
+
prev_path = config_dir() / "cursor-previous-settings.json"
|
|
524
|
+
db_path = cursor_state_db_path()
|
|
525
|
+
if prev_path.exists() and db_path.exists():
|
|
526
|
+
try:
|
|
527
|
+
previous = _json.loads(prev_path.read_text(encoding="utf-8"))
|
|
528
|
+
if restore_cursor_settings(previous, db_path=db_path):
|
|
529
|
+
prev_path.unlink(missing_ok=True)
|
|
530
|
+
print("Cursor DB settings restored to previous values.")
|
|
531
|
+
else:
|
|
532
|
+
print("[warn] Could not auto-restore Cursor DB settings.")
|
|
533
|
+
except Exception:
|
|
534
|
+
pass
|
|
535
|
+
|
|
536
|
+
if cursor_is_running():
|
|
537
|
+
print(" Restart Cursor for settings to take effect.")
|
|
538
|
+
|
|
539
|
+
|
|
495
540
|
def _cmd_status() -> int:
|
|
496
541
|
state = load_state()
|
|
497
542
|
auth = state["auth"]
|
|
@@ -526,6 +571,11 @@ def _cmd_status() -> int:
|
|
|
526
571
|
location = config_status["config_path"] or "unknown"
|
|
527
572
|
verified_at = config_status["last_verified_at"] or "-"
|
|
528
573
|
print(f" {tool}: {status_label} ({location}, verified={verified_at})")
|
|
574
|
+
# Cursor proxy status
|
|
575
|
+
from matrx.cli.cursor_service import is_proxy_running
|
|
576
|
+
proxy_running = is_proxy_running()
|
|
577
|
+
print(f" cursor proxy: {'running' if proxy_running else 'not running'}")
|
|
578
|
+
|
|
529
579
|
print("Executables:")
|
|
530
580
|
print(f" codex: {find_executable('codex') or 'not found'}")
|
|
531
581
|
print(f" claude: {find_executable('claude') or 'not found'}")
|
|
@@ -780,17 +830,55 @@ def _cmd_launch(tool: str, route: str | None, remainder: list[str]) -> int:
|
|
|
780
830
|
return launch(plan)
|
|
781
831
|
|
|
782
832
|
|
|
783
|
-
def _cmd_cursor(
|
|
784
|
-
import
|
|
833
|
+
def _cmd_cursor(args) -> int:
|
|
834
|
+
import json as _json
|
|
835
|
+
|
|
836
|
+
from matrx.cli.cursor_ca import (
|
|
837
|
+
ca_cert_path,
|
|
838
|
+
ca_exists,
|
|
839
|
+
generate_ca,
|
|
840
|
+
is_ca_trusted,
|
|
841
|
+
trust_ca_system,
|
|
842
|
+
)
|
|
843
|
+
from matrx.cli.cursor_proxy import DEFAULT_PORT, PROXY_HOST
|
|
844
|
+
from matrx.cli.cursor_service import (
|
|
845
|
+
get_proxy_status,
|
|
846
|
+
install_service,
|
|
847
|
+
is_proxy_running,
|
|
848
|
+
uninstall_service,
|
|
849
|
+
)
|
|
850
|
+
|
|
851
|
+
route = args.route
|
|
852
|
+
|
|
853
|
+
# --status: just report proxy health
|
|
854
|
+
if args.status:
|
|
855
|
+
status = get_proxy_status()
|
|
856
|
+
if status:
|
|
857
|
+
print("MTRX Cursor proxy: running")
|
|
858
|
+
print(f" requests processed: {status.get('requests', '?')}")
|
|
859
|
+
else:
|
|
860
|
+
print("MTRX Cursor proxy: not running")
|
|
861
|
+
return 0
|
|
862
|
+
|
|
863
|
+
# --stop: tear down the proxy
|
|
864
|
+
if args.stop:
|
|
865
|
+
_restore_cursor_if_needed()
|
|
866
|
+
print("Cursor route set to direct — MTRX proxy disabled.")
|
|
867
|
+
return 0
|
|
785
868
|
|
|
786
869
|
state = load_state()
|
|
787
870
|
route, promoted = _maybe_promote_direct_route(state, "cursor", route)
|
|
788
871
|
effective_route = resolve_route(state, "cursor", route)
|
|
789
872
|
|
|
790
873
|
if effective_route == "direct":
|
|
874
|
+
_restore_cursor_if_needed()
|
|
791
875
|
print("Cursor route set to direct — MTRX proxy disabled.")
|
|
876
|
+
if cursor_is_running():
|
|
877
|
+
print(" Restart Cursor for settings to take effect.")
|
|
792
878
|
return 0
|
|
793
879
|
|
|
880
|
+
# --- matrx route: set up the MITM proxy ---
|
|
881
|
+
|
|
794
882
|
try:
|
|
795
883
|
state, login_changed = _complete_matrx_login(state)
|
|
796
884
|
except ValueError as exc:
|
|
@@ -810,11 +898,9 @@ def _cmd_cursor(route: str | None, port: int) -> int:
|
|
|
810
898
|
print("No Matrx key available. Run: mtrx login matrx --key mx_...", file=sys.stderr)
|
|
811
899
|
return 1
|
|
812
900
|
|
|
813
|
-
matrx_base_url = ensure_root_url(
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
group_id = (os.environ.get("MTRX_GROUP_ID") or binding.get("group_id") or "").strip()
|
|
817
|
-
project_id = (os.environ.get("MTRX_PROJECT_ID") or binding.get("project_id") or "").strip()
|
|
901
|
+
matrx_base_url = ensure_root_url(
|
|
902
|
+
state.get("auth", {}).get("matrx", {}).get("base_url")
|
|
903
|
+
)
|
|
818
904
|
|
|
819
905
|
if initialized or login_changed or promoted:
|
|
820
906
|
save_state(state)
|
|
@@ -824,71 +910,66 @@ def _cmd_cursor(route: str | None, port: int) -> int:
|
|
|
824
910
|
"Use `mtrx use cursor direct` to opt out.",
|
|
825
911
|
)
|
|
826
912
|
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
)
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
return 1
|
|
839
|
-
|
|
840
|
-
proxy_url = proxy.url
|
|
841
|
-
print(f"MTRX Cursor proxy running at {proxy_url}")
|
|
842
|
-
print(f" session: {proxy.session_id}")
|
|
843
|
-
print(f" matrx_key: {mask_secret(mx_key)}")
|
|
844
|
-
print(f" upstream: {matrx_base_url}")
|
|
845
|
-
if group_id:
|
|
846
|
-
print(f" group: {group_id}")
|
|
847
|
-
if project_id:
|
|
848
|
-
print(f" project: {project_id}")
|
|
849
|
-
|
|
850
|
-
previous_settings = None
|
|
851
|
-
db_path = cursor_state_db_path()
|
|
852
|
-
if db_path.exists():
|
|
853
|
-
if cursor_is_running():
|
|
854
|
-
print()
|
|
855
|
-
print(" [warn] Cursor is currently running.")
|
|
856
|
-
print(" Settings changes may require restarting Cursor to take effect.")
|
|
857
|
-
previous_settings = configure_cursor_for_proxy(proxy_url, db_path=db_path)
|
|
858
|
-
if previous_settings is not None:
|
|
859
|
-
print()
|
|
860
|
-
print(" Cursor settings configured automatically.")
|
|
861
|
-
print(" Restart Cursor if it is already open.")
|
|
913
|
+
# Step 1: Generate CA certificate if needed
|
|
914
|
+
if not ca_exists():
|
|
915
|
+
print("Generating MTRX CA certificate...")
|
|
916
|
+
generate_ca()
|
|
917
|
+
print(f" CA cert: {ca_cert_path()}")
|
|
918
|
+
|
|
919
|
+
# Step 2: Trust CA (one-time)
|
|
920
|
+
if not is_ca_trusted():
|
|
921
|
+
print("Trusting MTRX CA certificate (may require password)...")
|
|
922
|
+
if trust_ca_system():
|
|
923
|
+
print(" CA trusted in system keychain.")
|
|
862
924
|
else:
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
stop_event = threading.Event()
|
|
925
|
+
print(
|
|
926
|
+
f" [warn] Could not auto-trust CA. Cursor needs NODE_EXTRA_CA_CERTS={ca_cert_path()}"
|
|
927
|
+
)
|
|
928
|
+
print(
|
|
929
|
+
" You can manually trust it or set the env var before launching Cursor."
|
|
930
|
+
)
|
|
871
931
|
|
|
872
|
-
|
|
873
|
-
|
|
932
|
+
# Step 3: Install and start the proxy service
|
|
933
|
+
proxy_url = f"http://{PROXY_HOST}:{DEFAULT_PORT}"
|
|
934
|
+
if is_proxy_running():
|
|
935
|
+
print(f"MTRX proxy already running on {proxy_url}")
|
|
936
|
+
else:
|
|
937
|
+
print("Starting MTRX Cursor proxy service...")
|
|
938
|
+
if install_service(
|
|
939
|
+
matrx_key=mx_key,
|
|
940
|
+
matrx_base_url=matrx_base_url,
|
|
941
|
+
host=PROXY_HOST,
|
|
942
|
+
port=DEFAULT_PORT,
|
|
943
|
+
):
|
|
944
|
+
print(f" Proxy running on {proxy_url}")
|
|
945
|
+
else:
|
|
946
|
+
print("[warn] Proxy service may not have started. Check logs at:")
|
|
947
|
+
print(f" {config_dir() / 'logs' / 'cursor-proxy.err.log'}")
|
|
874
948
|
|
|
875
|
-
|
|
876
|
-
|
|
949
|
+
# Step 4: Configure Cursor's settings.json
|
|
950
|
+
conf_dir = config_dir()
|
|
951
|
+
conf_dir.mkdir(parents=True, exist_ok=True)
|
|
877
952
|
|
|
878
|
-
|
|
953
|
+
previous = configure_cursor_proxy_settings(
|
|
954
|
+
proxy_url=proxy_url,
|
|
955
|
+
ca_cert_path=str(ca_cert_path()),
|
|
956
|
+
)
|
|
957
|
+
prev_path = conf_dir / "cursor-proxy-previous-settings.json"
|
|
958
|
+
prev_path.write_text(_json.dumps(previous), encoding="utf-8")
|
|
879
959
|
|
|
880
960
|
print()
|
|
881
|
-
print("
|
|
882
|
-
proxy
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
print("
|
|
961
|
+
print("Cursor configured to route ALL traffic through MTRX.")
|
|
962
|
+
print(f" proxy: {proxy_url}")
|
|
963
|
+
print(f" ca_cert: {ca_cert_path()}")
|
|
964
|
+
print(f" telemetry: {matrx_base_url}/v1/telemetry/cursor")
|
|
965
|
+
print()
|
|
966
|
+
if cursor_is_running():
|
|
967
|
+
print(" Cursor is running — restart it for settings to take effect.")
|
|
968
|
+
else:
|
|
969
|
+
print(" Settings will apply next time Cursor starts.")
|
|
970
|
+
print()
|
|
971
|
+
print(" Check status: mtrx cursor --status")
|
|
972
|
+
print(" To disable: mtrx use cursor direct")
|
|
892
973
|
return 0
|
|
893
974
|
|
|
894
975
|
|