@jclvsh/iris 0.2.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 +78 -0
- package/dist/cli-utils.d.ts +4 -0
- package/dist/cli-utils.js +31 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +43 -0
- package/dist/client.d.ts +11 -0
- package/dist/client.js +100 -0
- package/dist/commands/ai.d.ts +3 -0
- package/dist/commands/ai.js +97 -0
- package/dist/commands/analytics.d.ts +3 -0
- package/dist/commands/analytics.js +75 -0
- package/dist/commands/billing.d.ts +3 -0
- package/dist/commands/billing.js +16 -0
- package/dist/commands/campaigns.d.ts +3 -0
- package/dist/commands/campaigns.js +123 -0
- package/dist/commands/channels.d.ts +3 -0
- package/dist/commands/channels.js +53 -0
- package/dist/commands/contacts.d.ts +3 -0
- package/dist/commands/contacts.js +120 -0
- package/dist/commands/conversations.d.ts +3 -0
- package/dist/commands/conversations.js +71 -0
- package/dist/commands/credits.d.ts +10 -0
- package/dist/commands/credits.js +38 -0
- package/dist/commands/discovery.d.ts +3 -0
- package/dist/commands/discovery.js +76 -0
- package/dist/commands/keys.d.ts +3 -0
- package/dist/commands/keys.js +70 -0
- package/dist/commands/messages.d.ts +3 -0
- package/dist/commands/messages.js +110 -0
- package/dist/commands/notifications.d.ts +3 -0
- package/dist/commands/notifications.js +66 -0
- package/dist/commands/webhooks.d.ts +3 -0
- package/dist/commands/webhooks.js +122 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +37 -0
- package/dist/tools/agent-credits.d.ts +10 -0
- package/dist/tools/agent-credits.js +43 -0
- package/dist/tools/ai.d.ts +3 -0
- package/dist/tools/ai.js +81 -0
- package/dist/tools/analytics.d.ts +3 -0
- package/dist/tools/analytics.js +84 -0
- package/dist/tools/billing.d.ts +3 -0
- package/dist/tools/billing.js +14 -0
- package/dist/tools/campaigns.d.ts +3 -0
- package/dist/tools/campaigns.js +211 -0
- package/dist/tools/channels.d.ts +3 -0
- package/dist/tools/channels.js +49 -0
- package/dist/tools/contacts.d.ts +3 -0
- package/dist/tools/contacts.js +149 -0
- package/dist/tools/conversations.d.ts +3 -0
- package/dist/tools/conversations.js +84 -0
- package/dist/tools/discovery.d.ts +3 -0
- package/dist/tools/discovery.js +62 -0
- package/dist/tools/keys.d.ts +3 -0
- package/dist/tools/keys.js +92 -0
- package/dist/tools/messages.d.ts +3 -0
- package/dist/tools/messages.js +99 -0
- package/dist/tools/notifications.d.ts +3 -0
- package/dist/tools/notifications.js +64 -0
- package/dist/tools/webhooks.d.ts +3 -0
- package/dist/tools/webhooks.js +161 -0
- package/dist/types.d.ts +256 -0
- package/dist/types.js +2 -0
- package/package.json +33 -0
package/README.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# @jclvsh/iris
|
|
2
|
+
|
|
3
|
+
MCP server for the [Iris](https://iris-ai.dev) API — AI-powered messaging assistant for automated multi-channel outreach campaigns.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx @jclvsh/iris
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Configuration
|
|
12
|
+
|
|
13
|
+
### Claude Code
|
|
14
|
+
|
|
15
|
+
Add to your Claude Code settings:
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{
|
|
19
|
+
"mcpServers": {
|
|
20
|
+
"iris": {
|
|
21
|
+
"command": "npx",
|
|
22
|
+
"args": ["-y", "@jclvsh/iris"],
|
|
23
|
+
"env": {
|
|
24
|
+
"IRIS_API_KEY": "is_live_..."
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Cursor
|
|
32
|
+
|
|
33
|
+
Add to `.cursor/mcp.json`:
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"mcpServers": {
|
|
38
|
+
"iris": {
|
|
39
|
+
"command": "npx",
|
|
40
|
+
"args": ["-y", "@jclvsh/iris"],
|
|
41
|
+
"env": {
|
|
42
|
+
"IRIS_API_KEY": "is_live_..."
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Environment Variables
|
|
50
|
+
|
|
51
|
+
| Variable | Required | Description |
|
|
52
|
+
|----------|----------|-------------|
|
|
53
|
+
| `IRIS_API_KEY` | Yes | API key (must start with `is_live_`) |
|
|
54
|
+
| `IRIS_API_URL` | No | Base URL (default: `https://iris-ai.dev/api/v1`) |
|
|
55
|
+
|
|
56
|
+
Get your API key at [iris-ai.dev/settings/api-keys](https://iris-ai.dev/settings/api-keys).
|
|
57
|
+
|
|
58
|
+
## Available Tools
|
|
59
|
+
|
|
60
|
+
| Category | Tools |
|
|
61
|
+
|----------|-------|
|
|
62
|
+
| Campaigns | list, create, get, update, delete, contacts, usage, analyze, target profiles |
|
|
63
|
+
| Contacts | list, create, get, update, delete, bulk import, usage, conversations |
|
|
64
|
+
| Conversations | list, get, messages, mark read, engagement metrics |
|
|
65
|
+
| Messages | list pending, approve, reject, retry, feedback, bulk action |
|
|
66
|
+
| AI | generate content, campaign content, analyze response, usage, suggestions |
|
|
67
|
+
| Discovery | search contacts, campaign search, job status, sources |
|
|
68
|
+
| Analytics | overview, campaign analytics, performance, response time, contact types |
|
|
69
|
+
| Billing | plans |
|
|
70
|
+
| Channels | connected accounts, availability, send message |
|
|
71
|
+
| Notifications | list, update, delete, mark all read |
|
|
72
|
+
| API Keys | current key, list, create, revoke |
|
|
73
|
+
| Webhooks | list, create, get, update, delete, test, deliveries |
|
|
74
|
+
| Agent Credits | balance, purchase pack (x402/MPP only) |
|
|
75
|
+
|
|
76
|
+
## Documentation
|
|
77
|
+
|
|
78
|
+
Full API documentation: [iris-ai.dev/docs](https://iris-ai.dev/docs)
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function formatOutput(data: unknown, format?: string): void;
|
|
2
|
+
export declare function handleError(error: unknown): never;
|
|
3
|
+
export declare function getFormat(opts: Record<string, unknown>): string;
|
|
4
|
+
export declare function parseList(value: string): string[];
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export function formatOutput(data, format = "json") {
|
|
2
|
+
if (format === "table" && typeof data === "object" && data !== null) {
|
|
3
|
+
const obj = data;
|
|
4
|
+
if (Array.isArray(obj.data)) {
|
|
5
|
+
console.table(obj.data);
|
|
6
|
+
if (obj.pagination) {
|
|
7
|
+
const p = obj.pagination;
|
|
8
|
+
console.error(`\npage ${p.page}/${p.total_pages} (${p.total} total)`);
|
|
9
|
+
}
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
if (obj.data && typeof obj.data === "object") {
|
|
13
|
+
console.log(JSON.stringify(obj.data, null, 2));
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
console.log(JSON.stringify(data, null, 2));
|
|
18
|
+
}
|
|
19
|
+
export function handleError(error) {
|
|
20
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
21
|
+
console.error(`error: ${message}`);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
export function getFormat(opts) {
|
|
25
|
+
if (opts.json)
|
|
26
|
+
return "json";
|
|
27
|
+
return opts.format || "json";
|
|
28
|
+
}
|
|
29
|
+
export function parseList(value) {
|
|
30
|
+
return value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
31
|
+
}
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { IrisClient } from "./client.js";
|
|
4
|
+
import { handleError } from "./cli-utils.js";
|
|
5
|
+
import { registerCampaignCommands } from "./commands/campaigns.js";
|
|
6
|
+
import { registerContactCommands } from "./commands/contacts.js";
|
|
7
|
+
import { registerMessageCommands } from "./commands/messages.js";
|
|
8
|
+
import { registerConversationCommands } from "./commands/conversations.js";
|
|
9
|
+
import { registerDiscoveryCommands } from "./commands/discovery.js";
|
|
10
|
+
import { registerAICommands } from "./commands/ai.js";
|
|
11
|
+
import { registerKeyCommands } from "./commands/keys.js";
|
|
12
|
+
import { registerWebhookCommands } from "./commands/webhooks.js";
|
|
13
|
+
import { registerNotificationCommands } from "./commands/notifications.js";
|
|
14
|
+
import { registerAnalyticsCommands } from "./commands/analytics.js";
|
|
15
|
+
import { registerBillingCommands } from "./commands/billing.js";
|
|
16
|
+
import { registerChannelCommands } from "./commands/channels.js";
|
|
17
|
+
import { registerCreditCommands } from "./commands/credits.js";
|
|
18
|
+
const program = new Command()
|
|
19
|
+
.name("iris")
|
|
20
|
+
.description("iris CLI - manage campaigns, contacts, messages, and outreach")
|
|
21
|
+
.version("0.2.0")
|
|
22
|
+
.option("--json", "output as JSON (default)")
|
|
23
|
+
.option("--format <fmt>", "output format: json, table", "json");
|
|
24
|
+
try {
|
|
25
|
+
const client = new IrisClient();
|
|
26
|
+
registerCampaignCommands(program, client);
|
|
27
|
+
registerContactCommands(program, client);
|
|
28
|
+
registerMessageCommands(program, client);
|
|
29
|
+
registerConversationCommands(program, client);
|
|
30
|
+
registerDiscoveryCommands(program, client);
|
|
31
|
+
registerAICommands(program, client);
|
|
32
|
+
registerKeyCommands(program, client);
|
|
33
|
+
registerWebhookCommands(program, client);
|
|
34
|
+
registerNotificationCommands(program, client);
|
|
35
|
+
registerAnalyticsCommands(program, client);
|
|
36
|
+
registerBillingCommands(program, client);
|
|
37
|
+
registerChannelCommands(program, client);
|
|
38
|
+
registerCreditCommands(program, client);
|
|
39
|
+
await program.parseAsync(process.argv);
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
handleError(error);
|
|
43
|
+
}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare class IrisClient {
|
|
2
|
+
private apiKey;
|
|
3
|
+
private baseUrl;
|
|
4
|
+
constructor();
|
|
5
|
+
get<T>(path: string, params?: Record<string, string>): Promise<T>;
|
|
6
|
+
post<T>(path: string, body?: unknown): Promise<T>;
|
|
7
|
+
patch<T>(path: string, body: unknown): Promise<T>;
|
|
8
|
+
delete(path: string): Promise<void>;
|
|
9
|
+
private request;
|
|
10
|
+
private handleError;
|
|
11
|
+
}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
export class IrisClient {
|
|
2
|
+
apiKey;
|
|
3
|
+
baseUrl;
|
|
4
|
+
constructor() {
|
|
5
|
+
const apiKey = process.env.IRIS_API_KEY;
|
|
6
|
+
if (!apiKey) {
|
|
7
|
+
throw new Error("IRIS_API_KEY environment variable is required. " +
|
|
8
|
+
"Get your API key from https://iris-ai.dev/settings/api-keys");
|
|
9
|
+
}
|
|
10
|
+
if (!apiKey.startsWith("is_live_")) {
|
|
11
|
+
throw new Error("IRIS_API_KEY must start with 'is_live_'. " +
|
|
12
|
+
"Get a valid API key from https://iris-ai.dev/settings/api-keys");
|
|
13
|
+
}
|
|
14
|
+
this.apiKey = apiKey;
|
|
15
|
+
this.baseUrl = (process.env.IRIS_API_URL || "https://iris-ai.dev/api/v1").replace(/\/+$/, "");
|
|
16
|
+
}
|
|
17
|
+
async get(path, params) {
|
|
18
|
+
const url = new URL(`${this.baseUrl}${path}`);
|
|
19
|
+
if (params) {
|
|
20
|
+
for (const [key, value] of Object.entries(params)) {
|
|
21
|
+
if (value !== undefined && value !== "") {
|
|
22
|
+
url.searchParams.set(key, value);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return this.request(url.toString(), { method: "GET" });
|
|
27
|
+
}
|
|
28
|
+
async post(path, body) {
|
|
29
|
+
return this.request(`${this.baseUrl}${path}`, {
|
|
30
|
+
method: "POST",
|
|
31
|
+
headers: { "Content-Type": "application/json" },
|
|
32
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
async patch(path, body) {
|
|
36
|
+
return this.request(`${this.baseUrl}${path}`, {
|
|
37
|
+
method: "PATCH",
|
|
38
|
+
headers: { "Content-Type": "application/json" },
|
|
39
|
+
body: JSON.stringify(body),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
async delete(path) {
|
|
43
|
+
await this.request(`${this.baseUrl}${path}`, { method: "DELETE" });
|
|
44
|
+
}
|
|
45
|
+
async request(url, init) {
|
|
46
|
+
try {
|
|
47
|
+
const response = await fetch(url, {
|
|
48
|
+
...init,
|
|
49
|
+
headers: {
|
|
50
|
+
...init.headers,
|
|
51
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
52
|
+
},
|
|
53
|
+
signal: AbortSignal.timeout(30_000),
|
|
54
|
+
});
|
|
55
|
+
if (!response.ok && response.status !== 204) {
|
|
56
|
+
await this.handleError(response);
|
|
57
|
+
}
|
|
58
|
+
if (response.status === 204) {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
return (await response.json());
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
if (error instanceof Error && error.name === "TimeoutError") {
|
|
65
|
+
throw new Error("request timed out after 30 seconds");
|
|
66
|
+
}
|
|
67
|
+
if (error instanceof TypeError) {
|
|
68
|
+
throw new Error(`network error: ${error.message}`);
|
|
69
|
+
}
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async handleError(response) {
|
|
74
|
+
let message;
|
|
75
|
+
try {
|
|
76
|
+
const body = await response.json();
|
|
77
|
+
if (body?.error) {
|
|
78
|
+
const parts = [body.error.message || "unknown error"];
|
|
79
|
+
if (body.error.code)
|
|
80
|
+
parts.unshift(`[${body.error.code}]`);
|
|
81
|
+
if (body.error.field)
|
|
82
|
+
parts.push(`(field: ${body.error.field})`);
|
|
83
|
+
message = parts.join(" ");
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
message = `HTTP ${response.status}: ${response.statusText}`;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
message = `HTTP ${response.status}: ${response.statusText}`;
|
|
91
|
+
}
|
|
92
|
+
if (response.status === 429) {
|
|
93
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
94
|
+
if (retryAfter) {
|
|
95
|
+
message += ` — retry after ${retryAfter}s`;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
throw new Error(message);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { formatOutput, handleError, getFormat, parseList } from "../cli-utils.js";
|
|
2
|
+
export function registerAICommands(program, client) {
|
|
3
|
+
const ai = program.command("ai").description("AI content generation and analysis");
|
|
4
|
+
ai
|
|
5
|
+
.command("generate")
|
|
6
|
+
.description("generate AI content (email draft, subject line, etc.)")
|
|
7
|
+
.requiredOption("-t, --generation-type <type>", "generation type (e.g. email_draft, subject_line)")
|
|
8
|
+
.requiredOption("--tone <tone>", "tone (e.g. professional, casual, friendly)")
|
|
9
|
+
.option("--contact-id <id>", "contact ID for personalization")
|
|
10
|
+
.option("--campaign-id <id>", "campaign ID for context")
|
|
11
|
+
.option("--prompt <text>", "free-form prompt context")
|
|
12
|
+
.action(async (opts) => {
|
|
13
|
+
try {
|
|
14
|
+
const body = {
|
|
15
|
+
generation_type: opts.generationType,
|
|
16
|
+
tone: opts.tone,
|
|
17
|
+
};
|
|
18
|
+
if (opts.contactId)
|
|
19
|
+
body.contact_id = opts.contactId;
|
|
20
|
+
if (opts.campaignId)
|
|
21
|
+
body.campaign_id = opts.campaignId;
|
|
22
|
+
if (opts.prompt)
|
|
23
|
+
body.prompt = opts.prompt;
|
|
24
|
+
const result = await client.post("/ai/generate", body);
|
|
25
|
+
formatOutput(result, getFormat(program.opts()));
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
handleError(error);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
ai
|
|
32
|
+
.command("generate-campaign")
|
|
33
|
+
.description("generate campaign-level content (email sequence, templates)")
|
|
34
|
+
.requiredOption("-n, --campaign-name <name>", "campaign name")
|
|
35
|
+
.requiredOption("-o, --objective <text>", "campaign objective")
|
|
36
|
+
.requiredOption("--target-audience <text>", "target audience description")
|
|
37
|
+
.requiredOption("--key-benefits <list>", "comma-separated key benefits", parseList)
|
|
38
|
+
.requiredOption("--tone <tone>", "tone (e.g. professional, casual)")
|
|
39
|
+
.action(async (opts) => {
|
|
40
|
+
try {
|
|
41
|
+
const body = {
|
|
42
|
+
campaign_name: opts.campaignName,
|
|
43
|
+
objective: opts.objective,
|
|
44
|
+
target_audience: opts.targetAudience,
|
|
45
|
+
key_benefits: opts.keyBenefits,
|
|
46
|
+
tone: opts.tone,
|
|
47
|
+
};
|
|
48
|
+
const result = await client.post("/ai/generate/campaign", body);
|
|
49
|
+
formatOutput(result, getFormat(program.opts()));
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
handleError(error);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
ai
|
|
56
|
+
.command("analyze")
|
|
57
|
+
.description("analyze a response in a conversation")
|
|
58
|
+
.requiredOption("--conversation-id <id>", "conversation ID")
|
|
59
|
+
.requiredOption("--email-content <text>", "email content to analyze")
|
|
60
|
+
.action(async (opts) => {
|
|
61
|
+
try {
|
|
62
|
+
const result = await client.post("/ai/analyze", {
|
|
63
|
+
conversation_id: opts.conversationId,
|
|
64
|
+
email_content: opts.emailContent,
|
|
65
|
+
});
|
|
66
|
+
formatOutput(result, getFormat(program.opts()));
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
handleError(error);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
ai
|
|
73
|
+
.command("usage")
|
|
74
|
+
.description("get AI generation usage stats for the current billing period")
|
|
75
|
+
.action(async () => {
|
|
76
|
+
try {
|
|
77
|
+
const result = await client.get("/ai/usage");
|
|
78
|
+
formatOutput(result, getFormat(program.opts()));
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
handleError(error);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
ai
|
|
85
|
+
.command("suggestions")
|
|
86
|
+
.description("get AI-suggested replies for a conversation")
|
|
87
|
+
.requiredOption("--conversation-id <id>", "conversation ID")
|
|
88
|
+
.action(async (opts) => {
|
|
89
|
+
try {
|
|
90
|
+
const result = await client.get("/ai/suggestions", { conversation_id: opts.conversationId });
|
|
91
|
+
formatOutput(result, getFormat(program.opts()));
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
handleError(error);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { formatOutput, handleError, getFormat } from "../cli-utils.js";
|
|
2
|
+
export function registerAnalyticsCommands(program, client) {
|
|
3
|
+
const analytics = program.command("analytics").alias("a").description("campaign performance and message metrics");
|
|
4
|
+
analytics
|
|
5
|
+
.command("overview")
|
|
6
|
+
.description("high-level overview of all campaign analytics")
|
|
7
|
+
.action(async () => {
|
|
8
|
+
try {
|
|
9
|
+
const result = await client.get("/analytics/overview");
|
|
10
|
+
formatOutput(result, getFormat(program.opts()));
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
handleError(error);
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
analytics
|
|
17
|
+
.command("campaign <id>")
|
|
18
|
+
.description("detailed analytics for a specific campaign")
|
|
19
|
+
.action(async (id) => {
|
|
20
|
+
try {
|
|
21
|
+
const result = await client.get(`/analytics/campaigns/${id}`);
|
|
22
|
+
formatOutput(result, getFormat(program.opts()));
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
handleError(error);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
analytics
|
|
29
|
+
.command("performance")
|
|
30
|
+
.description("email performance metrics over time")
|
|
31
|
+
.option("--days <n>", "time window in days (1–90, default 30)", "30")
|
|
32
|
+
.option("--campaign-id <id>", "filter by campaign")
|
|
33
|
+
.action(async (opts) => {
|
|
34
|
+
try {
|
|
35
|
+
const params = {};
|
|
36
|
+
if (opts.days)
|
|
37
|
+
params.days = opts.days;
|
|
38
|
+
if (opts.campaignId)
|
|
39
|
+
params.campaign_id = opts.campaignId;
|
|
40
|
+
const result = await client.get("/analytics/performance", params);
|
|
41
|
+
formatOutput(result, getFormat(program.opts()));
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
handleError(error);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
analytics
|
|
48
|
+
.command("response-time")
|
|
49
|
+
.description("average response time analytics")
|
|
50
|
+
.option("--campaign-id <id>", "filter by campaign")
|
|
51
|
+
.action(async (opts) => {
|
|
52
|
+
try {
|
|
53
|
+
const params = {};
|
|
54
|
+
if (opts.campaignId)
|
|
55
|
+
params.campaign_id = opts.campaignId;
|
|
56
|
+
const result = await client.get("/analytics/response-time", params);
|
|
57
|
+
formatOutput(result, getFormat(program.opts()));
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
handleError(error);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
analytics
|
|
64
|
+
.command("contact-types")
|
|
65
|
+
.description("breakdown of contacts by type/source")
|
|
66
|
+
.action(async () => {
|
|
67
|
+
try {
|
|
68
|
+
const result = await client.get("/analytics/contact-types");
|
|
69
|
+
formatOutput(result, getFormat(program.opts()));
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
handleError(error);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { formatOutput, handleError, getFormat } from "../cli-utils.js";
|
|
2
|
+
export function registerBillingCommands(program, client) {
|
|
3
|
+
const billing = program.command("billing").alias("b").description("billing plans and pricing");
|
|
4
|
+
billing
|
|
5
|
+
.command("plans")
|
|
6
|
+
.description("list all available billing plans with pricing and feature limits")
|
|
7
|
+
.action(async () => {
|
|
8
|
+
try {
|
|
9
|
+
const result = await client.get("/billing/plans");
|
|
10
|
+
formatOutput(result, getFormat(program.opts()));
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
handleError(error);
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { formatOutput, handleError, getFormat } from "../cli-utils.js";
|
|
2
|
+
export function registerCampaignCommands(program, client) {
|
|
3
|
+
const campaigns = program.command("campaigns").alias("c").description("manage campaigns");
|
|
4
|
+
campaigns
|
|
5
|
+
.command("list")
|
|
6
|
+
.description("list campaigns with pagination")
|
|
7
|
+
.option("-p, --page <n>", "page number", "1")
|
|
8
|
+
.option("-s, --page-size <n>", "items per page (max 100)", "50")
|
|
9
|
+
.option("--status <status>", "filter by status: draft, active, paused, completed")
|
|
10
|
+
.action(async (opts) => {
|
|
11
|
+
try {
|
|
12
|
+
const params = {};
|
|
13
|
+
if (opts.page)
|
|
14
|
+
params.page = opts.page;
|
|
15
|
+
if (opts.pageSize)
|
|
16
|
+
params.page_size = opts.pageSize;
|
|
17
|
+
if (opts.status)
|
|
18
|
+
params.status = opts.status;
|
|
19
|
+
const result = await client.get("/campaigns", params);
|
|
20
|
+
formatOutput(result, getFormat(program.opts()));
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
handleError(error);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
campaigns
|
|
27
|
+
.command("get <id>")
|
|
28
|
+
.description("get a campaign by ID")
|
|
29
|
+
.action(async (id) => {
|
|
30
|
+
try {
|
|
31
|
+
const result = await client.get(`/campaigns/${id}`);
|
|
32
|
+
formatOutput(result, getFormat(program.opts()));
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
handleError(error);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
campaigns
|
|
39
|
+
.command("create")
|
|
40
|
+
.description("create a new campaign")
|
|
41
|
+
.requiredOption("-n, --name <name>", "campaign name")
|
|
42
|
+
.requiredOption("-o, --objective <type>", "objective type: venue_dj, venue_host, product_promotion, custom")
|
|
43
|
+
.option("-d, --description <text>", "goal description")
|
|
44
|
+
.option("--product-name <name>", "product name (for product_promotion)")
|
|
45
|
+
.option("--product-description <text>", "product description (for product_promotion)")
|
|
46
|
+
.action(async (opts) => {
|
|
47
|
+
try {
|
|
48
|
+
const body = {
|
|
49
|
+
name: opts.name,
|
|
50
|
+
objective_type: opts.objective,
|
|
51
|
+
};
|
|
52
|
+
if (opts.description)
|
|
53
|
+
body.goal_description = opts.description;
|
|
54
|
+
if (opts.productName)
|
|
55
|
+
body.product_name = opts.productName;
|
|
56
|
+
if (opts.productDescription)
|
|
57
|
+
body.product_description = opts.productDescription;
|
|
58
|
+
const result = await client.post("/campaigns", body);
|
|
59
|
+
formatOutput(result, getFormat(program.opts()));
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
handleError(error);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
campaigns
|
|
66
|
+
.command("update <id>")
|
|
67
|
+
.description("update a campaign")
|
|
68
|
+
.option("-n, --name <name>", "campaign name")
|
|
69
|
+
.option("--status <status>", "status: draft, scheduled, active, paused, completed, deleted")
|
|
70
|
+
.option("-d, --description <text>", "goal description")
|
|
71
|
+
.action(async (id, opts) => {
|
|
72
|
+
try {
|
|
73
|
+
const body = {};
|
|
74
|
+
if (opts.name)
|
|
75
|
+
body.name = opts.name;
|
|
76
|
+
if (opts.status)
|
|
77
|
+
body.status = opts.status;
|
|
78
|
+
if (opts.description)
|
|
79
|
+
body.goal_description = opts.description;
|
|
80
|
+
const result = await client.patch(`/campaigns/${id}`, body);
|
|
81
|
+
formatOutput(result, getFormat(program.opts()));
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
handleError(error);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
campaigns
|
|
88
|
+
.command("delete <id>")
|
|
89
|
+
.description("delete a campaign")
|
|
90
|
+
.action(async (id) => {
|
|
91
|
+
try {
|
|
92
|
+
await client.delete(`/campaigns/${id}`);
|
|
93
|
+
console.log("campaign deleted");
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
handleError(error);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
campaigns
|
|
100
|
+
.command("usage <id>")
|
|
101
|
+
.description("get campaign usage stats")
|
|
102
|
+
.action(async (id) => {
|
|
103
|
+
try {
|
|
104
|
+
const result = await client.get(`/campaigns/${id}/usage`);
|
|
105
|
+
formatOutput(result, getFormat(program.opts()));
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
handleError(error);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
campaigns
|
|
112
|
+
.command("analyze <id>")
|
|
113
|
+
.description("run AI analysis on a campaign to generate target profiles")
|
|
114
|
+
.action(async (id) => {
|
|
115
|
+
try {
|
|
116
|
+
const result = await client.post(`/campaigns/${id}/analyze`);
|
|
117
|
+
formatOutput(result, getFormat(program.opts()));
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
handleError(error);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { formatOutput, handleError, getFormat } from "../cli-utils.js";
|
|
2
|
+
export function registerChannelCommands(program, client) {
|
|
3
|
+
const channels = program.command("channels").alias("ch").description("manage outreach channels and connected accounts");
|
|
4
|
+
channels
|
|
5
|
+
.command("accounts")
|
|
6
|
+
.description("list connected social accounts")
|
|
7
|
+
.action(async () => {
|
|
8
|
+
try {
|
|
9
|
+
const result = await client.get("/channels/accounts");
|
|
10
|
+
formatOutput(result, getFormat(program.opts()));
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
handleError(error);
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
channels
|
|
17
|
+
.command("availability")
|
|
18
|
+
.description("check which outreach channels are available and configured")
|
|
19
|
+
.action(async () => {
|
|
20
|
+
try {
|
|
21
|
+
const result = await client.get("/channels/availability");
|
|
22
|
+
formatOutput(result, getFormat(program.opts()));
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
handleError(error);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
channels
|
|
29
|
+
.command("send")
|
|
30
|
+
.description("send a message through a specific channel in a conversation")
|
|
31
|
+
.requiredOption("--channel <channel>", "channel: email, instagram, twitter, linkedin")
|
|
32
|
+
.requiredOption("--conversation-id <id>", "conversation ID")
|
|
33
|
+
.requiredOption("--recipient-id <id>", "recipient (email for email channel; platform handle/ID for social)")
|
|
34
|
+
.requiredOption("-c, --content <text>", "message content")
|
|
35
|
+
.option("-s, --subject <text>", "subject (email only)")
|
|
36
|
+
.action(async (opts) => {
|
|
37
|
+
try {
|
|
38
|
+
const body = {
|
|
39
|
+
channel: opts.channel,
|
|
40
|
+
conversation_id: opts.conversationId,
|
|
41
|
+
recipient_id: opts.recipientId,
|
|
42
|
+
content: opts.content,
|
|
43
|
+
};
|
|
44
|
+
if (opts.subject)
|
|
45
|
+
body.subject = opts.subject;
|
|
46
|
+
const result = await client.post("/channels/send", body);
|
|
47
|
+
formatOutput(result, getFormat(program.opts()));
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
handleError(error);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|