mtrx-cli 0.1.11 → 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.
@@ -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,8 @@ 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")
118
123
 
119
124
  return parser
120
125
 
@@ -495,19 +500,39 @@ def _cmd_use(args) -> int:
495
500
 
496
501
  def _restore_cursor_if_needed() -> None:
497
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)
498
523
  prev_path = config_dir() / "cursor-previous-settings.json"
499
524
  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
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
+
511
536
  if cursor_is_running():
512
537
  print(" Restart Cursor for settings to take effect.")
513
538
 
@@ -546,6 +571,11 @@ def _cmd_status() -> int:
546
571
  location = config_status["config_path"] or "unknown"
547
572
  verified_at = config_status["last_verified_at"] or "-"
548
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
+
549
579
  print("Executables:")
550
580
  print(f" codex: {find_executable('codex') or 'not found'}")
551
581
  print(f" claude: {find_executable('claude') or 'not found'}")
@@ -800,32 +830,55 @@ def _cmd_launch(tool: str, route: str | None, remainder: list[str]) -> int:
800
830
  return launch(plan)
801
831
 
802
832
 
803
- def _cmd_cursor(route: str | None) -> int:
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
868
+
804
869
  state = load_state()
805
870
  route, promoted = _maybe_promote_direct_route(state, "cursor", route)
806
871
  effective_route = resolve_route(state, "cursor", route)
807
872
 
808
- db_path = cursor_state_db_path()
809
-
810
873
  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
874
+ _restore_cursor_if_needed()
824
875
  print("Cursor route set to direct — MTRX proxy disabled.")
825
876
  if cursor_is_running():
826
877
  print(" Restart Cursor for settings to take effect.")
827
878
  return 0
828
879
 
880
+ # --- matrx route: set up the MITM proxy ---
881
+
829
882
  try:
830
883
  state, login_changed = _complete_matrx_login(state)
831
884
  except ValueError as exc:
@@ -845,8 +898,9 @@ def _cmd_cursor(route: str | None) -> int:
845
898
  print("No Matrx key available. Run: mtrx login matrx --key mx_...", file=sys.stderr)
846
899
  return 1
847
900
 
848
- matrx_base_url = ensure_root_url(state.get("auth", {}).get("matrx", {}).get("base_url"))
849
- proxy_url = f"{matrx_base_url.rstrip('/')}/v1"
901
+ matrx_base_url = ensure_root_url(
902
+ state.get("auth", {}).get("matrx", {}).get("base_url")
903
+ )
850
904
 
851
905
  if initialized or login_changed or promoted:
852
906
  save_state(state)
@@ -856,34 +910,66 @@ def _cmd_cursor(route: str | None) -> int:
856
910
  "Use `mtrx use cursor direct` to opt out.",
857
911
  )
858
912
 
859
- if not db_path.exists():
860
- print_manual_setup_instructions(proxy_url)
861
- print(f" Use API Key: {mx_key}")
862
- return 0
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.")
924
+ else:
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
+ )
863
931
 
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
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'}")
871
948
 
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")
949
+ # Step 4: Configure Cursor's settings.json
950
+ conf_dir = config_dir()
951
+ conf_dir.mkdir(parents=True, exist_ok=True)
876
952
 
877
- print("Cursor configured to route through MTRX.")
878
- print(f" base_url: {proxy_url}")
879
- print(f" api_key: {mask_secret(mx_key)}")
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")
959
+
960
+ 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")
880
965
  print()
881
966
  if cursor_is_running():
882
967
  print(" Cursor is running — restart it for settings to take effect.")
883
968
  else:
884
969
  print(" Settings will apply next time Cursor starts.")
885
970
  print()
886
- print(" To disable: mtrx use cursor direct")
971
+ print(" Check status: mtrx cursor --status")
972
+ print(" To disable: mtrx use cursor direct")
887
973
  return 0
888
974
 
889
975