discolike-cli 0.4.0__tar.gz → 0.5.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.
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/PKG-INFO +27 -7
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/README.md +23 -2
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/pyproject.toml +4 -5
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/src/discolike_cli/_help.py +3 -0
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/src/discolike_cli/_output.py +3 -2
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/src/discolike_cli/auth.py +2 -2
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/src/discolike_cli/bulk.py +2 -2
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/src/discolike_cli/contacts.py +2 -2
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/src/discolike_cli/discogen.py +1 -1
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/src/discolike_cli/email.py +5 -5
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/src/discolike_cli/main.py +3 -1
- discolike_cli-0.5.0/src/discolike_cli/prospecting.py +363 -0
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/src/discolike_cli/queries.py +2 -2
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/tests/test_entrypoint.py +3 -3
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/tests/test_output.py +3 -3
- discolike_cli-0.5.0/tests/test_prospecting_cli.py +457 -0
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/tests/test_sdk_parity.py +1 -0
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/.gitignore +0 -0
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/LICENSE +0 -0
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/src/discolike_cli/__init__.py +0 -0
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/src/discolike_cli/_inputs.py +0 -0
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/src/discolike_cli/_loopback.py +0 -0
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/src/discolike_cli/account.py +0 -0
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/src/discolike_cli/company.py +0 -0
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/src/discolike_cli/discover.py +0 -0
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/src/discolike_cli/enrich.py +0 -0
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/src/discolike_cli/match.py +0 -0
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/src/discolike_cli/providers.py +0 -0
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/src/discolike_cli/py.typed +0 -0
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/src/discolike_cli/signup.py +0 -0
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/tests/conftest.py +0 -0
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/tests/test_account.py +0 -0
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/tests/test_auth.py +0 -0
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/tests/test_auth_oauth.py +0 -0
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/tests/test_bulk_cli.py +0 -0
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/tests/test_company.py +0 -0
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/tests/test_contacts_cli.py +0 -0
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/tests/test_discogen_cli.py +0 -0
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/tests/test_discover.py +0 -0
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/tests/test_email_cli.py +0 -0
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/tests/test_enrich_cli.py +0 -0
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/tests/test_file_inputs_cli.py +0 -0
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/tests/test_help.py +0 -0
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/tests/test_inputs.py +0 -0
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/tests/test_match_cli.py +0 -0
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/tests/test_providers_cli.py +0 -0
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/tests/test_queries_cli.py +0 -0
- {discolike_cli-0.4.0 → discolike_cli-0.5.0}/tests/test_signup_cli.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: discolike-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Official CLI for the DiscoLike API
|
|
5
5
|
Project-URL: Homepage, https://www.discolike.com
|
|
6
6
|
Project-URL: Documentation, https://docs.discolike.com
|
|
@@ -17,17 +17,16 @@ Classifier: Intended Audience :: Developers
|
|
|
17
17
|
Classifier: Operating System :: OS Independent
|
|
18
18
|
Classifier: Programming Language :: Python :: 3
|
|
19
19
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
20
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
21
20
|
Classifier: Programming Language :: Python :: 3.11
|
|
22
21
|
Classifier: Programming Language :: Python :: 3.12
|
|
23
22
|
Classifier: Programming Language :: Python :: 3.13
|
|
24
23
|
Classifier: Programming Language :: Python :: 3.14
|
|
25
24
|
Classifier: Topic :: Utilities
|
|
26
25
|
Classifier: Typing :: Typed
|
|
27
|
-
Requires-Python: >=3.
|
|
28
|
-
Requires-Dist: discolike==0.
|
|
26
|
+
Requires-Python: >=3.11
|
|
27
|
+
Requires-Dist: discolike==0.5.0
|
|
29
28
|
Requires-Dist: rich>=13.0
|
|
30
|
-
Requires-Dist: typer
|
|
29
|
+
Requires-Dist: typer<0.28,>=0.26.1
|
|
31
30
|
Description-Content-Type: text/markdown
|
|
32
31
|
|
|
33
32
|
# discolike-cli
|
|
@@ -46,7 +45,7 @@ Or run it without installing:
|
|
|
46
45
|
uvx --from discolike-cli discolike --help
|
|
47
46
|
```
|
|
48
47
|
|
|
49
|
-
Requires Python 3.
|
|
48
|
+
Requires Python 3.11+. Installing this package gives you the `discolike` command.
|
|
50
49
|
|
|
51
50
|
## Authentication
|
|
52
51
|
|
|
@@ -67,7 +66,7 @@ discolike company data stripe.com
|
|
|
67
66
|
discolike extract https://stripe.com/enterprise
|
|
68
67
|
```
|
|
69
68
|
|
|
70
|
-
Top-level commands: `discover`, `count`, `match`, `extract`, `validate-icp`, `append`, `segment` — plus `auth`, `bulk`, `company`, `contacts`, `discogen`, `queries`, `account`, `search-providers`, and `llm-providers` command groups.
|
|
69
|
+
Top-level commands: `discover`, `count`, `match`, `extract`, `validate-icp`, `append`, `segment` — plus `auth`, `bulk`, `company`, `contacts`, `discogen`, `prospecting`, `queries`, `account`, `search-providers`, and `llm-providers` command groups.
|
|
71
70
|
|
|
72
71
|
### Volume pulls
|
|
73
72
|
|
|
@@ -81,6 +80,26 @@ discolike bulk contacts --domains-file companies.csv --per-company 10 --summary
|
|
|
81
80
|
|
|
82
81
|
`companies` saves each page as an exclusion list (`<run-name>-round-N`) and excludes it from the next page; rerunning with the same `--out` resumes from the CSV. `contacts` slices the domain list at `10000 / per-company` domains per call and records finished slices in `<out>.checkpoint`. Both keep one call in flight under `--rate-limit` (default 10/min, the Pro rate on `/discover` and `/contacts`), retry on 429/5xx, and print a JSON summary at the end. Filters come from `--params-file`, `--param` and the common flags; the paging fields are managed for you.
|
|
83
82
|
|
|
83
|
+
### Managed prospecting
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
discolike prospecting start --brief "Find 100 US logistics companies and 3 operations directors each" --idempotency-key logistics-1
|
|
87
|
+
discolike prospecting start --brief "Find lookalikes of our customers and their CTOs" --customers-file customers.csv --idempotency-key seeded-1
|
|
88
|
+
discolike prospecting wait RUN_ID
|
|
89
|
+
# Review the proposed plan, then approve the exact version you saw:
|
|
90
|
+
discolike prospecting approve RUN_ID --plan-version 1
|
|
91
|
+
discolike prospecting list --limit 20
|
|
92
|
+
discolike prospecting message RUN_ID --text "Make it 250 companies" --idempotency-key logistics-edit-1
|
|
93
|
+
discolike prospecting status RUN_ID --events-after 12 --messages-after 8 --limit 100
|
|
94
|
+
discolike prospecting cancel RUN_ID
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`wait` returns on `proposed`, `needs_input`, `completed`, `failed`, or `cancelled`. A timeout stops polling only. Inspect status and stop reason; completion does not guarantee full coverage. Message replies arrive through `status --messages-after`; follow `next_message_seq` and `reply_pending`. A "segment these" request keeps `reply_pending` true until the segments message is posted, which can take more than an hour. If grouping stops without an outcome, the flag clears on its own after about 90 minutes.
|
|
98
|
+
|
|
99
|
+
`start` and `approve` default to pausing at checkpoints, like the web chat: a pilot check on large lists (`pilot`), a search drifting off target (`tail_quality`), candidates running out short of the target (`short`), the target being met (`target_reached`), another round of companies when the ones checked yield too few contacts (`top_up`), and, on wave runs, the researched contacts found before their emails are looked up (`contacts_review`, asked with `--auto` too; contacts already in DiscoLike's database get their lookups during research, which approving the plan starts; picking "Find emails" by number sends the question's `seq`, which the API needs to accept it). Pass `--auto` to never pause (except at a `top_up` round that would pass a spending limit); a poor pilot is then sharpened once and the run continues with a notice, stopping with `pilot_failed` only if the re-pilot fit is still under 20%. If sharpening itself fails, the run continues on the original criteria. On a terminal, `wait` shows the question, any sample companies and numbered replies at a checkpoint, sends your pick or your own text, and keeps waiting. Without a terminal, or with `--no-input`, it prints the run on stdout, a `needs_input` envelope (`message`, `stop_reason`, `suggested_replies`, `sample`) on stderr, and exits 7; answer with `prospecting message --text "<reply>"` (add `--question-seq` with the question's `seq` to answer only that question) and run `wait` again.
|
|
100
|
+
|
|
101
|
+
Omit `--target-companies` and `--contacts-per-company` to infer counts from the brief (fallback 1,000 and 3; up to 10 contacts per company). Explicit values override the text. `--deliverable accounts` returns the checked companies only, with no people; `--goal companies` takes `--target-companies` matching companies and works only those instead of adding rounds until the contact target is met. Both default to `leads`. `--max-candidates` and `--max-actions` are automatic when omitted or `0`; their maxima are 100,000 and 10,000. Targets allow up to 10,000 companies, status pages up to 500 rows, and lists up to 50 runs. Work caps do not cap provider charges.
|
|
102
|
+
|
|
84
103
|
### Conventions
|
|
85
104
|
|
|
86
105
|
- Results print as JSON to stdout; errors print as JSON (`error`, `message`, `status_code`) to stderr.
|
|
@@ -99,6 +118,7 @@ discolike bulk contacts --domains-file companies.csv --per-company 10 --summary
|
|
|
99
118
|
| 4 | Rate limited |
|
|
100
119
|
| 5 | Network error |
|
|
101
120
|
| 6 | Not found |
|
|
121
|
+
| 7 | Needs input: `prospecting wait` reached a checkpoint with no terminal to ask (or `--no-input`) |
|
|
102
122
|
|
|
103
123
|
## Links
|
|
104
124
|
|
|
@@ -14,7 +14,7 @@ Or run it without installing:
|
|
|
14
14
|
uvx --from discolike-cli discolike --help
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
Requires Python 3.
|
|
17
|
+
Requires Python 3.11+. Installing this package gives you the `discolike` command.
|
|
18
18
|
|
|
19
19
|
## Authentication
|
|
20
20
|
|
|
@@ -35,7 +35,7 @@ discolike company data stripe.com
|
|
|
35
35
|
discolike extract https://stripe.com/enterprise
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
Top-level commands: `discover`, `count`, `match`, `extract`, `validate-icp`, `append`, `segment` — plus `auth`, `bulk`, `company`, `contacts`, `discogen`, `queries`, `account`, `search-providers`, and `llm-providers` command groups.
|
|
38
|
+
Top-level commands: `discover`, `count`, `match`, `extract`, `validate-icp`, `append`, `segment` — plus `auth`, `bulk`, `company`, `contacts`, `discogen`, `prospecting`, `queries`, `account`, `search-providers`, and `llm-providers` command groups.
|
|
39
39
|
|
|
40
40
|
### Volume pulls
|
|
41
41
|
|
|
@@ -49,6 +49,26 @@ discolike bulk contacts --domains-file companies.csv --per-company 10 --summary
|
|
|
49
49
|
|
|
50
50
|
`companies` saves each page as an exclusion list (`<run-name>-round-N`) and excludes it from the next page; rerunning with the same `--out` resumes from the CSV. `contacts` slices the domain list at `10000 / per-company` domains per call and records finished slices in `<out>.checkpoint`. Both keep one call in flight under `--rate-limit` (default 10/min, the Pro rate on `/discover` and `/contacts`), retry on 429/5xx, and print a JSON summary at the end. Filters come from `--params-file`, `--param` and the common flags; the paging fields are managed for you.
|
|
51
51
|
|
|
52
|
+
### Managed prospecting
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
discolike prospecting start --brief "Find 100 US logistics companies and 3 operations directors each" --idempotency-key logistics-1
|
|
56
|
+
discolike prospecting start --brief "Find lookalikes of our customers and their CTOs" --customers-file customers.csv --idempotency-key seeded-1
|
|
57
|
+
discolike prospecting wait RUN_ID
|
|
58
|
+
# Review the proposed plan, then approve the exact version you saw:
|
|
59
|
+
discolike prospecting approve RUN_ID --plan-version 1
|
|
60
|
+
discolike prospecting list --limit 20
|
|
61
|
+
discolike prospecting message RUN_ID --text "Make it 250 companies" --idempotency-key logistics-edit-1
|
|
62
|
+
discolike prospecting status RUN_ID --events-after 12 --messages-after 8 --limit 100
|
|
63
|
+
discolike prospecting cancel RUN_ID
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`wait` returns on `proposed`, `needs_input`, `completed`, `failed`, or `cancelled`. A timeout stops polling only. Inspect status and stop reason; completion does not guarantee full coverage. Message replies arrive through `status --messages-after`; follow `next_message_seq` and `reply_pending`. A "segment these" request keeps `reply_pending` true until the segments message is posted, which can take more than an hour. If grouping stops without an outcome, the flag clears on its own after about 90 minutes.
|
|
67
|
+
|
|
68
|
+
`start` and `approve` default to pausing at checkpoints, like the web chat: a pilot check on large lists (`pilot`), a search drifting off target (`tail_quality`), candidates running out short of the target (`short`), the target being met (`target_reached`), another round of companies when the ones checked yield too few contacts (`top_up`), and, on wave runs, the researched contacts found before their emails are looked up (`contacts_review`, asked with `--auto` too; contacts already in DiscoLike's database get their lookups during research, which approving the plan starts; picking "Find emails" by number sends the question's `seq`, which the API needs to accept it). Pass `--auto` to never pause (except at a `top_up` round that would pass a spending limit); a poor pilot is then sharpened once and the run continues with a notice, stopping with `pilot_failed` only if the re-pilot fit is still under 20%. If sharpening itself fails, the run continues on the original criteria. On a terminal, `wait` shows the question, any sample companies and numbered replies at a checkpoint, sends your pick or your own text, and keeps waiting. Without a terminal, or with `--no-input`, it prints the run on stdout, a `needs_input` envelope (`message`, `stop_reason`, `suggested_replies`, `sample`) on stderr, and exits 7; answer with `prospecting message --text "<reply>"` (add `--question-seq` with the question's `seq` to answer only that question) and run `wait` again.
|
|
69
|
+
|
|
70
|
+
Omit `--target-companies` and `--contacts-per-company` to infer counts from the brief (fallback 1,000 and 3; up to 10 contacts per company). Explicit values override the text. `--deliverable accounts` returns the checked companies only, with no people; `--goal companies` takes `--target-companies` matching companies and works only those instead of adding rounds until the contact target is met. Both default to `leads`. `--max-candidates` and `--max-actions` are automatic when omitted or `0`; their maxima are 100,000 and 10,000. Targets allow up to 10,000 companies, status pages up to 500 rows, and lists up to 50 runs. Work caps do not cap provider charges.
|
|
71
|
+
|
|
52
72
|
### Conventions
|
|
53
73
|
|
|
54
74
|
- Results print as JSON to stdout; errors print as JSON (`error`, `message`, `status_code`) to stderr.
|
|
@@ -67,6 +87,7 @@ discolike bulk contacts --domains-file companies.csv --per-company 10 --summary
|
|
|
67
87
|
| 4 | Rate limited |
|
|
68
88
|
| 5 | Network error |
|
|
69
89
|
| 6 | Not found |
|
|
90
|
+
| 7 | Needs input: `prospecting wait` reached a checkpoint with no terminal to ask (or `--no-input`) |
|
|
70
91
|
|
|
71
92
|
## Links
|
|
72
93
|
|
|
@@ -4,16 +4,16 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "discolike-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.5.0"
|
|
8
8
|
description = "Official CLI for the DiscoLike API"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
11
11
|
license-files = ["LICENSE"]
|
|
12
|
-
requires-python = ">=3.
|
|
12
|
+
requires-python = ">=3.11"
|
|
13
13
|
authors = [{ name = "DiscoLike", email = "support@discolike.com" }]
|
|
14
14
|
dependencies = [
|
|
15
|
-
"discolike==0.
|
|
16
|
-
"typer>=0.
|
|
15
|
+
"discolike==0.5.0",
|
|
16
|
+
"typer>=0.26.1,<0.28",
|
|
17
17
|
"rich>=13.0",
|
|
18
18
|
]
|
|
19
19
|
keywords = ["discolike", "cli", "business-data", "enrichment"]
|
|
@@ -21,7 +21,6 @@ classifiers = [
|
|
|
21
21
|
"Development Status :: 4 - Beta",
|
|
22
22
|
"Intended Audience :: Developers",
|
|
23
23
|
"Programming Language :: Python :: 3",
|
|
24
|
-
"Programming Language :: Python :: 3.10",
|
|
25
24
|
"Programming Language :: Python :: 3.11",
|
|
26
25
|
"Programming Language :: Python :: 3.12",
|
|
27
26
|
"Programming Language :: Python :: 3.13",
|
|
@@ -74,6 +74,9 @@ Exit codes:
|
|
|
74
74
|
4 rate_limited HTTP 429; wait "retry_after" seconds, then retry
|
|
75
75
|
5 network_error could not reach the API
|
|
76
76
|
6 not_found HTTP 404
|
|
77
|
+
7 needs_input `prospecting wait` stopped at a checkpoint with no terminal to ask
|
|
78
|
+
(or --no-input): the run is on stdout, the question and
|
|
79
|
+
"suggested_replies" in the stderr envelope
|
|
77
80
|
|
|
78
81
|
Environment:
|
|
79
82
|
{ENV_API_KEY} API key; overrides the config file written by `discolike auth login`.
|
|
@@ -43,6 +43,7 @@ EXIT_CODES: dict[type, int] = {
|
|
|
43
43
|
NotFoundError: 6,
|
|
44
44
|
}
|
|
45
45
|
DEFAULT_EXIT_CODE = 1
|
|
46
|
+
NEEDS_INPUT_EXIT_CODE = 7
|
|
46
47
|
|
|
47
48
|
# Stable, snake_case error codes for agents and scripts to branch on. The class
|
|
48
49
|
# name in `error` is kept for backwards compatibility; `code` is the contract.
|
|
@@ -82,7 +83,7 @@ class SupportsWait(Protocol):
|
|
|
82
83
|
task_id: str
|
|
83
84
|
task_family: str
|
|
84
85
|
|
|
85
|
-
def wait(self, *,
|
|
86
|
+
def wait(self, *, max_wait: float, on_poll: Callable[[_JobStatusLike], None] | None = None) -> _JobStatusLike: ...
|
|
86
87
|
|
|
87
88
|
|
|
88
89
|
def _normalize(data: Any) -> Any: # noqa: ANN401 -- accepts arbitrary JSON-serializable CLI output data
|
|
@@ -203,5 +204,5 @@ def run_job(job: SupportsWait, *, wait: bool, timeout: float, fmt: str | None =
|
|
|
203
204
|
def _on_poll(status: _JobStatusLike) -> None:
|
|
204
205
|
sys.stderr.write(f"progress: {status.progress}%\n")
|
|
205
206
|
|
|
206
|
-
final = job.wait(
|
|
207
|
+
final = job.wait(max_wait=timeout, on_poll=_on_poll)
|
|
207
208
|
emit(final.results if final.results is not None else final.to_dict(), fmt=fmt)
|
|
@@ -4,8 +4,8 @@ import json
|
|
|
4
4
|
import secrets
|
|
5
5
|
import sys
|
|
6
6
|
import webbrowser
|
|
7
|
+
from datetime import UTC
|
|
7
8
|
from datetime import datetime
|
|
8
|
-
from datetime import timezone
|
|
9
9
|
from typing import Any
|
|
10
10
|
from typing import NoReturn
|
|
11
11
|
from urllib.parse import urlparse
|
|
@@ -88,7 +88,7 @@ def _offer_signup(ctx: typer.Context) -> None:
|
|
|
88
88
|
|
|
89
89
|
|
|
90
90
|
def _iso(epoch_seconds: float) -> str:
|
|
91
|
-
return datetime.fromtimestamp(epoch_seconds, tz=
|
|
91
|
+
return datetime.fromtimestamp(epoch_seconds, tz=UTC).isoformat()
|
|
92
92
|
|
|
93
93
|
|
|
94
94
|
def _global_key_source(ctx: typer.Context) -> str:
|
|
@@ -382,8 +382,8 @@ def _persona_ids_in(path: pathlib.Path) -> set[str]:
|
|
|
382
382
|
|
|
383
383
|
|
|
384
384
|
ICP_PROMPT_HELP = "Natural-language ICP prompt used to derive contact filters."
|
|
385
|
-
SUMMARY_HELP = "
|
|
386
|
-
NEGATE_SUMMARY_HELP = "Exclude contacts
|
|
385
|
+
SUMMARY_HELP = "Match profile summary text: every word of any one term, in any order. Quote a multi-word term to keep it together, prefix + to require it. Ranks who comes back per company."
|
|
386
|
+
NEGATE_SUMMARY_HELP = "Exclude contacts whose profile summary contains every word of any one term, in any order."
|
|
387
387
|
HAS_EMAIL_HELP = "Only contacts with an email address (on by default)."
|
|
388
388
|
|
|
389
389
|
|
|
@@ -30,8 +30,8 @@ WAIT_HELP = "Block until the job finishes, streaming progress to stderr."
|
|
|
30
30
|
TIMEOUT_HELP = "Max seconds to wait with --wait."
|
|
31
31
|
PARAM_HELP = "Extra key=value query parameter forwarded to the SDK (repeatable)."
|
|
32
32
|
JOBSTART_DATE_HELP = "Job start date filter: min date or 'min,max' range, e.g. 2025-01-01 or 2025-01-01,2025-06-30."
|
|
33
|
-
SUMMARY_HELP = "
|
|
34
|
-
NEGATE_SUMMARY_HELP = "Exclude contacts
|
|
33
|
+
SUMMARY_HELP = "Match profile summary text: every word of any one term, in any order. Quote a multi-word term to keep it together, prefix + to require it."
|
|
34
|
+
NEGATE_SUMMARY_HELP = "Exclude contacts whose profile summary contains every word of any one term, in any order."
|
|
35
35
|
NAME_HELP = "Filter by contact name (partial match supported)."
|
|
36
36
|
SKILLS_HELP = "Filter by skill (repeatable)."
|
|
37
37
|
FILTER_STATE_HELP = "Filter by company state/region (repeatable)."
|
|
@@ -44,7 +44,7 @@ TASK_ID_HELP = "Task ID returned when the job was started."
|
|
|
44
44
|
app = typer.Typer(help="Run DiscoGen research jobs and check status of or cancel any async task (see --family)")
|
|
45
45
|
|
|
46
46
|
|
|
47
|
-
class TaskFamily(
|
|
47
|
+
class TaskFamily(enum.StrEnum):
|
|
48
48
|
discogen = "discogen"
|
|
49
49
|
bulkmatch = "bulkmatch"
|
|
50
50
|
contactmatch = "contactmatch"
|
|
@@ -71,7 +71,7 @@ def _fetch_batch_snapshot(batch: EmailBatch) -> EmailBatchResults:
|
|
|
71
71
|
"""Fetch the batch results once, whether or not the batch has finished."""
|
|
72
72
|
snapshot: dict[str, EmailBatchResults] = {}
|
|
73
73
|
try:
|
|
74
|
-
return batch.results(
|
|
74
|
+
return batch.results(max_wait=0, on_poll=lambda results: snapshot.update(latest=results))
|
|
75
75
|
except JobTimeoutError:
|
|
76
76
|
return snapshot["latest"]
|
|
77
77
|
|
|
@@ -101,7 +101,7 @@ def find_command(
|
|
|
101
101
|
if not wait:
|
|
102
102
|
emit({"job_id": job.job_id, "hint": f"poll with: discolike email job {job.job_id}"})
|
|
103
103
|
return
|
|
104
|
-
emit(job.wait(
|
|
104
|
+
emit(job.wait(max_wait=timeout, on_poll=_job_status_to_stderr), fmt=fmt)
|
|
105
105
|
|
|
106
106
|
|
|
107
107
|
@app.command("find-batch")
|
|
@@ -136,7 +136,7 @@ def find_batch_command(
|
|
|
136
136
|
if not wait:
|
|
137
137
|
emit({"batch_id": batch.batch_id, "hint": f"fetch with: discolike email results {batch.batch_id}"})
|
|
138
138
|
return
|
|
139
|
-
emit(batch.results(
|
|
139
|
+
emit(batch.results(max_wait=timeout, on_poll=_batch_progress_to_stderr), fmt=fmt)
|
|
140
140
|
|
|
141
141
|
|
|
142
142
|
@app.command("results")
|
|
@@ -156,7 +156,7 @@ def results_command(
|
|
|
156
156
|
raise typer.BadParameter(f"--kind must be one of: {', '.join(EMAIL_KINDS)}")
|
|
157
157
|
batch = get_client(ctx).email.batch(batch_id, kind=kind) # type: ignore[arg-type]
|
|
158
158
|
if wait:
|
|
159
|
-
emit(batch.results(
|
|
159
|
+
emit(batch.results(max_wait=timeout, on_poll=_batch_progress_to_stderr), fmt=fmt)
|
|
160
160
|
return
|
|
161
161
|
emit(_fetch_batch_snapshot(batch), fmt=fmt)
|
|
162
162
|
|
|
@@ -175,6 +175,6 @@ def job_command(
|
|
|
175
175
|
|
|
176
176
|
job = get_client(ctx).email.job(job_id)
|
|
177
177
|
if wait:
|
|
178
|
-
emit(job.wait(
|
|
178
|
+
emit(job.wait(max_wait=timeout, on_poll=_job_status_to_stderr), fmt=fmt)
|
|
179
179
|
return
|
|
180
180
|
emit(job.status(), fmt=fmt)
|
|
@@ -5,7 +5,7 @@ from importlib.metadata import version as package_version
|
|
|
5
5
|
from typing import Any
|
|
6
6
|
|
|
7
7
|
import typer
|
|
8
|
-
from typer
|
|
8
|
+
from typer import Abort
|
|
9
9
|
from typer._click.exceptions import ClickException
|
|
10
10
|
from typer._click.exceptions import NoArgsIsHelpError
|
|
11
11
|
from typer._click.exceptions import UsageError
|
|
@@ -23,6 +23,7 @@ from discolike_cli import discover
|
|
|
23
23
|
from discolike_cli import email
|
|
24
24
|
from discolike_cli import enrich
|
|
25
25
|
from discolike_cli import match
|
|
26
|
+
from discolike_cli import prospecting
|
|
26
27
|
from discolike_cli import providers
|
|
27
28
|
from discolike_cli import queries
|
|
28
29
|
from discolike_cli import signup
|
|
@@ -85,6 +86,7 @@ app.add_typer(contacts.app, name="contacts")
|
|
|
85
86
|
app.add_typer(discogen.app, name="discogen")
|
|
86
87
|
app.add_typer(email.app, name="email")
|
|
87
88
|
app.add_typer(queries.app, name="queries")
|
|
89
|
+
app.add_typer(prospecting.app, name="prospecting")
|
|
88
90
|
app.add_typer(account.app, name="account")
|
|
89
91
|
app.add_typer(providers.search_providers_app, name="search-providers")
|
|
90
92
|
app.add_typer(providers.llm_providers_app, name="llm-providers")
|