teardrop-cli 0.3.2__tar.gz → 0.3.3__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 (68) hide show
  1. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/PKG-INFO +4 -2
  2. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/README.md +2 -0
  3. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/docs/cli-reference.md +3 -1
  4. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/pyproject.toml +2 -2
  5. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/src/teardrop_cli/commands/auth.py +3 -1
  6. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/src/teardrop_cli/commands/marketplace.py +74 -0
  7. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/tests/conftest.py +31 -0
  8. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/tests/test_auth.py +3 -1
  9. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/tests/test_marketplace.py +49 -0
  10. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/.github/workflows/publish.yml +0 -0
  11. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/.github/workflows/test.yml +0 -0
  12. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/.gitignore +0 -0
  13. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/.vscode/settings.json +0 -0
  14. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/LICENSE +0 -0
  15. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/docs/cli-json-schema.md +0 -0
  16. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/src/teardrop_cli/_fixtures.py +0 -0
  17. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/src/teardrop_cli/_lazy.py +0 -0
  18. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/src/teardrop_cli/_templates.py +0 -0
  19. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/src/teardrop_cli/cli.py +0 -0
  20. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/src/teardrop_cli/commands/__init__.py +0 -0
  21. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/src/teardrop_cli/commands/agent_tools.py +0 -0
  22. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/src/teardrop_cli/commands/balance.py +0 -0
  23. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/src/teardrop_cli/commands/chat.py +0 -0
  24. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/src/teardrop_cli/commands/config_cmd.py +0 -0
  25. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/src/teardrop_cli/commands/earnings.py +0 -0
  26. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/src/teardrop_cli/commands/event_triggers.py +0 -0
  27. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/src/teardrop_cli/commands/llm_config.py +0 -0
  28. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/src/teardrop_cli/commands/mcp.py +0 -0
  29. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/src/teardrop_cli/commands/models.py +0 -0
  30. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/src/teardrop_cli/commands/quickstart.py +0 -0
  31. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/src/teardrop_cli/commands/run.py +0 -0
  32. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/src/teardrop_cli/commands/schedules.py +0 -0
  33. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/src/teardrop_cli/commands/tools.py +0 -0
  34. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/src/teardrop_cli/commands/usage.py +0 -0
  35. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/src/teardrop_cli/config.py +0 -0
  36. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/src/teardrop_cli/formatting.py +0 -0
  37. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/src/teardrop_cli/pricing.py +0 -0
  38. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/tests/e2e/README.md +0 -0
  39. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/tests/e2e/__init__.py +0 -0
  40. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/tests/e2e/conftest.py +0 -0
  41. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/tests/e2e/test_auth_e2e.py +0 -0
  42. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/tests/e2e/test_billing_e2e.py +0 -0
  43. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/tests/e2e/test_marketplace_e2e.py +0 -0
  44. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/tests/e2e/test_run_e2e.py +0 -0
  45. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/tests/e2e/test_signup_e2e.py +0 -0
  46. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/tests/e2e/test_siwe_login_e2e.py +0 -0
  47. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/tests/smoke/__init__.py +0 -0
  48. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/tests/smoke/conftest.py +0 -0
  49. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/tests/smoke/test_config_persistence.py +0 -0
  50. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/tests/smoke/test_keyring_integration.py +0 -0
  51. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/tests/smoke/test_perf_lazygroup.py +0 -0
  52. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/tests/smoke/test_siwe_signing.py +0 -0
  53. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/tests/test_billing.py +0 -0
  54. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/tests/test_chat.py +0 -0
  55. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/tests/test_config_cmd.py +0 -0
  56. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/tests/test_credential_priority.py +0 -0
  57. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/tests/test_earnings.py +0 -0
  58. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/tests/test_event_triggers.py +0 -0
  59. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/tests/test_init_and_byok.py +0 -0
  60. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/tests/test_llm_config.py +0 -0
  61. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/tests/test_mcp.py +0 -0
  62. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/tests/test_models.py +0 -0
  63. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/tests/test_quickstart.py +0 -0
  64. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/tests/test_run.py +0 -0
  65. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/tests/test_schedules.py +0 -0
  66. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/tests/test_signup.py +0 -0
  67. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/tests/test_token_expiry_fallback.py +0 -0
  68. {teardrop_cli-0.3.2 → teardrop_cli-0.3.3}/tests/test_tools.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: teardrop-cli
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Summary: Command-line interface for the Teardrop AI agent platform
5
5
  Project-URL: Homepage, https://github.com/teardrop-ai/teardrop-cli
6
6
  Project-URL: Repository, https://github.com/teardrop-ai/teardrop-cli
@@ -45,7 +45,7 @@ Requires-Dist: httpx>=0.27
45
45
  Requires-Dist: keyring>=25.0
46
46
  Requires-Dist: platformdirs>=4.0
47
47
  Requires-Dist: rich>=13.0
48
- Requires-Dist: teardrop-sdk>=0.3.0
48
+ Requires-Dist: teardrop-sdk>=0.3.2
49
49
  Requires-Dist: tomli-w>=1.0
50
50
  Requires-Dist: typer>=0.12
51
51
  Provides-Extra: dev
@@ -315,6 +315,8 @@ New email-authenticated organizations may receive optional promotional credit af
315
315
  teardrop marketplace list
316
316
  teardrop marketplace search "weather"
317
317
  teardrop marketplace info acme/weather
318
+ teardrop marketplace reputation
319
+ teardrop marketplace reputation acme/weather --json
318
320
  teardrop marketplace subscribe acme/weather
319
321
  teardrop marketplace subscriptions
320
322
  ```
@@ -255,6 +255,8 @@ New email-authenticated organizations may receive optional promotional credit af
255
255
  teardrop marketplace list
256
256
  teardrop marketplace search "weather"
257
257
  teardrop marketplace info acme/weather
258
+ teardrop marketplace reputation
259
+ teardrop marketplace reputation acme/weather --json
258
260
  teardrop marketplace subscribe acme/weather
259
261
  teardrop marketplace subscriptions
260
262
  ```
@@ -192,7 +192,7 @@ teardrop usage --json
192
192
 
193
193
  The CLI does not support direct top-ups. To add credits or manage your billing methods, please visit the dashboard:
194
194
 
195
- [https://teardrop.dev/billing](https://teardrop.dev/billing)
195
+ [https://teardrop.dev](https://teardrop.dev)
196
196
 
197
197
  If you run out of credits during an agent run, the agent will pause and provide this link.
198
198
 
@@ -315,6 +315,8 @@ teardrop marketplace list
315
315
  teardrop marketplace list --category data --json
316
316
  teardrop marketplace search "weather"
317
317
  teardrop marketplace info acme/weather
318
+ teardrop marketplace reputation
319
+ teardrop marketplace reputation acme/weather --json
318
320
  teardrop marketplace subscribe acme/weather
319
321
  teardrop marketplace subscribe acme/weather --yes # skip confirmation
320
322
  teardrop marketplace subscriptions
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "teardrop-cli"
7
- version = "0.3.2"
7
+ version = "0.3.3"
8
8
  description = "Command-line interface for the Teardrop AI agent platform"
9
9
  readme = "README.md"
10
10
  license = { file = "LICENSE" }
@@ -24,7 +24,7 @@ classifiers = [
24
24
  "Topic :: Software Development :: Libraries :: Python Modules",
25
25
  ]
26
26
  dependencies = [
27
- "teardrop-sdk>=0.3.0",
27
+ "teardrop-sdk>=0.3.2",
28
28
  "typer>=0.12",
29
29
  "rich>=13.0",
30
30
  "platformdirs>=4.0",
@@ -462,7 +462,9 @@ async def _siwe_auth_async(
462
462
 
463
463
  async with AsyncTeardropClient(url) as client:
464
464
  nonce_resp = await client.get_siwe_nonce()
465
- nonce = nonce_resp.get("nonce", nonce_resp.get("value", ""))
465
+ # get_siwe_nonce() returns a SiweNonceResponse pydantic model (nonce: str),
466
+ # not a dict — access the attribute directly.
467
+ nonce = nonce_resp.nonce
466
468
 
467
469
  from urllib.parse import urlparse
468
470
 
@@ -240,6 +240,80 @@ def info(
240
240
  data_console.print_json(json.dumps(schema, default=str))
241
241
 
242
242
 
243
+ # ---------------------------------------------------------------------------
244
+ # reputation
245
+ # ---------------------------------------------------------------------------
246
+
247
+
248
+ @app.command()
249
+ def reputation(
250
+ qualified_name: Annotated[
251
+ str | None,
252
+ typer.Argument(metavar="ORG/TOOL", help="Optional qualified tool name filter."),
253
+ ] = None,
254
+ as_json: Annotated[bool, typer.Option("--json", help="Output as JSON.")] = False,
255
+ base_url: Annotated[str | None, typer.Option("--base-url", hidden=True)] = None,
256
+ ) -> None:
257
+ """Show public reputation metrics for marketplace tools (no auth required)."""
258
+ from teardrop_cli import config
259
+ from teardrop_cli.formatting import data_console, print_error, print_json, print_table, spinner
260
+
261
+ client = config.get_client(base_url, require_auth=False)
262
+
263
+ async def _fetch() -> dict:
264
+ try:
265
+ response = await client.get_public_reputation()
266
+ return response.model_dump() if hasattr(response, "model_dump") else dict(response)
267
+ finally:
268
+ await client.close()
269
+
270
+ with spinner("Fetching public reputation…"):
271
+ data = asyncio.run(_fetch())
272
+
273
+ tools = data.get("tools", [])
274
+ if qualified_name is not None:
275
+ tools = [tool for tool in tools if tool.get("qualified_tool_name") == qualified_name]
276
+ if not tools:
277
+ print_error(f"No public reputation found for {qualified_name!r}.")
278
+ raise typer.Exit(1)
279
+ data["tools"] = tools
280
+
281
+ if as_json:
282
+ print_json(data)
283
+ return
284
+
285
+ if not tools:
286
+ data_console.print("[dim]No public reputation data available.[/dim]")
287
+ return
288
+
289
+ data_console.print(
290
+ f"Schema: {data.get('schema_version', '—')} | "
291
+ f"Generated: {data.get('generated_at') or '—'} | "
292
+ f"Methodology: {data.get('methodology_url') or '—'}"
293
+ )
294
+
295
+ rows = [
296
+ [
297
+ tool.get("qualified_tool_name", "—"),
298
+ (
299
+ f"score={tool.get('reputation_score', '—')} "
300
+ f"success={tool.get('success_rate', '—')} "
301
+ f"sample={tool.get('sample_size', '—')} "
302
+ f"confidence={tool.get('confidence', '—')} "
303
+ f"freshness={tool.get('freshness', '—')} "
304
+ f"latency_ms={tool.get('average_latency_ms', '—')} "
305
+ f"callers={tool.get('unique_caller_count', '—')}"
306
+ ),
307
+ ]
308
+ for tool in tools
309
+ ]
310
+ print_table(
311
+ ["Tool", "Metrics"],
312
+ rows,
313
+ title="Public Tool Reputation",
314
+ )
315
+
316
+
243
317
  # ---------------------------------------------------------------------------
244
318
  # subscribe
245
319
  # ---------------------------------------------------------------------------
@@ -159,6 +159,37 @@ def mock_client():
159
159
  "next_cursor": None,
160
160
  }
161
161
  )
162
+ client.get_public_reputation = AsyncMock(
163
+ return_value=MagicMock(
164
+ model_dump=lambda: {
165
+ "schema_version": "1.0",
166
+ "generated_at": "2026-08-02T12:00:00Z",
167
+ "methodology_url": "https://teardrop.dev/reputation-methodology",
168
+ "tools": [
169
+ {
170
+ "qualified_tool_name": "acme/weather",
171
+ "reputation_score": 0.91,
172
+ "success_rate": 0.96,
173
+ "sample_size": 125.0,
174
+ "confidence": 0.88,
175
+ "freshness": 0.97,
176
+ "average_latency_ms": 142.5,
177
+ "unique_caller_count": 18,
178
+ },
179
+ {
180
+ "qualified_tool_name": "acme/search",
181
+ "reputation_score": 0.84,
182
+ "success_rate": 0.9,
183
+ "sample_size": 80.0,
184
+ "confidence": 0.75,
185
+ "freshness": 0.92,
186
+ "average_latency_ms": 210.0,
187
+ "unique_caller_count": None,
188
+ },
189
+ ],
190
+ }
191
+ )
192
+ )
162
193
  client.subscribe = AsyncMock(
163
194
  return_value=MagicMock(
164
195
  model_dump=lambda: {"id": "sub_1", "qualified_tool_name": "acme/weather"}
@@ -2,6 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ from types import SimpleNamespace
5
6
  from unittest.mock import AsyncMock, MagicMock, patch
6
7
 
7
8
  import pytest
@@ -140,7 +141,8 @@ class TestSiweLogin:
140
141
  return None
141
142
 
142
143
  async def get_siwe_nonce(self):
143
- return {"nonce": "abc123"}
144
+ # Mirrors the real SDK: returns a SiweNonceResponse pydantic model.
145
+ return SimpleNamespace(nonce="abc123")
144
146
 
145
147
  async def authenticate_siwe(self, message, signature):
146
148
  seen["message"] = message
@@ -91,6 +91,55 @@ class TestInfo:
91
91
  assert result.exit_code == 1
92
92
 
93
93
 
94
+ class TestReputation:
95
+ def test_reputation_table(self, runner: CliRunner, patch_get_client):
96
+ result = runner.invoke(app, ["marketplace", "reputation"])
97
+ assert result.exit_code == 0, result.output
98
+ assert "Public Tool Reputation" in result.output
99
+ assert "Schema: 1.0" in result.output
100
+ assert "acme/weather" in result.output
101
+ assert "0.91" in result.output
102
+ assert "142.5" in result.output
103
+ assert "acme/search" in result.output
104
+
105
+ def test_reputation_json(self, runner: CliRunner, patch_get_client):
106
+ import json
107
+
108
+ result = runner.invoke(app, ["marketplace", "reputation", "--json"])
109
+ assert result.exit_code == 0, result.output
110
+ data = json.loads(result.output)
111
+ assert data["schema_version"] == "1.0"
112
+ assert data["methodology_url"].endswith("reputation-methodology")
113
+ assert len(data["tools"]) == 2
114
+ assert data["tools"][0]["unique_caller_count"] == 18
115
+ assert data["tools"][1]["unique_caller_count"] is None
116
+
117
+ def test_reputation_filters_by_qualified_name(self, runner: CliRunner, patch_get_client):
118
+ import json
119
+
120
+ result = runner.invoke(app, ["marketplace", "reputation", "acme/weather", "--json"])
121
+ assert result.exit_code == 0, result.output
122
+ data = json.loads(result.output)
123
+ assert [tool["qualified_tool_name"] for tool in data["tools"]] == ["acme/weather"]
124
+
125
+ def test_reputation_missing_tool(self, runner: CliRunner, patch_get_client):
126
+ result = runner.invoke(app, ["marketplace", "reputation", "nope/none"])
127
+ assert result.exit_code == 1
128
+ assert "No public reputation found" in result.output
129
+
130
+ def test_reputation_does_not_require_auth(self, runner: CliRunner, mock_client, monkeypatch):
131
+ calls = {}
132
+
133
+ def get_client(*args, **kwargs):
134
+ calls.update(kwargs)
135
+ return mock_client
136
+
137
+ monkeypatch.setattr("teardrop_cli.config.get_client", get_client)
138
+ result = runner.invoke(app, ["marketplace", "reputation"])
139
+ assert result.exit_code == 0, result.output
140
+ assert calls["require_auth"] is False
141
+
142
+
94
143
  class TestSubscribe:
95
144
  def test_subscribe_with_yes(self, runner: CliRunner, patch_get_client, mock_client):
96
145
  result = runner.invoke(app, ["marketplace", "subscribe", "acme/weather", "--yes"])
File without changes
File without changes