minitest-cli 0.4.4__tar.gz → 0.4.5__tar.gz

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.
Files changed (57) hide show
  1. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/PKG-INFO +39 -33
  2. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/README.md +38 -32
  3. minitest_cli-0.4.5/install.sh +73 -0
  4. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/pyproject.toml +1 -1
  5. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/src/minitest_cli/commands/auth.py +52 -1
  6. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/src/minitest_cli/core/oauth.py +27 -28
  7. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/src/minitest_cli/core/token_exchange.py +38 -0
  8. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/uv.lock +1 -1
  9. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/.env.example +0 -0
  10. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/.github/workflows/ci.yml +0 -0
  11. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/.github/workflows/release.yml +0 -0
  12. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/.gitignore +0 -0
  13. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/.opencode/skill/release/SKILL.md +0 -0
  14. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/AGENTS.md +0 -0
  15. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/RELEASE.md +0 -0
  16. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/pyrightconfig.json +0 -0
  17. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/src/minitest_cli/__init__.py +0 -0
  18. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/src/minitest_cli/api/__init__.py +0 -0
  19. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/src/minitest_cli/api/client.py +0 -0
  20. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/src/minitest_cli/assets/__init__.py +0 -0
  21. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/src/minitest_cli/assets/callback.html +0 -0
  22. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/src/minitest_cli/commands/__init__.py +0 -0
  23. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/src/minitest_cli/commands/apps.py +0 -0
  24. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/src/minitest_cli/commands/build.py +0 -0
  25. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/src/minitest_cli/commands/build_helpers.py +0 -0
  26. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/src/minitest_cli/commands/flow.py +0 -0
  27. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/src/minitest_cli/commands/flow_helpers.py +0 -0
  28. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/src/minitest_cli/commands/flow_modify.py +0 -0
  29. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/src/minitest_cli/commands/run.py +0 -0
  30. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/src/minitest_cli/commands/run_display.py +0 -0
  31. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/src/minitest_cli/commands/run_helpers.py +0 -0
  32. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/src/minitest_cli/commands/skill.py +0 -0
  33. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/src/minitest_cli/core/__init__.py +0 -0
  34. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/src/minitest_cli/core/app_context.py +0 -0
  35. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/src/minitest_cli/core/auth.py +0 -0
  36. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/src/minitest_cli/core/config.py +0 -0
  37. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/src/minitest_cli/core/credentials.py +0 -0
  38. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/src/minitest_cli/main.py +0 -0
  39. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/src/minitest_cli/models/__init__.py +0 -0
  40. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/src/minitest_cli/models/app.py +0 -0
  41. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/src/minitest_cli/models/base.py +0 -0
  42. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/src/minitest_cli/models/build.py +0 -0
  43. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/src/minitest_cli/models/flow_run.py +0 -0
  44. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/src/minitest_cli/models/flow_template.py +0 -0
  45. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/src/minitest_cli/utils/__init__.py +0 -0
  46. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/src/minitest_cli/utils/output.py +0 -0
  47. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/src/minitest_cli/utils/update_check.py +0 -0
  48. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/tests/__init__.py +0 -0
  49. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/tests/test_apps_commands.py +0 -0
  50. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/tests/test_auth.py +0 -0
  51. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/tests/test_auth_commands.py +0 -0
  52. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/tests/test_build_commands.py +0 -0
  53. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/tests/test_code_quality.py +0 -0
  54. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/tests/test_flow_commands.py +0 -0
  55. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/tests/test_run_commands.py +0 -0
  56. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/tests/test_skill_command.py +0 -0
  57. {minitest_cli-0.4.4 → minitest_cli-0.4.5}/tests/test_version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: minitest-cli
3
- Version: 0.4.4
3
+ Version: 0.4.5
4
4
  Summary: Minitest CLI – command-line interface for the Minitest testing platform
5
5
  Project-URL: Homepage, https://minitap.ai/
6
6
  Project-URL: Source, https://github.com/minitap-ai/minitest-cli
@@ -37,24 +37,30 @@ Command-line interface for the Minitest testing platform.
37
37
 
38
38
  ## Installation
39
39
 
40
- ### pip (recommended)
40
+ ### curl (recommended)
41
41
 
42
42
  ```bash
43
- pip install minitest-cli
43
+ curl -fsSL https://raw.githubusercontent.com/minitap-ai/minitest-cli/main/install.sh | bash
44
44
  ```
45
45
 
46
- ### uvx (zero-install)
46
+ ### Homebrew
47
47
 
48
- Run without installing:
48
+ ```bash
49
+ brew install minitap-ai/tap/minitest-cli
50
+ ```
51
+
52
+ ### pip
49
53
 
50
54
  ```bash
51
- uvx --from minitest-cli minitest --help
55
+ python3 -m pip install --user minitest-cli
52
56
  ```
53
57
 
54
- ### Homebrew
58
+ ### uvx (zero-install)
59
+
60
+ Run without installing:
55
61
 
56
62
  ```bash
57
- brew install minitap-ai/tap/minitest-cli
63
+ uvx --from minitest-cli minitest --help
58
64
  ```
59
65
 
60
66
  ### From source
@@ -81,40 +87,40 @@ minitest run --app <app-id>
81
87
 
82
88
  ## Configuration
83
89
 
84
- | Environment Variable | Description | Required |
85
- |---------------------|-------------|----------|
86
- | `MINITEST_TOKEN` | API authentication token | Yes (or use `minitest auth login`) |
87
- | `MINITEST_APP_ID` | Default app ID | No (can use `--app` flag) |
88
- | `MINITEST_API_URL` | API base URL | No (defaults to production) |
90
+ | Environment Variable | Description | Required |
91
+ | -------------------- | ------------------------ | ---------------------------------- |
92
+ | `MINITEST_TOKEN` | API authentication token | Yes (or use `minitest auth login`) |
93
+ | `MINITEST_APP_ID` | Default app ID | No (can use `--app` flag) |
94
+ | `MINITEST_API_URL` | API base URL | No (defaults to production) |
89
95
 
90
96
  ## Global Flags
91
97
 
92
- | Flag | Description |
93
- |------|-------------|
94
- | `--json` | Output JSON to stdout (diagnostics go to stderr) |
95
- | `--app <id-or-name>` | Target app for commands that require one |
96
- | `--version` | Show CLI version |
97
- | `--help` | Show help |
98
+ | Flag | Description |
99
+ | -------------------- | ------------------------------------------------ |
100
+ | `--json` | Output JSON to stdout (diagnostics go to stderr) |
101
+ | `--app <id-or-name>` | Target app for commands that require one |
102
+ | `--version` | Show CLI version |
103
+ | `--help` | Show help |
98
104
 
99
105
  ## Commands
100
106
 
101
- | Command | Description |
102
- |---------|-------------|
103
- | `minitest auth` | Authentication management |
104
- | `minitest apps` | App management |
105
- | `minitest flow` | Testing flow operations |
106
- | `minitest build` | Build management |
107
- | `minitest run` | Test execution |
107
+ | Command | Description |
108
+ | ---------------- | ------------------------- |
109
+ | `minitest auth` | Authentication management |
110
+ | `minitest apps` | App management |
111
+ | `minitest flow` | Testing flow operations |
112
+ | `minitest build` | Build management |
113
+ | `minitest run` | Test execution |
108
114
 
109
115
  ## Exit Codes
110
116
 
111
- | Code | Meaning |
112
- |------|---------|
113
- | 0 | Success |
114
- | 1 | General error |
115
- | 2 | Authentication error |
116
- | 3 | Network / API error |
117
- | 4 | Resource not found |
117
+ | Code | Meaning |
118
+ | ---- | -------------------- |
119
+ | 0 | Success |
120
+ | 1 | General error |
121
+ | 2 | Authentication error |
122
+ | 3 | Network / API error |
123
+ | 4 | Resource not found |
118
124
 
119
125
  ## Using the Dev Environment
120
126
 
@@ -4,24 +4,30 @@ Command-line interface for the Minitest testing platform.
4
4
 
5
5
  ## Installation
6
6
 
7
- ### pip (recommended)
7
+ ### curl (recommended)
8
8
 
9
9
  ```bash
10
- pip install minitest-cli
10
+ curl -fsSL https://raw.githubusercontent.com/minitap-ai/minitest-cli/main/install.sh | bash
11
11
  ```
12
12
 
13
- ### uvx (zero-install)
13
+ ### Homebrew
14
14
 
15
- Run without installing:
15
+ ```bash
16
+ brew install minitap-ai/tap/minitest-cli
17
+ ```
18
+
19
+ ### pip
16
20
 
17
21
  ```bash
18
- uvx --from minitest-cli minitest --help
22
+ python3 -m pip install --user minitest-cli
19
23
  ```
20
24
 
21
- ### Homebrew
25
+ ### uvx (zero-install)
26
+
27
+ Run without installing:
22
28
 
23
29
  ```bash
24
- brew install minitap-ai/tap/minitest-cli
30
+ uvx --from minitest-cli minitest --help
25
31
  ```
26
32
 
27
33
  ### From source
@@ -48,40 +54,40 @@ minitest run --app <app-id>
48
54
 
49
55
  ## Configuration
50
56
 
51
- | Environment Variable | Description | Required |
52
- |---------------------|-------------|----------|
53
- | `MINITEST_TOKEN` | API authentication token | Yes (or use `minitest auth login`) |
54
- | `MINITEST_APP_ID` | Default app ID | No (can use `--app` flag) |
55
- | `MINITEST_API_URL` | API base URL | No (defaults to production) |
57
+ | Environment Variable | Description | Required |
58
+ | -------------------- | ------------------------ | ---------------------------------- |
59
+ | `MINITEST_TOKEN` | API authentication token | Yes (or use `minitest auth login`) |
60
+ | `MINITEST_APP_ID` | Default app ID | No (can use `--app` flag) |
61
+ | `MINITEST_API_URL` | API base URL | No (defaults to production) |
56
62
 
57
63
  ## Global Flags
58
64
 
59
- | Flag | Description |
60
- |------|-------------|
61
- | `--json` | Output JSON to stdout (diagnostics go to stderr) |
62
- | `--app <id-or-name>` | Target app for commands that require one |
63
- | `--version` | Show CLI version |
64
- | `--help` | Show help |
65
+ | Flag | Description |
66
+ | -------------------- | ------------------------------------------------ |
67
+ | `--json` | Output JSON to stdout (diagnostics go to stderr) |
68
+ | `--app <id-or-name>` | Target app for commands that require one |
69
+ | `--version` | Show CLI version |
70
+ | `--help` | Show help |
65
71
 
66
72
  ## Commands
67
73
 
68
- | Command | Description |
69
- |---------|-------------|
70
- | `minitest auth` | Authentication management |
71
- | `minitest apps` | App management |
72
- | `minitest flow` | Testing flow operations |
73
- | `minitest build` | Build management |
74
- | `minitest run` | Test execution |
74
+ | Command | Description |
75
+ | ---------------- | ------------------------- |
76
+ | `minitest auth` | Authentication management |
77
+ | `minitest apps` | App management |
78
+ | `minitest flow` | Testing flow operations |
79
+ | `minitest build` | Build management |
80
+ | `minitest run` | Test execution |
75
81
 
76
82
  ## Exit Codes
77
83
 
78
- | Code | Meaning |
79
- |------|---------|
80
- | 0 | Success |
81
- | 1 | General error |
82
- | 2 | Authentication error |
83
- | 3 | Network / API error |
84
- | 4 | Resource not found |
84
+ | Code | Meaning |
85
+ | ---- | -------------------- |
86
+ | 0 | Success |
87
+ | 1 | General error |
88
+ | 2 | Authentication error |
89
+ | 3 | Network / API error |
90
+ | 4 | Resource not found |
85
91
 
86
92
  ## Using the Dev Environment
87
93
 
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env bash
2
+ # install.sh — Install minitest-cli (brew → pipx → python3 -m pip fallback)
3
+ #
4
+ # Usage:
5
+ # curl -fsSL https://raw.githubusercontent.com/minitap-ai/minitest-cli/main/install.sh | bash
6
+ #
7
+ set -euo pipefail
8
+
9
+ PACKAGE="minitest-cli"
10
+ BREW_TAP="minitap-ai/tap/minitest-cli"
11
+ INSTALLED_VIA=""
12
+
13
+ info() { printf '\033[1;34m==>\033[0m %s\n' "$*"; }
14
+ warn() { printf '\033[1;33mWarning:\033[0m %s\n' "$*"; }
15
+ error() { printf '\033[1;31mError:\033[0m %s\n' "$*" >&2; }
16
+
17
+ # -------------------------------------------------------------------
18
+ # 1. Try installers in order: brew → pipx → python3 -m pip
19
+ # Each attempt is conditional — failure falls through to the next.
20
+ # -------------------------------------------------------------------
21
+ if command -v brew &>/dev/null; then
22
+ info "Installing $PACKAGE with Homebrew…"
23
+ if brew install "$BREW_TAP"; then
24
+ INSTALLED_VIA="brew"
25
+ else
26
+ warn "Homebrew install failed — trying next method."
27
+ fi
28
+ fi
29
+
30
+ if [[ -z "$INSTALLED_VIA" ]] && command -v pipx &>/dev/null; then
31
+ info "Installing $PACKAGE with pipx…"
32
+ if pipx install "$PACKAGE" --force; then
33
+ INSTALLED_VIA="pipx"
34
+ else
35
+ warn "pipx install failed — trying next method."
36
+ fi
37
+ fi
38
+
39
+ if [[ -z "$INSTALLED_VIA" ]] && command -v python3 &>/dev/null; then
40
+ info "Installing $PACKAGE with python3 -m pip…"
41
+ if python3 -m pip install --user "$PACKAGE"; then
42
+ INSTALLED_VIA="pip"
43
+ else
44
+ warn "pip install failed."
45
+ fi
46
+ fi
47
+
48
+ if [[ -z "$INSTALLED_VIA" ]]; then
49
+ error "All install methods failed or no supported package manager found."
50
+ error " macOS: Install Homebrew → https://brew.sh"
51
+ error " Linux: sudo apt install pipx && pipx ensurepath"
52
+ exit 1
53
+ fi
54
+
55
+ # -------------------------------------------------------------------
56
+ # 2. Verify installation
57
+ # -------------------------------------------------------------------
58
+ if command -v minitest &>/dev/null; then
59
+ info "minitest-cli installed successfully via $INSTALLED_VIA! 🎉"
60
+ minitest --version
61
+ echo ""
62
+ info "Next steps:"
63
+ echo " minitest auth login # authenticate"
64
+ echo " minitest apps list # list your apps"
65
+ echo " minitest --help # see all commands"
66
+ else
67
+ warn "Installation completed, but 'minitest' is not on your PATH."
68
+ if [[ "$INSTALLED_VIA" == "brew" ]]; then
69
+ warn "Try restarting your shell or check: brew --prefix"
70
+ else
71
+ warn "You may need to restart your shell or add ~/.local/bin to your PATH."
72
+ fi
73
+ fi
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "minitest-cli"
3
- version = "0.4.4"
3
+ version = "0.4.5"
4
4
  description = "Minitest CLI – command-line interface for the Minitest testing platform"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -1,5 +1,7 @@
1
1
  """Authentication commands: login, logout, status."""
2
2
 
3
+ import json
4
+ import subprocess
3
5
  from datetime import UTC, datetime
4
6
 
5
7
  import typer
@@ -13,10 +15,37 @@ from minitest_cli.core.auth import (
13
15
  oauth_pkce_login,
14
16
  )
15
17
  from minitest_cli.core.config import Settings
16
- from minitest_cli.utils.output import output, print_error, print_success
18
+ from minitest_cli.utils.output import output, print_error, print_info, print_success
17
19
 
18
20
  app = typer.Typer(name="auth", help="Authentication management.")
19
21
 
22
+ SKILL_NAME = "minitest-cli"
23
+ SKILL_INSTALL_CMD = "npx skills add minitap-ai/agent-skills --skill minitest-cli"
24
+
25
+
26
+ def _is_skill_installed() -> bool:
27
+ """Check if the minitest-cli skill is installed via ``npx skills ls``.
28
+
29
+ Queries both project-level and global scopes so the detection stays in
30
+ sync with whatever directories the ``skills`` CLI manages.
31
+ """
32
+ for flags in (["--json"], ["--json", "-g"]):
33
+ try:
34
+ result = subprocess.run(
35
+ ["npx", "skills", "ls", *flags],
36
+ capture_output=True,
37
+ text=True,
38
+ timeout=30,
39
+ )
40
+ if result.returncode == 0:
41
+ skills = json.loads(result.stdout)
42
+ if any(s.get("name") == SKILL_NAME for s in skills):
43
+ return True
44
+ except (subprocess.TimeoutExpired, json.JSONDecodeError, FileNotFoundError, OSError):
45
+ # npx not available or unexpected output – fall through
46
+ pass
47
+ return False
48
+
20
49
 
21
50
  def _get_settings() -> Settings:
22
51
  """Retrieve settings stored by the main callback."""
@@ -40,6 +69,28 @@ def login() -> None:
40
69
  creds = oauth_pkce_login(settings)
41
70
  print_success(f"Authenticated as {creds.email}")
42
71
 
72
+ # Check if the minitest-cli agent skill is installed
73
+ if not _is_skill_installed():
74
+ print_info("")
75
+ print_info("💡 The minitest-cli agent skill is not installed in this project.")
76
+ print_info(" Your AI agent needs it to know how to use minitest.")
77
+ print_info("")
78
+ try:
79
+ answer = input(" Install it now? [Y/n] ").strip().lower()
80
+ except (EOFError, KeyboardInterrupt):
81
+ answer = "n"
82
+ print() # newline after ^C / ^D
83
+ if answer in ("", "y", "yes"):
84
+ print_info("")
85
+ print_info(f" Running: {SKILL_INSTALL_CMD}")
86
+ print_info("")
87
+ subprocess.run(SKILL_INSTALL_CMD.split(), check=False)
88
+ else:
89
+ print_info("")
90
+ print_info(" You can install it later with:")
91
+ print_info(f" {SKILL_INSTALL_CMD}")
92
+ print_info("")
93
+
43
94
 
44
95
  @app.command()
45
96
  def logout() -> None:
@@ -1,4 +1,4 @@
1
- """OAuth PKCE login flow and token refresh."""
1
+ """OAuth PKCE login flow via Supabase OAuth2 server, and token refresh."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
@@ -20,9 +20,8 @@ from minitest_cli.core.config import Settings
20
20
  from minitest_cli.core.credentials import Credentials
21
21
  from minitest_cli.core.token_exchange import (
22
22
  auth_error,
23
- get_apikey_header,
24
23
  parse_and_save_token_response,
25
- require_supabase_url,
24
+ register_oauth_client,
26
25
  )
27
26
 
28
27
  _ASSETS = importlib.resources.files("minitest_cli.assets")
@@ -59,31 +58,24 @@ def refresh_token(settings: Settings, creds: Credentials) -> Credentials | None:
59
58
 
60
59
 
61
60
  def oauth_pkce_login(settings: Settings) -> Credentials:
62
- """Run the full OAuth PKCE login flow.
61
+ """Run the full OAuth PKCE login flow via Supabase's OAuth2 server.
63
62
 
64
63
  Steps:
65
- 1. Generate code verifier + challenge
66
- 2. Start local callback server
67
- 3. Open browser to Supabase authorize endpoint
68
- 4. Wait for callback with auth code
69
- 5. Exchange code for tokens
70
- 6. Save and return credentials
64
+ 1. Start local callback server
65
+ 2. Dynamically register an OAuth2 client with Supabase
66
+ 3. Generate PKCE code verifier + challenge
67
+ 4. Open browser to Supabase authorize endpoint (shows hosted sign-in page)
68
+ 5. Wait for callback with authorization code
69
+ 6. Exchange code + verifier for tokens at Supabase token endpoint
70
+ 7. Save and return credentials
71
71
  """
72
- supabase_url = require_supabase_url(settings)
72
+ supabase_url = settings.supabase_url.rstrip("/")
73
73
 
74
74
  # PKCE challenge: base64url(sha256(verifier)) without padding
75
75
  code_verifier = secrets.token_urlsafe(64)
76
76
  digest = hashlib.sha256(code_verifier.encode()).digest()
77
77
  code_challenge = base64.urlsafe_b64encode(digest).rstrip(b"=").decode()
78
78
 
79
- # CSRF protection: We do NOT generate our own state parameter.
80
- # Supabase internally uses 'state' as a FlowState UUID (database key) to track
81
- # the OAuth flow context (redirect_to, PKCE params, etc.). If we override it,
82
- # Supabase can't find the FlowState record and falls back to Site URL.
83
- # We still have CSRF protection via:
84
- # 1. Supabase's FlowState UUID (validated on callback)
85
- # 2. PKCE (code_challenge + code_verifier) per OAuth 2.1
86
-
87
79
  # Start callback server
88
80
  auth_code_holder: dict[str, str | None] = {"code": None, "error": None}
89
81
  ready_event = Event()
@@ -122,17 +114,21 @@ def oauth_pkce_login(settings: Settings) -> Credentials:
122
114
  port = server.server_address[1]
123
115
  redirect_uri = f"http://127.0.0.1:{port}/callback"
124
116
 
125
- # Build authorize URL (no custom state - see comment above)
117
+ # Register OAuth client with Supabase (dynamic client registration)
118
+ client_id = register_oauth_client(supabase_url, redirect_uri)
119
+
120
+ # Build authorize URL — Supabase's OAuth2 server shows its hosted sign-in page
126
121
  authorize_params = urllib.parse.urlencode(
127
122
  {
128
- "provider": "google",
123
+ "client_id": client_id,
124
+ "redirect_uri": redirect_uri,
129
125
  "response_type": "code",
130
126
  "code_challenge": code_challenge,
131
127
  "code_challenge_method": "S256",
132
- "redirect_to": redirect_uri,
128
+ "scope": "openid email profile",
133
129
  }
134
130
  )
135
- authorize_url = f"{supabase_url}/auth/v1/authorize?{authorize_params}"
131
+ authorize_url = f"{supabase_url}/auth/v1/oauth/authorize?{authorize_params}"
136
132
 
137
133
  print("Opening browser for authentication...", file=sys.stderr) # noqa: T201
138
134
  print(f"If the browser doesn't open, visit:\n{authorize_url}", file=sys.stderr) # noqa: T201
@@ -156,16 +152,19 @@ def oauth_pkce_login(settings: Settings) -> Credentials:
156
152
  if not auth_code:
157
153
  auth_error("No authorization code received.")
158
154
 
159
- # Exchange code for tokens (Supabase uses grant_type=pkce for PKCE flow)
155
+ # Exchange code for tokens at Supabase's OAuth2 token endpoint
160
156
  assert auth_code is not None # for type narrowing
161
157
  try:
162
158
  token_response = httpx.post(
163
- f"{supabase_url}/auth/v1/token?grant_type=pkce",
164
- json={
165
- "auth_code": auth_code,
159
+ f"{supabase_url}/auth/v1/oauth/token",
160
+ data={
161
+ "grant_type": "authorization_code",
162
+ "code": auth_code,
163
+ "redirect_uri": redirect_uri,
164
+ "client_id": client_id,
166
165
  "code_verifier": code_verifier,
167
166
  },
168
- headers={"Content-Type": "application/json", "apikey": get_apikey_header(settings)},
167
+ headers={"Content-Type": "application/x-www-form-urlencoded"},
169
168
  timeout=15.0,
170
169
  )
171
170
  except httpx.HTTPError as exc:
@@ -6,6 +6,8 @@ import sys
6
6
  import time
7
7
  from typing import Any, NoReturn
8
8
 
9
+ import httpx
10
+
9
11
  from minitest_cli.core.config import Settings
10
12
  from minitest_cli.core.credentials import Credentials, save_credentials
11
13
 
@@ -51,6 +53,42 @@ def parse_and_save_token_response(settings: Settings, data: dict[str, Any]) -> C
51
53
  return None
52
54
 
53
55
 
56
+ def register_oauth_client(supabase_url: str, redirect_uri: str) -> str:
57
+ """Dynamically register an OAuth2 client with Supabase and return the client_id."""
58
+ register_url = f"{supabase_url}/auth/v1/oauth/clients/register"
59
+ try:
60
+ resp = httpx.post(
61
+ register_url,
62
+ json={
63
+ "client_name": "minitest-cli",
64
+ "redirect_uris": [redirect_uri],
65
+ "grant_types": ["authorization_code", "refresh_token"],
66
+ "response_types": ["code"],
67
+ "token_endpoint_auth_method": "none",
68
+ },
69
+ headers={"Content-Type": "application/json"},
70
+ timeout=15.0,
71
+ )
72
+ except httpx.HTTPError as exc:
73
+ auth_error(f"Failed to register OAuth client: {exc}")
74
+
75
+ if resp.status_code not in (200, 201):
76
+ auth_error(f"OAuth client registration failed: {resp.text}")
77
+
78
+ try:
79
+ data = resp.json()
80
+ except ValueError:
81
+ auth_error(
82
+ f"OAuth client registration returned invalid response "
83
+ f"(HTTP {resp.status_code}): {resp.text}"
84
+ )
85
+
86
+ client_id: str | None = data.get("client_id")
87
+ if not client_id:
88
+ auth_error("OAuth client registration returned no client_id.")
89
+ return client_id # type: ignore[return-value]
90
+
91
+
54
92
  def auth_error(message: str) -> NoReturn:
55
93
  """Print auth error to stderr and exit with code 2."""
56
94
  print(f"Error: {message}", file=sys.stderr) # noqa: T201
@@ -141,7 +141,7 @@ wheels = [
141
141
 
142
142
  [[package]]
143
143
  name = "minitest-cli"
144
- version = "0.4.3"
144
+ version = "0.4.5"
145
145
  source = { editable = "." }
146
146
  dependencies = [
147
147
  { name = "httpx" },
File without changes
File without changes
File without changes
File without changes