argorant 0.1.0
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 +55 -0
- package/bin/argorant.js +406 -0
- package/package.json +32 -0
package/README.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# argorant
|
|
2
|
+
|
|
3
|
+
Search, count, reveal, and export verified B2B contacts from the Argorant
|
|
4
|
+
database — from your terminal, scripts, or coding agent. No install required.
|
|
5
|
+
|
|
6
|
+
```sh
|
|
7
|
+
npx argorant count "fintech CFOs in germany"
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Counts and searches are **free**. Reveals and exports draw on your Argorant
|
|
11
|
+
workspace quota/credits — the same pool as the app, API, and MCP server.
|
|
12
|
+
|
|
13
|
+
## Authenticate
|
|
14
|
+
|
|
15
|
+
Create an API key at **app.argorant.com/profile** (API keys), then:
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
npx argorant login # paste your ag_live_ key (stored in ~/.argorant)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Or set `ARGORANT_API_KEY=ag_live_…` in your environment — ideal for scripts,
|
|
22
|
+
CI, and agents.
|
|
23
|
+
|
|
24
|
+
## Commands
|
|
25
|
+
|
|
26
|
+
| Command | What it does | Cost |
|
|
27
|
+
| --- | --- | --- |
|
|
28
|
+
| `login [key]` | Save an API key | — |
|
|
29
|
+
| `whoami` | Account, scopes, daily quota | free |
|
|
30
|
+
| `count "<query>"` | Count matching contacts | free |
|
|
31
|
+
| `search "<query>" -n 10` | Preview matches (details redacted) | free |
|
|
32
|
+
| `reveal "<query>" -n 25` | Reveal full contact details | quota |
|
|
33
|
+
| `export "<query>" -n 1000 -o leads.csv` | Verified CSV export, polled until ready | quota |
|
|
34
|
+
|
|
35
|
+
## Filters
|
|
36
|
+
|
|
37
|
+
Combine free text with structured filters:
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
--title --seniority --department --industry
|
|
41
|
+
--country --state --city --company --domain
|
|
42
|
+
--verify-status --has-phone --has-linkedin --has-email
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Options: `-n/--limit`, `-o/--output`, `--json`, `-y/--yes`, `--base`.
|
|
46
|
+
|
|
47
|
+
## Built for agents
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
export ARGORANT_API_KEY=ag_live_...
|
|
51
|
+
npx argorant count --industry logistics --country "United States" --seniority vp --json
|
|
52
|
+
npx argorant reveal "heads of procurement" --country Germany -n 25 --json --yes
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Full docs: https://argorant.com/docs/cli
|
package/bin/argorant.js
ADDED
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
// Argorant CLI — a thin, dependency-free wrapper over the Argorant REST API.
|
|
5
|
+
// Search, count, reveal, and export verified B2B contacts from the terminal.
|
|
6
|
+
// Auth: an Argorant API key (ag_live_*) via `argorant login`, or ARGORANT_API_KEY.
|
|
7
|
+
|
|
8
|
+
const https = require("https");
|
|
9
|
+
const http = require("http");
|
|
10
|
+
const fs = require("fs");
|
|
11
|
+
const os = require("os");
|
|
12
|
+
const path = require("path");
|
|
13
|
+
const readline = require("readline");
|
|
14
|
+
const { URL } = require("url");
|
|
15
|
+
|
|
16
|
+
const VERSION = require("../package.json").version;
|
|
17
|
+
const DEFAULT_BASE = process.env.ARGORANT_API_BASE || "https://argorant.com";
|
|
18
|
+
const CONFIG_DIR = path.join(os.homedir(), ".argorant");
|
|
19
|
+
const CONFIG_PATH = path.join(CONFIG_DIR, "config.json");
|
|
20
|
+
|
|
21
|
+
// ---- tiny ANSI helpers (auto-disabled when not a TTY) ----
|
|
22
|
+
const useColor = process.stdout.isTTY && !process.env.NO_COLOR;
|
|
23
|
+
const c = (code, s) => (useColor ? `\x1b[${code}m${s}\x1b[0m` : s);
|
|
24
|
+
const bold = (s) => c("1", s);
|
|
25
|
+
const dim = (s) => c("2", s);
|
|
26
|
+
const green = (s) => c("32", s);
|
|
27
|
+
const red = (s) => c("31", s);
|
|
28
|
+
const cyan = (s) => c("36", s);
|
|
29
|
+
|
|
30
|
+
function die(msg, code = 1) {
|
|
31
|
+
process.stderr.write(red("error: ") + msg + "\n");
|
|
32
|
+
process.exit(code);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// ---- config / key storage ----
|
|
36
|
+
function loadConfig() {
|
|
37
|
+
try {
|
|
38
|
+
return JSON.parse(fs.readFileSync(CONFIG_PATH, "utf8"));
|
|
39
|
+
} catch {
|
|
40
|
+
return {};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function saveConfig(cfg) {
|
|
44
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
45
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(cfg, null, 2) + "\n", { mode: 0o600 });
|
|
46
|
+
}
|
|
47
|
+
function resolveKey() {
|
|
48
|
+
return process.env.ARGORANT_API_KEY || loadConfig().apiKey || "";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ---- arg parsing ----
|
|
52
|
+
// Flags that map to API filter params. Value flags take the next token.
|
|
53
|
+
const VALUE_FLAGS = {
|
|
54
|
+
"--title": "title",
|
|
55
|
+
"--seniority": "seniority",
|
|
56
|
+
"--department": "departments",
|
|
57
|
+
"--departments": "departments",
|
|
58
|
+
"--industry": "industry",
|
|
59
|
+
"--country": "country",
|
|
60
|
+
"--state": "state",
|
|
61
|
+
"--city": "city",
|
|
62
|
+
"--company": "company_name",
|
|
63
|
+
"--domain": "company_domain",
|
|
64
|
+
"--verify-status": "verify_status",
|
|
65
|
+
};
|
|
66
|
+
// Boolean filter flags (presence => "true").
|
|
67
|
+
const BOOL_FLAGS = {
|
|
68
|
+
"--has-phone": "has_phone",
|
|
69
|
+
"--has-linkedin": "has_linkedin",
|
|
70
|
+
"--has-email": "has_email",
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
function parseArgs(argv) {
|
|
74
|
+
const out = { _: [], filters: {}, limit: null, output: null, json: false, yes: false, base: DEFAULT_BASE };
|
|
75
|
+
for (let i = 0; i < argv.length; i++) {
|
|
76
|
+
const a = argv[i];
|
|
77
|
+
if (a === "--json") out.json = true;
|
|
78
|
+
else if (a === "--yes" || a === "-y") out.yes = true;
|
|
79
|
+
else if (a === "-n" || a === "--limit") out.limit = parseInt(argv[++i], 10);
|
|
80
|
+
else if (a === "-o" || a === "--output") out.output = argv[++i];
|
|
81
|
+
else if (a === "--base") out.base = argv[++i];
|
|
82
|
+
else if (a in VALUE_FLAGS) out.filters[VALUE_FLAGS[a]] = argv[++i];
|
|
83
|
+
else if (a in BOOL_FLAGS) out.filters[BOOL_FLAGS[a]] = "true";
|
|
84
|
+
else if (a.startsWith("--") && a.includes("=")) {
|
|
85
|
+
const [k, v] = [a.slice(0, a.indexOf("=")), a.slice(a.indexOf("=") + 1)];
|
|
86
|
+
if (k in VALUE_FLAGS) out.filters[VALUE_FLAGS[k]] = v;
|
|
87
|
+
else die(`unknown flag: ${k}`);
|
|
88
|
+
} else if (a.startsWith("-") && a !== "-") {
|
|
89
|
+
die(`unknown flag: ${a}`);
|
|
90
|
+
} else {
|
|
91
|
+
out._.push(a);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Free-text positional → q
|
|
95
|
+
if (out._.length) out.filters.q = out._.join(" ");
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ---- HTTP ----
|
|
100
|
+
function request(method, base, urlPath, { key, query, body } = {}) {
|
|
101
|
+
return new Promise((resolve, reject) => {
|
|
102
|
+
let u;
|
|
103
|
+
try {
|
|
104
|
+
u = new URL(urlPath, base);
|
|
105
|
+
} catch (e) {
|
|
106
|
+
return reject(new Error(`bad URL: ${urlPath}`));
|
|
107
|
+
}
|
|
108
|
+
if (query) {
|
|
109
|
+
for (const [k, v] of Object.entries(query)) {
|
|
110
|
+
if (v !== undefined && v !== null && v !== "") u.searchParams.set(k, v);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const payload = body ? Buffer.from(JSON.stringify(body)) : null;
|
|
114
|
+
const headers = { Accept: "application/json", "User-Agent": `argorant-cli/${VERSION}` };
|
|
115
|
+
if (key) headers["Authorization"] = `Bearer ${key}`;
|
|
116
|
+
if (payload) {
|
|
117
|
+
headers["Content-Type"] = "application/json";
|
|
118
|
+
headers["Content-Length"] = payload.length;
|
|
119
|
+
}
|
|
120
|
+
const lib = u.protocol === "http:" ? http : https;
|
|
121
|
+
const req = lib.request(
|
|
122
|
+
u,
|
|
123
|
+
{ method, headers },
|
|
124
|
+
(res) => {
|
|
125
|
+
const chunks = [];
|
|
126
|
+
res.on("data", (d) => chunks.push(d));
|
|
127
|
+
res.on("end", () => {
|
|
128
|
+
const raw = Buffer.concat(chunks).toString("utf8");
|
|
129
|
+
let json = null;
|
|
130
|
+
try {
|
|
131
|
+
json = raw ? JSON.parse(raw) : null;
|
|
132
|
+
} catch {
|
|
133
|
+
/* non-JSON (e.g. CSV download) */
|
|
134
|
+
}
|
|
135
|
+
resolve({ status: res.statusCode, json, raw, res });
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
);
|
|
139
|
+
req.on("error", reject);
|
|
140
|
+
if (payload) req.write(payload);
|
|
141
|
+
req.end();
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function downloadTo(base, urlPath, key, dest) {
|
|
146
|
+
return new Promise((resolve, reject) => {
|
|
147
|
+
const u = new URL(urlPath, base);
|
|
148
|
+
const lib = u.protocol === "http:" ? http : https;
|
|
149
|
+
const req = lib.request(
|
|
150
|
+
u,
|
|
151
|
+
{ method: "GET", headers: { Authorization: `Bearer ${key}`, "User-Agent": `argorant-cli/${VERSION}` } },
|
|
152
|
+
(res) => {
|
|
153
|
+
if (res.statusCode !== 200) {
|
|
154
|
+
const chunks = [];
|
|
155
|
+
res.on("data", (d) => chunks.push(d));
|
|
156
|
+
res.on("end", () => reject(new Error(`download failed (HTTP ${res.statusCode}): ${Buffer.concat(chunks).toString("utf8").slice(0, 300)}`)));
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const file = fs.createWriteStream(dest);
|
|
160
|
+
res.pipe(file);
|
|
161
|
+
file.on("finish", () => file.close(() => resolve(dest)));
|
|
162
|
+
file.on("error", reject);
|
|
163
|
+
}
|
|
164
|
+
);
|
|
165
|
+
req.on("error", reject);
|
|
166
|
+
req.end();
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function need(res, what) {
|
|
171
|
+
if (res.status === 401) die("not authenticated. Run `argorant login` or set ARGORANT_API_KEY.", 2);
|
|
172
|
+
if (res.status === 403) die((res.json && res.json.detail) || `forbidden — your key lacks the scope for ${what}.`, 3);
|
|
173
|
+
if (res.status === 429) die((res.json && res.json.detail) || "rate limit / daily quota reached.", 4);
|
|
174
|
+
if (res.status >= 400) die((res.json && res.json.detail) || `${what} failed (HTTP ${res.status}).`);
|
|
175
|
+
return res.json || {};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function requireKey() {
|
|
179
|
+
const k = resolveKey();
|
|
180
|
+
if (!k) die("no API key. Run `argorant login` or set ARGORANT_API_KEY.", 2);
|
|
181
|
+
return k;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function prompt(question, { hidden = false } = {}) {
|
|
185
|
+
return new Promise((resolve) => {
|
|
186
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: true });
|
|
187
|
+
if (hidden) {
|
|
188
|
+
// best-effort masking
|
|
189
|
+
const onData = () => {
|
|
190
|
+
readline.clearLine(process.stdout, 0);
|
|
191
|
+
readline.cursorTo(process.stdout, 0);
|
|
192
|
+
process.stdout.write(question);
|
|
193
|
+
};
|
|
194
|
+
process.stdin.on("data", onData);
|
|
195
|
+
rl.question(question, (ans) => {
|
|
196
|
+
process.stdin.removeListener("data", onData);
|
|
197
|
+
rl.close();
|
|
198
|
+
process.stdout.write("\n");
|
|
199
|
+
resolve(ans.trim());
|
|
200
|
+
});
|
|
201
|
+
} else {
|
|
202
|
+
rl.question(question, (ans) => {
|
|
203
|
+
rl.close();
|
|
204
|
+
resolve(ans.trim());
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// ---- commands ----
|
|
211
|
+
async function cmdLogin(args) {
|
|
212
|
+
let key = args._[0];
|
|
213
|
+
if (!key) key = await prompt("Paste your Argorant API key (ag_live_…): ", { hidden: true });
|
|
214
|
+
if (!key) die("no key provided.");
|
|
215
|
+
if (!/^ag_(live|test)_/.test(key)) process.stderr.write(dim("note: keys normally start with ag_live_ — continuing anyway.\n"));
|
|
216
|
+
const res = await request("GET", args.base, "/api/mcp/account", { key });
|
|
217
|
+
if (res.status === 401) die("that key was rejected (401). Double-check you copied the whole ag_live_ key.", 2);
|
|
218
|
+
const acct = need(res, "login");
|
|
219
|
+
saveConfig({ apiKey: key, base: args.base !== DEFAULT_BASE ? args.base : undefined });
|
|
220
|
+
console.log(green("✓") + ` Logged in as ${bold(acct.email || "your account")} ${dim("(" + (acct.role || "member") + ")")}`);
|
|
221
|
+
console.log(dim(`Key saved to ${CONFIG_PATH}`));
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
async function cmdWhoami(args) {
|
|
225
|
+
const key = requireKey();
|
|
226
|
+
const res = await request("GET", args.base, "/api/mcp/account", { key });
|
|
227
|
+
const a = need(res, "whoami");
|
|
228
|
+
if (args.json) return console.log(JSON.stringify(a, null, 2));
|
|
229
|
+
console.log(`${bold("Account")} ${a.email || "—"} ${dim("(" + (a.role || "member") + ")")}`);
|
|
230
|
+
console.log(`${bold("Scopes")} ${(a.scopes || []).join(", ") || "—"}`);
|
|
231
|
+
const u = a.usage || {};
|
|
232
|
+
const line = (label, k) => {
|
|
233
|
+
const x = u[k];
|
|
234
|
+
if (!x) return;
|
|
235
|
+
const lim = x.daily_limit == null ? "unlimited" : x.daily_limit;
|
|
236
|
+
console.log(` ${label.padEnd(8)} ${x.used ?? 0}/${lim} today`);
|
|
237
|
+
};
|
|
238
|
+
if (!u.unlimited) {
|
|
239
|
+
console.log(bold("Quota (today)"));
|
|
240
|
+
line("count", "count");
|
|
241
|
+
line("preview", "preview");
|
|
242
|
+
line("reveal", "reveal");
|
|
243
|
+
line("export", "export");
|
|
244
|
+
} else {
|
|
245
|
+
console.log(dim("Quota: unlimited"));
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
async function cmdCount(args) {
|
|
250
|
+
const key = requireKey();
|
|
251
|
+
const res = await request("GET", args.base, "/api/mcp/people/count", { key, query: args.filters });
|
|
252
|
+
const r = need(res, "count");
|
|
253
|
+
if (args.json) return console.log(JSON.stringify(r, null, 2));
|
|
254
|
+
console.log(bold(Number(r.count).toLocaleString()) + dim(" matching contacts"));
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
async function cmdSearch(args) {
|
|
258
|
+
const key = requireKey();
|
|
259
|
+
const query = { ...args.filters, limit: args.limit || 5 };
|
|
260
|
+
const res = await request("GET", args.base, "/api/mcp/people/preview", { key, query });
|
|
261
|
+
const r = need(res, "search");
|
|
262
|
+
if (args.json) return console.log(JSON.stringify(r, null, 2));
|
|
263
|
+
console.log(dim(`${Number(r.total).toLocaleString()} total · showing ${r.returned} (details redacted — use \`reveal\` or \`export\`)`));
|
|
264
|
+
for (const p of r.results || []) {
|
|
265
|
+
const who = [p.name, p.title].filter(Boolean).join(" · ");
|
|
266
|
+
const where = [p.company || p.company_name, p.country].filter(Boolean).join(", ");
|
|
267
|
+
console.log(` ${bold(who || "—")}${where ? dim(" " + where) : ""}`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
async function cmdReveal(args) {
|
|
272
|
+
const key = requireKey();
|
|
273
|
+
const limit = args.limit || 10;
|
|
274
|
+
if (!args.yes && !args.json && process.stdin.isTTY) {
|
|
275
|
+
const ans = await prompt(`Reveal up to ${bold(limit)} contacts? This uses your quota/credits. [y/N] `);
|
|
276
|
+
if (!/^y(es)?$/i.test(ans)) return console.log(dim("aborted."));
|
|
277
|
+
}
|
|
278
|
+
const query = { ...args.filters, limit };
|
|
279
|
+
const res = await request("GET", args.base, "/api/mcp/people/reveal", { key, query });
|
|
280
|
+
const r = need(res, "reveal");
|
|
281
|
+
if (args.json) return console.log(JSON.stringify(r, null, 2));
|
|
282
|
+
console.log(dim(`${Number(r.total).toLocaleString()} total · revealed ${r.returned}`));
|
|
283
|
+
for (const p of r.results || []) {
|
|
284
|
+
const who = [p.name, p.title].filter(Boolean).join(" · ");
|
|
285
|
+
console.log(` ${bold(who || "—")}`);
|
|
286
|
+
const bits = [p.email && cyan(p.email), p.phone, p.linkedin_url, [p.company || p.company_name, p.country].filter(Boolean).join(", ")].filter(Boolean);
|
|
287
|
+
if (bits.length) console.log(" " + bits.join(dim(" · ")));
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async function cmdExport(args) {
|
|
292
|
+
const key = requireKey();
|
|
293
|
+
const limit = args.limit || 1000;
|
|
294
|
+
const dest = args.output || "argorant-leads.csv";
|
|
295
|
+
if (!args.yes && !args.json && process.stdin.isTTY) {
|
|
296
|
+
const ans = await prompt(`Export up to ${bold(limit)} verified contacts to ${bold(dest)}? Uses quota/credits. [y/N] `);
|
|
297
|
+
if (!/^y(es)?$/i.test(ans)) return console.log(dim("aborted."));
|
|
298
|
+
}
|
|
299
|
+
const create = await request("POST", args.base, "/api/mcp/exports/create", { key, body: { limit, filters: args.filters } });
|
|
300
|
+
const job = need(create, "export");
|
|
301
|
+
const statusPath = job.status_api_path || (job.job_id ? `/api/mcp/exports/${job.job_id}` : null);
|
|
302
|
+
if (!statusPath) {
|
|
303
|
+
if (args.json) return console.log(JSON.stringify(job, null, 2));
|
|
304
|
+
return console.log("Export queued. " + JSON.stringify(job));
|
|
305
|
+
}
|
|
306
|
+
if (!args.json) process.stdout.write(dim("Export queued — verifying & building CSV"));
|
|
307
|
+
let downloadPath = job.download_api_path || null;
|
|
308
|
+
const started = Date.now();
|
|
309
|
+
// Poll until the job reports a terminal state.
|
|
310
|
+
/* eslint-disable no-constant-condition */
|
|
311
|
+
while (true) {
|
|
312
|
+
await new Promise((r) => setTimeout(r, 2500));
|
|
313
|
+
const st = await request("GET", args.base, statusPath, { key });
|
|
314
|
+
const s = need(st, "export status");
|
|
315
|
+
const status = (s.status || "").toLowerCase();
|
|
316
|
+
if (!args.json) process.stdout.write(".");
|
|
317
|
+
if (s.download_api_path) downloadPath = s.download_api_path;
|
|
318
|
+
if (["completed", "done", "ready", "succeeded"].includes(status) || s.downloadable) {
|
|
319
|
+
downloadPath = downloadPath || `/api/mcp/exports/${job.job_id}/download`;
|
|
320
|
+
break;
|
|
321
|
+
}
|
|
322
|
+
if (["failed", "error", "cancelled", "canceled"].includes(status)) die(`\nexport ${status}.`);
|
|
323
|
+
if (Date.now() - started > 1000 * 60 * 20) die("\nexport timed out after 20 minutes.");
|
|
324
|
+
}
|
|
325
|
+
if (!args.json) process.stdout.write("\n");
|
|
326
|
+
if (!downloadPath) {
|
|
327
|
+
if (args.json) return console.log(JSON.stringify(job, null, 2));
|
|
328
|
+
return console.log("Export ready but no download path returned. Check `argorant export-list`.");
|
|
329
|
+
}
|
|
330
|
+
await downloadTo(args.base, downloadPath, key, dest);
|
|
331
|
+
if (args.json) return console.log(JSON.stringify({ ok: true, file: dest, job_id: job.job_id }, null, 2));
|
|
332
|
+
const rows = (() => {
|
|
333
|
+
try {
|
|
334
|
+
return fs.readFileSync(dest, "utf8").split("\n").filter(Boolean).length - 1;
|
|
335
|
+
} catch {
|
|
336
|
+
return null;
|
|
337
|
+
}
|
|
338
|
+
})();
|
|
339
|
+
console.log(green("✓") + ` Saved ${rows != null ? bold(rows.toLocaleString()) + " rows → " : ""}${bold(dest)}`);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function help() {
|
|
343
|
+
const p = bold("argorant");
|
|
344
|
+
console.log(`
|
|
345
|
+
${bold("Argorant")} — verified B2B contacts from your terminal ${dim("v" + VERSION)}
|
|
346
|
+
|
|
347
|
+
${bold("USAGE")}
|
|
348
|
+
${p} <command> "<query>" [filters]
|
|
349
|
+
|
|
350
|
+
${bold("COMMANDS")}
|
|
351
|
+
${cyan("login")} [key] Save an API key (or set ARGORANT_API_KEY)
|
|
352
|
+
${cyan("whoami")} Account, scopes, and daily quota
|
|
353
|
+
${cyan("count")} "<query>" Count matching contacts ${dim("(free)")}
|
|
354
|
+
${cyan("search")} "<query>" -n 10 Preview matches, details redacted ${dim("(free)")}
|
|
355
|
+
${cyan("reveal")} "<query>" -n 25 Reveal full contact details ${dim("(uses quota)")}
|
|
356
|
+
${cyan("export")} "<query>" -n 1000 -o leads.csv Verified CSV export ${dim("(uses quota)")}
|
|
357
|
+
|
|
358
|
+
${bold("FILTERS")}
|
|
359
|
+
--title <t> --seniority <s> --department <d>
|
|
360
|
+
--industry <i> --country <c> --state <s> --city <c>
|
|
361
|
+
--company <name> --domain <domain> --verify-status <v>
|
|
362
|
+
--has-phone --has-linkedin --has-email
|
|
363
|
+
|
|
364
|
+
${bold("OPTIONS")}
|
|
365
|
+
-n, --limit <n> Max rows -o, --output <file> CSV path (export)
|
|
366
|
+
--json Raw JSON output -y, --yes Skip confirmations
|
|
367
|
+
--base <url> Override API base (or ARGORANT_API_BASE)
|
|
368
|
+
|
|
369
|
+
${bold("EXAMPLES")}
|
|
370
|
+
${p} count "fintech CFOs in germany"
|
|
371
|
+
${p} search "heads of procurement" --country Germany -n 10
|
|
372
|
+
${p} export --industry fintech --title CFO --country Germany -n 500 -o cfos.csv
|
|
373
|
+
|
|
374
|
+
Docs: ${cyan("https://argorant.com/docs/cli")}
|
|
375
|
+
`);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
async function main() {
|
|
379
|
+
const argv = process.argv.slice(2);
|
|
380
|
+
const cmd = argv[0];
|
|
381
|
+
if (!cmd || cmd === "help" || cmd === "--help" || cmd === "-h") return help();
|
|
382
|
+
if (cmd === "version" || cmd === "--version" || cmd === "-v") return console.log(VERSION);
|
|
383
|
+
const args = parseArgs(argv.slice(1));
|
|
384
|
+
// Allow a saved non-default base from login.
|
|
385
|
+
if (args.base === DEFAULT_BASE) {
|
|
386
|
+
const saved = loadConfig().base;
|
|
387
|
+
if (saved && !process.env.ARGORANT_API_BASE) args.base = saved;
|
|
388
|
+
}
|
|
389
|
+
const table = {
|
|
390
|
+
login: cmdLogin,
|
|
391
|
+
whoami: cmdWhoami,
|
|
392
|
+
count: cmdCount,
|
|
393
|
+
search: cmdSearch,
|
|
394
|
+
reveal: cmdReveal,
|
|
395
|
+
export: cmdExport,
|
|
396
|
+
};
|
|
397
|
+
const fn = table[cmd];
|
|
398
|
+
if (!fn) die(`unknown command: ${cmd}\nRun \`argorant help\` for usage.`);
|
|
399
|
+
try {
|
|
400
|
+
await fn(args);
|
|
401
|
+
} catch (e) {
|
|
402
|
+
die(e && e.message ? e.message : String(e));
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
main();
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "argorant",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Search, count, reveal, and export verified B2B contacts from the Argorant database — from your terminal, scripts, or coding agent.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"argorant": "bin/argorant.js"
|
|
7
|
+
},
|
|
8
|
+
"type": "commonjs",
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=16"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"bin",
|
|
14
|
+
"README.md"
|
|
15
|
+
],
|
|
16
|
+
"keywords": [
|
|
17
|
+
"argorant",
|
|
18
|
+
"b2b",
|
|
19
|
+
"leads",
|
|
20
|
+
"prospecting",
|
|
21
|
+
"email-verification",
|
|
22
|
+
"sales",
|
|
23
|
+
"contacts",
|
|
24
|
+
"cli"
|
|
25
|
+
],
|
|
26
|
+
"homepage": "https://argorant.com/docs/cli",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "https://argorant.com/docs/cli"
|
|
30
|
+
},
|
|
31
|
+
"license": "MIT"
|
|
32
|
+
}
|