mtrx-cli 0.1.11 → 0.1.14

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.
@@ -32,10 +32,13 @@ 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
44
  from matrx.cli.state import (
@@ -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.route)
80
+ return _cmd_cursor(args)
78
81
 
79
82
  parser.print_help()
80
83
  return 1
@@ -115,6 +118,13 @@ def _build_parser() -> argparse.ArgumentParser:
115
118
 
116
119
  cursor = subparsers.add_parser("cursor")
117
120
  cursor.add_argument("--route", choices=["direct", "matrx"])
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")
123
+ cursor.add_argument(
124
+ "--launch",
125
+ action="store_true",
126
+ help="Launch Cursor with proxy env (required for traffic to flow)",
127
+ )
118
128
 
119
129
  return parser
120
130
 
@@ -495,19 +505,39 @@ def _cmd_use(args) -> int:
495
505
 
496
506
  def _restore_cursor_if_needed() -> None:
497
507
  import json as _json
508
+
509
+ from matrx.cli.cursor_service import is_proxy_running, uninstall_service
510
+
511
+ # Stop the MITM proxy service if it's running
512
+ if is_proxy_running():
513
+ uninstall_service()
514
+ print("MTRX Cursor proxy service stopped.")
515
+
516
+ # Restore settings.json (MITM proxy settings)
517
+ proxy_prev_path = config_dir() / "cursor-proxy-previous-settings.json"
518
+ if proxy_prev_path.exists():
519
+ try:
520
+ previous = _json.loads(proxy_prev_path.read_text(encoding="utf-8"))
521
+ if restore_cursor_proxy_settings(previous):
522
+ proxy_prev_path.unlink(missing_ok=True)
523
+ print("Cursor proxy settings restored.")
524
+ except Exception:
525
+ pass
526
+
527
+ # Restore state.vscdb (legacy Override Base URL settings)
498
528
  prev_path = config_dir() / "cursor-previous-settings.json"
499
529
  db_path = cursor_state_db_path()
500
- if not prev_path.exists() or not db_path.exists():
501
- return
502
- try:
503
- previous = _json.loads(prev_path.read_text(encoding="utf-8"))
504
- if restore_cursor_settings(previous, db_path=db_path):
505
- prev_path.unlink(missing_ok=True)
506
- print("Cursor settings restored to previous values.")
507
- else:
508
- print("[warn] Could not auto-restore Cursor settings.")
509
- except Exception:
510
- pass
530
+ if prev_path.exists() and db_path.exists():
531
+ try:
532
+ previous = _json.loads(prev_path.read_text(encoding="utf-8"))
533
+ if restore_cursor_settings(previous, db_path=db_path):
534
+ prev_path.unlink(missing_ok=True)
535
+ print("Cursor DB settings restored to previous values.")
536
+ else:
537
+ print("[warn] Could not auto-restore Cursor DB settings.")
538
+ except Exception:
539
+ pass
540
+
511
541
  if cursor_is_running():
512
542
  print(" Restart Cursor for settings to take effect.")
513
543
 
@@ -546,6 +576,11 @@ def _cmd_status() -> int:
546
576
  location = config_status["config_path"] or "unknown"
547
577
  verified_at = config_status["last_verified_at"] or "-"
548
578
  print(f" {tool}: {status_label} ({location}, verified={verified_at})")
579
+ # Cursor proxy status
580
+ from matrx.cli.cursor_service import is_proxy_running
581
+ proxy_running = is_proxy_running()
582
+ print(f" cursor proxy: {'running' if proxy_running else 'not running'}")
583
+
549
584
  print("Executables:")
550
585
  print(f" codex: {find_executable('codex') or 'not found'}")
551
586
  print(f" claude: {find_executable('claude') or 'not found'}")
@@ -800,32 +835,59 @@ def _cmd_launch(tool: str, route: str | None, remainder: list[str]) -> int:
800
835
  return launch(plan)
801
836
 
802
837
 
803
- def _cmd_cursor(route: str | None) -> int:
838
+ def _cmd_cursor(args) -> int:
839
+ import json as _json
840
+
841
+ from matrx.cli.cursor_ca import (
842
+ ca_cert_path,
843
+ ca_exists,
844
+ generate_ca,
845
+ is_ca_trusted,
846
+ trust_ca_system,
847
+ )
848
+ from matrx.cli.cursor_proxy import DEFAULT_PORT, PROXY_HOST
849
+ from matrx.cli.cursor_launcher import (
850
+ find_cursor_executable,
851
+ launch_cursor_with_proxy,
852
+ )
853
+ from matrx.cli.cursor_service import (
854
+ get_proxy_status,
855
+ install_service,
856
+ is_proxy_running,
857
+ uninstall_service,
858
+ )
859
+
860
+ route = args.route
861
+
862
+ # --status: just report proxy health
863
+ if args.status:
864
+ status = get_proxy_status()
865
+ if status:
866
+ print("MTRX Cursor proxy: running")
867
+ print(f" requests processed: {status.get('requests', '?')}")
868
+ else:
869
+ print("MTRX Cursor proxy: not running")
870
+ return 0
871
+
872
+ # --stop: tear down the proxy
873
+ if args.stop:
874
+ _restore_cursor_if_needed()
875
+ print("Cursor route set to direct — MTRX proxy disabled.")
876
+ return 0
877
+
804
878
  state = load_state()
805
879
  route, promoted = _maybe_promote_direct_route(state, "cursor", route)
806
880
  effective_route = resolve_route(state, "cursor", route)
807
881
 
808
- db_path = cursor_state_db_path()
809
-
810
882
  if effective_route == "direct":
811
- if db_path.exists():
812
- previous_json = config_dir() / "cursor-previous-settings.json"
813
- if previous_json.exists():
814
- import json as _json
815
- try:
816
- previous = _json.loads(previous_json.read_text(encoding="utf-8"))
817
- if restore_cursor_settings(previous, db_path=db_path):
818
- previous_json.unlink(missing_ok=True)
819
- print("Cursor settings restored to previous values.")
820
- else:
821
- print("[warn] Could not restore Cursor settings automatically.")
822
- except Exception:
823
- pass
883
+ _restore_cursor_if_needed()
824
884
  print("Cursor route set to direct — MTRX proxy disabled.")
825
885
  if cursor_is_running():
826
886
  print(" Restart Cursor for settings to take effect.")
827
887
  return 0
828
888
 
889
+ # --- matrx route: set up the MITM proxy ---
890
+
829
891
  try:
830
892
  state, login_changed = _complete_matrx_login(state)
831
893
  except ValueError as exc:
@@ -845,8 +907,9 @@ def _cmd_cursor(route: str | None) -> int:
845
907
  print("No Matrx key available. Run: mtrx login matrx --key mx_...", file=sys.stderr)
846
908
  return 1
847
909
 
848
- matrx_base_url = ensure_root_url(state.get("auth", {}).get("matrx", {}).get("base_url"))
849
- proxy_url = f"{matrx_base_url.rstrip('/')}/v1"
910
+ matrx_base_url = ensure_root_url(
911
+ state.get("auth", {}).get("matrx", {}).get("base_url")
912
+ )
850
913
 
851
914
  if initialized or login_changed or promoted:
852
915
  save_state(state)
@@ -856,34 +919,75 @@ def _cmd_cursor(route: str | None) -> int:
856
919
  "Use `mtrx use cursor direct` to opt out.",
857
920
  )
858
921
 
859
- if not db_path.exists():
860
- print_manual_setup_instructions(proxy_url)
861
- print(f" Use API Key: {mx_key}")
862
- return 0
922
+ # Step 1: Generate CA certificate if needed
923
+ if not ca_exists():
924
+ print("Generating MTRX CA certificate...")
925
+ generate_ca()
926
+ print(f" CA cert: {ca_cert_path()}")
927
+
928
+ # Step 2: Trust CA (one-time)
929
+ if not is_ca_trusted():
930
+ print("Trusting MTRX CA certificate (may require password)...")
931
+ if trust_ca_system():
932
+ print(" CA trusted in system keychain.")
933
+ else:
934
+ print(
935
+ f" [warn] Could not auto-trust CA. Cursor needs NODE_EXTRA_CA_CERTS={ca_cert_path()}"
936
+ )
937
+ print(
938
+ " You can manually trust it or set the env var before launching Cursor."
939
+ )
863
940
 
864
- previous_settings = configure_cursor_for_proxy(
865
- proxy_url, proxy_api_key=mx_key, db_path=db_path,
866
- )
867
- if previous_settings is None:
868
- print_manual_setup_instructions(proxy_url)
869
- print(f" Use API Key: {mask_secret(mx_key)}")
870
- return 0
941
+ # Step 3: Install and start the proxy service
942
+ proxy_url = f"http://{PROXY_HOST}:{DEFAULT_PORT}"
943
+ if is_proxy_running():
944
+ print(f"MTRX proxy already running on {proxy_url}")
945
+ else:
946
+ print("Starting MTRX Cursor proxy service...")
947
+ if install_service(
948
+ matrx_key=mx_key,
949
+ matrx_base_url=matrx_base_url,
950
+ host=PROXY_HOST,
951
+ port=DEFAULT_PORT,
952
+ ):
953
+ print(f" Proxy running on {proxy_url}")
954
+ else:
955
+ print("[warn] Proxy service may not have started. Check logs at:")
956
+ print(f" {config_dir() / 'logs' / 'cursor-proxy.err.log'}")
871
957
 
872
- import json as _json
873
- prev_path = config_dir() / "cursor-previous-settings.json"
874
- config_dir().mkdir(parents=True, exist_ok=True)
875
- prev_path.write_text(_json.dumps(previous_settings), encoding="utf-8")
958
+ # Step 4: Configure Cursor's settings.json
959
+ conf_dir = config_dir()
960
+ conf_dir.mkdir(parents=True, exist_ok=True)
961
+
962
+ previous = configure_cursor_proxy_settings(
963
+ proxy_url=proxy_url,
964
+ ca_cert_path=str(ca_cert_path()),
965
+ )
966
+ prev_path = conf_dir / "cursor-proxy-previous-settings.json"
967
+ prev_path.write_text(_json.dumps(previous), encoding="utf-8")
876
968
 
877
- print("Cursor configured to route through MTRX.")
878
- print(f" base_url: {proxy_url}")
879
- print(f" api_key: {mask_secret(mx_key)}")
880
969
  print()
881
- if cursor_is_running():
882
- print(" Cursor is running — restart it for settings to take effect.")
970
+ print("Cursor configured to route ALL traffic through MTRX.")
971
+ print(f" proxy: {proxy_url}")
972
+ print(f" ca_cert: {ca_cert_path()}")
973
+ print(f" telemetry: {matrx_base_url}/v1/telemetry/cursor")
974
+ print()
975
+
976
+ # Launch Cursor with proxy env vars (required for traffic to flow)
977
+ if getattr(args, "launch", False):
978
+ from matrx.cli.cursor_launcher import find_cursor_executable, launch_cursor_with_proxy
979
+
980
+ if launch_cursor_with_proxy(proxy_url, str(ca_cert_path())):
981
+ print(" Launched Cursor with proxy env vars — traffic will flow through MTRX.")
982
+ else:
983
+ print(" [warn] Could not launch Cursor. Is it installed?")
984
+ print(" To route traffic, launch Cursor via: mtrx cursor --launch")
883
985
  else:
884
- print(" Settings will apply next time Cursor starts.")
986
+ print(" To route traffic, launch Cursor via: mtrx cursor --launch")
987
+ print(" (Cursor must be started with proxy env vars; Dock/Spotlight launch won't work)")
885
988
  print()
886
- print(" To disable: mtrx use cursor direct")
989
+ print(" Check status: mtrx cursor --status")
990
+ print(" To disable: mtrx use cursor direct")
887
991
  return 0
888
992
 
889
993