teardrop-cli 0.1.1__tar.gz → 0.2.0__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 (71) hide show
  1. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/PKG-INFO +53 -8
  2. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/README.md +51 -6
  3. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/docs/cli-json-schema.md +4 -2
  4. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/docs/cli-reference.md +44 -2
  5. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/pyproject.toml +2 -2
  6. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/src/teardrop_cli/_fixtures.py +57 -3
  7. teardrop_cli-0.2.0/src/teardrop_cli/_templates.py +53 -0
  8. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/src/teardrop_cli/cli.py +2 -0
  9. teardrop_cli-0.2.0/src/teardrop_cli/commands/agent_tools.py +90 -0
  10. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/src/teardrop_cli/commands/auth.py +185 -39
  11. teardrop_cli-0.2.0/src/teardrop_cli/commands/balance.py +202 -0
  12. teardrop_cli-0.2.0/src/teardrop_cli/commands/chat.py +177 -0
  13. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/src/teardrop_cli/commands/earnings.py +31 -13
  14. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/src/teardrop_cli/commands/marketplace.py +50 -15
  15. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/src/teardrop_cli/commands/models.py +4 -2
  16. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/src/teardrop_cli/commands/quickstart.py +31 -59
  17. teardrop_cli-0.2.0/src/teardrop_cli/commands/run.py +373 -0
  18. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/src/teardrop_cli/commands/tools.py +28 -0
  19. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/src/teardrop_cli/config.py +156 -1
  20. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/src/teardrop_cli/formatting.py +159 -35
  21. teardrop_cli-0.2.0/src/teardrop_cli/pricing.py +266 -0
  22. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/tests/conftest.py +21 -1
  23. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/tests/e2e/test_billing_e2e.py +13 -3
  24. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/tests/test_auth.py +47 -11
  25. teardrop_cli-0.2.0/tests/test_billing.py +78 -0
  26. teardrop_cli-0.2.0/tests/test_chat.py +237 -0
  27. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/tests/test_credential_priority.py +57 -0
  28. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/tests/test_init_and_byok.py +65 -0
  29. teardrop_cli-0.2.0/tests/test_marketplace.py +159 -0
  30. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/tests/test_models.py +4 -4
  31. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/tests/test_quickstart.py +64 -18
  32. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/tests/test_run.py +44 -0
  33. teardrop_cli-0.2.0/tests/test_token_expiry_fallback.py +123 -0
  34. teardrop_cli-0.1.1/src/teardrop_cli/_templates.py +0 -37
  35. teardrop_cli-0.1.1/src/teardrop_cli/commands/balance.py +0 -74
  36. teardrop_cli-0.1.1/src/teardrop_cli/commands/run.py +0 -189
  37. teardrop_cli-0.1.1/tests/test_billing.py +0 -39
  38. teardrop_cli-0.1.1/tests/test_marketplace.py +0 -80
  39. teardrop_cli-0.1.1/tests/test_token_expiry_fallback.py +0 -62
  40. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/.github/workflows/publish.yml +0 -0
  41. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/.github/workflows/test.yml +0 -0
  42. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/.gitignore +0 -0
  43. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/.vscode/settings.json +0 -0
  44. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/LICENSE +0 -0
  45. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/src/teardrop_cli/__init__.py +0 -0
  46. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/src/teardrop_cli/_lazy.py +0 -0
  47. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/src/teardrop_cli/commands/__init__.py +0 -0
  48. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/src/teardrop_cli/commands/config_cmd.py +0 -0
  49. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/src/teardrop_cli/commands/llm_config.py +0 -0
  50. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/src/teardrop_cli/commands/mcp.py +0 -0
  51. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/src/teardrop_cli/commands/usage.py +0 -0
  52. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/tests/e2e/README.md +0 -0
  53. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/tests/e2e/__init__.py +0 -0
  54. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/tests/e2e/conftest.py +0 -0
  55. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/tests/e2e/test_auth_e2e.py +0 -0
  56. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/tests/e2e/test_marketplace_e2e.py +0 -0
  57. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/tests/e2e/test_run_e2e.py +0 -0
  58. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/tests/e2e/test_signup_e2e.py +0 -0
  59. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/tests/e2e/test_siwe_login_e2e.py +0 -0
  60. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/tests/smoke/__init__.py +0 -0
  61. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/tests/smoke/conftest.py +0 -0
  62. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/tests/smoke/test_config_persistence.py +0 -0
  63. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/tests/smoke/test_keyring_integration.py +0 -0
  64. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/tests/smoke/test_perf_lazygroup.py +0 -0
  65. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/tests/smoke/test_siwe_signing.py +0 -0
  66. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/tests/test_config_cmd.py +0 -0
  67. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/tests/test_earnings.py +0 -0
  68. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/tests/test_llm_config.py +0 -0
  69. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/tests/test_mcp.py +0 -0
  70. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/tests/test_signup.py +0 -0
  71. {teardrop_cli-0.1.1 → teardrop_cli-0.2.0}/tests/test_tools.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: teardrop-cli
3
- Version: 0.1.1
3
+ Version: 0.2.0
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.1.5
48
+ Requires-Dist: teardrop-sdk>=0.1.8
49
49
  Requires-Dist: tomli-w>=1.0
50
50
  Requires-Dist: typer>=0.12
51
51
  Provides-Extra: dev
@@ -118,14 +118,19 @@ teardrop run "Summarize the latest ETH gas trends"
118
118
  | Goal | Command |
119
119
  |------|---------|
120
120
  | Create account & org | `teardrop auth signup` |
121
- | Sign in with wallet (existing key) | `teardrop auth login --siwe --key-file wallet.key` |
122
- | Generate a new wallet & sign in | `teardrop auth login --siwe --generate-wallet` |
121
+ | Generate a new wallet & sign in | `teardrop auth login --siwe --generate-wallet [--save-key]` |
122
+ | Sign in with wallet (existing key) | `teardrop auth login --siwe --key-file wallet.key [--save-key]` |
123
123
  | Email + password | `teardrop auth login --email you@example.com` |
124
124
  | Pre-issued JWT | `teardrop auth login --token <jwt>` |
125
+ | Invite a team member | `teardrop auth invite colleague@example.com` |
125
126
  | Show identity | `teardrop auth status` |
126
127
  | Sign out | `teardrop auth logout` |
127
128
 
128
- Private keys are never persisted by the CLI. Sessions are stored in your OS keyring.
129
+ Private keys are never persisted by the CLI unless you pass ``--save-key``.
130
+ When saved, the key is stored in your OS keyring (never in a config file)
131
+ and can be used for automatic re-authentication when the session token
132
+ expires. An encrypted keyring backend is required — plaintext fallbacks
133
+ are refused.
129
134
 
130
135
  For machine-to-machine credentials, env-var precedence, and CI patterns, see [docs/cli-reference.md](docs/cli-reference.md#authentication).
131
136
 
@@ -181,6 +186,15 @@ teardrop run "Follow up" --thread <thread-id>
181
186
  # Pass structured context
182
187
  teardrop run "Process this order" --context '{"order_id":"ord_123"}'
183
188
 
189
+ # Exclude specific tools
190
+ teardrop run "Summarize news" --exclude platform/web_search
191
+
192
+ # Estimate cost based on current pricing & config (no run performed)
193
+ teardrop run "Analyze this data" --estimate-cost
194
+
195
+ # Apply a tool policy from a file
196
+ teardrop run "Execute workflow" --policy-file policy.json
197
+
184
198
  # Machine-readable output
185
199
  teardrop run "..." --json --no-stream
186
200
 
@@ -188,18 +202,49 @@ teardrop run "..." --json --no-stream
188
202
  teardrop run "..." --json --with-ui
189
203
  ```
190
204
 
191
- Streaming output renders Markdown live with inline tool calls. A token + cost summary prints at the end. By default, CLI output is optimized for scripting and automation (`emit_ui=false`); use `--with-ui` only if you need structured UI component data in the JSON output.
205
+ Streaming output renders Markdown live with inline tool calls and structured UI components. A token + cost summary prints at the end. By default, CLI output is optimized for scripting and automation (`emit_ui=false`); use `--with-ui` only if you need structured UI component data in the JSON output.
206
+
207
+ ---
208
+
209
+ ## Chat with an Agent
192
210
 
193
- If a run fails on credit, top up via the dashboard:
211
+ A stateful chat mode that automatically continues the same thread across invocations:
194
212
 
195
213
  ```bash
196
- teardrop balance # check credits
214
+ teardrop chat "What is the current ETH gas price?" # auto-creates a thread
215
+ teardrop chat "Follow up on that" # continues the same thread
216
+ teardrop chat "Start fresh" --new # starts a new conversation
217
+ teardrop chat "Specific thread" --thread thr_abc123 # explicit thread by id
218
+ teardrop chat "..." --json # machine-readable output
219
+ ```
220
+
221
+ The active thread id is stored in `~/.teardrop/config.toml` and reused on your next `teardrop chat` call. The thread id is printed after every turn for copy/paste. All run flags (`--context`, `--exclude`, `--policy-file`, `--estimate-cost`, `--with-ui`, `--no-stream`) are supported.
222
+
223
+ Use `teardrop run` for one-shot, script-friendly calls — use `teardrop chat` for conversational interaction.
224
+
225
+ ---
226
+
227
+ If a run fails on credit, check your balance and history:
228
+
229
+ ```bash
230
+ teardrop balance # current credits
231
+ teardrop balance credit-history # past charges and top-ups
197
232
  ```
198
233
 
199
234
  To add credits, visit [teardrop.dev](https://teardrop.dev).
200
235
 
201
236
  ---
202
237
 
238
+ ## Agent Tools
239
+
240
+ List all tools visible to your agent (platform, marketplace subscriptions, and your own tools):
241
+
242
+ ```bash
243
+ teardrop agent-tools list
244
+ ```
245
+
246
+ ---
247
+
203
248
  ## Configure Your LLM
204
249
 
205
250
  One-shot bring-your-own-key wizard:
@@ -58,14 +58,19 @@ teardrop run "Summarize the latest ETH gas trends"
58
58
  | Goal | Command |
59
59
  |------|---------|
60
60
  | Create account & org | `teardrop auth signup` |
61
- | Sign in with wallet (existing key) | `teardrop auth login --siwe --key-file wallet.key` |
62
- | Generate a new wallet & sign in | `teardrop auth login --siwe --generate-wallet` |
61
+ | Generate a new wallet & sign in | `teardrop auth login --siwe --generate-wallet [--save-key]` |
62
+ | Sign in with wallet (existing key) | `teardrop auth login --siwe --key-file wallet.key [--save-key]` |
63
63
  | Email + password | `teardrop auth login --email you@example.com` |
64
64
  | Pre-issued JWT | `teardrop auth login --token <jwt>` |
65
+ | Invite a team member | `teardrop auth invite colleague@example.com` |
65
66
  | Show identity | `teardrop auth status` |
66
67
  | Sign out | `teardrop auth logout` |
67
68
 
68
- Private keys are never persisted by the CLI. Sessions are stored in your OS keyring.
69
+ Private keys are never persisted by the CLI unless you pass ``--save-key``.
70
+ When saved, the key is stored in your OS keyring (never in a config file)
71
+ and can be used for automatic re-authentication when the session token
72
+ expires. An encrypted keyring backend is required — plaintext fallbacks
73
+ are refused.
69
74
 
70
75
  For machine-to-machine credentials, env-var precedence, and CI patterns, see [docs/cli-reference.md](docs/cli-reference.md#authentication).
71
76
 
@@ -121,6 +126,15 @@ teardrop run "Follow up" --thread <thread-id>
121
126
  # Pass structured context
122
127
  teardrop run "Process this order" --context '{"order_id":"ord_123"}'
123
128
 
129
+ # Exclude specific tools
130
+ teardrop run "Summarize news" --exclude platform/web_search
131
+
132
+ # Estimate cost based on current pricing & config (no run performed)
133
+ teardrop run "Analyze this data" --estimate-cost
134
+
135
+ # Apply a tool policy from a file
136
+ teardrop run "Execute workflow" --policy-file policy.json
137
+
124
138
  # Machine-readable output
125
139
  teardrop run "..." --json --no-stream
126
140
 
@@ -128,18 +142,49 @@ teardrop run "..." --json --no-stream
128
142
  teardrop run "..." --json --with-ui
129
143
  ```
130
144
 
131
- Streaming output renders Markdown live with inline tool calls. A token + cost summary prints at the end. By default, CLI output is optimized for scripting and automation (`emit_ui=false`); use `--with-ui` only if you need structured UI component data in the JSON output.
145
+ Streaming output renders Markdown live with inline tool calls and structured UI components. A token + cost summary prints at the end. By default, CLI output is optimized for scripting and automation (`emit_ui=false`); use `--with-ui` only if you need structured UI component data in the JSON output.
146
+
147
+ ---
148
+
149
+ ## Chat with an Agent
132
150
 
133
- If a run fails on credit, top up via the dashboard:
151
+ A stateful chat mode that automatically continues the same thread across invocations:
134
152
 
135
153
  ```bash
136
- teardrop balance # check credits
154
+ teardrop chat "What is the current ETH gas price?" # auto-creates a thread
155
+ teardrop chat "Follow up on that" # continues the same thread
156
+ teardrop chat "Start fresh" --new # starts a new conversation
157
+ teardrop chat "Specific thread" --thread thr_abc123 # explicit thread by id
158
+ teardrop chat "..." --json # machine-readable output
159
+ ```
160
+
161
+ The active thread id is stored in `~/.teardrop/config.toml` and reused on your next `teardrop chat` call. The thread id is printed after every turn for copy/paste. All run flags (`--context`, `--exclude`, `--policy-file`, `--estimate-cost`, `--with-ui`, `--no-stream`) are supported.
162
+
163
+ Use `teardrop run` for one-shot, script-friendly calls — use `teardrop chat` for conversational interaction.
164
+
165
+ ---
166
+
167
+ If a run fails on credit, check your balance and history:
168
+
169
+ ```bash
170
+ teardrop balance # current credits
171
+ teardrop balance credit-history # past charges and top-ups
137
172
  ```
138
173
 
139
174
  To add credits, visit [teardrop.dev](https://teardrop.dev).
140
175
 
141
176
  ---
142
177
 
178
+ ## Agent Tools
179
+
180
+ List all tools visible to your agent (platform, marketplace subscriptions, and your own tools):
181
+
182
+ ```bash
183
+ teardrop agent-tools list
184
+ ```
185
+
186
+ ---
187
+
143
188
  ## Configure Your LLM
144
189
 
145
190
  One-shot bring-your-own-key wizard:
@@ -1,13 +1,14 @@
1
1
  # Teardrop CLI Agent Run JSON Schema
2
2
 
3
- When running `teardrop run "..." --json`, the output is a single JSON object (implies `--no-stream`).
3
+ When running `teardrop run "..." --json` or `teardrop chat "..." --json`,
4
+ the output is a single JSON object (implies `--no-stream`).
4
5
 
5
6
  ## Schema Details
6
7
 
7
8
  The JSON object contains the following fields:
8
9
 
9
10
  - `text` (string): The final text response from the agent.
10
- - `thread_id` (string | null): The ID of the thread used for the run. This can be used with the `--thread` flag in future runs to continue the conversation.
11
+ - `thread_id` (string | null): The ID of the thread used for the run. This can be used with the `--thread` flag in future runs (or `teardrop chat`) to continue the conversation.
11
12
 
12
13
  ## Example Output
13
14
 
@@ -22,4 +23,5 @@ The JSON object contains the following fields:
22
23
  To capture specific fields, use `jq`:
23
24
  ```bash
24
25
  teardrop run "ETH price" --json | jq -r .text
26
+ teardrop chat "ETH price" --json | jq -r .thread_id
25
27
  ```
@@ -8,6 +8,7 @@ This document covers every flag, env var, and subcommand. For the quick path, st
8
8
 
9
9
  - [Authentication](#authentication)
10
10
  - [Running Agents](#running-agents)
11
+ - [Chat Sessions](#chat-sessions)
11
12
  - [Billing & Credits](#billing--credits)
12
13
  - [Tool Management](#tool-management)
13
14
  - [Marketplace](#marketplace)
@@ -90,6 +91,34 @@ Streaming output renders Markdown live with inline tool calls. A token + cost su
90
91
 
91
92
  ---
92
93
 
94
+ ## Chat Sessions
95
+
96
+ ```bash
97
+ teardrop chat "What is the current ETH gas price?" # auto-creates a thread
98
+ teardrop chat "Follow up on that" # continues the same thread
99
+ teardrop chat "Start fresh" --new # starts a new conversation
100
+ teardrop chat "Specific thread" --thread thr_abc123 # explicit thread by id
101
+ teardrop chat "..." --json # machine-readable output
102
+ ```
103
+
104
+ The active thread id is stored in `~/.teardrop/config.toml` (`chat.active_thread_id`).
105
+ The file is chmod'd 600 and the thread id is printed to stderr after every turn.
106
+
107
+ **Flag precedence** (highest first):
108
+ 1. `--new` — clears the stored thread; a fresh one is created server-side.
109
+ 2. `--thread <id>` — overrides any stored thread.
110
+ 3. **Stored thread** — read from config if neither `--new` nor `--thread` is given.
111
+ 4. **Server‑minted** — if none of the above resolve to a value, the server assigns one and it is persisted automatically.
112
+
113
+ Chat supports all the same options as `run` (`--context`, `--exclude`, `--policy-file`,
114
+ `--estimate-cost`, `--with-ui`, `--no-stream`, `--base-url`).
115
+
116
+ **Logout clears state**: `teardrop auth logout` removes both credentials **and**
117
+ the active thread id from the config file. The next `teardrop chat` after logout
118
+ will start a fresh thread.
119
+
120
+ ---
121
+
93
122
  ## Billing & Credits
94
123
 
95
124
  ```bash
@@ -134,17 +163,30 @@ Example `tool.json`:
134
163
  {
135
164
  "name": "get_weather",
136
165
  "description": "Fetch current weather conditions for a given city.",
137
- "webhook_url": "https://api.example.com/webhooks/teardrop",
138
166
  "input_schema": {
139
167
  "type": "object",
140
168
  "properties": {
141
169
  "city": { "type": "string", "description": "City name, e.g. 'London'" }
142
170
  },
143
- "required": ["city"]
171
+ "required": ["city"],
172
+ "additionalProperties": false
173
+ },
174
+ "output_schema": {
175
+ "type": "object",
176
+ "properties": {
177
+ "result": { "type": "string", "description": "Weather summary for the requested city." }
178
+ },
179
+ "required": ["result"],
180
+ "additionalProperties": true
144
181
  },
182
+ "webhook_url": "https://api.example.com/webhooks/teardrop",
183
+ "webhook_method": "GET",
184
+ "auth_header_name": null,
185
+ "auth_header_value": null,
145
186
  "timeout_seconds": 10,
146
187
  "publish_as_mcp": true,
147
188
  "marketplace_description": "Real-time weather data for any city worldwide.",
189
+ "category": "data",
148
190
  "base_price_usdc": 5000
149
191
  }
150
192
  ```
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "teardrop-cli"
7
- version = "0.1.1"
7
+ version = "0.2.0"
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.1.5",
27
+ "teardrop-sdk>=0.1.8",
28
28
  "typer>=0.12",
29
29
  "rich>=13.0",
30
30
  "platformdirs>=4.0",
@@ -176,9 +176,9 @@ def make_benchmarks_response(models: list[dict] | None = None) -> MagicMock:
176
176
  "supports_streaming": True,
177
177
  "quality_tier": 2,
178
178
  "pricing": {
179
- "tokens_in_cost_per_1k": 0.08,
180
- "tokens_out_cost_per_1k": 0.24,
181
- "tool_call_cost": 0.0,
179
+ "tokens_in_cost_per_1k": 1_250.0,
180
+ "tokens_out_cost_per_1k": 6_250.0,
181
+ "tool_call_cost": 1_000.0,
182
182
  },
183
183
  "benchmarks": {
184
184
  "total_runs_7d": 1250,
@@ -195,3 +195,57 @@ def make_benchmarks_response(models: list[dict] | None = None) -> MagicMock:
195
195
  obj.models = models
196
196
  obj.updated_at = data["updated_at"]
197
197
  return obj
198
+
199
+
200
+ def make_pricing_response(
201
+ base_cost_usdc: int = 500,
202
+ tools: list[dict] | None = None,
203
+ ) -> MagicMock:
204
+ """Return a minimal BillingPricingResponse-like mock."""
205
+ if tools is None:
206
+ tools = [
207
+ {
208
+ "tool_name": "acme/weather",
209
+ "price_usdc": 5000,
210
+ "description": "Get the weather",
211
+ }
212
+ ]
213
+ data = {"tools": tools, "base_cost_usdc": base_cost_usdc, "updated_at": "2026-04-16T12:00:00Z"}
214
+ obj = MagicMock()
215
+ obj.model_dump = lambda: dict(data)
216
+ obj.tools = tools
217
+ obj.base_cost_usdc = base_cost_usdc
218
+ obj.updated_at = data["updated_at"]
219
+ return obj
220
+
221
+
222
+ def make_credit_history_entries(
223
+ count: int = 3,
224
+ ) -> list[MagicMock]:
225
+ """Return a list of CreditHistoryEntry-like mocks.
226
+
227
+ Each mock has a ``model_dump`` method so the CLI can normalise it, matching
228
+ the real SDK's ``list[CreditHistoryEntry]`` return type.
229
+ """
230
+ entries = []
231
+ samples = [
232
+ {"reason": "Subscription fee — acme/weather", "amount_usdc": 5000, "operation": "debit"},
233
+ {"reason": "Top-up", "amount_usdc": 50_000_000, "operation": "topup"},
234
+ {"reason": "Agent run — code review", "amount_usdc": 1250, "operation": "debit"},
235
+ ]
236
+ for i in range(count):
237
+ base = samples[i % len(samples)]
238
+ data = {
239
+ "id": f"ch_{i}",
240
+ "amount_usdc": base["amount_usdc"],
241
+ "operation": base["operation"],
242
+ "balance_usdc_after": 100_000_000 - base["amount_usdc"] * (i + 1),
243
+ "reason": base["reason"],
244
+ "created_at": f"2026-06-{10 - i:02d}T12:00:00Z",
245
+ }
246
+ obj = MagicMock()
247
+ obj.model_dump = lambda d=data: dict(d)
248
+ for k, v in data.items():
249
+ setattr(obj, k, v)
250
+ entries.append(obj)
251
+ return entries
@@ -0,0 +1,53 @@
1
+ """In-package templates for ``teardrop tools init``."""
2
+
3
+ from __future__ import annotations
4
+
5
+ # Complete starter template matching CreateOrgToolRequest schema.
6
+ # All fields are explicit --- nullable fields use None so they appear as
7
+ # ``null`` in the output JSON and users discover them early.
8
+ _BASE_TEMPLATE = {
9
+ "name": "{name}",
10
+ "description": "Short one-line summary of what this tool does.",
11
+ "input_schema": {
12
+ "type": "object",
13
+ "properties": {
14
+ "query": {
15
+ "type": "string",
16
+ "description": "Example input field. Replace with your real schema.",
17
+ }
18
+ },
19
+ "required": ["query"],
20
+ "additionalProperties": False,
21
+ },
22
+ "output_schema": {
23
+ "type": "object",
24
+ "properties": {
25
+ "result": {
26
+ "type": "string",
27
+ "description": "Example output field. Replace with your real schema.",
28
+ }
29
+ },
30
+ "required": ["result"],
31
+ "additionalProperties": True,
32
+ },
33
+ "webhook_url": "https://example.com/your-webhook-endpoint",
34
+ "webhook_method": "GET",
35
+ "auth_header_name": None,
36
+ "auth_header_value": None,
37
+ "timeout_seconds": 10,
38
+ "publish_as_mcp": False,
39
+ "marketplace_description": None,
40
+ "category": "",
41
+ "base_price_usdc": 0,
42
+ }
43
+
44
+
45
+ def render_tool_template(name: str, *, with_marketplace: bool = False) -> dict:
46
+ """Return a dict suitable for ``json.dumps`` describing a starter tool."""
47
+ data = {**_BASE_TEMPLATE, "name": name}
48
+ if with_marketplace:
49
+ data["publish_as_mcp"] = True
50
+ data["marketplace_description"] = (
51
+ "One-line marketplace pitch (max 200 characters)."
52
+ )
53
+ return data
@@ -18,12 +18,14 @@ from teardrop_cli._lazy import LazyGroup
18
18
  cls=LazyGroup,
19
19
  lazy_subcommands={
20
20
  "auth": "teardrop_cli.commands.auth:app",
21
+ "chat": "teardrop_cli.commands.chat:app",
21
22
  "init": "teardrop_cli.commands.config_cmd:init_app",
22
23
  "quickstart": "teardrop_cli.commands.quickstart:app",
23
24
  "config": "teardrop_cli.commands.config_cmd:app",
24
25
  "run": "teardrop_cli.commands.run:app",
25
26
  "balance": "teardrop_cli.commands.balance:app",
26
27
  "usage": "teardrop_cli.commands.usage:app",
28
+ "agent-tools": "teardrop_cli.commands.agent_tools:app",
27
29
  "marketplace": "teardrop_cli.commands.marketplace:app",
28
30
  "tools": "teardrop_cli.commands.tools:app",
29
31
  "earnings": "teardrop_cli.commands.earnings:app",
@@ -0,0 +1,90 @@
1
+ """``teardrop agent-tools`` — list tools visible to the agent."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+
7
+ import click
8
+
9
+
10
+ @click.group(name="agent-tools", help="Tools visible to your agent.")
11
+ def app() -> None:
12
+ """Query tools available to the agent (platform + subscribed + custom)."""
13
+
14
+
15
+ @app.command(name="list", help="List all tools visible to your agent.")
16
+ @click.option("--json", "as_json", is_flag=True, default=False, help="Output as JSON.")
17
+ @click.option("--base-url", "base_url", default=None, hidden=True)
18
+ def _list(as_json: bool, base_url: str | None) -> None:
19
+ from teardrop_cli import config
20
+ from teardrop_cli.formatting import (
21
+ data_console,
22
+ handle_token_expiry,
23
+ print_json,
24
+ print_table,
25
+ spinner,
26
+ )
27
+
28
+ async def _fetch_once(client):
29
+ try:
30
+ return await client.get_agent_tools()
31
+ finally:
32
+ await client.close()
33
+
34
+ def _call_once():
35
+ client = config.get_client(base_url)
36
+ return asyncio.run(_fetch_once(client))
37
+
38
+ try:
39
+ with spinner("Fetching agent tools…"):
40
+ tools = _call_once()
41
+ except Exception as exc:
42
+ action = asyncio.run(
43
+ handle_token_expiry(
44
+ exc,
45
+ base_url,
46
+ allow_prompt_login=not as_json,
47
+ )
48
+ )
49
+
50
+ if action == "retry":
51
+ with spinner("Fetching agent tools…"):
52
+ tools = _call_once()
53
+ elif action == "prompt_login":
54
+ from teardrop_cli.commands.auth import interactive_reauthenticate
55
+
56
+ if not interactive_reauthenticate(base_url=base_url):
57
+ raise click.exceptions.Exit(1) from None
58
+ with spinner("Fetching agent tools…"):
59
+ tools = _call_once()
60
+ elif action == "fail":
61
+ raise click.exceptions.Exit(1) from None
62
+ else:
63
+ raise
64
+
65
+ items = [t.model_dump() if hasattr(t, "model_dump") else dict(t) for t in tools]
66
+
67
+ if as_json:
68
+ print_json(items)
69
+ return
70
+
71
+ if not items:
72
+ data_console.print("[dim]No agent tools found.[/dim]")
73
+ return
74
+
75
+ rows = []
76
+ for t in items:
77
+ name = t.get("name", "—")
78
+ source = t.get("source", "—")
79
+ access = t.get("access_mode", "—")
80
+ rows.append([name, source, access])
81
+
82
+ print_table(
83
+ [
84
+ ("Name", {"style": "bold cyan"}),
85
+ ("Source", {"style": "bold"}),
86
+ ("Access", {"style": "bold"}),
87
+ ],
88
+ rows,
89
+ title="Agent Tools",
90
+ )