mtrx-cli 0.1.5 → 0.1.6

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mtrx-cli",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "MATRX CLI for routing Codex and Claude through Matrx",
5
5
  "homepage": "https://mtrx.so",
6
6
  "repository": {
@@ -1 +1 @@
1
- __version__ = "0.1.5"
1
+ __version__ = "0.1.6"
@@ -221,7 +221,16 @@ def codex_auth_path() -> Path:
221
221
 
222
222
  def claude_oauth_available() -> bool:
223
223
  token = read_claude_oauth_token()
224
- return bool(token)
224
+ if token:
225
+ return True
226
+ state = _read_claude_app_state()
227
+ oauth_account = state.get("oauthAccount")
228
+ if not isinstance(oauth_account, dict):
229
+ return False
230
+ return bool(
231
+ (oauth_account.get("accountUuid") or "").strip()
232
+ or (oauth_account.get("emailAddress") or "").strip()
233
+ )
225
234
 
226
235
 
227
236
  def read_claude_oauth_token() -> str | None:
@@ -500,6 +509,7 @@ def _build_claude_env(
500
509
  env_b64 = base64.b64encode(json.dumps(env_snap).encode()).decode() if env_snap else ""
501
510
  custom_headers = "\n".join(
502
511
  [
512
+ f"x-matrx-key: {mx_key}",
503
513
  "x-matrx-agent-id: claude-cli",
504
514
  "x-matrx-provider: claude_code",
505
515
  f"x-matrx-session-id: {session_id}",
@@ -7,7 +7,6 @@ import os
7
7
  import subprocess
8
8
  import sys
9
9
  import threading
10
- import time
11
10
  import urllib.parse
12
11
  import webbrowser
13
12
 
@@ -263,103 +262,23 @@ def _complete_codex_login() -> None:
263
262
  raise ValueError("Codex login did not complete successfully")
264
263
 
265
264
 
266
- def _resolve_matrx_launch_key(state: dict, env: dict[str, str] | None = None) -> str:
267
- env = env or os.environ
268
- env_key = (env.get("MTRX_KEY") or "").strip()
269
- if env_key:
270
- return env_key
271
- workspace_binding = get_workspace_binding(state, cwd=env.get("PWD") or os.getcwd()) or {}
272
- workspace_key = (workspace_binding.get("matrx_key") or "").strip()
273
- if workspace_key:
274
- return workspace_key
275
- return (state.get("auth", {}).get("matrx", {}).get("key") or "").strip()
276
-
277
-
278
- def _matrx_request_context(state: dict) -> tuple[str, dict[str, str]]:
279
- mx_key = _resolve_matrx_launch_key(state)
280
- if not mx_key.startswith("mx_"):
281
- raise ValueError("Matrx login required before connecting Claude Code")
282
- base_url = ensure_root_url(state.get("auth", {}).get("matrx", {}).get("base_url"))
283
- return base_url, {"X-Matrx-Key": mx_key}
284
-
285
-
286
- def _list_matrx_subscriptions(state: dict) -> list[dict]:
287
- base_url, headers = _matrx_request_context(state)
288
- try:
289
- with httpx.Client(timeout=15) as client:
290
- response = client.get(f"{base_url.rstrip('/')}/v1/subscriptions", headers=headers)
291
- response.raise_for_status()
292
- except httpx.HTTPStatusError as exc:
293
- detail = exc.response.text.strip() or f"HTTP {exc.response.status_code}"
294
- raise ValueError(f"Could not query Matrx subscriptions: {detail}") from exc
295
- except httpx.HTTPError as exc:
296
- raise ValueError(f"Could not query Matrx subscriptions: {exc}") from exc
297
-
298
- payload = response.json()
299
- subscriptions = payload.get("subscriptions")
300
- if not isinstance(subscriptions, list):
301
- return []
302
- return [entry for entry in subscriptions if isinstance(entry, dict)]
303
-
304
-
305
- def _has_server_claude_subscription(state: dict) -> bool:
306
- try:
307
- subscriptions = _list_matrx_subscriptions(state)
308
- except ValueError:
309
- return False
310
- return any((entry.get("provider") or "").strip() == "claude_code" for entry in subscriptions)
311
-
312
-
313
- def _run_claude_subscription_browser_login(state: dict) -> None:
314
- base_url, headers = _matrx_request_context(state)
315
- try:
316
- with httpx.Client(timeout=15) as client:
317
- response = client.get(
318
- f"{base_url.rstrip('/')}/v1/subscriptions/claude-code/authorize",
319
- headers=headers,
320
- )
321
- response.raise_for_status()
322
- except httpx.HTTPStatusError as exc:
323
- detail = exc.response.text.strip() or f"HTTP {exc.response.status_code}"
324
- raise ValueError(f"Could not start Claude Code connection: {detail}") from exc
325
- except httpx.HTTPError as exc:
326
- raise ValueError(f"Could not start Claude Code connection: {exc}") from exc
327
-
328
- payload = response.json()
329
- auth_url = (payload.get("auth_url") or "").strip()
330
- if not auth_url:
331
- raise ValueError("Matrx did not return a Claude Code authorization URL")
332
-
333
- print(f"Open this URL to connect Claude Code to Matrx:\n {auth_url}")
334
- webbrowser.open(auth_url)
335
-
336
- deadline = time.monotonic() + 300
337
- while time.monotonic() < deadline:
338
- if _has_server_claude_subscription(state):
339
- return
340
- time.sleep(2)
341
-
342
- raise ValueError("Claude Code connection timed out")
343
-
344
-
345
265
  def _complete_claude_login(state: dict) -> tuple[dict, bool]:
346
- token = (read_claude_oauth_token() or "").strip()
347
266
  imported = (state.get("auth", {}).get("claude_code", {}).get("oauth_token") or "").strip()
348
- if token or imported:
349
- return state, False
350
- if _has_server_claude_subscription(state):
267
+ if imported or claude_oauth_available():
351
268
  return state, False
352
269
  if not _is_interactive_terminal():
353
270
  raise ValueError(
354
- "Claude provider connection required. "
355
- "Run `mtrx login claude-code --import` or rerun interactively to connect in the browser"
271
+ "Claude login required. Run: claude auth login"
356
272
  )
357
273
 
358
- print("Claude provider connection required.")
359
- if not _prompt_yes_no("Open the browser-based Claude Code connection flow now?", default=True):
360
- raise ValueError("Claude connection cancelled")
274
+ print("Claude login required.")
275
+ if not _prompt_yes_no("Run `claude auth login` now?", default=True):
276
+ raise ValueError("Claude login cancelled")
361
277
 
362
- _run_claude_subscription_browser_login(state)
278
+ executable = find_executable("claude") or "claude"
279
+ result = subprocess.run([executable, "auth", "login"], check=False)
280
+ if result.returncode != 0 or not claude_oauth_available():
281
+ raise ValueError("Claude login did not complete successfully")
363
282
 
364
283
  return state, False
365
284