foxreach-cli 0.1.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.
@@ -0,0 +1,46 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ build:
9
+ name: Build distribution
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+
14
+ - name: Set up Python
15
+ uses: actions/setup-python@v5
16
+ with:
17
+ python-version: "3.12"
18
+
19
+ - name: Install build tools
20
+ run: pip install build
21
+
22
+ - name: Build package
23
+ run: python -m build
24
+
25
+ - name: Upload dist artifacts
26
+ uses: actions/upload-artifact@v4
27
+ with:
28
+ name: dist
29
+ path: dist/
30
+
31
+ publish:
32
+ name: Publish to PyPI
33
+ needs: build
34
+ runs-on: ubuntu-latest
35
+ environment: pypi
36
+ permissions:
37
+ id-token: write
38
+ steps:
39
+ - name: Download dist artifacts
40
+ uses: actions/download-artifact@v4
41
+ with:
42
+ name: dist
43
+ path: dist/
44
+
45
+ - name: Publish to PyPI
46
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,6 @@
1
+ dist/
2
+ build/
3
+ *.egg-info/
4
+ __pycache__/
5
+ *.pyc
6
+ .venv/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 FoxReach
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,145 @@
1
+ Metadata-Version: 2.4
2
+ Name: foxreach-cli
3
+ Version: 0.1.0
4
+ Summary: CLI for the FoxReach cold email outreach API
5
+ Project-URL: Homepage, https://foxreach.io
6
+ Project-URL: Documentation, https://docs.foxreach.io/sdks/cli
7
+ Project-URL: Repository, https://github.com/foxreach/foxreach-cli
8
+ Project-URL: Changelog, https://docs.foxreach.io/changelog
9
+ Author-email: FoxReach <dev@foxreach.io>
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: cli,cold-email,email,foxreach,outreach
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Communications :: Email
24
+ Requires-Python: >=3.9
25
+ Requires-Dist: click>=8.0
26
+ Requires-Dist: foxreach>=0.1.0
27
+ Requires-Dist: rich>=13.0
28
+ Description-Content-Type: text/markdown
29
+
30
+ # FoxReach CLI
31
+
32
+ Command-line interface for the [FoxReach](https://foxreach.io) cold email outreach API.
33
+
34
+ ## Installation
35
+
36
+ ```bash
37
+ pip install foxreach-cli
38
+ ```
39
+
40
+ ## Setup
41
+
42
+ ```bash
43
+ # Save your API key
44
+ foxreach config set-key
45
+ # Enter: otr_your_api_key
46
+
47
+ # Verify
48
+ foxreach config show
49
+ ```
50
+
51
+ You can also use environment variables:
52
+
53
+ ```bash
54
+ export FOXREACH_API_KEY=otr_your_api_key
55
+ export FOXREACH_BASE_URL=https://api.foxreach.io/api/v1 # optional
56
+ ```
57
+
58
+ ## Usage
59
+
60
+ ### Leads
61
+
62
+ ```bash
63
+ foxreach leads list
64
+ foxreach leads list --search "jane" --status active
65
+ foxreach leads get cld_abc123
66
+ foxreach leads create --email jane@example.com --first-name Jane --company "Acme"
67
+ foxreach leads update cld_abc123 --company "New Corp"
68
+ foxreach leads delete cld_abc123
69
+ ```
70
+
71
+ ### Campaigns
72
+
73
+ ```bash
74
+ foxreach campaigns list
75
+ foxreach campaigns list --status active
76
+ foxreach campaigns get cmp_xyz789
77
+ foxreach campaigns create --name "Q1 Outreach" --daily-limit 100
78
+ foxreach campaigns update cmp_xyz789 --daily-limit 200
79
+ foxreach campaigns start cmp_xyz789
80
+ foxreach campaigns pause cmp_xyz789
81
+ foxreach campaigns add-leads cmp_xyz789 --lead-ids cld_1,cld_2,cld_3
82
+ foxreach campaigns add-accounts cmp_xyz789 --account-ids acc_1
83
+ foxreach campaigns delete cmp_xyz789
84
+ ```
85
+
86
+ ### Sequences
87
+
88
+ ```bash
89
+ foxreach sequences list --campaign cmp_xyz789
90
+ foxreach sequences create --campaign cmp_xyz789 --subject "Hi {{firstName}}" --body "Hello!"
91
+ foxreach sequences update --campaign cmp_xyz789 --sequence seq_1 --delay-days 3
92
+ foxreach sequences delete --campaign cmp_xyz789 --sequence seq_1
93
+ ```
94
+
95
+ ### Templates
96
+
97
+ ```bash
98
+ foxreach templates list
99
+ foxreach templates get tpl_abc123
100
+ foxreach templates create --name "Follow-up" --body "Hi {{firstName}}, just checking in."
101
+ foxreach templates update tpl_abc123 --name "New Follow-up"
102
+ foxreach templates delete tpl_abc123
103
+ ```
104
+
105
+ ### Email Accounts
106
+
107
+ ```bash
108
+ foxreach accounts list
109
+ foxreach accounts get acc_abc123
110
+ foxreach accounts delete acc_abc123
111
+ ```
112
+
113
+ ### Inbox
114
+
115
+ ```bash
116
+ foxreach inbox list
117
+ foxreach inbox list --unread --category interested
118
+ foxreach inbox get rpl_abc123
119
+ foxreach inbox update rpl_abc123 --read --category qualified
120
+ ```
121
+
122
+ ### Analytics
123
+
124
+ ```bash
125
+ foxreach analytics overview
126
+ foxreach analytics campaign cmp_xyz789
127
+ ```
128
+
129
+ ## JSON Output
130
+
131
+ Add `--json` to any command to get raw JSON output for scripting:
132
+
133
+ ```bash
134
+ foxreach leads list --json | jq '.data[].email'
135
+ foxreach campaigns get cmp_xyz789 --json
136
+ ```
137
+
138
+ ## Configuration
139
+
140
+ Config is stored at `~/.foxreach/config.json`. Environment variables take precedence:
141
+
142
+ | Setting | Env Var | Config Key |
143
+ |---------|---------|------------|
144
+ | API Key | `FOXREACH_API_KEY` | `api_key` |
145
+ | Base URL | `FOXREACH_BASE_URL` | `base_url` |
@@ -0,0 +1,116 @@
1
+ # FoxReach CLI
2
+
3
+ Command-line interface for the [FoxReach](https://foxreach.io) cold email outreach API.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install foxreach-cli
9
+ ```
10
+
11
+ ## Setup
12
+
13
+ ```bash
14
+ # Save your API key
15
+ foxreach config set-key
16
+ # Enter: otr_your_api_key
17
+
18
+ # Verify
19
+ foxreach config show
20
+ ```
21
+
22
+ You can also use environment variables:
23
+
24
+ ```bash
25
+ export FOXREACH_API_KEY=otr_your_api_key
26
+ export FOXREACH_BASE_URL=https://api.foxreach.io/api/v1 # optional
27
+ ```
28
+
29
+ ## Usage
30
+
31
+ ### Leads
32
+
33
+ ```bash
34
+ foxreach leads list
35
+ foxreach leads list --search "jane" --status active
36
+ foxreach leads get cld_abc123
37
+ foxreach leads create --email jane@example.com --first-name Jane --company "Acme"
38
+ foxreach leads update cld_abc123 --company "New Corp"
39
+ foxreach leads delete cld_abc123
40
+ ```
41
+
42
+ ### Campaigns
43
+
44
+ ```bash
45
+ foxreach campaigns list
46
+ foxreach campaigns list --status active
47
+ foxreach campaigns get cmp_xyz789
48
+ foxreach campaigns create --name "Q1 Outreach" --daily-limit 100
49
+ foxreach campaigns update cmp_xyz789 --daily-limit 200
50
+ foxreach campaigns start cmp_xyz789
51
+ foxreach campaigns pause cmp_xyz789
52
+ foxreach campaigns add-leads cmp_xyz789 --lead-ids cld_1,cld_2,cld_3
53
+ foxreach campaigns add-accounts cmp_xyz789 --account-ids acc_1
54
+ foxreach campaigns delete cmp_xyz789
55
+ ```
56
+
57
+ ### Sequences
58
+
59
+ ```bash
60
+ foxreach sequences list --campaign cmp_xyz789
61
+ foxreach sequences create --campaign cmp_xyz789 --subject "Hi {{firstName}}" --body "Hello!"
62
+ foxreach sequences update --campaign cmp_xyz789 --sequence seq_1 --delay-days 3
63
+ foxreach sequences delete --campaign cmp_xyz789 --sequence seq_1
64
+ ```
65
+
66
+ ### Templates
67
+
68
+ ```bash
69
+ foxreach templates list
70
+ foxreach templates get tpl_abc123
71
+ foxreach templates create --name "Follow-up" --body "Hi {{firstName}}, just checking in."
72
+ foxreach templates update tpl_abc123 --name "New Follow-up"
73
+ foxreach templates delete tpl_abc123
74
+ ```
75
+
76
+ ### Email Accounts
77
+
78
+ ```bash
79
+ foxreach accounts list
80
+ foxreach accounts get acc_abc123
81
+ foxreach accounts delete acc_abc123
82
+ ```
83
+
84
+ ### Inbox
85
+
86
+ ```bash
87
+ foxreach inbox list
88
+ foxreach inbox list --unread --category interested
89
+ foxreach inbox get rpl_abc123
90
+ foxreach inbox update rpl_abc123 --read --category qualified
91
+ ```
92
+
93
+ ### Analytics
94
+
95
+ ```bash
96
+ foxreach analytics overview
97
+ foxreach analytics campaign cmp_xyz789
98
+ ```
99
+
100
+ ## JSON Output
101
+
102
+ Add `--json` to any command to get raw JSON output for scripting:
103
+
104
+ ```bash
105
+ foxreach leads list --json | jq '.data[].email'
106
+ foxreach campaigns get cmp_xyz789 --json
107
+ ```
108
+
109
+ ## Configuration
110
+
111
+ Config is stored at `~/.foxreach/config.json`. Environment variables take precedence:
112
+
113
+ | Setting | Env Var | Config Key |
114
+ |---------|---------|------------|
115
+ | API Key | `FOXREACH_API_KEY` | `api_key` |
116
+ | Base URL | `FOXREACH_BASE_URL` | `base_url` |
@@ -0,0 +1,3 @@
1
+ """FoxReach CLI — command-line interface for the FoxReach API."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,22 @@
1
+ from __future__ import annotations
2
+
3
+ import sys
4
+
5
+ from foxreach import FoxReach
6
+
7
+ from . import config as cfg
8
+ from .output import print_error
9
+
10
+
11
+ def get_client() -> FoxReach:
12
+ api_key = cfg.get_api_key()
13
+ if not api_key:
14
+ print_error("No API key configured. Run: foxreach config set-key")
15
+ sys.exit(1)
16
+
17
+ kwargs = {"api_key": api_key}
18
+ base_url = cfg.get_base_url()
19
+ if base_url:
20
+ kwargs["base_url"] = base_url
21
+
22
+ return FoxReach(**kwargs)
File without changes
@@ -0,0 +1,87 @@
1
+ from __future__ import annotations
2
+
3
+ import sys
4
+ from dataclasses import asdict
5
+
6
+ import click
7
+
8
+ from foxreach import FoxReachError
9
+
10
+ from ..client_factory import get_client
11
+ from ..output import print_detail, print_error, print_json, print_meta, print_success, print_table
12
+
13
+
14
+ @click.group()
15
+ def accounts() -> None:
16
+ """Manage email accounts."""
17
+
18
+
19
+ @accounts.command("list")
20
+ @click.option("--page", default=1, type=int)
21
+ @click.option("--limit", default=50, type=int)
22
+ @click.option("--json", "as_json", is_flag=True, help="Output as JSON")
23
+ def accounts_list(page: int, limit: int, as_json: bool) -> None:
24
+ """List email accounts."""
25
+ client = get_client()
26
+ try:
27
+ result = client.email_accounts.list(page=page, page_size=limit)
28
+ except FoxReachError as e:
29
+ print_error(str(e))
30
+ sys.exit(1)
31
+
32
+ if as_json:
33
+ print_json({"data": [asdict(a) for a in result.data], "meta": asdict(result.meta)})
34
+ return
35
+
36
+ if not result.data:
37
+ click.echo("No email accounts found.")
38
+ return
39
+
40
+ print_table(
41
+ result.data,
42
+ ["ID", "Email", "Display Name", "Active", "Daily Limit", "Sent Today", "Health"],
43
+ {
44
+ "ID": "id", "Email": "email", "Display Name": "display_name",
45
+ "Active": "is_active", "Daily Limit": "daily_limit",
46
+ "Sent Today": "sent_today", "Health": "health_score",
47
+ },
48
+ )
49
+ print_meta(result.meta)
50
+
51
+
52
+ @accounts.command("get")
53
+ @click.argument("account_id")
54
+ @click.option("--json", "as_json", is_flag=True, help="Output as JSON")
55
+ def accounts_get(account_id: str, as_json: bool) -> None:
56
+ """Get an email account by ID."""
57
+ client = get_client()
58
+ try:
59
+ account = client.email_accounts.get(account_id)
60
+ except FoxReachError as e:
61
+ print_error(str(e))
62
+ sys.exit(1)
63
+
64
+ if as_json:
65
+ print_json(account)
66
+ return
67
+
68
+ print_detail(account, [
69
+ "id", "email", "display_name", "provider", "is_active",
70
+ "daily_limit", "sent_today", "warmup_enabled",
71
+ "health_score", "bounce_rate", "reply_rate",
72
+ "connection_status", "created_at",
73
+ ])
74
+
75
+
76
+ @accounts.command("delete")
77
+ @click.argument("account_id")
78
+ @click.confirmation_option(prompt="Are you sure you want to delete this email account?")
79
+ def accounts_delete(account_id: str) -> None:
80
+ """Delete an email account."""
81
+ client = get_client()
82
+ try:
83
+ client.email_accounts.delete(account_id)
84
+ except FoxReachError as e:
85
+ print_error(str(e))
86
+ sys.exit(1)
87
+ print_success(f"Email account deleted: {account_id}")
@@ -0,0 +1,69 @@
1
+ from __future__ import annotations
2
+
3
+ import sys
4
+ from dataclasses import asdict
5
+
6
+ import click
7
+
8
+ from foxreach import FoxReachError
9
+
10
+ from ..client_factory import get_client
11
+ from ..output import print_detail, print_error, print_json, print_table
12
+
13
+
14
+ @click.group()
15
+ def analytics() -> None:
16
+ """View analytics and stats."""
17
+
18
+
19
+ @analytics.command("overview")
20
+ @click.option("--json", "as_json", is_flag=True, help="Output as JSON")
21
+ def analytics_overview(as_json: bool) -> None:
22
+ """Show dashboard overview stats."""
23
+ client = get_client()
24
+ try:
25
+ stats = client.analytics.overview()
26
+ except FoxReachError as e:
27
+ print_error(str(e))
28
+ sys.exit(1)
29
+
30
+ if as_json:
31
+ print_json(stats)
32
+ return
33
+
34
+ print_detail(stats, [
35
+ "total_accounts", "active_accounts",
36
+ "total_campaigns", "active_campaigns",
37
+ "total_leads", "total_sent", "total_replies",
38
+ "reply_rate", "account_health_avg",
39
+ ])
40
+
41
+
42
+ @analytics.command("campaign")
43
+ @click.argument("campaign_id")
44
+ @click.option("--json", "as_json", is_flag=True, help="Output as JSON")
45
+ def analytics_campaign(campaign_id: str, as_json: bool) -> None:
46
+ """Show campaign analytics."""
47
+ client = get_client()
48
+ try:
49
+ stats = client.analytics.campaign(campaign_id)
50
+ except FoxReachError as e:
51
+ print_error(str(e))
52
+ sys.exit(1)
53
+
54
+ if as_json:
55
+ print_json(stats)
56
+ return
57
+
58
+ print_detail(stats, [
59
+ "sent", "delivered", "bounced", "replied", "opened",
60
+ "reply_rate", "bounce_rate",
61
+ ])
62
+
63
+ if stats.daily_stats:
64
+ click.echo("\nDaily Stats:")
65
+ print_table(
66
+ stats.daily_stats,
67
+ ["Date", "Sent", "Delivered", "Bounced", "Replied", "Opened"],
68
+ {"Date": "date", "Sent": "sent", "Delivered": "delivered", "Bounced": "bounced", "Replied": "replied", "Opened": "opened"},
69
+ )