Skip to content

Commit 3aee4e7

Browse files
authored
Use asta-gateway for API requests
1 parent 51b594e commit 3aee4e7

25 files changed

+535
-45
lines changed

.claude-plugin/marketplace.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"name": "asta",
1212
"source": "./",
1313
"description": "Paper search, citations, literature reports, and Semantic Scholar API tools",
14-
"version": "0.6",
14+
"version": "0.7.0",
1515
"author": {
1616
"name": "AI2 Asta Team"
1717
},

.claude-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "asta",
3-
"version": "0.6",
3+
"version": "0.7.0",
44
"description": "Asta science tools for Claude Code - paper search, citations, and more",
55
"author": {
66
"name": "AI2 Asta Team"

DEVELOPER.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ hooks/ # Claude Code permission hooks
4242

4343
### Design Principles
4444

45-
1. **Core is dependency-free**: `asta.core.client` and `asta.papers.client` use only stdlib (json, urllib, time, etc.)
45+
1. **Core clients are dependency-free**: `asta.literature.client` and `asta.papers.client` use only stdlib (json, urllib, time, etc.)
4646
2. **CLI layer is thin**: Click commands wrap the core clients
4747
3. **Generic passthrough architecture**: `asta.utils.passthrough` provides reusable utilities for external tool integration
4848
- `ensure_tool_installed()`: Checks for tool, auto-installs if missing
@@ -172,7 +172,7 @@ Add a new file in `src/asta/literature/` (or create a new subpackage):
172172
```python
173173
# src/asta/literature/analyze.py
174174
import click
175-
from asta.core import AstaPaperFinder
175+
from asta.literature.client import AstaPaperFinder
176176

177177
@click.command()
178178
@click.argument("widget_id")
@@ -213,10 +213,10 @@ Update README.md and this DEVELOPER.md with the new command.
213213

214214
### AstaPaperFinder
215215

216-
The `AstaPaperFinder` client in `src/asta/core/client.py` handles paper finder API interactions.
216+
The `AstaPaperFinder` client in `src/asta/literature/client.py` handles paper finder API interactions.
217217

218218
```python
219-
from asta.core import AstaPaperFinder
219+
from asta.literature import AstaPaperFinder
220220

221221
client = AstaPaperFinder()
222222

@@ -494,13 +494,13 @@ Only add dependencies if absolutely necessary:
494494
3. Update tests to verify it works
495495
4. Document why it's needed
496496

497-
Core (`asta.core`) should remain dependency-free.
497+
Core API clients (`asta.literature.client`, `asta.papers.client`) should remain dependency-free.
498498

499499
### Updating API Endpoints
500500

501501
If Asta's API changes:
502502

503-
1. Update `AstaPaperFinder` in `src/asta/core/client.py`
503+
1. Update `AstaPaperFinder` in `src/asta/literature/client.py`
504504
2. Add/update tests in `tests/test_client.py`
505505
3. Update documentation examples
506506
4. Consider backward compatibility

pyproject.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "asta"
7-
version = "0.6"
7+
version = "0.7.0"
88
description = "Asta CLI for scientific literature review"
99
readme = "README.md"
1010
requires-python = ">=3.11"
@@ -52,4 +52,7 @@ python_files = ["test_*.py"]
5252
asyncio_mode = "auto"
5353
filterwarnings = [
5454
"ignore::pyparsing.exceptions.PyparsingDeprecationWarning",
55+
"ignore:builtin type SwigPyPacked has no __module__ attribute:DeprecationWarning",
56+
"ignore:builtin type SwigPyObject has no __module__ attribute:DeprecationWarning",
57+
"ignore:builtin type swigvarlink has no __module__ attribute:DeprecationWarning",
5558
]

src/asta/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""Asta - Science literature research tools"""
22

3-
__version__ = "0.6"
3+
__version__ = "0.7.0"

src/asta/auth/device_flow.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def __init__(
4545
domain: str,
4646
client_id: str,
4747
audience: str,
48-
scopes: str = "openid profile email offline_access",
48+
scopes: str = "openid profile email offline_access access:all",
4949
):
5050
self.domain = domain
5151
self.client_id = client_id
@@ -55,11 +55,6 @@ def __init__(
5555
self.token_url = f"https://{domain}/oauth/token"
5656

5757
async def initiate(self) -> DeviceCodeResponse:
58-
"""
59-
Step 1: Initiate device authorization flow.
60-
61-
Returns device code and user instructions.
62-
"""
6358
async with httpx.AsyncClient() as client:
6459
response = await client.post(
6560
self.device_code_url,

src/asta/commands/auth.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
"""
44

55
import asyncio
6+
import base64
67
import datetime
8+
import json
79

810
import click
911
from rich.console import Console
@@ -183,3 +185,59 @@ def status():
183185
console.print(
184186
" You may need to run [cyan]asta auth login[/cyan] to re-authenticate."
185187
)
188+
189+
190+
@auth.command(name="print-token")
191+
@click.option("--raw", is_flag=True, help="Print raw base64-encoded token")
192+
def print_token(raw):
193+
"""Print the stored access token."""
194+
storage = TokenStorage()
195+
tokens = storage.load_tokens()
196+
197+
if not tokens or not tokens.get("access_token"):
198+
console.print("❌ [red]No token found[/red]")
199+
console.print(" Run [cyan]asta auth login[/cyan] to authenticate")
200+
raise click.Abort()
201+
202+
access_token = tokens["access_token"]
203+
204+
if raw:
205+
# Print raw base64-encoded token
206+
click.echo(access_token)
207+
else:
208+
# Decode and pretty-print the JWT
209+
try:
210+
# JWT format: header.payload.signature
211+
parts = access_token.split(".")
212+
if len(parts) != 3:
213+
console.print("❌ [red]Invalid JWT format[/red]")
214+
raise click.Abort()
215+
216+
# Decode header and payload (add padding if needed)
217+
def decode_base64url(data):
218+
"""Decode base64url, adding padding if necessary."""
219+
# Add padding if needed
220+
padding = 4 - (len(data) % 4)
221+
if padding != 4:
222+
data += "=" * padding
223+
# Replace URL-safe characters
224+
data = data.replace("-", "+").replace("_", "/")
225+
return base64.b64decode(data)
226+
227+
header = json.loads(decode_base64url(parts[0]))
228+
payload = json.loads(decode_base64url(parts[1]))
229+
230+
# Pretty print the decoded token
231+
console.print("[bold]JWT Header:[/bold]")
232+
console.print(json.dumps(header, indent=2))
233+
console.print()
234+
console.print("[bold]JWT Payload:[/bold]")
235+
console.print(json.dumps(payload, indent=2))
236+
237+
except Exception as e:
238+
console.print(f"❌ [red]Failed to decode token: {e}[/red]")
239+
console.print()
240+
console.print(
241+
"[yellow]Tip:[/yellow] Use [cyan]--raw[/cyan] to see the encoded token"
242+
)
243+
raise click.Abort()

src/asta/core/__init__.py

Lines changed: 0 additions & 5 deletions
This file was deleted.

src/asta/literature/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
11
"""Literature research commands"""
2+
3+
from .client import AstaPaperFinder
4+
5+
__all__ = ["AstaPaperFinder"]
Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,38 @@
1010
from pathlib import Path
1111
from typing import Any
1212

13+
from asta.utils.auth_helper import get_access_token
14+
from asta.utils.config import get_api_config, get_config_path
15+
1316

1417
class AstaPaperFinder:
1518
"""Client for Asta Paper Finder API using headless endpoint"""
1619

17-
def __init__(self, base_url: str = "REDACTED_MABOOL_WORKERS_URL"):
20+
def __init__(self, base_url: str | None = None, access_token: str | None = None):
21+
# Load base URL from config if not provided
22+
if base_url is None:
23+
try:
24+
config = get_api_config("paper_finder")
25+
base_url = config.get("base_url")
26+
except (KeyError, FileNotFoundError):
27+
base_url = None
28+
29+
if not base_url:
30+
raise ValueError(
31+
f"No value for apis.paper_finder.base_url in {get_config_path()}"
32+
"base_url is required. Either provide it as a parameter or configure it in asta.conf"
33+
)
34+
35+
# Load access token from storage if not provided
36+
# AuthenticationError will propagate with helpful message
37+
if access_token is None:
38+
access_token = get_access_token()
39+
1840
self.base_url = base_url
41+
self.access_token = access_token
1942
self.headers = {
2043
"Content-Type": "application/json",
44+
"Authorization": f"Bearer {self.access_token}",
2145
}
2246

2347
def _request(

0 commit comments

Comments
 (0)