@clawpro/node 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/dist/index.cjs +69 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -1
- package/dist/index.d.ts +24 -1
- package/dist/index.js +65 -19
- package/dist/index.js.map +1 -1
- package/package.json +29 -8
package/dist/index.cjs
CHANGED
|
@@ -21,9 +21,56 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
ClawPro: () => ClawPro,
|
|
24
|
-
ClawProError: () => ClawProError
|
|
24
|
+
ClawProError: () => ClawProError,
|
|
25
|
+
verifyWebhook: () => verifyWebhook
|
|
25
26
|
});
|
|
26
27
|
module.exports = __toCommonJS(index_exports);
|
|
28
|
+
|
|
29
|
+
// src/internal.ts
|
|
30
|
+
var import_node_crypto = require("crypto");
|
|
31
|
+
function parseSignature(header) {
|
|
32
|
+
const parts = {};
|
|
33
|
+
for (const p of header.split(",")) {
|
|
34
|
+
const i = p.indexOf("=");
|
|
35
|
+
if (i > 0) parts[p.slice(0, i).trim()] = p.slice(i + 1).trim();
|
|
36
|
+
}
|
|
37
|
+
const t = Number(parts.t);
|
|
38
|
+
if (!Number.isFinite(t) || t <= 0 || !parts.v1) return null;
|
|
39
|
+
return { t, v1: parts.v1 };
|
|
40
|
+
}
|
|
41
|
+
var signingString = (t, payload) => `${t}.${payload}`;
|
|
42
|
+
var hmacHex = (secret, message) => (0, import_node_crypto.createHmac)("sha256", secret).update(message).digest("hex");
|
|
43
|
+
function timestampFresh(t, toleranceSec, nowMs) {
|
|
44
|
+
return toleranceSec <= 0 || Math.abs(nowMs / 1e3 - t) <= toleranceSec;
|
|
45
|
+
}
|
|
46
|
+
function timingSafeEqualStr(a, b) {
|
|
47
|
+
const ba = Buffer.from(a);
|
|
48
|
+
const bb = Buffer.from(b);
|
|
49
|
+
return ba.length === bb.length && (0, import_node_crypto.timingSafeEqual)(ba, bb);
|
|
50
|
+
}
|
|
51
|
+
function buildUrl(base, path, query) {
|
|
52
|
+
const url = new URL(base + path);
|
|
53
|
+
if (query) {
|
|
54
|
+
for (const [k, v] of Object.entries(query)) {
|
|
55
|
+
if (v !== void 0 && v !== null && v !== "") url.searchParams.set(k, String(v));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return url.toString();
|
|
59
|
+
}
|
|
60
|
+
var isIdempotent = (method) => method === "GET" || method === "DELETE" || method === "PUT";
|
|
61
|
+
var shouldRetryNetwork = (method, attempt, maxRetries) => isIdempotent(method) && attempt < maxRetries;
|
|
62
|
+
function shouldRetryStatus(method, status, attempt, maxRetries) {
|
|
63
|
+
const transient = status === 429 || status >= 500;
|
|
64
|
+
const allowed = status === 429 || isIdempotent(method);
|
|
65
|
+
return transient && allowed && attempt < maxRetries;
|
|
66
|
+
}
|
|
67
|
+
var backoffMs = (attempt, rand) => Math.round(250 * 2 ** attempt * (0.8 + rand() * 0.4));
|
|
68
|
+
function retryDelayMs(retryAfter, attempt, rand) {
|
|
69
|
+
const ra = Number(retryAfter);
|
|
70
|
+
return Number.isFinite(ra) && ra > 0 ? ra * 1e3 : backoffMs(attempt, rand);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// src/index.ts
|
|
27
74
|
var ClawProError = class extends Error {
|
|
28
75
|
status;
|
|
29
76
|
code;
|
|
@@ -48,6 +95,8 @@ var ClawPro = class {
|
|
|
48
95
|
fetchImpl;
|
|
49
96
|
timeoutMs;
|
|
50
97
|
maxRetries;
|
|
98
|
+
sleep;
|
|
99
|
+
rand;
|
|
51
100
|
constructor(opts) {
|
|
52
101
|
if (!opts?.apiKey) throw new Error("ClawPro: `apiKey` is required");
|
|
53
102
|
this.apiKey = opts.apiKey;
|
|
@@ -57,6 +106,8 @@ var ClawPro = class {
|
|
|
57
106
|
if (!this.fetchImpl) throw new Error("ClawPro: no fetch available \u2014 pass `fetch` (Node <18)");
|
|
58
107
|
this.timeoutMs = opts.timeoutMs ?? 3e4;
|
|
59
108
|
this.maxRetries = opts.maxRetries ?? 2;
|
|
109
|
+
this.sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
110
|
+
this.rand = opts.rand ?? Math.random;
|
|
60
111
|
this.accounts = new AccountsResource(this);
|
|
61
112
|
this.campaigns = new CampaignsResource(this);
|
|
62
113
|
this.leads = new LeadsResource(this);
|
|
@@ -66,19 +117,13 @@ var ClawPro = class {
|
|
|
66
117
|
}
|
|
67
118
|
/** @internal */
|
|
68
119
|
async request(method, path, opts = {}) {
|
|
69
|
-
const url =
|
|
70
|
-
if (opts.query) {
|
|
71
|
-
for (const [k, v] of Object.entries(opts.query)) {
|
|
72
|
-
if (v !== void 0 && v !== null && v !== "") url.searchParams.set(k, String(v));
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
const idempotent = method === "GET" || method === "DELETE" || method === "PUT";
|
|
120
|
+
const url = buildUrl(this.base, path, opts.query);
|
|
76
121
|
for (let attempt = 0; ; attempt++) {
|
|
77
122
|
const ctrl = new AbortController();
|
|
78
123
|
const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
|
|
79
124
|
let res;
|
|
80
125
|
try {
|
|
81
|
-
res = await this.fetchImpl(url
|
|
126
|
+
res = await this.fetchImpl(url, {
|
|
82
127
|
method,
|
|
83
128
|
headers: {
|
|
84
129
|
"X-API-Key": this.apiKey,
|
|
@@ -90,21 +135,18 @@ var ClawPro = class {
|
|
|
90
135
|
});
|
|
91
136
|
} catch (err) {
|
|
92
137
|
clearTimeout(timer);
|
|
93
|
-
if (
|
|
94
|
-
await sleep(
|
|
138
|
+
if (shouldRetryNetwork(method, attempt, this.maxRetries)) {
|
|
139
|
+
await this.sleep(backoffMs(attempt, this.rand));
|
|
95
140
|
continue;
|
|
96
141
|
}
|
|
97
142
|
throw new ClawProError(`Network error: ${err.message}`, { status: 0, code: "network_error" });
|
|
98
143
|
}
|
|
99
144
|
clearTimeout(timer);
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
const retryable = res.status === 429 || idempotent;
|
|
103
|
-
if (transient && retryable && attempt < this.maxRetries) {
|
|
104
|
-
const ra = Number(res.headers.get("retry-after"));
|
|
105
|
-
await sleep(Number.isFinite(ra) && ra > 0 ? ra * 1e3 : backoff(attempt));
|
|
145
|
+
if (shouldRetryStatus(method, res.status, attempt, this.maxRetries)) {
|
|
146
|
+
await this.sleep(retryDelayMs(res.headers.get("retry-after"), attempt, this.rand));
|
|
106
147
|
continue;
|
|
107
148
|
}
|
|
149
|
+
const requestId = res.headers.get("x-request-id") ?? void 0;
|
|
108
150
|
const text = await res.text();
|
|
109
151
|
const data = text ? safeJson(text) : void 0;
|
|
110
152
|
if (!res.ok) {
|
|
@@ -115,8 +157,6 @@ var ClawPro = class {
|
|
|
115
157
|
}
|
|
116
158
|
}
|
|
117
159
|
};
|
|
118
|
-
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
119
|
-
var backoff = (attempt) => Math.round(250 * 2 ** attempt * (0.8 + Math.random() * 0.4));
|
|
120
160
|
function safeJson(text) {
|
|
121
161
|
try {
|
|
122
162
|
return JSON.parse(text);
|
|
@@ -246,9 +286,18 @@ var UsageResource = class {
|
|
|
246
286
|
}
|
|
247
287
|
}
|
|
248
288
|
};
|
|
289
|
+
function verifyWebhook(opts) {
|
|
290
|
+
const { payload, signature, secret, toleranceSec = 300 } = opts;
|
|
291
|
+
const parsed = parseSignature(signature);
|
|
292
|
+
if (!parsed) return false;
|
|
293
|
+
if (!timestampFresh(parsed.t, toleranceSec, Date.now())) return false;
|
|
294
|
+
const expected = hmacHex(secret, signingString(parsed.t, payload));
|
|
295
|
+
return timingSafeEqualStr(expected, parsed.v1);
|
|
296
|
+
}
|
|
249
297
|
// Annotate the CommonJS export names for ESM import in node:
|
|
250
298
|
0 && (module.exports = {
|
|
251
299
|
ClawPro,
|
|
252
|
-
ClawProError
|
|
300
|
+
ClawProError,
|
|
301
|
+
verifyWebhook
|
|
253
302
|
});
|
|
254
303
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @clawpro/node — official SDK for the ClawPro Instagram outbound API.\n *\n * ```ts\n * import { ClawPro } from '@clawpro/node'\n * const clawpro = new ClawPro({ apiKey: process.env.CLAWPRO_API_KEY! })\n * const { accounts } = await clawpro.accounts.list()\n * ```\n */\n\n// ─────────────────────────────── Types ───────────────────────────────────────\n\nexport type AccountStatus = 'new' | 'warming' | 'active' | 'challenged' | 'banned' | 'disconnected';\nexport type ProxyType = 'mobile' | 'isp' | 'residential' | 'none';\n\nexport interface Account {\n id: string;\n username: string;\n status: AccountStatus;\n warmupTier?: number;\n dailyDmCap?: number;\n}\n\nexport type CampaignStatus = 'draft' | 'active' | 'paused';\n\nexport interface CampaignTarget {\n username: string;\n lastCheckedPostId: string | null;\n}\n\nexport interface Campaign {\n id: string;\n accountId: string;\n name: string;\n status: CampaignStatus;\n targets: CampaignTarget[];\n offer: string;\n icpCriteria: string;\n dailyDmTarget: number;\n}\n\nexport type LeadStatus =\n | 'discovered' | 'enriched' | 'scored' | 'qualified' | 'rejected'\n | 'queued' | 'dmed' | 'replied' | 'booked';\n\nexport interface Message {\n id: string;\n direction: 'out' | 'in';\n body: string;\n status: string;\n sentAt: string | null;\n createdAt: string;\n}\n\nexport interface Lead {\n id: string;\n igUsername: string;\n fullName: string | null;\n sourceTargetUsername: string;\n engagementType: 'like' | 'comment';\n commentText: string | null;\n score: number | null;\n scoreReason: string | null;\n status: LeadStatus;\n messages?: Message[];\n}\n\nexport type WebhookType = 'generic' | 'slack';\nexport type WebhookEvent =\n | 'reply.received' | 'dm.sent' | 'dm.failed'\n | 'lead.qualified' | 'lead.booked'\n | 'account.active' | 'account.challenged' | 'account.banned';\n\nexport interface Webhook {\n id: string;\n label: string;\n url: string;\n type: WebhookType;\n events: (WebhookEvent | '*')[];\n active: boolean;\n lastStatus: number | null;\n lastDeliveryAt: string | null;\n lastError: string | null;\n /** Present only on the create response (generic HMAC secret). */\n secret?: string;\n}\n\nexport interface WebhookDelivery {\n id: string;\n event: string;\n status: 'pending' | 'delivered' | 'dead';\n attempts: number;\n lastStatusCode: number | null;\n lastError: string | null;\n nextRetryAt: string | null;\n deliveredAt: string | null;\n createdAt: string;\n}\n\nexport type ApiKeyEnv = 'production' | 'development';\nexport type ApiKeyAccess = 'full' | 'read' | 'write';\n\nexport interface ApiKey {\n id: string;\n name: string;\n environment: ApiKeyEnv;\n access: ApiKeyAccess;\n keyPrefix: string;\n createdAt: string;\n expiresAt: string | null;\n lastUsedAt: string | null;\n}\n\nexport interface UsageSummary {\n callsThisMonth: number;\n calls24h: number;\n errors: number;\n avgLatencyMs: number;\n}\n\nexport interface RequestLog {\n id: string;\n requestId: string;\n method: string;\n path: string;\n status: number;\n latencyMs: number;\n error: string | null;\n createdAt: string;\n keyName: string | null;\n keyPrefix: string | null;\n}\n\n// ─────────────────────────────── Errors ──────────────────────────────────────\n\n/** Thrown for any non-2xx API response. Inspect `status` / `requestId`. */\nexport class ClawProError extends Error {\n readonly status: number;\n readonly code: string;\n readonly requestId?: string;\n constructor(message: string, opts: { status: number; code?: string; requestId?: string }) {\n super(message);\n this.name = 'ClawProError';\n this.status = opts.status;\n this.code = opts.code ?? 'error';\n this.requestId = opts.requestId;\n }\n}\n\n// ─────────────────────────────── Client ──────────────────────────────────────\n\nexport interface ClawProOptions {\n /** A `sk_live_…` / `sk_test_…` key from the developer portal. Sent as `X-API-Key`. */\n apiKey: string;\n /** API origin (no trailing slash). Defaults to https://api.tryclawpro.com */\n baseUrl?: string;\n /** Override the fetch implementation (defaults to global fetch). */\n fetch?: typeof fetch;\n /** Per-request timeout in ms (default 30000). */\n timeoutMs?: number;\n /** Auto-retry transient failures (network, 429, 5xx) with exponential backoff.\n * Default 2. 429s honor the `Retry-After` header; only idempotent methods\n * (GET/DELETE/PUT) are retried on network/5xx. */\n maxRetries?: number;\n}\n\ntype Query = Record<string, string | number | boolean | undefined | null>;\n\nexport class ClawPro {\n readonly accounts: AccountsResource;\n readonly campaigns: CampaignsResource;\n readonly leads: LeadsResource;\n readonly webhooks: WebhooksResource;\n readonly keys: ApiKeysResource;\n readonly usage: UsageResource;\n\n private readonly apiKey: string;\n private readonly base: string;\n private readonly fetchImpl: typeof fetch;\n private readonly timeoutMs: number;\n private readonly maxRetries: number;\n\n constructor(opts: ClawProOptions) {\n if (!opts?.apiKey) throw new Error('ClawPro: `apiKey` is required');\n this.apiKey = opts.apiKey;\n const origin = (opts.baseUrl ?? 'https://api.tryclawpro.com').replace(/\\/$/, '');\n this.base = `${origin}/api/instagram`;\n this.fetchImpl = opts.fetch ?? globalThis.fetch;\n if (!this.fetchImpl) throw new Error('ClawPro: no fetch available — pass `fetch` (Node <18)');\n this.timeoutMs = opts.timeoutMs ?? 30_000;\n this.maxRetries = opts.maxRetries ?? 2;\n\n this.accounts = new AccountsResource(this);\n this.campaigns = new CampaignsResource(this);\n this.leads = new LeadsResource(this);\n this.webhooks = new WebhooksResource(this);\n this.keys = new ApiKeysResource(this);\n this.usage = new UsageResource(this);\n }\n\n /** @internal */\n async request<T>(method: string, path: string, opts: { body?: unknown; query?: Query } = {}): Promise<T> {\n const url = new URL(this.base + path);\n if (opts.query) {\n for (const [k, v] of Object.entries(opts.query)) {\n if (v !== undefined && v !== null && v !== '') url.searchParams.set(k, String(v));\n }\n }\n const idempotent = method === 'GET' || method === 'DELETE' || method === 'PUT';\n\n for (let attempt = 0; ; attempt++) {\n const ctrl = new AbortController();\n const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);\n let res: Response;\n try {\n res = await this.fetchImpl(url.toString(), {\n method,\n headers: {\n 'X-API-Key': this.apiKey,\n Accept: 'application/json',\n ...(opts.body !== undefined ? { 'Content-Type': 'application/json' } : {}),\n },\n body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined,\n signal: ctrl.signal,\n });\n } catch (err) {\n clearTimeout(timer);\n // Network/timeout: safe to retry only idempotent methods.\n if (idempotent && attempt < this.maxRetries) { await sleep(backoff(attempt)); continue; }\n throw new ClawProError(`Network error: ${(err as Error).message}`, { status: 0, code: 'network_error' });\n }\n clearTimeout(timer);\n\n const requestId = res.headers.get('x-request-id') ?? undefined;\n // Transient server-side: 429 (didn't process → retry any method) or 5xx (idempotent only).\n const transient = res.status === 429 || res.status >= 500;\n const retryable = res.status === 429 || idempotent;\n if (transient && retryable && attempt < this.maxRetries) {\n const ra = Number(res.headers.get('retry-after'));\n await sleep(Number.isFinite(ra) && ra > 0 ? ra * 1000 : backoff(attempt));\n continue;\n }\n\n const text = await res.text();\n const data = text ? safeJson(text) : undefined;\n if (!res.ok) {\n const message = (data && (data.message || data.error)) || `HTTP ${res.status}`;\n throw new ClawProError(message, { status: res.status, code: (data && data.error) || 'error', requestId });\n }\n return data as T;\n }\n }\n}\n\nconst sleep = (ms: number) => new Promise<void>((r) => setTimeout(r, ms));\n/** Exponential backoff with jitter: ~250ms, ~500ms, ~1s … */\nconst backoff = (attempt: number) => Math.round((250 * 2 ** attempt) * (0.8 + Math.random() * 0.4));\n\nfunction safeJson(text: string): any {\n try { return JSON.parse(text); } catch { return { raw: text }; }\n}\n\n// ───────────────────────────── Resources ─────────────────────────────────────\n\nclass AccountsResource {\n constructor(private c: ClawPro) {}\n /** List connected sending accounts. */\n list() { return this.c.request<{ accounts: Account[] }>('GET', '/accounts').then((r) => r.accounts); }\n /** Connect a new sending account (a geo-matched mobile proxy is assigned). */\n create(input: { username: string; country?: string; proxyType?: ProxyType; proxyUrl?: string; timezone?: string }) {\n return this.c.request<{ account: Account }>('POST', '/accounts', { body: input }).then((r) => r.account);\n }\n /** Remove an account (cascades to its campaigns/leads/messages). */\n delete(id: string) { return this.c.request<{ ok: true }>('DELETE', `/accounts/${id}`); }\n}\n\nclass CampaignsResource {\n constructor(private c: ClawPro) {}\n list() { return this.c.request<{ campaigns: Campaign[] }>('GET', '/campaigns').then((r) => r.campaigns); }\n create(input: { accountId: string; name: string; targets: string[]; offer?: string; icpCriteria?: string; dailyDmTarget?: number }) {\n return this.c.request<{ campaign: Campaign }>('POST', '/campaigns', { body: input }).then((r) => r.campaign);\n }\n update(id: string, patch: Partial<{ status: CampaignStatus; targets: string[]; offer: string; icpCriteria: string; dailyDmTarget: number; name: string }>) {\n return this.c.request<{ campaign: Campaign }>('PATCH', `/campaigns/${id}`, { body: patch }).then((r) => r.campaign);\n }\n /** Kick a discovery/scoring/queue run for a campaign. */\n run(id: string) { return this.c.request<{ discovered: number; scored: number; queued: number }>('POST', `/campaigns/${id}/run`); }\n /** Leads for a campaign, optionally filtered by status. */\n leads(id: string, opts: { status?: LeadStatus } = {}) {\n return this.c.request<{ leads: Lead[] }>('GET', `/campaigns/${id}/leads`, { query: { status: opts.status } }).then((r) => r.leads);\n }\n /** Threads with at least one message (the unified inbox). */\n inbox(id: string) { return this.c.request<{ threads: Lead[] }>('GET', `/campaigns/${id}/inbox`).then((r) => r.threads); }\n}\n\nclass LeadsResource {\n constructor(private c: ClawPro) {}\n /** Advance a lead — e.g. `{ status: 'booked' }` once a call is set (fires `lead.booked`). */\n update(id: string, patch: { status: 'qualified' | 'rejected' | 'booked' }) {\n return this.c.request<{ lead: Lead }>('PATCH', `/leads/${id}`, { body: patch }).then((r) => r.lead);\n }\n}\n\nclass WebhooksResource {\n constructor(private c: ClawPro) {}\n /** The catalog of subscribable event types. */\n events() { return this.c.request<{ events: WebhookEvent[] }>('GET', '/webhooks/events').then((r) => r.events); }\n list() { return this.c.request<{ webhooks: Webhook[] }>('GET', '/webhooks').then((r) => r.webhooks); }\n /** Create an endpoint. The generic HMAC `secret` is returned once, here. */\n create(input: { url: string; type?: WebhookType; events?: (WebhookEvent | '*')[]; label?: string }) {\n return this.c.request<{ webhook: Webhook }>('POST', '/webhooks', { body: { type: 'generic', events: ['*'], ...input } }).then((r) => r.webhook);\n }\n delete(id: string) { return this.c.request<{ ok: true }>('DELETE', `/webhooks/${id}`); }\n /** Send a `webhook.test` delivery. */\n test(id: string) { return this.c.request<unknown>('POST', `/webhooks/${id}/test`); }\n deliveries(id: string) { return this.c.request<{ deliveries: WebhookDelivery[] }>('GET', `/webhooks/${id}/deliveries`).then((r) => r.deliveries); }\n}\n\nclass ApiKeysResource {\n constructor(private c: ClawPro) {}\n list() { return this.c.request<{ keys: ApiKey[] }>('GET', '/keys').then((r) => r.keys); }\n /** Returns `{ secret, key }` — the plaintext `secret` is shown only here. */\n create(input: { name: string; environment?: ApiKeyEnv; access?: ApiKeyAccess; expiresAt?: string | null }) {\n return this.c.request<{ secret: string; key: ApiKey }>('POST', '/keys', { body: input });\n }\n revoke(id: string) { return this.c.request<{ ok: true }>('DELETE', `/keys/${id}`); }\n}\n\nexport interface LogFilter {\n key?: string; method?: string; status?: '2xx' | '4xx' | '5xx';\n from?: string; to?: string; endpoint?: string; requestId?: string; search?: string;\n limit?: number; offset?: number;\n}\n\nclass UsageResource {\n constructor(private c: ClawPro) {}\n /** Calls this month / 24h / errors / avg latency. */\n summary() { return this.c.request<UsageSummary>('GET', '/usage'); }\n /** A single page of request logs (`limit` + `offset`). */\n logs(filter: LogFilter = {}) {\n return this.c.request<{ logs: RequestLog[] }>('GET', '/logs', { query: filter as Query }).then((r) => r.logs);\n }\n /** Auto-paginate every matching log:\n * `for await (const log of clawpro.usage.iterateLogs()) { … }` */\n async *iterateLogs(filter: Omit<LogFilter, 'offset'> = {}): AsyncGenerator<RequestLog, void, unknown> {\n const pageSize = filter.limit ?? 100;\n for (let offset = 0; ; offset += pageSize) {\n const page = await this.logs({ ...filter, limit: pageSize, offset });\n for (const row of page) yield row;\n if (page.length < pageSize) return;\n }\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/internal.ts"],"sourcesContent":["/**\n * @clawpro/node — official SDK for the ClawPro Instagram outbound API.\n *\n * ```ts\n * import { ClawPro } from '@clawpro/node'\n * const clawpro = new ClawPro({ apiKey: process.env.CLAWPRO_API_KEY! })\n * const { accounts } = await clawpro.accounts.list()\n * ```\n */\n\nimport {\n parseSignature, timestampFresh, hmacHex, signingString, timingSafeEqualStr,\n buildUrl, shouldRetryNetwork, shouldRetryStatus, backoffMs, retryDelayMs,\n} from './internal';\n\n// ─────────────────────────────── Types ───────────────────────────────────────\n\nexport type AccountStatus = 'new' | 'warming' | 'active' | 'challenged' | 'banned' | 'disconnected';\nexport type ProxyType = 'mobile' | 'isp' | 'residential' | 'none';\n\nexport interface Account {\n id: string;\n username: string;\n status: AccountStatus;\n warmupTier?: number;\n dailyDmCap?: number;\n}\n\nexport type CampaignStatus = 'draft' | 'active' | 'paused';\n\nexport interface CampaignTarget {\n username: string;\n lastCheckedPostId: string | null;\n}\n\nexport interface Campaign {\n id: string;\n accountId: string;\n name: string;\n status: CampaignStatus;\n targets: CampaignTarget[];\n offer: string;\n icpCriteria: string;\n dailyDmTarget: number;\n}\n\nexport type LeadStatus =\n | 'discovered' | 'enriched' | 'scored' | 'qualified' | 'rejected'\n | 'queued' | 'dmed' | 'replied' | 'booked';\n\nexport interface Message {\n id: string;\n direction: 'out' | 'in';\n body: string;\n status: string;\n sentAt: string | null;\n createdAt: string;\n}\n\nexport interface Lead {\n id: string;\n igUsername: string;\n fullName: string | null;\n sourceTargetUsername: string;\n engagementType: 'like' | 'comment';\n commentText: string | null;\n score: number | null;\n scoreReason: string | null;\n status: LeadStatus;\n messages?: Message[];\n}\n\nexport type WebhookType = 'generic' | 'slack';\nexport type WebhookEvent =\n | 'reply.received' | 'dm.sent' | 'dm.failed'\n | 'lead.qualified' | 'lead.booked'\n | 'account.active' | 'account.challenged' | 'account.banned';\n\nexport interface Webhook {\n id: string;\n label: string;\n url: string;\n type: WebhookType;\n events: (WebhookEvent | '*')[];\n active: boolean;\n lastStatus: number | null;\n lastDeliveryAt: string | null;\n lastError: string | null;\n /** Present only on the create response (generic HMAC secret). */\n secret?: string;\n}\n\nexport interface WebhookDelivery {\n id: string;\n event: string;\n status: 'pending' | 'delivered' | 'dead';\n attempts: number;\n lastStatusCode: number | null;\n lastError: string | null;\n nextRetryAt: string | null;\n deliveredAt: string | null;\n createdAt: string;\n}\n\nexport type ApiKeyEnv = 'production' | 'development';\nexport type ApiKeyAccess = 'full' | 'read' | 'write';\n\nexport interface ApiKey {\n id: string;\n name: string;\n environment: ApiKeyEnv;\n access: ApiKeyAccess;\n keyPrefix: string;\n createdAt: string;\n expiresAt: string | null;\n lastUsedAt: string | null;\n}\n\nexport interface UsageSummary {\n callsThisMonth: number;\n calls24h: number;\n errors: number;\n avgLatencyMs: number;\n}\n\nexport interface RequestLog {\n id: string;\n requestId: string;\n method: string;\n path: string;\n status: number;\n latencyMs: number;\n error: string | null;\n createdAt: string;\n keyName: string | null;\n keyPrefix: string | null;\n}\n\n// ─────────────────────────────── Errors ──────────────────────────────────────\n\n/** Thrown for any non-2xx API response. Inspect `status` / `requestId`. */\nexport class ClawProError extends Error {\n readonly status: number;\n readonly code: string;\n readonly requestId?: string;\n constructor(message: string, opts: { status: number; code?: string; requestId?: string }) {\n super(message);\n this.name = 'ClawProError';\n this.status = opts.status;\n this.code = opts.code ?? 'error';\n this.requestId = opts.requestId;\n }\n}\n\n// ─────────────────────────────── Client ──────────────────────────────────────\n\nexport interface ClawProOptions {\n /** A `sk_live_…` / `sk_test_…` key from the developer portal. Sent as `X-API-Key`. */\n apiKey: string;\n /** API origin (no trailing slash). Defaults to https://api.tryclawpro.com */\n baseUrl?: string;\n /** Override the fetch implementation (defaults to global fetch). */\n fetch?: typeof fetch;\n /** Per-request timeout in ms (default 30000). */\n timeoutMs?: number;\n /** Auto-retry transient failures (network, 429, 5xx) with exponential backoff.\n * Default 2. 429s honor the `Retry-After` header; only idempotent methods\n * (GET/DELETE/PUT) are retried on network/5xx. */\n maxRetries?: number;\n /** Injected for determinism/testing — defaults to a real timer. */\n sleep?: (ms: number) => Promise<void>;\n /** Injected for determinism/testing — defaults to Math.random. */\n rand?: () => number;\n}\n\ntype Query = Record<string, string | number | boolean | undefined | null>;\n\nexport class ClawPro {\n readonly accounts: AccountsResource;\n readonly campaigns: CampaignsResource;\n readonly leads: LeadsResource;\n readonly webhooks: WebhooksResource;\n readonly keys: ApiKeysResource;\n readonly usage: UsageResource;\n\n private readonly apiKey: string;\n private readonly base: string;\n private readonly fetchImpl: typeof fetch;\n private readonly timeoutMs: number;\n private readonly maxRetries: number;\n private readonly sleep: (ms: number) => Promise<void>;\n private readonly rand: () => number;\n\n constructor(opts: ClawProOptions) {\n if (!opts?.apiKey) throw new Error('ClawPro: `apiKey` is required');\n this.apiKey = opts.apiKey;\n const origin = (opts.baseUrl ?? 'https://api.tryclawpro.com').replace(/\\/$/, '');\n this.base = `${origin}/api/instagram`;\n this.fetchImpl = opts.fetch ?? globalThis.fetch;\n if (!this.fetchImpl) throw new Error('ClawPro: no fetch available — pass `fetch` (Node <18)');\n this.timeoutMs = opts.timeoutMs ?? 30_000;\n this.maxRetries = opts.maxRetries ?? 2;\n this.sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));\n this.rand = opts.rand ?? Math.random;\n\n this.accounts = new AccountsResource(this);\n this.campaigns = new CampaignsResource(this);\n this.leads = new LeadsResource(this);\n this.webhooks = new WebhooksResource(this);\n this.keys = new ApiKeysResource(this);\n this.usage = new UsageResource(this);\n }\n\n /** @internal */\n async request<T>(method: string, path: string, opts: { body?: unknown; query?: Query } = {}): Promise<T> {\n const url = buildUrl(this.base, path, opts.query);\n\n for (let attempt = 0; ; attempt++) {\n const ctrl = new AbortController();\n const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);\n let res: Response;\n try {\n res = await this.fetchImpl(url, {\n method,\n headers: {\n 'X-API-Key': this.apiKey,\n Accept: 'application/json',\n ...(opts.body !== undefined ? { 'Content-Type': 'application/json' } : {}),\n },\n body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined,\n signal: ctrl.signal,\n });\n } catch (err) {\n clearTimeout(timer);\n if (shouldRetryNetwork(method, attempt, this.maxRetries)) {\n await this.sleep(backoffMs(attempt, this.rand));\n continue;\n }\n throw new ClawProError(`Network error: ${(err as Error).message}`, { status: 0, code: 'network_error' });\n }\n clearTimeout(timer);\n\n if (shouldRetryStatus(method, res.status, attempt, this.maxRetries)) {\n await this.sleep(retryDelayMs(res.headers.get('retry-after'), attempt, this.rand));\n continue;\n }\n\n const requestId = res.headers.get('x-request-id') ?? undefined;\n const text = await res.text();\n const data = text ? safeJson(text) : undefined;\n if (!res.ok) {\n const message = (data && (data.message || data.error)) || `HTTP ${res.status}`;\n throw new ClawProError(message, { status: res.status, code: (data && data.error) || 'error', requestId });\n }\n return data as T;\n }\n }\n}\n\nfunction safeJson(text: string): any {\n try { return JSON.parse(text); } catch { return { raw: text }; }\n}\n\n// ───────────────────────────── Resources ─────────────────────────────────────\n\nclass AccountsResource {\n constructor(private c: ClawPro) {}\n /** List connected sending accounts. */\n list() { return this.c.request<{ accounts: Account[] }>('GET', '/accounts').then((r) => r.accounts); }\n /** Connect a new sending account (a geo-matched mobile proxy is assigned). */\n create(input: { username: string; country?: string; proxyType?: ProxyType; proxyUrl?: string; timezone?: string }) {\n return this.c.request<{ account: Account }>('POST', '/accounts', { body: input }).then((r) => r.account);\n }\n /** Remove an account (cascades to its campaigns/leads/messages). */\n delete(id: string) { return this.c.request<{ ok: true }>('DELETE', `/accounts/${id}`); }\n}\n\nclass CampaignsResource {\n constructor(private c: ClawPro) {}\n list() { return this.c.request<{ campaigns: Campaign[] }>('GET', '/campaigns').then((r) => r.campaigns); }\n create(input: { accountId: string; name: string; targets: string[]; offer?: string; icpCriteria?: string; dailyDmTarget?: number }) {\n return this.c.request<{ campaign: Campaign }>('POST', '/campaigns', { body: input }).then((r) => r.campaign);\n }\n update(id: string, patch: Partial<{ status: CampaignStatus; targets: string[]; offer: string; icpCriteria: string; dailyDmTarget: number; name: string }>) {\n return this.c.request<{ campaign: Campaign }>('PATCH', `/campaigns/${id}`, { body: patch }).then((r) => r.campaign);\n }\n /** Kick a discovery/scoring/queue run for a campaign. */\n run(id: string) { return this.c.request<{ discovered: number; scored: number; queued: number }>('POST', `/campaigns/${id}/run`); }\n /** Leads for a campaign, optionally filtered by status. */\n leads(id: string, opts: { status?: LeadStatus } = {}) {\n return this.c.request<{ leads: Lead[] }>('GET', `/campaigns/${id}/leads`, { query: { status: opts.status } }).then((r) => r.leads);\n }\n /** Threads with at least one message (the unified inbox). */\n inbox(id: string) { return this.c.request<{ threads: Lead[] }>('GET', `/campaigns/${id}/inbox`).then((r) => r.threads); }\n}\n\nclass LeadsResource {\n constructor(private c: ClawPro) {}\n /** Advance a lead — e.g. `{ status: 'booked' }` once a call is set (fires `lead.booked`). */\n update(id: string, patch: { status: 'qualified' | 'rejected' | 'booked' }) {\n return this.c.request<{ lead: Lead }>('PATCH', `/leads/${id}`, { body: patch }).then((r) => r.lead);\n }\n}\n\nclass WebhooksResource {\n constructor(private c: ClawPro) {}\n /** The catalog of subscribable event types. */\n events() { return this.c.request<{ events: WebhookEvent[] }>('GET', '/webhooks/events').then((r) => r.events); }\n list() { return this.c.request<{ webhooks: Webhook[] }>('GET', '/webhooks').then((r) => r.webhooks); }\n /** Create an endpoint. The generic HMAC `secret` is returned once, here. */\n create(input: { url: string; type?: WebhookType; events?: (WebhookEvent | '*')[]; label?: string }) {\n return this.c.request<{ webhook: Webhook }>('POST', '/webhooks', { body: { type: 'generic', events: ['*'], ...input } }).then((r) => r.webhook);\n }\n delete(id: string) { return this.c.request<{ ok: true }>('DELETE', `/webhooks/${id}`); }\n /** Send a `webhook.test` delivery. */\n test(id: string) { return this.c.request<unknown>('POST', `/webhooks/${id}/test`); }\n deliveries(id: string) { return this.c.request<{ deliveries: WebhookDelivery[] }>('GET', `/webhooks/${id}/deliveries`).then((r) => r.deliveries); }\n}\n\nclass ApiKeysResource {\n constructor(private c: ClawPro) {}\n list() { return this.c.request<{ keys: ApiKey[] }>('GET', '/keys').then((r) => r.keys); }\n /** Returns `{ secret, key }` — the plaintext `secret` is shown only here. */\n create(input: { name: string; environment?: ApiKeyEnv; access?: ApiKeyAccess; expiresAt?: string | null }) {\n return this.c.request<{ secret: string; key: ApiKey }>('POST', '/keys', { body: input });\n }\n revoke(id: string) { return this.c.request<{ ok: true }>('DELETE', `/keys/${id}`); }\n}\n\nexport interface LogFilter {\n key?: string; method?: string; status?: '2xx' | '4xx' | '5xx';\n from?: string; to?: string; endpoint?: string; requestId?: string; search?: string;\n limit?: number; offset?: number;\n}\n\nclass UsageResource {\n constructor(private c: ClawPro) {}\n /** Calls this month / 24h / errors / avg latency. */\n summary() { return this.c.request<UsageSummary>('GET', '/usage'); }\n /** A single page of request logs (`limit` + `offset`). */\n logs(filter: LogFilter = {}) {\n return this.c.request<{ logs: RequestLog[] }>('GET', '/logs', { query: filter as Query }).then((r) => r.logs);\n }\n /** Auto-paginate every matching log:\n * `for await (const log of clawpro.usage.iterateLogs()) { … }` */\n async *iterateLogs(filter: Omit<LogFilter, 'offset'> = {}): AsyncGenerator<RequestLog, void, unknown> {\n const pageSize = filter.limit ?? 100;\n for (let offset = 0; ; offset += pageSize) {\n const page = await this.logs({ ...filter, limit: pageSize, offset });\n for (const row of page) yield row;\n if (page.length < pageSize) return;\n }\n }\n}\n\n// ─────────────────────────── Webhook verification ────────────────────────────\n\nexport interface VerifyWebhookOptions {\n /** The raw request body, exactly as received (string). */\n payload: string;\n /** The `X-Souk-Signature` header value (`t=…,v1=…`). */\n signature: string;\n /** The endpoint's signing secret (shown once on webhook creation). */\n secret: string;\n /** Reject signatures older than this many seconds (default 300; 0 disables). */\n toleranceSec?: number;\n}\n\n/** Verify a ClawPro webhook's HMAC signature (timing-safe). Returns true if valid.\n *\n * ```ts\n * if (!verifyWebhook({ payload: rawBody, signature: req.headers['x-souk-signature'], secret })) return res.sendStatus(400)\n * ```\n */\nexport function verifyWebhook(opts: VerifyWebhookOptions): boolean {\n const { payload, signature, secret, toleranceSec = 300 } = opts;\n const parsed = parseSignature(signature);\n if (!parsed) return false;\n if (!timestampFresh(parsed.t, toleranceSec, Date.now())) return false;\n const expected = hmacHex(secret, signingString(parsed.t, payload));\n return timingSafeEqualStr(expected, parsed.v1);\n}\n","/**\n * Pure, total helpers — the testable core of the SDK. No I/O, no throwing\n * (except the deliberately-effectful `hmacHex`, which is referentially\n * transparent: same inputs → same digest). The client (index.ts) is the thin\n * effectful shell that threads `fetch`, sleep, and randomness around these.\n */\nimport { createHmac, timingSafeEqual } from 'node:crypto';\n\n// ── Webhook signatures ───────────────────────────────────────────────────────\nexport interface ParsedSignature { t: number; v1: string }\n\n/** Parse `t=<unix>,v1=<hex>`. Returns null for anything malformed (total). */\nexport function parseSignature(header: string): ParsedSignature | null {\n const parts: Record<string, string> = {};\n for (const p of header.split(',')) {\n const i = p.indexOf('=');\n if (i > 0) parts[p.slice(0, i).trim()] = p.slice(i + 1).trim();\n }\n const t = Number(parts.t);\n if (!Number.isFinite(t) || t <= 0 || !parts.v1) return null;\n return { t, v1: parts.v1 };\n}\n\nexport const signingString = (t: number, payload: string): string => `${t}.${payload}`;\n\nexport const hmacHex = (secret: string, message: string): string =>\n createHmac('sha256', secret).update(message).digest('hex');\n\n/** Is `t` within `toleranceSec` of `nowMs`? `toleranceSec <= 0` disables. */\nexport function timestampFresh(t: number, toleranceSec: number, nowMs: number): boolean {\n return toleranceSec <= 0 || Math.abs(nowMs / 1000 - t) <= toleranceSec;\n}\n\n/** Constant-time string equality (length-aware). */\nexport function timingSafeEqualStr(a: string, b: string): boolean {\n const ba = Buffer.from(a);\n const bb = Buffer.from(b);\n return ba.length === bb.length && timingSafeEqual(ba, bb);\n}\n\n// ── URL building ─────────────────────────────────────────────────────────────\nexport type QueryValue = string | number | boolean | undefined | null;\n\nexport function buildUrl(base: string, path: string, query?: Record<string, QueryValue>): string {\n const url = new URL(base + path);\n if (query) {\n for (const [k, v] of Object.entries(query)) {\n if (v !== undefined && v !== null && v !== '') url.searchParams.set(k, String(v));\n }\n }\n return url.toString();\n}\n\n// ── Retry decisions (pure) ───────────────────────────────────────────────────\nexport const isIdempotent = (method: string): boolean =>\n method === 'GET' || method === 'DELETE' || method === 'PUT';\n\n/** Network/timeout error: retry only idempotent methods, within budget. */\nexport const shouldRetryNetwork = (method: string, attempt: number, maxRetries: number): boolean =>\n isIdempotent(method) && attempt < maxRetries;\n\n/** A 429 (didn't process → any method) or 5xx (idempotent only), within budget. */\nexport function shouldRetryStatus(method: string, status: number, attempt: number, maxRetries: number): boolean {\n const transient = status === 429 || status >= 500;\n const allowed = status === 429 || isIdempotent(method);\n return transient && allowed && attempt < maxRetries;\n}\n\n/** Exponential backoff with jitter: ~250ms · 2^attempt · [0.8,1.2). `rand` injected. */\nexport const backoffMs = (attempt: number, rand: () => number): number =>\n Math.round(250 * 2 ** attempt * (0.8 + rand() * 0.4));\n\n/** Honor a numeric `Retry-After` (seconds), else fall back to backoff. */\nexport function retryDelayMs(retryAfter: string | null, attempt: number, rand: () => number): number {\n const ra = Number(retryAfter);\n return Number.isFinite(ra) && ra > 0 ? ra * 1000 : backoffMs(attempt, rand);\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
CHANGED
|
@@ -132,6 +132,10 @@ interface ClawProOptions {
|
|
|
132
132
|
* Default 2. 429s honor the `Retry-After` header; only idempotent methods
|
|
133
133
|
* (GET/DELETE/PUT) are retried on network/5xx. */
|
|
134
134
|
maxRetries?: number;
|
|
135
|
+
/** Injected for determinism/testing — defaults to a real timer. */
|
|
136
|
+
sleep?: (ms: number) => Promise<void>;
|
|
137
|
+
/** Injected for determinism/testing — defaults to Math.random. */
|
|
138
|
+
rand?: () => number;
|
|
135
139
|
}
|
|
136
140
|
type Query = Record<string, string | number | boolean | undefined | null>;
|
|
137
141
|
declare class ClawPro {
|
|
@@ -146,6 +150,8 @@ declare class ClawPro {
|
|
|
146
150
|
private readonly fetchImpl;
|
|
147
151
|
private readonly timeoutMs;
|
|
148
152
|
private readonly maxRetries;
|
|
153
|
+
private readonly sleep;
|
|
154
|
+
private readonly rand;
|
|
149
155
|
constructor(opts: ClawProOptions);
|
|
150
156
|
/** @internal */
|
|
151
157
|
request<T>(method: string, path: string, opts?: {
|
|
@@ -273,5 +279,22 @@ declare class UsageResource {
|
|
|
273
279
|
* `for await (const log of clawpro.usage.iterateLogs()) { … }` */
|
|
274
280
|
iterateLogs(filter?: Omit<LogFilter, 'offset'>): AsyncGenerator<RequestLog, void, unknown>;
|
|
275
281
|
}
|
|
282
|
+
interface VerifyWebhookOptions {
|
|
283
|
+
/** The raw request body, exactly as received (string). */
|
|
284
|
+
payload: string;
|
|
285
|
+
/** The `X-Souk-Signature` header value (`t=…,v1=…`). */
|
|
286
|
+
signature: string;
|
|
287
|
+
/** The endpoint's signing secret (shown once on webhook creation). */
|
|
288
|
+
secret: string;
|
|
289
|
+
/** Reject signatures older than this many seconds (default 300; 0 disables). */
|
|
290
|
+
toleranceSec?: number;
|
|
291
|
+
}
|
|
292
|
+
/** Verify a ClawPro webhook's HMAC signature (timing-safe). Returns true if valid.
|
|
293
|
+
*
|
|
294
|
+
* ```ts
|
|
295
|
+
* if (!verifyWebhook({ payload: rawBody, signature: req.headers['x-souk-signature'], secret })) return res.sendStatus(400)
|
|
296
|
+
* ```
|
|
297
|
+
*/
|
|
298
|
+
declare function verifyWebhook(opts: VerifyWebhookOptions): boolean;
|
|
276
299
|
|
|
277
|
-
export { type Account, type AccountStatus, type ApiKey, type ApiKeyAccess, type ApiKeyEnv, type Campaign, type CampaignStatus, type CampaignTarget, ClawPro, ClawProError, type ClawProOptions, type Lead, type LeadStatus, type LogFilter, type Message, type ProxyType, type RequestLog, type UsageSummary, type Webhook, type WebhookDelivery, type WebhookEvent, type WebhookType };
|
|
300
|
+
export { type Account, type AccountStatus, type ApiKey, type ApiKeyAccess, type ApiKeyEnv, type Campaign, type CampaignStatus, type CampaignTarget, ClawPro, ClawProError, type ClawProOptions, type Lead, type LeadStatus, type LogFilter, type Message, type ProxyType, type RequestLog, type UsageSummary, type VerifyWebhookOptions, type Webhook, type WebhookDelivery, type WebhookEvent, type WebhookType, verifyWebhook };
|
package/dist/index.d.ts
CHANGED
|
@@ -132,6 +132,10 @@ interface ClawProOptions {
|
|
|
132
132
|
* Default 2. 429s honor the `Retry-After` header; only idempotent methods
|
|
133
133
|
* (GET/DELETE/PUT) are retried on network/5xx. */
|
|
134
134
|
maxRetries?: number;
|
|
135
|
+
/** Injected for determinism/testing — defaults to a real timer. */
|
|
136
|
+
sleep?: (ms: number) => Promise<void>;
|
|
137
|
+
/** Injected for determinism/testing — defaults to Math.random. */
|
|
138
|
+
rand?: () => number;
|
|
135
139
|
}
|
|
136
140
|
type Query = Record<string, string | number | boolean | undefined | null>;
|
|
137
141
|
declare class ClawPro {
|
|
@@ -146,6 +150,8 @@ declare class ClawPro {
|
|
|
146
150
|
private readonly fetchImpl;
|
|
147
151
|
private readonly timeoutMs;
|
|
148
152
|
private readonly maxRetries;
|
|
153
|
+
private readonly sleep;
|
|
154
|
+
private readonly rand;
|
|
149
155
|
constructor(opts: ClawProOptions);
|
|
150
156
|
/** @internal */
|
|
151
157
|
request<T>(method: string, path: string, opts?: {
|
|
@@ -273,5 +279,22 @@ declare class UsageResource {
|
|
|
273
279
|
* `for await (const log of clawpro.usage.iterateLogs()) { … }` */
|
|
274
280
|
iterateLogs(filter?: Omit<LogFilter, 'offset'>): AsyncGenerator<RequestLog, void, unknown>;
|
|
275
281
|
}
|
|
282
|
+
interface VerifyWebhookOptions {
|
|
283
|
+
/** The raw request body, exactly as received (string). */
|
|
284
|
+
payload: string;
|
|
285
|
+
/** The `X-Souk-Signature` header value (`t=…,v1=…`). */
|
|
286
|
+
signature: string;
|
|
287
|
+
/** The endpoint's signing secret (shown once on webhook creation). */
|
|
288
|
+
secret: string;
|
|
289
|
+
/** Reject signatures older than this many seconds (default 300; 0 disables). */
|
|
290
|
+
toleranceSec?: number;
|
|
291
|
+
}
|
|
292
|
+
/** Verify a ClawPro webhook's HMAC signature (timing-safe). Returns true if valid.
|
|
293
|
+
*
|
|
294
|
+
* ```ts
|
|
295
|
+
* if (!verifyWebhook({ payload: rawBody, signature: req.headers['x-souk-signature'], secret })) return res.sendStatus(400)
|
|
296
|
+
* ```
|
|
297
|
+
*/
|
|
298
|
+
declare function verifyWebhook(opts: VerifyWebhookOptions): boolean;
|
|
276
299
|
|
|
277
|
-
export { type Account, type AccountStatus, type ApiKey, type ApiKeyAccess, type ApiKeyEnv, type Campaign, type CampaignStatus, type CampaignTarget, ClawPro, ClawProError, type ClawProOptions, type Lead, type LeadStatus, type LogFilter, type Message, type ProxyType, type RequestLog, type UsageSummary, type Webhook, type WebhookDelivery, type WebhookEvent, type WebhookType };
|
|
300
|
+
export { type Account, type AccountStatus, type ApiKey, type ApiKeyAccess, type ApiKeyEnv, type Campaign, type CampaignStatus, type CampaignTarget, ClawPro, ClawProError, type ClawProOptions, type Lead, type LeadStatus, type LogFilter, type Message, type ProxyType, type RequestLog, type UsageSummary, type VerifyWebhookOptions, type Webhook, type WebhookDelivery, type WebhookEvent, type WebhookType, verifyWebhook };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,47 @@
|
|
|
1
|
+
// src/internal.ts
|
|
2
|
+
import { createHmac, timingSafeEqual } from "crypto";
|
|
3
|
+
function parseSignature(header) {
|
|
4
|
+
const parts = {};
|
|
5
|
+
for (const p of header.split(",")) {
|
|
6
|
+
const i = p.indexOf("=");
|
|
7
|
+
if (i > 0) parts[p.slice(0, i).trim()] = p.slice(i + 1).trim();
|
|
8
|
+
}
|
|
9
|
+
const t = Number(parts.t);
|
|
10
|
+
if (!Number.isFinite(t) || t <= 0 || !parts.v1) return null;
|
|
11
|
+
return { t, v1: parts.v1 };
|
|
12
|
+
}
|
|
13
|
+
var signingString = (t, payload) => `${t}.${payload}`;
|
|
14
|
+
var hmacHex = (secret, message) => createHmac("sha256", secret).update(message).digest("hex");
|
|
15
|
+
function timestampFresh(t, toleranceSec, nowMs) {
|
|
16
|
+
return toleranceSec <= 0 || Math.abs(nowMs / 1e3 - t) <= toleranceSec;
|
|
17
|
+
}
|
|
18
|
+
function timingSafeEqualStr(a, b) {
|
|
19
|
+
const ba = Buffer.from(a);
|
|
20
|
+
const bb = Buffer.from(b);
|
|
21
|
+
return ba.length === bb.length && timingSafeEqual(ba, bb);
|
|
22
|
+
}
|
|
23
|
+
function buildUrl(base, path, query) {
|
|
24
|
+
const url = new URL(base + path);
|
|
25
|
+
if (query) {
|
|
26
|
+
for (const [k, v] of Object.entries(query)) {
|
|
27
|
+
if (v !== void 0 && v !== null && v !== "") url.searchParams.set(k, String(v));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return url.toString();
|
|
31
|
+
}
|
|
32
|
+
var isIdempotent = (method) => method === "GET" || method === "DELETE" || method === "PUT";
|
|
33
|
+
var shouldRetryNetwork = (method, attempt, maxRetries) => isIdempotent(method) && attempt < maxRetries;
|
|
34
|
+
function shouldRetryStatus(method, status, attempt, maxRetries) {
|
|
35
|
+
const transient = status === 429 || status >= 500;
|
|
36
|
+
const allowed = status === 429 || isIdempotent(method);
|
|
37
|
+
return transient && allowed && attempt < maxRetries;
|
|
38
|
+
}
|
|
39
|
+
var backoffMs = (attempt, rand) => Math.round(250 * 2 ** attempt * (0.8 + rand() * 0.4));
|
|
40
|
+
function retryDelayMs(retryAfter, attempt, rand) {
|
|
41
|
+
const ra = Number(retryAfter);
|
|
42
|
+
return Number.isFinite(ra) && ra > 0 ? ra * 1e3 : backoffMs(attempt, rand);
|
|
43
|
+
}
|
|
44
|
+
|
|
1
45
|
// src/index.ts
|
|
2
46
|
var ClawProError = class extends Error {
|
|
3
47
|
status;
|
|
@@ -23,6 +67,8 @@ var ClawPro = class {
|
|
|
23
67
|
fetchImpl;
|
|
24
68
|
timeoutMs;
|
|
25
69
|
maxRetries;
|
|
70
|
+
sleep;
|
|
71
|
+
rand;
|
|
26
72
|
constructor(opts) {
|
|
27
73
|
if (!opts?.apiKey) throw new Error("ClawPro: `apiKey` is required");
|
|
28
74
|
this.apiKey = opts.apiKey;
|
|
@@ -32,6 +78,8 @@ var ClawPro = class {
|
|
|
32
78
|
if (!this.fetchImpl) throw new Error("ClawPro: no fetch available \u2014 pass `fetch` (Node <18)");
|
|
33
79
|
this.timeoutMs = opts.timeoutMs ?? 3e4;
|
|
34
80
|
this.maxRetries = opts.maxRetries ?? 2;
|
|
81
|
+
this.sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
82
|
+
this.rand = opts.rand ?? Math.random;
|
|
35
83
|
this.accounts = new AccountsResource(this);
|
|
36
84
|
this.campaigns = new CampaignsResource(this);
|
|
37
85
|
this.leads = new LeadsResource(this);
|
|
@@ -41,19 +89,13 @@ var ClawPro = class {
|
|
|
41
89
|
}
|
|
42
90
|
/** @internal */
|
|
43
91
|
async request(method, path, opts = {}) {
|
|
44
|
-
const url =
|
|
45
|
-
if (opts.query) {
|
|
46
|
-
for (const [k, v] of Object.entries(opts.query)) {
|
|
47
|
-
if (v !== void 0 && v !== null && v !== "") url.searchParams.set(k, String(v));
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
const idempotent = method === "GET" || method === "DELETE" || method === "PUT";
|
|
92
|
+
const url = buildUrl(this.base, path, opts.query);
|
|
51
93
|
for (let attempt = 0; ; attempt++) {
|
|
52
94
|
const ctrl = new AbortController();
|
|
53
95
|
const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
|
|
54
96
|
let res;
|
|
55
97
|
try {
|
|
56
|
-
res = await this.fetchImpl(url
|
|
98
|
+
res = await this.fetchImpl(url, {
|
|
57
99
|
method,
|
|
58
100
|
headers: {
|
|
59
101
|
"X-API-Key": this.apiKey,
|
|
@@ -65,21 +107,18 @@ var ClawPro = class {
|
|
|
65
107
|
});
|
|
66
108
|
} catch (err) {
|
|
67
109
|
clearTimeout(timer);
|
|
68
|
-
if (
|
|
69
|
-
await sleep(
|
|
110
|
+
if (shouldRetryNetwork(method, attempt, this.maxRetries)) {
|
|
111
|
+
await this.sleep(backoffMs(attempt, this.rand));
|
|
70
112
|
continue;
|
|
71
113
|
}
|
|
72
114
|
throw new ClawProError(`Network error: ${err.message}`, { status: 0, code: "network_error" });
|
|
73
115
|
}
|
|
74
116
|
clearTimeout(timer);
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const retryable = res.status === 429 || idempotent;
|
|
78
|
-
if (transient && retryable && attempt < this.maxRetries) {
|
|
79
|
-
const ra = Number(res.headers.get("retry-after"));
|
|
80
|
-
await sleep(Number.isFinite(ra) && ra > 0 ? ra * 1e3 : backoff(attempt));
|
|
117
|
+
if (shouldRetryStatus(method, res.status, attempt, this.maxRetries)) {
|
|
118
|
+
await this.sleep(retryDelayMs(res.headers.get("retry-after"), attempt, this.rand));
|
|
81
119
|
continue;
|
|
82
120
|
}
|
|
121
|
+
const requestId = res.headers.get("x-request-id") ?? void 0;
|
|
83
122
|
const text = await res.text();
|
|
84
123
|
const data = text ? safeJson(text) : void 0;
|
|
85
124
|
if (!res.ok) {
|
|
@@ -90,8 +129,6 @@ var ClawPro = class {
|
|
|
90
129
|
}
|
|
91
130
|
}
|
|
92
131
|
};
|
|
93
|
-
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
94
|
-
var backoff = (attempt) => Math.round(250 * 2 ** attempt * (0.8 + Math.random() * 0.4));
|
|
95
132
|
function safeJson(text) {
|
|
96
133
|
try {
|
|
97
134
|
return JSON.parse(text);
|
|
@@ -221,8 +258,17 @@ var UsageResource = class {
|
|
|
221
258
|
}
|
|
222
259
|
}
|
|
223
260
|
};
|
|
261
|
+
function verifyWebhook(opts) {
|
|
262
|
+
const { payload, signature, secret, toleranceSec = 300 } = opts;
|
|
263
|
+
const parsed = parseSignature(signature);
|
|
264
|
+
if (!parsed) return false;
|
|
265
|
+
if (!timestampFresh(parsed.t, toleranceSec, Date.now())) return false;
|
|
266
|
+
const expected = hmacHex(secret, signingString(parsed.t, payload));
|
|
267
|
+
return timingSafeEqualStr(expected, parsed.v1);
|
|
268
|
+
}
|
|
224
269
|
export {
|
|
225
270
|
ClawPro,
|
|
226
|
-
ClawProError
|
|
271
|
+
ClawProError,
|
|
272
|
+
verifyWebhook
|
|
227
273
|
};
|
|
228
274
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @clawpro/node — official SDK for the ClawPro Instagram outbound API.\n *\n * ```ts\n * import { ClawPro } from '@clawpro/node'\n * const clawpro = new ClawPro({ apiKey: process.env.CLAWPRO_API_KEY! })\n * const { accounts } = await clawpro.accounts.list()\n * ```\n */\n\n// ─────────────────────────────── Types ───────────────────────────────────────\n\nexport type AccountStatus = 'new' | 'warming' | 'active' | 'challenged' | 'banned' | 'disconnected';\nexport type ProxyType = 'mobile' | 'isp' | 'residential' | 'none';\n\nexport interface Account {\n id: string;\n username: string;\n status: AccountStatus;\n warmupTier?: number;\n dailyDmCap?: number;\n}\n\nexport type CampaignStatus = 'draft' | 'active' | 'paused';\n\nexport interface CampaignTarget {\n username: string;\n lastCheckedPostId: string | null;\n}\n\nexport interface Campaign {\n id: string;\n accountId: string;\n name: string;\n status: CampaignStatus;\n targets: CampaignTarget[];\n offer: string;\n icpCriteria: string;\n dailyDmTarget: number;\n}\n\nexport type LeadStatus =\n | 'discovered' | 'enriched' | 'scored' | 'qualified' | 'rejected'\n | 'queued' | 'dmed' | 'replied' | 'booked';\n\nexport interface Message {\n id: string;\n direction: 'out' | 'in';\n body: string;\n status: string;\n sentAt: string | null;\n createdAt: string;\n}\n\nexport interface Lead {\n id: string;\n igUsername: string;\n fullName: string | null;\n sourceTargetUsername: string;\n engagementType: 'like' | 'comment';\n commentText: string | null;\n score: number | null;\n scoreReason: string | null;\n status: LeadStatus;\n messages?: Message[];\n}\n\nexport type WebhookType = 'generic' | 'slack';\nexport type WebhookEvent =\n | 'reply.received' | 'dm.sent' | 'dm.failed'\n | 'lead.qualified' | 'lead.booked'\n | 'account.active' | 'account.challenged' | 'account.banned';\n\nexport interface Webhook {\n id: string;\n label: string;\n url: string;\n type: WebhookType;\n events: (WebhookEvent | '*')[];\n active: boolean;\n lastStatus: number | null;\n lastDeliveryAt: string | null;\n lastError: string | null;\n /** Present only on the create response (generic HMAC secret). */\n secret?: string;\n}\n\nexport interface WebhookDelivery {\n id: string;\n event: string;\n status: 'pending' | 'delivered' | 'dead';\n attempts: number;\n lastStatusCode: number | null;\n lastError: string | null;\n nextRetryAt: string | null;\n deliveredAt: string | null;\n createdAt: string;\n}\n\nexport type ApiKeyEnv = 'production' | 'development';\nexport type ApiKeyAccess = 'full' | 'read' | 'write';\n\nexport interface ApiKey {\n id: string;\n name: string;\n environment: ApiKeyEnv;\n access: ApiKeyAccess;\n keyPrefix: string;\n createdAt: string;\n expiresAt: string | null;\n lastUsedAt: string | null;\n}\n\nexport interface UsageSummary {\n callsThisMonth: number;\n calls24h: number;\n errors: number;\n avgLatencyMs: number;\n}\n\nexport interface RequestLog {\n id: string;\n requestId: string;\n method: string;\n path: string;\n status: number;\n latencyMs: number;\n error: string | null;\n createdAt: string;\n keyName: string | null;\n keyPrefix: string | null;\n}\n\n// ─────────────────────────────── Errors ──────────────────────────────────────\n\n/** Thrown for any non-2xx API response. Inspect `status` / `requestId`. */\nexport class ClawProError extends Error {\n readonly status: number;\n readonly code: string;\n readonly requestId?: string;\n constructor(message: string, opts: { status: number; code?: string; requestId?: string }) {\n super(message);\n this.name = 'ClawProError';\n this.status = opts.status;\n this.code = opts.code ?? 'error';\n this.requestId = opts.requestId;\n }\n}\n\n// ─────────────────────────────── Client ──────────────────────────────────────\n\nexport interface ClawProOptions {\n /** A `sk_live_…` / `sk_test_…` key from the developer portal. Sent as `X-API-Key`. */\n apiKey: string;\n /** API origin (no trailing slash). Defaults to https://api.tryclawpro.com */\n baseUrl?: string;\n /** Override the fetch implementation (defaults to global fetch). */\n fetch?: typeof fetch;\n /** Per-request timeout in ms (default 30000). */\n timeoutMs?: number;\n /** Auto-retry transient failures (network, 429, 5xx) with exponential backoff.\n * Default 2. 429s honor the `Retry-After` header; only idempotent methods\n * (GET/DELETE/PUT) are retried on network/5xx. */\n maxRetries?: number;\n}\n\ntype Query = Record<string, string | number | boolean | undefined | null>;\n\nexport class ClawPro {\n readonly accounts: AccountsResource;\n readonly campaigns: CampaignsResource;\n readonly leads: LeadsResource;\n readonly webhooks: WebhooksResource;\n readonly keys: ApiKeysResource;\n readonly usage: UsageResource;\n\n private readonly apiKey: string;\n private readonly base: string;\n private readonly fetchImpl: typeof fetch;\n private readonly timeoutMs: number;\n private readonly maxRetries: number;\n\n constructor(opts: ClawProOptions) {\n if (!opts?.apiKey) throw new Error('ClawPro: `apiKey` is required');\n this.apiKey = opts.apiKey;\n const origin = (opts.baseUrl ?? 'https://api.tryclawpro.com').replace(/\\/$/, '');\n this.base = `${origin}/api/instagram`;\n this.fetchImpl = opts.fetch ?? globalThis.fetch;\n if (!this.fetchImpl) throw new Error('ClawPro: no fetch available — pass `fetch` (Node <18)');\n this.timeoutMs = opts.timeoutMs ?? 30_000;\n this.maxRetries = opts.maxRetries ?? 2;\n\n this.accounts = new AccountsResource(this);\n this.campaigns = new CampaignsResource(this);\n this.leads = new LeadsResource(this);\n this.webhooks = new WebhooksResource(this);\n this.keys = new ApiKeysResource(this);\n this.usage = new UsageResource(this);\n }\n\n /** @internal */\n async request<T>(method: string, path: string, opts: { body?: unknown; query?: Query } = {}): Promise<T> {\n const url = new URL(this.base + path);\n if (opts.query) {\n for (const [k, v] of Object.entries(opts.query)) {\n if (v !== undefined && v !== null && v !== '') url.searchParams.set(k, String(v));\n }\n }\n const idempotent = method === 'GET' || method === 'DELETE' || method === 'PUT';\n\n for (let attempt = 0; ; attempt++) {\n const ctrl = new AbortController();\n const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);\n let res: Response;\n try {\n res = await this.fetchImpl(url.toString(), {\n method,\n headers: {\n 'X-API-Key': this.apiKey,\n Accept: 'application/json',\n ...(opts.body !== undefined ? { 'Content-Type': 'application/json' } : {}),\n },\n body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined,\n signal: ctrl.signal,\n });\n } catch (err) {\n clearTimeout(timer);\n // Network/timeout: safe to retry only idempotent methods.\n if (idempotent && attempt < this.maxRetries) { await sleep(backoff(attempt)); continue; }\n throw new ClawProError(`Network error: ${(err as Error).message}`, { status: 0, code: 'network_error' });\n }\n clearTimeout(timer);\n\n const requestId = res.headers.get('x-request-id') ?? undefined;\n // Transient server-side: 429 (didn't process → retry any method) or 5xx (idempotent only).\n const transient = res.status === 429 || res.status >= 500;\n const retryable = res.status === 429 || idempotent;\n if (transient && retryable && attempt < this.maxRetries) {\n const ra = Number(res.headers.get('retry-after'));\n await sleep(Number.isFinite(ra) && ra > 0 ? ra * 1000 : backoff(attempt));\n continue;\n }\n\n const text = await res.text();\n const data = text ? safeJson(text) : undefined;\n if (!res.ok) {\n const message = (data && (data.message || data.error)) || `HTTP ${res.status}`;\n throw new ClawProError(message, { status: res.status, code: (data && data.error) || 'error', requestId });\n }\n return data as T;\n }\n }\n}\n\nconst sleep = (ms: number) => new Promise<void>((r) => setTimeout(r, ms));\n/** Exponential backoff with jitter: ~250ms, ~500ms, ~1s … */\nconst backoff = (attempt: number) => Math.round((250 * 2 ** attempt) * (0.8 + Math.random() * 0.4));\n\nfunction safeJson(text: string): any {\n try { return JSON.parse(text); } catch { return { raw: text }; }\n}\n\n// ───────────────────────────── Resources ─────────────────────────────────────\n\nclass AccountsResource {\n constructor(private c: ClawPro) {}\n /** List connected sending accounts. */\n list() { return this.c.request<{ accounts: Account[] }>('GET', '/accounts').then((r) => r.accounts); }\n /** Connect a new sending account (a geo-matched mobile proxy is assigned). */\n create(input: { username: string; country?: string; proxyType?: ProxyType; proxyUrl?: string; timezone?: string }) {\n return this.c.request<{ account: Account }>('POST', '/accounts', { body: input }).then((r) => r.account);\n }\n /** Remove an account (cascades to its campaigns/leads/messages). */\n delete(id: string) { return this.c.request<{ ok: true }>('DELETE', `/accounts/${id}`); }\n}\n\nclass CampaignsResource {\n constructor(private c: ClawPro) {}\n list() { return this.c.request<{ campaigns: Campaign[] }>('GET', '/campaigns').then((r) => r.campaigns); }\n create(input: { accountId: string; name: string; targets: string[]; offer?: string; icpCriteria?: string; dailyDmTarget?: number }) {\n return this.c.request<{ campaign: Campaign }>('POST', '/campaigns', { body: input }).then((r) => r.campaign);\n }\n update(id: string, patch: Partial<{ status: CampaignStatus; targets: string[]; offer: string; icpCriteria: string; dailyDmTarget: number; name: string }>) {\n return this.c.request<{ campaign: Campaign }>('PATCH', `/campaigns/${id}`, { body: patch }).then((r) => r.campaign);\n }\n /** Kick a discovery/scoring/queue run for a campaign. */\n run(id: string) { return this.c.request<{ discovered: number; scored: number; queued: number }>('POST', `/campaigns/${id}/run`); }\n /** Leads for a campaign, optionally filtered by status. */\n leads(id: string, opts: { status?: LeadStatus } = {}) {\n return this.c.request<{ leads: Lead[] }>('GET', `/campaigns/${id}/leads`, { query: { status: opts.status } }).then((r) => r.leads);\n }\n /** Threads with at least one message (the unified inbox). */\n inbox(id: string) { return this.c.request<{ threads: Lead[] }>('GET', `/campaigns/${id}/inbox`).then((r) => r.threads); }\n}\n\nclass LeadsResource {\n constructor(private c: ClawPro) {}\n /** Advance a lead — e.g. `{ status: 'booked' }` once a call is set (fires `lead.booked`). */\n update(id: string, patch: { status: 'qualified' | 'rejected' | 'booked' }) {\n return this.c.request<{ lead: Lead }>('PATCH', `/leads/${id}`, { body: patch }).then((r) => r.lead);\n }\n}\n\nclass WebhooksResource {\n constructor(private c: ClawPro) {}\n /** The catalog of subscribable event types. */\n events() { return this.c.request<{ events: WebhookEvent[] }>('GET', '/webhooks/events').then((r) => r.events); }\n list() { return this.c.request<{ webhooks: Webhook[] }>('GET', '/webhooks').then((r) => r.webhooks); }\n /** Create an endpoint. The generic HMAC `secret` is returned once, here. */\n create(input: { url: string; type?: WebhookType; events?: (WebhookEvent | '*')[]; label?: string }) {\n return this.c.request<{ webhook: Webhook }>('POST', '/webhooks', { body: { type: 'generic', events: ['*'], ...input } }).then((r) => r.webhook);\n }\n delete(id: string) { return this.c.request<{ ok: true }>('DELETE', `/webhooks/${id}`); }\n /** Send a `webhook.test` delivery. */\n test(id: string) { return this.c.request<unknown>('POST', `/webhooks/${id}/test`); }\n deliveries(id: string) { return this.c.request<{ deliveries: WebhookDelivery[] }>('GET', `/webhooks/${id}/deliveries`).then((r) => r.deliveries); }\n}\n\nclass ApiKeysResource {\n constructor(private c: ClawPro) {}\n list() { return this.c.request<{ keys: ApiKey[] }>('GET', '/keys').then((r) => r.keys); }\n /** Returns `{ secret, key }` — the plaintext `secret` is shown only here. */\n create(input: { name: string; environment?: ApiKeyEnv; access?: ApiKeyAccess; expiresAt?: string | null }) {\n return this.c.request<{ secret: string; key: ApiKey }>('POST', '/keys', { body: input });\n }\n revoke(id: string) { return this.c.request<{ ok: true }>('DELETE', `/keys/${id}`); }\n}\n\nexport interface LogFilter {\n key?: string; method?: string; status?: '2xx' | '4xx' | '5xx';\n from?: string; to?: string; endpoint?: string; requestId?: string; search?: string;\n limit?: number; offset?: number;\n}\n\nclass UsageResource {\n constructor(private c: ClawPro) {}\n /** Calls this month / 24h / errors / avg latency. */\n summary() { return this.c.request<UsageSummary>('GET', '/usage'); }\n /** A single page of request logs (`limit` + `offset`). */\n logs(filter: LogFilter = {}) {\n return this.c.request<{ logs: RequestLog[] }>('GET', '/logs', { query: filter as Query }).then((r) => r.logs);\n }\n /** Auto-paginate every matching log:\n * `for await (const log of clawpro.usage.iterateLogs()) { … }` */\n async *iterateLogs(filter: Omit<LogFilter, 'offset'> = {}): AsyncGenerator<RequestLog, void, unknown> {\n const pageSize = filter.limit ?? 100;\n for (let offset = 0; ; offset += pageSize) {\n const page = await this.logs({ ...filter, limit: pageSize, offset });\n for (const row of page) yield row;\n if (page.length < pageSize) return;\n }\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/internal.ts","../src/index.ts"],"sourcesContent":["/**\n * Pure, total helpers — the testable core of the SDK. No I/O, no throwing\n * (except the deliberately-effectful `hmacHex`, which is referentially\n * transparent: same inputs → same digest). The client (index.ts) is the thin\n * effectful shell that threads `fetch`, sleep, and randomness around these.\n */\nimport { createHmac, timingSafeEqual } from 'node:crypto';\n\n// ── Webhook signatures ───────────────────────────────────────────────────────\nexport interface ParsedSignature { t: number; v1: string }\n\n/** Parse `t=<unix>,v1=<hex>`. Returns null for anything malformed (total). */\nexport function parseSignature(header: string): ParsedSignature | null {\n const parts: Record<string, string> = {};\n for (const p of header.split(',')) {\n const i = p.indexOf('=');\n if (i > 0) parts[p.slice(0, i).trim()] = p.slice(i + 1).trim();\n }\n const t = Number(parts.t);\n if (!Number.isFinite(t) || t <= 0 || !parts.v1) return null;\n return { t, v1: parts.v1 };\n}\n\nexport const signingString = (t: number, payload: string): string => `${t}.${payload}`;\n\nexport const hmacHex = (secret: string, message: string): string =>\n createHmac('sha256', secret).update(message).digest('hex');\n\n/** Is `t` within `toleranceSec` of `nowMs`? `toleranceSec <= 0` disables. */\nexport function timestampFresh(t: number, toleranceSec: number, nowMs: number): boolean {\n return toleranceSec <= 0 || Math.abs(nowMs / 1000 - t) <= toleranceSec;\n}\n\n/** Constant-time string equality (length-aware). */\nexport function timingSafeEqualStr(a: string, b: string): boolean {\n const ba = Buffer.from(a);\n const bb = Buffer.from(b);\n return ba.length === bb.length && timingSafeEqual(ba, bb);\n}\n\n// ── URL building ─────────────────────────────────────────────────────────────\nexport type QueryValue = string | number | boolean | undefined | null;\n\nexport function buildUrl(base: string, path: string, query?: Record<string, QueryValue>): string {\n const url = new URL(base + path);\n if (query) {\n for (const [k, v] of Object.entries(query)) {\n if (v !== undefined && v !== null && v !== '') url.searchParams.set(k, String(v));\n }\n }\n return url.toString();\n}\n\n// ── Retry decisions (pure) ───────────────────────────────────────────────────\nexport const isIdempotent = (method: string): boolean =>\n method === 'GET' || method === 'DELETE' || method === 'PUT';\n\n/** Network/timeout error: retry only idempotent methods, within budget. */\nexport const shouldRetryNetwork = (method: string, attempt: number, maxRetries: number): boolean =>\n isIdempotent(method) && attempt < maxRetries;\n\n/** A 429 (didn't process → any method) or 5xx (idempotent only), within budget. */\nexport function shouldRetryStatus(method: string, status: number, attempt: number, maxRetries: number): boolean {\n const transient = status === 429 || status >= 500;\n const allowed = status === 429 || isIdempotent(method);\n return transient && allowed && attempt < maxRetries;\n}\n\n/** Exponential backoff with jitter: ~250ms · 2^attempt · [0.8,1.2). `rand` injected. */\nexport const backoffMs = (attempt: number, rand: () => number): number =>\n Math.round(250 * 2 ** attempt * (0.8 + rand() * 0.4));\n\n/** Honor a numeric `Retry-After` (seconds), else fall back to backoff. */\nexport function retryDelayMs(retryAfter: string | null, attempt: number, rand: () => number): number {\n const ra = Number(retryAfter);\n return Number.isFinite(ra) && ra > 0 ? ra * 1000 : backoffMs(attempt, rand);\n}\n","/**\n * @clawpro/node — official SDK for the ClawPro Instagram outbound API.\n *\n * ```ts\n * import { ClawPro } from '@clawpro/node'\n * const clawpro = new ClawPro({ apiKey: process.env.CLAWPRO_API_KEY! })\n * const { accounts } = await clawpro.accounts.list()\n * ```\n */\n\nimport {\n parseSignature, timestampFresh, hmacHex, signingString, timingSafeEqualStr,\n buildUrl, shouldRetryNetwork, shouldRetryStatus, backoffMs, retryDelayMs,\n} from './internal';\n\n// ─────────────────────────────── Types ───────────────────────────────────────\n\nexport type AccountStatus = 'new' | 'warming' | 'active' | 'challenged' | 'banned' | 'disconnected';\nexport type ProxyType = 'mobile' | 'isp' | 'residential' | 'none';\n\nexport interface Account {\n id: string;\n username: string;\n status: AccountStatus;\n warmupTier?: number;\n dailyDmCap?: number;\n}\n\nexport type CampaignStatus = 'draft' | 'active' | 'paused';\n\nexport interface CampaignTarget {\n username: string;\n lastCheckedPostId: string | null;\n}\n\nexport interface Campaign {\n id: string;\n accountId: string;\n name: string;\n status: CampaignStatus;\n targets: CampaignTarget[];\n offer: string;\n icpCriteria: string;\n dailyDmTarget: number;\n}\n\nexport type LeadStatus =\n | 'discovered' | 'enriched' | 'scored' | 'qualified' | 'rejected'\n | 'queued' | 'dmed' | 'replied' | 'booked';\n\nexport interface Message {\n id: string;\n direction: 'out' | 'in';\n body: string;\n status: string;\n sentAt: string | null;\n createdAt: string;\n}\n\nexport interface Lead {\n id: string;\n igUsername: string;\n fullName: string | null;\n sourceTargetUsername: string;\n engagementType: 'like' | 'comment';\n commentText: string | null;\n score: number | null;\n scoreReason: string | null;\n status: LeadStatus;\n messages?: Message[];\n}\n\nexport type WebhookType = 'generic' | 'slack';\nexport type WebhookEvent =\n | 'reply.received' | 'dm.sent' | 'dm.failed'\n | 'lead.qualified' | 'lead.booked'\n | 'account.active' | 'account.challenged' | 'account.banned';\n\nexport interface Webhook {\n id: string;\n label: string;\n url: string;\n type: WebhookType;\n events: (WebhookEvent | '*')[];\n active: boolean;\n lastStatus: number | null;\n lastDeliveryAt: string | null;\n lastError: string | null;\n /** Present only on the create response (generic HMAC secret). */\n secret?: string;\n}\n\nexport interface WebhookDelivery {\n id: string;\n event: string;\n status: 'pending' | 'delivered' | 'dead';\n attempts: number;\n lastStatusCode: number | null;\n lastError: string | null;\n nextRetryAt: string | null;\n deliveredAt: string | null;\n createdAt: string;\n}\n\nexport type ApiKeyEnv = 'production' | 'development';\nexport type ApiKeyAccess = 'full' | 'read' | 'write';\n\nexport interface ApiKey {\n id: string;\n name: string;\n environment: ApiKeyEnv;\n access: ApiKeyAccess;\n keyPrefix: string;\n createdAt: string;\n expiresAt: string | null;\n lastUsedAt: string | null;\n}\n\nexport interface UsageSummary {\n callsThisMonth: number;\n calls24h: number;\n errors: number;\n avgLatencyMs: number;\n}\n\nexport interface RequestLog {\n id: string;\n requestId: string;\n method: string;\n path: string;\n status: number;\n latencyMs: number;\n error: string | null;\n createdAt: string;\n keyName: string | null;\n keyPrefix: string | null;\n}\n\n// ─────────────────────────────── Errors ──────────────────────────────────────\n\n/** Thrown for any non-2xx API response. Inspect `status` / `requestId`. */\nexport class ClawProError extends Error {\n readonly status: number;\n readonly code: string;\n readonly requestId?: string;\n constructor(message: string, opts: { status: number; code?: string; requestId?: string }) {\n super(message);\n this.name = 'ClawProError';\n this.status = opts.status;\n this.code = opts.code ?? 'error';\n this.requestId = opts.requestId;\n }\n}\n\n// ─────────────────────────────── Client ──────────────────────────────────────\n\nexport interface ClawProOptions {\n /** A `sk_live_…` / `sk_test_…` key from the developer portal. Sent as `X-API-Key`. */\n apiKey: string;\n /** API origin (no trailing slash). Defaults to https://api.tryclawpro.com */\n baseUrl?: string;\n /** Override the fetch implementation (defaults to global fetch). */\n fetch?: typeof fetch;\n /** Per-request timeout in ms (default 30000). */\n timeoutMs?: number;\n /** Auto-retry transient failures (network, 429, 5xx) with exponential backoff.\n * Default 2. 429s honor the `Retry-After` header; only idempotent methods\n * (GET/DELETE/PUT) are retried on network/5xx. */\n maxRetries?: number;\n /** Injected for determinism/testing — defaults to a real timer. */\n sleep?: (ms: number) => Promise<void>;\n /** Injected for determinism/testing — defaults to Math.random. */\n rand?: () => number;\n}\n\ntype Query = Record<string, string | number | boolean | undefined | null>;\n\nexport class ClawPro {\n readonly accounts: AccountsResource;\n readonly campaigns: CampaignsResource;\n readonly leads: LeadsResource;\n readonly webhooks: WebhooksResource;\n readonly keys: ApiKeysResource;\n readonly usage: UsageResource;\n\n private readonly apiKey: string;\n private readonly base: string;\n private readonly fetchImpl: typeof fetch;\n private readonly timeoutMs: number;\n private readonly maxRetries: number;\n private readonly sleep: (ms: number) => Promise<void>;\n private readonly rand: () => number;\n\n constructor(opts: ClawProOptions) {\n if (!opts?.apiKey) throw new Error('ClawPro: `apiKey` is required');\n this.apiKey = opts.apiKey;\n const origin = (opts.baseUrl ?? 'https://api.tryclawpro.com').replace(/\\/$/, '');\n this.base = `${origin}/api/instagram`;\n this.fetchImpl = opts.fetch ?? globalThis.fetch;\n if (!this.fetchImpl) throw new Error('ClawPro: no fetch available — pass `fetch` (Node <18)');\n this.timeoutMs = opts.timeoutMs ?? 30_000;\n this.maxRetries = opts.maxRetries ?? 2;\n this.sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));\n this.rand = opts.rand ?? Math.random;\n\n this.accounts = new AccountsResource(this);\n this.campaigns = new CampaignsResource(this);\n this.leads = new LeadsResource(this);\n this.webhooks = new WebhooksResource(this);\n this.keys = new ApiKeysResource(this);\n this.usage = new UsageResource(this);\n }\n\n /** @internal */\n async request<T>(method: string, path: string, opts: { body?: unknown; query?: Query } = {}): Promise<T> {\n const url = buildUrl(this.base, path, opts.query);\n\n for (let attempt = 0; ; attempt++) {\n const ctrl = new AbortController();\n const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);\n let res: Response;\n try {\n res = await this.fetchImpl(url, {\n method,\n headers: {\n 'X-API-Key': this.apiKey,\n Accept: 'application/json',\n ...(opts.body !== undefined ? { 'Content-Type': 'application/json' } : {}),\n },\n body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined,\n signal: ctrl.signal,\n });\n } catch (err) {\n clearTimeout(timer);\n if (shouldRetryNetwork(method, attempt, this.maxRetries)) {\n await this.sleep(backoffMs(attempt, this.rand));\n continue;\n }\n throw new ClawProError(`Network error: ${(err as Error).message}`, { status: 0, code: 'network_error' });\n }\n clearTimeout(timer);\n\n if (shouldRetryStatus(method, res.status, attempt, this.maxRetries)) {\n await this.sleep(retryDelayMs(res.headers.get('retry-after'), attempt, this.rand));\n continue;\n }\n\n const requestId = res.headers.get('x-request-id') ?? undefined;\n const text = await res.text();\n const data = text ? safeJson(text) : undefined;\n if (!res.ok) {\n const message = (data && (data.message || data.error)) || `HTTP ${res.status}`;\n throw new ClawProError(message, { status: res.status, code: (data && data.error) || 'error', requestId });\n }\n return data as T;\n }\n }\n}\n\nfunction safeJson(text: string): any {\n try { return JSON.parse(text); } catch { return { raw: text }; }\n}\n\n// ───────────────────────────── Resources ─────────────────────────────────────\n\nclass AccountsResource {\n constructor(private c: ClawPro) {}\n /** List connected sending accounts. */\n list() { return this.c.request<{ accounts: Account[] }>('GET', '/accounts').then((r) => r.accounts); }\n /** Connect a new sending account (a geo-matched mobile proxy is assigned). */\n create(input: { username: string; country?: string; proxyType?: ProxyType; proxyUrl?: string; timezone?: string }) {\n return this.c.request<{ account: Account }>('POST', '/accounts', { body: input }).then((r) => r.account);\n }\n /** Remove an account (cascades to its campaigns/leads/messages). */\n delete(id: string) { return this.c.request<{ ok: true }>('DELETE', `/accounts/${id}`); }\n}\n\nclass CampaignsResource {\n constructor(private c: ClawPro) {}\n list() { return this.c.request<{ campaigns: Campaign[] }>('GET', '/campaigns').then((r) => r.campaigns); }\n create(input: { accountId: string; name: string; targets: string[]; offer?: string; icpCriteria?: string; dailyDmTarget?: number }) {\n return this.c.request<{ campaign: Campaign }>('POST', '/campaigns', { body: input }).then((r) => r.campaign);\n }\n update(id: string, patch: Partial<{ status: CampaignStatus; targets: string[]; offer: string; icpCriteria: string; dailyDmTarget: number; name: string }>) {\n return this.c.request<{ campaign: Campaign }>('PATCH', `/campaigns/${id}`, { body: patch }).then((r) => r.campaign);\n }\n /** Kick a discovery/scoring/queue run for a campaign. */\n run(id: string) { return this.c.request<{ discovered: number; scored: number; queued: number }>('POST', `/campaigns/${id}/run`); }\n /** Leads for a campaign, optionally filtered by status. */\n leads(id: string, opts: { status?: LeadStatus } = {}) {\n return this.c.request<{ leads: Lead[] }>('GET', `/campaigns/${id}/leads`, { query: { status: opts.status } }).then((r) => r.leads);\n }\n /** Threads with at least one message (the unified inbox). */\n inbox(id: string) { return this.c.request<{ threads: Lead[] }>('GET', `/campaigns/${id}/inbox`).then((r) => r.threads); }\n}\n\nclass LeadsResource {\n constructor(private c: ClawPro) {}\n /** Advance a lead — e.g. `{ status: 'booked' }` once a call is set (fires `lead.booked`). */\n update(id: string, patch: { status: 'qualified' | 'rejected' | 'booked' }) {\n return this.c.request<{ lead: Lead }>('PATCH', `/leads/${id}`, { body: patch }).then((r) => r.lead);\n }\n}\n\nclass WebhooksResource {\n constructor(private c: ClawPro) {}\n /** The catalog of subscribable event types. */\n events() { return this.c.request<{ events: WebhookEvent[] }>('GET', '/webhooks/events').then((r) => r.events); }\n list() { return this.c.request<{ webhooks: Webhook[] }>('GET', '/webhooks').then((r) => r.webhooks); }\n /** Create an endpoint. The generic HMAC `secret` is returned once, here. */\n create(input: { url: string; type?: WebhookType; events?: (WebhookEvent | '*')[]; label?: string }) {\n return this.c.request<{ webhook: Webhook }>('POST', '/webhooks', { body: { type: 'generic', events: ['*'], ...input } }).then((r) => r.webhook);\n }\n delete(id: string) { return this.c.request<{ ok: true }>('DELETE', `/webhooks/${id}`); }\n /** Send a `webhook.test` delivery. */\n test(id: string) { return this.c.request<unknown>('POST', `/webhooks/${id}/test`); }\n deliveries(id: string) { return this.c.request<{ deliveries: WebhookDelivery[] }>('GET', `/webhooks/${id}/deliveries`).then((r) => r.deliveries); }\n}\n\nclass ApiKeysResource {\n constructor(private c: ClawPro) {}\n list() { return this.c.request<{ keys: ApiKey[] }>('GET', '/keys').then((r) => r.keys); }\n /** Returns `{ secret, key }` — the plaintext `secret` is shown only here. */\n create(input: { name: string; environment?: ApiKeyEnv; access?: ApiKeyAccess; expiresAt?: string | null }) {\n return this.c.request<{ secret: string; key: ApiKey }>('POST', '/keys', { body: input });\n }\n revoke(id: string) { return this.c.request<{ ok: true }>('DELETE', `/keys/${id}`); }\n}\n\nexport interface LogFilter {\n key?: string; method?: string; status?: '2xx' | '4xx' | '5xx';\n from?: string; to?: string; endpoint?: string; requestId?: string; search?: string;\n limit?: number; offset?: number;\n}\n\nclass UsageResource {\n constructor(private c: ClawPro) {}\n /** Calls this month / 24h / errors / avg latency. */\n summary() { return this.c.request<UsageSummary>('GET', '/usage'); }\n /** A single page of request logs (`limit` + `offset`). */\n logs(filter: LogFilter = {}) {\n return this.c.request<{ logs: RequestLog[] }>('GET', '/logs', { query: filter as Query }).then((r) => r.logs);\n }\n /** Auto-paginate every matching log:\n * `for await (const log of clawpro.usage.iterateLogs()) { … }` */\n async *iterateLogs(filter: Omit<LogFilter, 'offset'> = {}): AsyncGenerator<RequestLog, void, unknown> {\n const pageSize = filter.limit ?? 100;\n for (let offset = 0; ; offset += pageSize) {\n const page = await this.logs({ ...filter, limit: pageSize, offset });\n for (const row of page) yield row;\n if (page.length < pageSize) return;\n }\n }\n}\n\n// ─────────────────────────── Webhook verification ────────────────────────────\n\nexport interface VerifyWebhookOptions {\n /** The raw request body, exactly as received (string). */\n payload: string;\n /** The `X-Souk-Signature` header value (`t=…,v1=…`). */\n signature: string;\n /** The endpoint's signing secret (shown once on webhook creation). */\n secret: string;\n /** Reject signatures older than this many seconds (default 300; 0 disables). */\n toleranceSec?: number;\n}\n\n/** Verify a ClawPro webhook's HMAC signature (timing-safe). Returns true if valid.\n *\n * ```ts\n * if (!verifyWebhook({ payload: rawBody, signature: req.headers['x-souk-signature'], secret })) return res.sendStatus(400)\n * ```\n */\nexport function verifyWebhook(opts: VerifyWebhookOptions): boolean {\n const { payload, signature, secret, toleranceSec = 300 } = opts;\n const parsed = parseSignature(signature);\n if (!parsed) return false;\n if (!timestampFresh(parsed.t, toleranceSec, Date.now())) return false;\n const expected = hmacHex(secret, signingString(parsed.t, payload));\n return timingSafeEqualStr(expected, parsed.v1);\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clawpro/node",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Official Node.js / TypeScript SDK for the ClawPro Instagram outbound API.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -13,22 +13,43 @@
|
|
|
13
13
|
"require": "./dist/index.cjs"
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
|
-
"files": [
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
17
20
|
"sideEffects": false,
|
|
18
21
|
"scripts": {
|
|
19
22
|
"build": "tsup",
|
|
20
23
|
"typecheck": "tsc --noEmit",
|
|
21
|
-
"prepublishOnly": "npm run build"
|
|
24
|
+
"prepublishOnly": "npm run build",
|
|
25
|
+
"test": "vitest run"
|
|
22
26
|
},
|
|
23
|
-
"keywords": [
|
|
27
|
+
"keywords": [
|
|
28
|
+
"clawpro",
|
|
29
|
+
"instagram",
|
|
30
|
+
"outbound",
|
|
31
|
+
"dm",
|
|
32
|
+
"sdk",
|
|
33
|
+
"api",
|
|
34
|
+
"tryclawpro"
|
|
35
|
+
],
|
|
24
36
|
"author": "ClawPro",
|
|
25
37
|
"license": "MIT",
|
|
26
38
|
"homepage": "https://tryclawpro.com",
|
|
27
|
-
"repository": {
|
|
28
|
-
|
|
29
|
-
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "git+https://github.com/Bojale-Labs/clawpro-node.git"
|
|
42
|
+
},
|
|
43
|
+
"bugs": {
|
|
44
|
+
"url": "https://github.com/Bojale-Labs/clawpro-node/issues"
|
|
45
|
+
},
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=18"
|
|
48
|
+
},
|
|
30
49
|
"devDependencies": {
|
|
50
|
+
"@types/node": "^26.0.1",
|
|
31
51
|
"tsup": "^8.3.0",
|
|
32
|
-
"typescript": "^5.6.0"
|
|
52
|
+
"typescript": "^5.6.0",
|
|
53
|
+
"vitest": "^4.1.9"
|
|
33
54
|
}
|
|
34
55
|
}
|