salesprompter-cli 0.1.2 → 0.1.4
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.
- package/README.md +83 -7
- package/dist/auth.js +27 -4
- package/dist/cli.js +9 -4
- package/package.json +27 -3
package/README.md
CHANGED
|
@@ -12,6 +12,80 @@
|
|
|
12
12
|
- Analyze upstream lead-list and domain-enrichment bottlenecks
|
|
13
13
|
- Replace opaque Pipedream logic with deterministic CLI workflows
|
|
14
14
|
|
|
15
|
+
It is built for two users at the same time:
|
|
16
|
+
|
|
17
|
+
- humans working in a terminal
|
|
18
|
+
- coding agents such as Codex, Claude Code, and other LLM-driven shell workflows
|
|
19
|
+
|
|
20
|
+
## Start Here
|
|
21
|
+
|
|
22
|
+
If someone discovers Salesprompter from a vague prompt, the first thing they need is the shortest working path.
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npx salesprompter-cli@latest --help
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Or install it globally:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install -g salesprompter-cli
|
|
32
|
+
salesprompter --help
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Prompt To Command
|
|
36
|
+
|
|
37
|
+
If the user says something like "I need to determine the ICP of deel.com", there are two different meanings.
|
|
38
|
+
|
|
39
|
+
### 1. They want leads at Deel itself
|
|
40
|
+
|
|
41
|
+
This means Deel is the target account.
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
salesprompter account:resolve --domain deel.com --company-name Deel --out ./data/deel-account.json
|
|
45
|
+
salesprompter leads:generate --icp ./data/icp.json --count 5 --domain deel.com --company-name Deel --out ./data/deel-leads.json
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 2. They sell for Deel and need Deel's ideal customer profile
|
|
49
|
+
|
|
50
|
+
This means Deel is the vendor, not the target account.
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
salesprompter icp:vendor --vendor deel --market dach --out ./data/deel-icp.json
|
|
54
|
+
salesprompter leads:lookup:bq --icp ./data/deel-icp.json --limit 100 --execute --out ./data/deel-leads-raw.json --lead-out ./data/deel-leads.json
|
|
55
|
+
salesprompter leads:enrich --in ./data/deel-leads.json --out ./data/deel-enriched.json
|
|
56
|
+
salesprompter leads:score --icp ./data/deel-icp.json --in ./data/deel-enriched.json --out ./data/deel-scored.json
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### 3. They want the LLM to call the CLI directly
|
|
60
|
+
|
|
61
|
+
Use the same commands, but prefer machine-readable output:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
salesprompter --json icp:vendor --vendor deel --market dach --out ./data/deel-icp.json
|
|
65
|
+
salesprompter --json leads:lookup:bq --icp ./data/deel-icp.json --limit 100 --lead-out ./data/deel-leads.json
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Documentation
|
|
69
|
+
|
|
70
|
+
This repository now includes a Mintlify docs site for the wider Salesprompter universe, including the app contract, CLI surface, Chrome extension contract, and the main warehouse-backed workflows.
|
|
71
|
+
|
|
72
|
+
- Docs home: `./index.mdx`
|
|
73
|
+
- Quickstart: `./quickstart.mdx`
|
|
74
|
+
- Architecture: `./architecture.mdx`
|
|
75
|
+
- App: `./platform/app.mdx`
|
|
76
|
+
- CLI: `./platform/cli.mdx`
|
|
77
|
+
- Chrome extension: `./platform/chrome-extension.mdx`
|
|
78
|
+
- Domain finder: `./workflows/domain-finder.mdx`
|
|
79
|
+
- Command reference: `./reference/cli.mdx`
|
|
80
|
+
- Environment variables: `./reference/environment-variables.mdx`
|
|
81
|
+
- Troubleshooting: `./operations/troubleshooting.mdx`
|
|
82
|
+
|
|
83
|
+
Run the docs locally with:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
npm run docs:dev
|
|
87
|
+
```
|
|
88
|
+
|
|
15
89
|
## Integration Contract
|
|
16
90
|
|
|
17
91
|
This CLI is not a standalone toy. It is a production integration surface for the Salesprompter app.
|
|
@@ -39,11 +113,11 @@ Global output flags:
|
|
|
39
113
|
The CLI stores a local session file at `~/.config/salesprompter/auth-session.json` (or `SALESPROMPTER_CONFIG_DIR`).
|
|
40
114
|
|
|
41
115
|
```bash
|
|
42
|
-
#
|
|
43
|
-
salesprompter auth:login
|
|
116
|
+
# Production path: generate a short-lived CLI token in the Salesprompter app
|
|
117
|
+
salesprompter auth:login --token "$SALESPROMPTER_TOKEN" --api-url "https://salesprompter.ai"
|
|
44
118
|
|
|
45
|
-
#
|
|
46
|
-
salesprompter auth:login
|
|
119
|
+
# Optional: use device flow only if your Salesprompter app exposes it
|
|
120
|
+
salesprompter auth:login
|
|
47
121
|
|
|
48
122
|
# Verify active identity with backend
|
|
49
123
|
salesprompter auth:whoami --verify
|
|
@@ -54,7 +128,7 @@ salesprompter auth:logout
|
|
|
54
128
|
|
|
55
129
|
Environment variables:
|
|
56
130
|
|
|
57
|
-
- `SALESPROMPTER_API_BASE_URL`: override backend URL (default `https://
|
|
131
|
+
- `SALESPROMPTER_API_BASE_URL`: override backend URL (default `https://salesprompter.ai`)
|
|
58
132
|
- `SALESPROMPTER_CONFIG_DIR`: override local config dir
|
|
59
133
|
- `SALESPROMPTER_SKIP_AUTH=1`: bypass auth guard (tests/dev only)
|
|
60
134
|
- `INSTANTLY_API_KEY`: required for real `sync:outreach --target instantly`
|
|
@@ -63,17 +137,19 @@ Environment variables:
|
|
|
63
137
|
|
|
64
138
|
App compatibility:
|
|
65
139
|
|
|
66
|
-
-
|
|
140
|
+
- Current Salesprompter production uses app-issued CLI tokens.
|
|
141
|
+
- If your app exposes `/api/cli/auth/device/start` and `/api/cli/auth/device/poll`, `salesprompter auth:login` will use device flow.
|
|
67
142
|
- If device auth is not enabled, create a CLI token from the app endpoint `POST /api/cli/auth/token` and use:
|
|
68
143
|
|
|
69
144
|
```bash
|
|
70
|
-
salesprompter auth:login --token "<token-from-app>"
|
|
145
|
+
salesprompter auth:login --token "<token-from-app>" --api-url "https://salesprompter.ai"
|
|
71
146
|
```
|
|
72
147
|
|
|
73
148
|
## Why this shape works for humans and LLMs
|
|
74
149
|
|
|
75
150
|
- Every command reads and writes plain JSON.
|
|
76
151
|
- Output is machine-readable and composable (`--json` for compact transport).
|
|
152
|
+
- The top-level use cases map ambiguous prompts like "determine the ICP of deel.com" into explicit command paths.
|
|
77
153
|
- Domain contracts are explicit and validated with `zod`.
|
|
78
154
|
- External integrations are behind narrow provider interfaces.
|
|
79
155
|
- Lead generation reports which provider and mode produced the result.
|
package/dist/auth.js
CHANGED
|
@@ -2,7 +2,7 @@ import os from "node:os";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { access, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
4
4
|
import { z } from "zod";
|
|
5
|
-
const DEFAULT_API_BASE_URL = "https://
|
|
5
|
+
const DEFAULT_API_BASE_URL = "https://salesprompter.ai";
|
|
6
6
|
const CLIENT_HEADER = "salesprompter-cli/0.2";
|
|
7
7
|
const DEFAULT_DEVICE_POLL_INTERVAL_SECONDS = 3;
|
|
8
8
|
const DEFAULT_DEVICE_TIMEOUT_SECONDS = 180;
|
|
@@ -94,6 +94,18 @@ function normalizeApiBaseUrl(apiBaseUrl) {
|
|
|
94
94
|
const value = (apiBaseUrl ?? process.env.SALESPROMPTER_API_BASE_URL ?? DEFAULT_API_BASE_URL).trim();
|
|
95
95
|
return value.replace(/\/+$/, "");
|
|
96
96
|
}
|
|
97
|
+
function isDeviceFlowUnavailableError(message) {
|
|
98
|
+
if (message.includes("invalid JSON response")) {
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
return /request failed \((401|403|404|405|500|501|502|503|504)\)/.test(message);
|
|
102
|
+
}
|
|
103
|
+
function buildDeviceFlowUnavailableMessage(apiBaseUrl) {
|
|
104
|
+
return [
|
|
105
|
+
"device login is not configured on this Salesprompter app.",
|
|
106
|
+
`Generate a CLI token in the app and run \`salesprompter auth:login --token <token> --api-url "${apiBaseUrl}"\`.`
|
|
107
|
+
].join(" ");
|
|
108
|
+
}
|
|
97
109
|
async function hasSessionFile() {
|
|
98
110
|
try {
|
|
99
111
|
await access(getSessionPath());
|
|
@@ -106,7 +118,18 @@ async function hasSessionFile() {
|
|
|
106
118
|
async function httpJson(url, init, schema) {
|
|
107
119
|
const response = await fetch(url, init);
|
|
108
120
|
const text = await response.text();
|
|
109
|
-
|
|
121
|
+
let payload = {};
|
|
122
|
+
if (text.length > 0) {
|
|
123
|
+
try {
|
|
124
|
+
payload = JSON.parse(text);
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
if (!response.ok) {
|
|
128
|
+
throw new Error(`request failed (${response.status}) for ${url}`);
|
|
129
|
+
}
|
|
130
|
+
throw new Error(`invalid JSON response for ${url}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
110
133
|
if (!response.ok) {
|
|
111
134
|
throw new Error(`request failed (${response.status}) for ${url}`);
|
|
112
135
|
}
|
|
@@ -195,8 +218,8 @@ export async function loginWithDeviceFlow(options) {
|
|
|
195
218
|
}
|
|
196
219
|
catch (error) {
|
|
197
220
|
const message = error instanceof Error ? error.message : String(error);
|
|
198
|
-
if (message
|
|
199
|
-
throw new Error(
|
|
221
|
+
if (isDeviceFlowUnavailableError(message)) {
|
|
222
|
+
throw new Error(buildDeviceFlowUnavailableMessage(apiBaseUrl));
|
|
200
223
|
}
|
|
201
224
|
throw error;
|
|
202
225
|
}
|
package/dist/cli.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from "node:module";
|
|
2
3
|
import { Command } from "commander";
|
|
3
4
|
import { z } from "zod";
|
|
4
5
|
import { clearAuthSession, loginWithDeviceFlow, loginWithToken, requireAuthSession, shouldBypassAuth, verifySession } from "./auth.js";
|
|
@@ -11,6 +12,8 @@ import { buildHistoricalVendorIcp, buildVendorIcp } from "./icp-templates.js";
|
|
|
11
12
|
import { InstantlySyncProvider } from "./instantly.js";
|
|
12
13
|
import { buildLeadlistsFunnelQueries } from "./leadlists-funnel.js";
|
|
13
14
|
import { readJsonFile, splitCsv, writeJsonFile, writeTextFile } from "./io.js";
|
|
15
|
+
const require = createRequire(import.meta.url);
|
|
16
|
+
const { version: packageVersion } = require("../package.json");
|
|
14
17
|
const program = new Command();
|
|
15
18
|
const leadProvider = new AccountLeadProvider(new HeuristicCompanyProvider(), new HeuristicPeopleSearchProvider());
|
|
16
19
|
const enrichmentProvider = new HeuristicEnrichmentProvider();
|
|
@@ -166,14 +169,14 @@ async function fetchHistoricalQueryRows(tables) {
|
|
|
166
169
|
program
|
|
167
170
|
.name("salesprompter")
|
|
168
171
|
.description("Sales workflow CLI for ICP definition, lead generation, enrichment, scoring, and sync.")
|
|
169
|
-
.version(
|
|
172
|
+
.version(packageVersion)
|
|
170
173
|
.option("--json", "Emit compact machine-readable JSON output", false)
|
|
171
174
|
.option("--quiet", "Suppress successful stdout output", false);
|
|
172
175
|
program
|
|
173
176
|
.command("auth:login")
|
|
174
|
-
.description("Authenticate CLI with Salesprompter app
|
|
177
|
+
.description("Authenticate CLI with a Salesprompter app token, or device flow if the app supports it.")
|
|
175
178
|
.option("--token <token>", "App-issued bearer token for direct login")
|
|
176
|
-
.option("--api-url <url>", "Salesprompter API base URL, defaults to SALESPROMPTER_API_BASE_URL or
|
|
179
|
+
.option("--api-url <url>", "Salesprompter API base URL, defaults to SALESPROMPTER_API_BASE_URL or salesprompter.ai")
|
|
177
180
|
.option("--timeout-seconds <number>", "Device flow wait timeout in seconds", "180")
|
|
178
181
|
.action(async (options) => {
|
|
179
182
|
const timeoutSeconds = z.coerce.number().int().min(30).max(1800).parse(options.timeoutSeconds);
|
|
@@ -190,7 +193,9 @@ program
|
|
|
190
193
|
return;
|
|
191
194
|
}
|
|
192
195
|
const startedAt = new Date().toISOString();
|
|
193
|
-
|
|
196
|
+
if (!runtimeOutputOptions.json && !runtimeOutputOptions.quiet) {
|
|
197
|
+
process.stderr.write("Starting device login flow. Complete login in the browser.\n");
|
|
198
|
+
}
|
|
194
199
|
const result = await loginWithDeviceFlow({
|
|
195
200
|
apiBaseUrl: options.apiUrl,
|
|
196
201
|
timeoutSeconds
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "salesprompter-cli",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "CLI for
|
|
3
|
+
"version": "0.1.4",
|
|
4
|
+
"description": "JSON-first sales prospecting CLI for ICP definition, lead generation, enrichment, scoring, and CRM/outreach sync.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"salesprompter": "dist/cli.js"
|
|
@@ -13,16 +13,39 @@
|
|
|
13
13
|
"scripts": {
|
|
14
14
|
"build": "tsc -p tsconfig.json",
|
|
15
15
|
"check": "tsc --noEmit -p tsconfig.json",
|
|
16
|
+
"docs:dev": "mint dev",
|
|
17
|
+
"docs:broken-links": "mint broken-links",
|
|
18
|
+
"docs:a11y": "mint a11y",
|
|
16
19
|
"start": "node ./dist/cli.js",
|
|
17
20
|
"test": "npm run build && tsc -p tsconfig.test.json && node --test dist-tests/tests/**/*.test.js"
|
|
18
21
|
},
|
|
19
22
|
"keywords": [
|
|
20
23
|
"sales",
|
|
24
|
+
"salesprompter",
|
|
21
25
|
"cli",
|
|
26
|
+
"icp",
|
|
27
|
+
"ideal-customer-profile",
|
|
28
|
+
"prospecting",
|
|
22
29
|
"lead-generation",
|
|
30
|
+
"lead-enrichment",
|
|
31
|
+
"lead-scoring",
|
|
32
|
+
"sales-intelligence",
|
|
23
33
|
"crm",
|
|
24
|
-
"outreach"
|
|
34
|
+
"outreach",
|
|
35
|
+
"instantly",
|
|
36
|
+
"hubspot",
|
|
37
|
+
"llm",
|
|
38
|
+
"ai-agent",
|
|
39
|
+
"codex"
|
|
25
40
|
],
|
|
41
|
+
"homepage": "https://salesprompter.ai/docs",
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "git+https://github.com/danielsinewe/salesprompter-cli.git"
|
|
45
|
+
},
|
|
46
|
+
"bugs": {
|
|
47
|
+
"url": "https://github.com/danielsinewe/salesprompter-cli/issues"
|
|
48
|
+
},
|
|
26
49
|
"license": "MIT",
|
|
27
50
|
"dependencies": {
|
|
28
51
|
"commander": "^14.0.1",
|
|
@@ -30,6 +53,7 @@
|
|
|
30
53
|
},
|
|
31
54
|
"devDependencies": {
|
|
32
55
|
"@types/node": "^24.3.0",
|
|
56
|
+
"mint": "^4.2.420",
|
|
33
57
|
"typescript": "^5.9.2"
|
|
34
58
|
}
|
|
35
59
|
}
|