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.
@@ -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.route, args.port)
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("--port", type=int, default=0)
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(route: str | None, port: int) -> int:
784
- import signal as _signal
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(state.get("auth", {}).get("matrx", {}).get("base_url"))
814
-
815
- binding = get_workspace_binding(state, cwd=os.getcwd()) or {}
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
- proxy = CursorProxyServer(
828
- matrx_key=mx_key,
829
- matrx_base_url=matrx_base_url,
830
- group_id=group_id,
831
- project_id=project_id,
832
- port=port,
833
- )
834
- try:
835
- proxy.start_background()
836
- except RuntimeError as exc:
837
- print(f"Failed to start proxy: {exc}", file=sys.stderr)
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
- print_manual_setup_instructions(proxy_url)
864
- else:
865
- print_manual_setup_instructions(proxy_url)
866
-
867
- print()
868
- print("Press Ctrl+C to stop the proxy and restore settings.")
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
- def _on_signal(sig, frame):
873
- stop_event.set()
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
- _signal.signal(_signal.SIGINT, _on_signal)
876
- _signal.signal(_signal.SIGTERM, _on_signal)
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
- stop_event.wait()
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("Shutting down MTRX Cursor proxy...")
882
- proxy.stop()
883
-
884
- if previous_settings is not None:
885
- if restore_cursor_settings(previous_settings, db_path=db_path):
886
- print("Cursor settings restored to previous values.")
887
- else:
888
- print("[warn] Could not restore Cursor settings automatically.")
889
- print(" You may need to reset your OpenAI API Key and Base URL in Cursor Settings > Models.")
890
-
891
- print("Done.")
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