teardrop-cli 0.3.0__tar.gz → 0.3.2__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 (69) hide show
  1. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/PKG-INFO +20 -8
  2. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/README.md +18 -6
  3. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/docs/cli-reference.md +36 -17
  4. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/pyproject.toml +2 -2
  5. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/src/teardrop_cli/commands/auth.py +22 -12
  6. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/src/teardrop_cli/commands/quickstart.py +136 -22
  7. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/src/teardrop_cli/commands/run.py +10 -6
  8. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/src/teardrop_cli/commands/tools.py +74 -20
  9. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/src/teardrop_cli/formatting.py +3 -2
  10. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/tests/smoke/conftest.py +1 -0
  11. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/tests/test_auth.py +20 -0
  12. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/tests/test_quickstart.py +98 -25
  13. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/tests/test_token_expiry_fallback.py +24 -0
  14. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/tests/test_tools.py +128 -0
  15. teardrop_cli-0.3.0/src/teardrop_cli/__init__.py +0 -1
  16. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/.github/workflows/publish.yml +0 -0
  17. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/.github/workflows/test.yml +0 -0
  18. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/.gitignore +0 -0
  19. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/.vscode/settings.json +0 -0
  20. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/LICENSE +0 -0
  21. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/docs/cli-json-schema.md +0 -0
  22. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/src/teardrop_cli/_fixtures.py +0 -0
  23. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/src/teardrop_cli/_lazy.py +0 -0
  24. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/src/teardrop_cli/_templates.py +0 -0
  25. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/src/teardrop_cli/cli.py +0 -0
  26. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/src/teardrop_cli/commands/__init__.py +0 -0
  27. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/src/teardrop_cli/commands/agent_tools.py +0 -0
  28. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/src/teardrop_cli/commands/balance.py +0 -0
  29. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/src/teardrop_cli/commands/chat.py +0 -0
  30. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/src/teardrop_cli/commands/config_cmd.py +0 -0
  31. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/src/teardrop_cli/commands/earnings.py +0 -0
  32. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/src/teardrop_cli/commands/event_triggers.py +0 -0
  33. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/src/teardrop_cli/commands/llm_config.py +0 -0
  34. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/src/teardrop_cli/commands/marketplace.py +0 -0
  35. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/src/teardrop_cli/commands/mcp.py +0 -0
  36. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/src/teardrop_cli/commands/models.py +0 -0
  37. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/src/teardrop_cli/commands/schedules.py +0 -0
  38. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/src/teardrop_cli/commands/usage.py +0 -0
  39. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/src/teardrop_cli/config.py +0 -0
  40. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/src/teardrop_cli/pricing.py +0 -0
  41. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/tests/conftest.py +0 -0
  42. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/tests/e2e/README.md +0 -0
  43. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/tests/e2e/__init__.py +0 -0
  44. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/tests/e2e/conftest.py +0 -0
  45. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/tests/e2e/test_auth_e2e.py +0 -0
  46. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/tests/e2e/test_billing_e2e.py +0 -0
  47. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/tests/e2e/test_marketplace_e2e.py +0 -0
  48. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/tests/e2e/test_run_e2e.py +0 -0
  49. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/tests/e2e/test_signup_e2e.py +0 -0
  50. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/tests/e2e/test_siwe_login_e2e.py +0 -0
  51. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/tests/smoke/__init__.py +0 -0
  52. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/tests/smoke/test_config_persistence.py +0 -0
  53. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/tests/smoke/test_keyring_integration.py +0 -0
  54. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/tests/smoke/test_perf_lazygroup.py +0 -0
  55. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/tests/smoke/test_siwe_signing.py +0 -0
  56. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/tests/test_billing.py +0 -0
  57. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/tests/test_chat.py +0 -0
  58. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/tests/test_config_cmd.py +0 -0
  59. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/tests/test_credential_priority.py +0 -0
  60. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/tests/test_earnings.py +0 -0
  61. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/tests/test_event_triggers.py +0 -0
  62. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/tests/test_init_and_byok.py +0 -0
  63. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/tests/test_llm_config.py +0 -0
  64. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/tests/test_marketplace.py +0 -0
  65. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/tests/test_mcp.py +0 -0
  66. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/tests/test_models.py +0 -0
  67. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/tests/test_run.py +0 -0
  68. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/tests/test_schedules.py +0 -0
  69. {teardrop_cli-0.3.0 → teardrop_cli-0.3.2}/tests/test_signup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: teardrop-cli
3
- Version: 0.3.0
3
+ Version: 0.3.2
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.2.0
48
+ Requires-Dist: teardrop-sdk>=0.3.0
49
49
  Requires-Dist: tomli-w>=1.0
50
50
  Requires-Dist: typer>=0.12
51
51
  Provides-Extra: dev
@@ -86,21 +86,27 @@ Requires Python ≥ 3.11.
86
86
 
87
87
  ## 60-Second Quickstart
88
88
 
89
- The wizard walks you through sign-in, BYOK LLM setup, and your first agent run or scaffolded tool:
89
+ The wizard walks you through sign-in, optional BYOK LLM setup, and your first agent run or scaffolded tool:
90
90
 
91
91
  ```bash
92
92
  teardrop quickstart
93
93
  ```
94
94
 
95
+ Quickstart keeps local tool scaffolding and public marketplace browsing available
96
+ without sign-in. A saved session is verified when you choose an agent run; if it
97
+ is stale, the wizard offers sign-in again before asking for a prompt or payment
98
+ confirmation. Environment credentials must be updated or unset when they are
99
+ rejected because they take precedence over interactive login.
100
+
95
101
  Prefer the manual path? Pick a sign-in method:
96
102
 
97
103
  ```bash
98
- # Wallet-first (no email needed) — generates a wallet on first run if you don't have one
99
- teardrop auth login --siwe --generate-wallet
100
-
101
104
  # Email + password
102
105
  teardrop auth signup --email you@example.com --org-name acme
103
106
 
107
+ # Wallet-first (no email needed) — generates a wallet on first run if you don't have one
108
+ teardrop auth login --siwe --generate-wallet
109
+
104
110
  # Already have an account
105
111
  teardrop auth login --email you@example.com
106
112
  ```
@@ -143,7 +149,7 @@ Scaffold a starter spec, edit it, and publish:
143
149
  ```bash
144
150
  teardrop tools init my_scraper # writes ./tool.json
145
151
  $EDITOR tool.json # set webhook_url, schema, price
146
- teardrop tools probe my_scraper # test webhook health before publishing
152
+ teardrop tools probe --from-file tool.json # test webhook health before publishing
147
153
  teardrop tools publish --from-file tool.json \
148
154
  --settlement-wallet 0xYourChecksumAddress
149
155
  ```
@@ -156,7 +162,7 @@ teardrop tools publish
156
162
 
157
163
  Tool name rules: `^[a-z][a-z0-9_]*$`, ≤ 64 chars. Prices are in atomic USDC (6 decimals): `5000` = **$0.005** per call. Settlement wallet is required once before your first payout.
158
164
 
159
- Test your webhook before publishing with `teardrop tools probe <tool-name>`. Optionally pass `--auth-header-name` and `--auth-header-value` if your webhook requires auth, or use `--method` and `--payload` to customize the probe request.
165
+ Test a local webhook before publishing with `teardrop tools probe --from-file tool.json`; the spec's method and auth settings are used by default. After publishing, use `teardrop tools probe <tool-name>`. Pass `--auth-header-name` and `--auth-header-value` to override auth settings, or use `--method` and `--payload` to customize the probe request.
160
166
 
161
167
  Manage existing tools: `teardrop tools list | info | update | pause | delete | probe`. Full reference: [docs/cli-reference.md](docs/cli-reference.md#tool-management).
162
168
 
@@ -295,6 +301,12 @@ teardrop llm-config set --provider google --model gemini-3-flash --routing speed
295
301
 
296
302
  Pipe a key from stdin to keep it out of shell history: `--byok-key -`. See [docs/cli-reference.md](docs/cli-reference.md#llm-configuration) for advanced tuning, BYOK details, and benchmarks.
297
303
 
304
+ ### LLM credit model
305
+
306
+ Non-BYOK organizations use Teardrop's shared provider keys. Platform credits or x402 cover the model token costs and Teardrop platform fee. BYOK organizations pay their model provider directly through their own encrypted key, but still need platform credits or x402 for the Teardrop orchestration fee. BYOK uses the configured model and does not use pooled smart routing.
307
+
308
+ New email-authenticated organizations may receive optional promotional credit after verifying their email when the program is enabled. Eligibility and availability are server-configured; SIWE users and marketplace author tools are excluded. A real top-up removes promotional restrictions.
309
+
298
310
  ---
299
311
 
300
312
  ## Browse the Marketplace
@@ -26,21 +26,27 @@ Requires Python ≥ 3.11.
26
26
 
27
27
  ## 60-Second Quickstart
28
28
 
29
- The wizard walks you through sign-in, BYOK LLM setup, and your first agent run or scaffolded tool:
29
+ The wizard walks you through sign-in, optional BYOK LLM setup, and your first agent run or scaffolded tool:
30
30
 
31
31
  ```bash
32
32
  teardrop quickstart
33
33
  ```
34
34
 
35
+ Quickstart keeps local tool scaffolding and public marketplace browsing available
36
+ without sign-in. A saved session is verified when you choose an agent run; if it
37
+ is stale, the wizard offers sign-in again before asking for a prompt or payment
38
+ confirmation. Environment credentials must be updated or unset when they are
39
+ rejected because they take precedence over interactive login.
40
+
35
41
  Prefer the manual path? Pick a sign-in method:
36
42
 
37
43
  ```bash
38
- # Wallet-first (no email needed) — generates a wallet on first run if you don't have one
39
- teardrop auth login --siwe --generate-wallet
40
-
41
44
  # Email + password
42
45
  teardrop auth signup --email you@example.com --org-name acme
43
46
 
47
+ # Wallet-first (no email needed) — generates a wallet on first run if you don't have one
48
+ teardrop auth login --siwe --generate-wallet
49
+
44
50
  # Already have an account
45
51
  teardrop auth login --email you@example.com
46
52
  ```
@@ -83,7 +89,7 @@ Scaffold a starter spec, edit it, and publish:
83
89
  ```bash
84
90
  teardrop tools init my_scraper # writes ./tool.json
85
91
  $EDITOR tool.json # set webhook_url, schema, price
86
- teardrop tools probe my_scraper # test webhook health before publishing
92
+ teardrop tools probe --from-file tool.json # test webhook health before publishing
87
93
  teardrop tools publish --from-file tool.json \
88
94
  --settlement-wallet 0xYourChecksumAddress
89
95
  ```
@@ -96,7 +102,7 @@ teardrop tools publish
96
102
 
97
103
  Tool name rules: `^[a-z][a-z0-9_]*$`, ≤ 64 chars. Prices are in atomic USDC (6 decimals): `5000` = **$0.005** per call. Settlement wallet is required once before your first payout.
98
104
 
99
- Test your webhook before publishing with `teardrop tools probe <tool-name>`. Optionally pass `--auth-header-name` and `--auth-header-value` if your webhook requires auth, or use `--method` and `--payload` to customize the probe request.
105
+ Test a local webhook before publishing with `teardrop tools probe --from-file tool.json`; the spec's method and auth settings are used by default. After publishing, use `teardrop tools probe <tool-name>`. Pass `--auth-header-name` and `--auth-header-value` to override auth settings, or use `--method` and `--payload` to customize the probe request.
100
106
 
101
107
  Manage existing tools: `teardrop tools list | info | update | pause | delete | probe`. Full reference: [docs/cli-reference.md](docs/cli-reference.md#tool-management).
102
108
 
@@ -235,6 +241,12 @@ teardrop llm-config set --provider google --model gemini-3-flash --routing speed
235
241
 
236
242
  Pipe a key from stdin to keep it out of shell history: `--byok-key -`. See [docs/cli-reference.md](docs/cli-reference.md#llm-configuration) for advanced tuning, BYOK details, and benchmarks.
237
243
 
244
+ ### LLM credit model
245
+
246
+ Non-BYOK organizations use Teardrop's shared provider keys. Platform credits or x402 cover the model token costs and Teardrop platform fee. BYOK organizations pay their model provider directly through their own encrypted key, but still need platform credits or x402 for the Teardrop orchestration fee. BYOK uses the configured model and does not use pooled smart routing.
247
+
248
+ New email-authenticated organizations may receive optional promotional credit after verifying their email when the program is enabled. Eligibility and availability are server-configured; SIWE users and marketplace author tools are excluded. A real top-up removes promotional restrictions.
249
+
238
250
  ---
239
251
 
240
252
  ## Browse the Marketplace
@@ -39,7 +39,7 @@ teardrop auth login --client-id <id> --client-secret <secret>
39
39
  teardrop auth login --token <jwt>
40
40
 
41
41
  # Sign-In With Ethereum (EIP-4361) — pick one source for the key:
42
- teardrop auth login --siwe # interactive hidden prompt
42
+ teardrop auth login --siwe # hidden prompt when no other source is supplied
43
43
  teardrop auth login --siwe --key-file ./wallet.key # read once, never persisted
44
44
  teardrop auth login --siwe --generate-wallet # creates a fresh wallet
45
45
  TEARDROP_SIWE_PRIVATE_KEY=0x... teardrop auth login --siwe # CI / non-interactive
@@ -72,8 +72,9 @@ teardrop auth logout # revoke refresh token + clear stored credent
72
72
  | 1 | `TEARDROP_API_KEY` env var (static JWT) |
73
73
  | 2 | `TEARDROP_EMAIL` + `TEARDROP_SECRET` env vars |
74
74
  | 3 | `TEARDROP_CLIENT_ID` + `TEARDROP_CLIENT_SECRET` env vars |
75
- | 4 | `access_token` in `~/.teardrop/config.toml` |
76
- | 5 | System keyring (email + secret, or client credentials) |
75
+ | 4 | System keyring (email + secret, or client credentials) |
76
+ | 5 | `access_token` in `~/.teardrop/config.toml` |
77
+ | 6 | Legacy `auth.token` in `~/.teardrop/config.toml` |
77
78
 
78
79
  ---
79
80
 
@@ -123,17 +124,18 @@ will start a fresh thread.
123
124
 
124
125
  ## Schedules
125
126
 
126
- Recurring interval-based runs:
127
+ Recurring interval-based runs. Most commands accept `--json` to return raw structured payloads for pipeline automation:
127
128
 
128
129
  ```bash
129
130
  teardrop schedules create \
130
131
  --name hourly-briefing \
131
132
  --prompt "Summarize open incidents" \
132
- --interval-seconds 3600
133
+ --interval-seconds 3600 \
134
+ --json
133
135
 
134
- teardrop schedules list
135
- teardrop schedules get <schedule-id>
136
- teardrop schedules update <schedule-id> --enabled false
136
+ teardrop schedules list --json
137
+ teardrop schedules get <schedule-id> --json
138
+ teardrop schedules update <schedule-id> --enabled false --json
137
139
  teardrop schedules update <schedule-id> --clear-callback-url
138
140
  teardrop schedules runs <schedule-id> --limit 50 --json
139
141
  teardrop schedules delete <schedule-id>
@@ -150,24 +152,25 @@ Run history output includes: `Run ID`, `Status`, `Cost (USDC)`, `Error Message`,
150
152
 
151
153
  ## Event Triggers
152
154
 
153
- Reactive runs exposed as signed public webhook endpoints:
155
+ Reactive runs exposed as signed public webhook endpoints. Commands support a `--json` option to output formatted shapes (with creation and rotation payloads securely exposing the one-time dispatch secret):
154
156
 
155
157
  ```bash
156
158
  teardrop event-triggers create \
157
159
  --name inbound-orders \
158
- --prompt "Validate and process this order payload"
160
+ --prompt "Validate and process this order payload" \
161
+ --json
159
162
 
160
- teardrop event-triggers list
161
- teardrop event-triggers get <trigger-id>
162
- teardrop event-triggers update <trigger-id> --enabled false
163
+ teardrop event-triggers list --json
164
+ teardrop event-triggers get <trigger-id> --json
165
+ teardrop event-triggers update <trigger-id> --enabled false --json
163
166
  teardrop event-triggers update <trigger-id> --callback-url https://example.com/hook
164
167
  teardrop event-triggers runs <trigger-id> --limit 50 --json
165
- teardrop event-triggers rotate-secret <trigger-id>
168
+ teardrop event-triggers rotate-secret <trigger-id> --json
166
169
  teardrop event-triggers delete <trigger-id>
167
170
  teardrop event-triggers delete <trigger-id> --yes
168
171
  ```
169
172
 
170
- `create` and `rotate-secret` display the signing secret once. Store it immediately; subsequent `list` and `get` calls do not return the plaintext secret.
173
+ `create` and `rotate-secret` display the signing secret once (both inside non-JSON stderr summaries and their corresponding use-once JSON attributes). Store it immediately; subsequent `list`, `get`, or `update` calls permanently exclude the plaintext secret.
171
174
 
172
175
  Non-JSON list output includes: `ID`, `Name`, `Enabled`, `Kind`, `Consecutive Failures`, `Last Run At`.
173
176
 
@@ -193,6 +196,12 @@ The CLI does not support direct top-ups. To add credits or manage your billing m
193
196
 
194
197
  If you run out of credits during an agent run, the agent will pause and provide this link.
195
198
 
199
+ ### LLM credit model
200
+
201
+ Non-BYOK organizations use Teardrop's shared provider keys and need credits or x402 for model token costs plus the platform fee. BYOK organizations pay their provider directly through their encrypted key, but still need credits or x402 for the Teardrop orchestration fee. BYOK uses the configured model; pooled smart routing is disabled.
202
+
203
+ Optional promotional credit may be granted after successful email verification when the server-side program is enabled. It is not available to SIWE users or marketplace author tools, and a real top-up removes promotional restrictions. The CLI does not assume eligibility or a fixed promotional amount.
204
+
196
205
  ---
197
206
 
198
207
  ## Tool Management
@@ -264,7 +273,13 @@ teardrop tools delete get_weather --yes # skip confirmation
264
273
 
265
274
  ### Test webhook health
266
275
 
267
- Before publishing or after updating a webhook URL, test it:
276
+ Before publishing, test a local spec without authentication or an API lookup:
277
+
278
+ ```bash
279
+ teardrop tools probe --from-file tool.json
280
+ ```
281
+
282
+ After publishing or updating a webhook URL, test the registered tool:
268
283
 
269
284
  ```bash
270
285
  teardrop tools probe get_weather # test default POST request
@@ -287,6 +302,8 @@ teardrop tools probe get_weather --method GET
287
302
  teardrop tools probe get_weather --payload '{"test": "data"}'
288
303
  ```
289
304
 
305
+ For `--from-file`, the spec's `webhook_method`, `auth_header_name`, and `auth_header_value` are used by default. Explicit probe flags override them.
306
+
290
307
  Exit code: 0 on success (2xx–3xx), 1 on timeout/5xx/connection error. Warning (exit 0) on 4xx responses (e.g., auth failures).
291
308
 
292
309
  ---
@@ -352,7 +369,7 @@ teardrop llm-config set \
352
369
  --timeout-seconds 60
353
370
  ```
354
371
 
355
- **Providers:** `openrouter`, `google`, `anthropic`
372
+ **Providers:** `openrouter`, `google`, `anthropic`, `openai`
356
373
  **Routing:** `default` · `cost` · `speed` · `quality`
357
374
  **Validation:** temperature 0.0–2.0 · max tokens 1–200,000 · timeout ≥ 1 s
358
375
 
@@ -373,6 +390,8 @@ Get-Content "$key_file" | teardrop llm-config set `
373
390
  teardrop llm-config set --provider anthropic --model claude-sonnet-4-6 --clear-key
374
391
  ```
375
392
 
393
+ BYOK keys are encrypted by the platform and used only for the configured provider/model. Teardrop still charges the orchestration fee through credits or x402; it does not charge Teardrop for the provider's model usage.
394
+
376
395
  ---
377
396
 
378
397
  ## MCP Servers
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "teardrop-cli"
7
- version = "0.3.0"
7
+ version = "0.3.2"
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.2.0",
27
+ "teardrop-sdk>=0.3.0",
28
28
  "typer>=0.12",
29
29
  "rich>=13.0",
30
30
  "platformdirs>=4.0",
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  import asyncio
6
6
  import os
7
7
  import re
8
+ import sys
8
9
  from datetime import UTC, datetime
9
10
  from pathlib import Path
10
11
  from typing import Annotated
@@ -319,7 +320,11 @@ def login(
319
320
  print_success(f"Logged in as [bold]{email}[/bold]{org_label}")
320
321
 
321
322
 
322
- def interactive_reauthenticate(base_url: str | None = None) -> bool:
323
+ def interactive_reauthenticate(
324
+ base_url: str | None = None,
325
+ *,
326
+ warning_message: str = "Your session expired during this command.",
327
+ ) -> bool:
323
328
  """Prompt the user to sign in again after an expired session.
324
329
 
325
330
  Returns ``True`` when authentication completed and the caller should retry,
@@ -328,7 +333,7 @@ def interactive_reauthenticate(base_url: str | None = None) -> bool:
328
333
  from teardrop_cli import config
329
334
  from teardrop_cli.formatting import console, print_warning
330
335
 
331
- print_warning("Your session expired during this command.")
336
+ print_warning(warning_message)
332
337
  console.print("\n[bold]Sign in to continue:[/bold]")
333
338
  console.print(" [cyan]0[/cyan] Cancel")
334
339
  console.print(" [cyan]1[/cyan] Email + password [recommended]")
@@ -375,7 +380,7 @@ def _resolve_siwe_private_key(
375
380
  try:
376
381
  from eth_account import Account
377
382
  except ImportError:
378
- print_error("eth-account is required.", hint="pip install 'teardrop-cli[siwe]'")
383
+ print_error("eth-account is required.", hint="pip install teardrop-cli")
379
384
  raise typer.Exit(1) from None
380
385
  acct = Account.create()
381
386
  from rich.panel import Panel
@@ -406,16 +411,21 @@ def _resolve_siwe_private_key(
406
411
  if env_key := os.environ.get("TEARDROP_SIWE_PRIVATE_KEY"):
407
412
  return env_key, False
408
413
 
409
- print_error(
410
- "No Ethereum private key found.",
411
- hint=(
412
- "Provide one via:\n"
413
- " • TEARDROP_SIWE_PRIVATE_KEY env var\n"
414
- " • --key-file PATH\n"
415
- " • --generate-wallet (creates a new wallet)"
416
- ),
414
+ hint = (
415
+ "Provide one via:\n"
416
+ " • TEARDROP_SIWE_PRIVATE_KEY env var\n"
417
+ " • --key-file PATH\n"
418
+ " • --generate-wallet (creates a new wallet)"
417
419
  )
418
- raise typer.Exit(1)
420
+ if not sys.stdin.isatty():
421
+ print_error("No Ethereum private key found.", hint=hint)
422
+ raise typer.Exit(1)
423
+
424
+ private_key = typer.prompt("Ethereum private key", hide_input=True).strip()
425
+ if not private_key:
426
+ print_error("No Ethereum private key provided.", hint=hint)
427
+ raise typer.Exit(1)
428
+ return private_key, False
419
429
 
420
430
 
421
431
  async def _siwe_auth_async(
@@ -7,6 +7,8 @@ orchestration: each step delegates to an existing command implementation.
7
7
 
8
8
  from __future__ import annotations
9
9
 
10
+ import asyncio
11
+
10
12
  import click
11
13
  import typer
12
14
 
@@ -20,6 +22,110 @@ def app(base_url: str | None) -> None:
20
22
  _run_quickstart(base_url)
21
23
 
22
24
 
25
+ def _is_authentication_failure(exc: BaseException) -> bool:
26
+ from teardrop import AuthenticationError
27
+
28
+ return isinstance(exc, AuthenticationError) or getattr(exc, "status_code", None) == 401
29
+
30
+
31
+ def _verify_saved_session(
32
+ base_url: str | None,
33
+ source: str,
34
+ *,
35
+ allow_recovery: bool = True,
36
+ ) -> bool:
37
+ from teardrop_cli import config
38
+ from teardrop_cli.formatting import print_error, spinner
39
+
40
+ try:
41
+ client = config.get_client(base_url)
42
+
43
+ async def _fetch_identity():
44
+ try:
45
+ return await client.get_me()
46
+ finally:
47
+ await client.close()
48
+
49
+ with spinner("Verifying saved credentials…"):
50
+ identity = asyncio.run(_fetch_identity())
51
+ except (click.exceptions.Exit, SystemExit):
52
+ return False
53
+ except Exception as exc: # noqa: BLE001
54
+ if _is_authentication_failure(exc):
55
+ if not allow_recovery:
56
+ print_error(
57
+ "Sign-in failed.",
58
+ hint="Run [bold]teardrop auth login[/bold] to try again.",
59
+ )
60
+ return False
61
+ if source.startswith("env:"):
62
+ print_error(
63
+ "Saved environment credentials were rejected.",
64
+ hint=(
65
+ "Update or unset TEARDROP_API_KEY, TEARDROP_EMAIL, or "
66
+ "TEARDROP_CLIENT_ID, then run [bold]teardrop auth login[/bold]."
67
+ ),
68
+ )
69
+ return False
70
+
71
+ from teardrop_cli.commands.auth import interactive_reauthenticate
72
+
73
+ try:
74
+ if not interactive_reauthenticate(
75
+ base_url=base_url,
76
+ warning_message="Your saved session is no longer valid. Sign in again to continue.",
77
+ ):
78
+ return False
79
+ except (click.exceptions.Exit, SystemExit):
80
+ return False
81
+
82
+ refreshed_source = config.detect_credential_source()
83
+ if refreshed_source is None:
84
+ print_error("Sign-in did not produce a saved session.")
85
+ return False
86
+ return _verify_saved_session(
87
+ base_url,
88
+ refreshed_source,
89
+ allow_recovery=False,
90
+ )
91
+
92
+ print_error(
93
+ "Could not verify your saved session.",
94
+ hint="Check your network connection and API URL, then try again.",
95
+ )
96
+ return False
97
+
98
+ label = None
99
+ if isinstance(identity, dict):
100
+ label = identity.get("email") or identity.get("sub")
101
+ else:
102
+ label = getattr(identity, "email", None) or getattr(identity, "sub", None)
103
+ suffix = f" for [bold]{label}[/bold]" if label else ""
104
+ from teardrop_cli.formatting import console
105
+
106
+ console.print(f"[bold green]✓[/bold green] Session verified{suffix}.")
107
+ return True
108
+
109
+
110
+ def _ensure_authenticated(base_url: str | None) -> bool:
111
+ from teardrop_cli import config
112
+ from teardrop_cli.formatting import console, print_error
113
+
114
+ source = config.detect_credential_source()
115
+ if source is None:
116
+ console.print("\n[bold]Sign-in is required to run an agent.[/bold]")
117
+ try:
118
+ _auth_menu(base_url)
119
+ except (click.exceptions.Exit, SystemExit):
120
+ return False
121
+ source = config.detect_credential_source()
122
+ if source is None:
123
+ print_error("Sign-in did not produce a saved session.")
124
+ return False
125
+
126
+ return _verify_saved_session(base_url, source)
127
+
128
+
23
129
  def _run_quickstart(base_url: str | None) -> None:
24
130
  from teardrop_cli import config
25
131
  from teardrop_cli.formatting import console
@@ -31,19 +137,15 @@ def _run_quickstart(base_url: str | None) -> None:
31
137
  "\n[bold cyan]Welcome to Teardrop.[/bold cyan] "
32
138
  "[dim]This wizard will get you running in under a minute.[/dim]\n"
33
139
  )
34
-
35
- # 1. Auth — use existing creds if present, otherwise guide through auth.
36
- if is_repeat:
37
- console.print("[dim]Existing credentials detected.[/dim]")
38
- console.print(f"[dim](source: {config.detect_credential_source()})[/dim]")
39
140
  console.print(
40
- "[dim]Stored credentials found locally. "
41
- "They will be verified on first authenticated command.[/dim]"
141
+ "[dim]You can explore locally now; sign-in is requested only for "
142
+ "account actions and agent runs.[/dim]"
42
143
  )
43
144
  else:
44
- _auth_menu(base_url)
145
+ console.print("[dim]Saved session found locally.[/dim]")
146
+ console.print(f"[dim](source: {config.detect_credential_source()})[/dim]")
147
+ console.print("[dim]It will be checked when you choose an authenticated action.[/dim]")
45
148
 
46
- # 2. What's next?
47
149
  console.print()
48
150
  console.print("[bold]What would you like to do next?[/bold]")
49
151
  console.print(" [cyan]0[/cyan] Nothing, just exploring [dim](exit)[/dim]")
@@ -70,13 +172,11 @@ def _auth_menu(base_url: str | None) -> None:
70
172
 
71
173
  if not has_account:
72
174
  console.print("\n[bold]Create your account:[/bold]")
73
- console.print(" [cyan]1[/cyan] Ethereum wallet — fastest, no email needed [recommended]")
74
- console.print(" [cyan]2[/cyan] Email + password")
175
+ console.print(" [cyan]1[/cyan] Email + password [recommended]")
176
+ console.print(" [cyan]2[/cyan] Ethereum wallet — no email needed")
75
177
  choice = typer.prompt("Choice", default="1")
76
178
 
77
179
  if choice == "2":
78
- signup(base_url=base_url)
79
- else:
80
180
  from teardrop_cli import config
81
181
 
82
182
  url = base_url or config.get_base_url()
@@ -86,6 +186,8 @@ def _auth_menu(base_url: str | None) -> None:
86
186
  default=False,
87
187
  )
88
188
  _login_siwe(url, generate_wallet=generate, save_key=save_key)
189
+ else:
190
+ signup(base_url=base_url)
89
191
  else:
90
192
  console.print("\n[bold]Sign in:[/bold]")
91
193
  console.print(" [cyan]1[/cyan] Ethereum wallet")
@@ -121,27 +223,39 @@ def _scaffold_branch() -> None:
121
223
 
122
224
 
123
225
  def _sample_run_branch(base_url: str | None) -> None:
124
- import asyncio
125
-
126
- from teardrop_cli import config
127
- from teardrop_cli.commands.run import _handle_run_error, _stream
226
+ from teardrop_cli.commands.run import _estimate_cost, _execute_run
128
227
  from teardrop_cli.formatting import console
129
228
 
229
+ if not _ensure_authenticated(base_url):
230
+ return
231
+
130
232
  message = typer.prompt(
131
233
  "Prompt", default="What is the current ETH gas price?", show_default=True
132
234
  )
133
235
  console.print()
134
- if typer.confirm("Configure a BYOK LLM key first? (skip if you have credits)", default=False):
236
+ console.print(
237
+ "[dim]Billing note: platform credits or x402 cover non-BYOK model usage and platform costs. "
238
+ "BYOK pays your provider directly but still needs Teardrop payment capacity for orchestration.[/dim]"
239
+ )
240
+ if typer.confirm("Configure a BYOK LLM key first?", default=False):
135
241
  from teardrop_cli.commands.llm_config import _byok_interactive
136
242
 
137
243
  _byok_interactive(base_url=base_url)
138
244
  console.print()
139
245
 
140
- client = config.get_client(base_url)
246
+ if typer.confirm(
247
+ "Show an estimated cost before running?", default=True
248
+ ) and not _estimate_cost(message, base_url=base_url):
249
+ console.print("\nNo run started. Resolve the issue and run quickstart again.")
250
+ return
251
+
252
+ if not typer.confirm("Run this prompt now? It may use credits or x402.", default=True):
253
+ console.print("\nRun cancelled. No agent request was sent.")
254
+ return
255
+
141
256
  try:
142
- asyncio.run(_stream(client, message, None, None))
143
- except Exception as exc: # noqa: BLE001
144
- _handle_run_error(exc)
257
+ _execute_run(message, base_url=base_url)
258
+ except click.exceptions.Exit:
145
259
  return # error already printed; skip next-steps
146
260
 
147
261
  console.print(
@@ -216,7 +216,7 @@ def _estimate_cost(
216
216
  context: dict | None = None,
217
217
  tool_policy=None,
218
218
  base_url: str | None = None,
219
- ) -> None:
219
+ ) -> bool:
220
220
  """Show an estimated cost based on current pricing and config — no inference."""
221
221
  from teardrop_cli.formatting import console, print_table
222
222
  from teardrop_cli.pricing import estimate_run_cost
@@ -227,7 +227,7 @@ def _estimate_cost(
227
227
  )
228
228
  except Exception as exc: # noqa: BLE001
229
229
  _handle_run_error(exc)
230
- return
230
+ return False
231
231
 
232
232
  from teardrop import format_usdc
233
233
 
@@ -247,6 +247,7 @@ def _estimate_cost(
247
247
  ]
248
248
  print_table([("Item", {"style": "bold cyan"}), "Value"], rows, title="Cost Estimate")
249
249
  console.print(f"[dim]{est.disclaimer}[/dim]")
250
+ return True
250
251
 
251
252
 
252
253
  async def _stream(
@@ -341,23 +342,26 @@ async def _collect(
341
342
 
342
343
 
343
344
  def _handle_run_error(exc: BaseException) -> None:
345
+ from teardrop import AuthenticationError, PaymentRequiredError, RateLimitError
346
+
344
347
  from teardrop_cli.formatting import print_error
345
348
 
346
349
  name = type(exc).__name__
347
350
  msg = str(exc)
351
+ status_code = getattr(exc, "status_code", None)
348
352
 
349
- if name == "PaymentRequiredError" or "402" in msg:
353
+ if isinstance(exc, PaymentRequiredError) or status_code == 402:
350
354
  print_error(
351
355
  "Insufficient credit.",
352
- hint="Topup at: https://teardrop.dev/billing",
356
+ hint="Check [bold]teardrop balance[/bold]. Add funds at: https://teardrop.dev/billing",
353
357
  )
354
358
  return
355
- if name == "RateLimitError" or "429" in msg:
359
+ if isinstance(exc, RateLimitError) or status_code == 429:
356
360
  retry = getattr(exc, "retry_after", None)
357
361
  suffix = f" (retry after {retry}s)" if retry else ""
358
362
  print_error(f"Rate limit exceeded{suffix}.")
359
363
  return
360
- if name == "AuthenticationError" or "401" in msg:
364
+ if isinstance(exc, AuthenticationError) or status_code == 401:
361
365
  print_error(
362
366
  "Not authenticated.",
363
367
  hint="Run `teardrop auth login` to sign in.",