@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.
Files changed (64) hide show
  1. package/README.md +78 -0
  2. package/dist/cli-utils.d.ts +4 -0
  3. package/dist/cli-utils.js +31 -0
  4. package/dist/cli.d.ts +2 -0
  5. package/dist/cli.js +43 -0
  6. package/dist/client.d.ts +11 -0
  7. package/dist/client.js +100 -0
  8. package/dist/commands/ai.d.ts +3 -0
  9. package/dist/commands/ai.js +97 -0
  10. package/dist/commands/analytics.d.ts +3 -0
  11. package/dist/commands/analytics.js +75 -0
  12. package/dist/commands/billing.d.ts +3 -0
  13. package/dist/commands/billing.js +16 -0
  14. package/dist/commands/campaigns.d.ts +3 -0
  15. package/dist/commands/campaigns.js +123 -0
  16. package/dist/commands/channels.d.ts +3 -0
  17. package/dist/commands/channels.js +53 -0
  18. package/dist/commands/contacts.d.ts +3 -0
  19. package/dist/commands/contacts.js +120 -0
  20. package/dist/commands/conversations.d.ts +3 -0
  21. package/dist/commands/conversations.js +71 -0
  22. package/dist/commands/credits.d.ts +10 -0
  23. package/dist/commands/credits.js +38 -0
  24. package/dist/commands/discovery.d.ts +3 -0
  25. package/dist/commands/discovery.js +76 -0
  26. package/dist/commands/keys.d.ts +3 -0
  27. package/dist/commands/keys.js +70 -0
  28. package/dist/commands/messages.d.ts +3 -0
  29. package/dist/commands/messages.js +110 -0
  30. package/dist/commands/notifications.d.ts +3 -0
  31. package/dist/commands/notifications.js +66 -0
  32. package/dist/commands/webhooks.d.ts +3 -0
  33. package/dist/commands/webhooks.js +122 -0
  34. package/dist/index.d.ts +2 -0
  35. package/dist/index.js +37 -0
  36. package/dist/tools/agent-credits.d.ts +10 -0
  37. package/dist/tools/agent-credits.js +43 -0
  38. package/dist/tools/ai.d.ts +3 -0
  39. package/dist/tools/ai.js +81 -0
  40. package/dist/tools/analytics.d.ts +3 -0
  41. package/dist/tools/analytics.js +84 -0
  42. package/dist/tools/billing.d.ts +3 -0
  43. package/dist/tools/billing.js +14 -0
  44. package/dist/tools/campaigns.d.ts +3 -0
  45. package/dist/tools/campaigns.js +211 -0
  46. package/dist/tools/channels.d.ts +3 -0
  47. package/dist/tools/channels.js +49 -0
  48. package/dist/tools/contacts.d.ts +3 -0
  49. package/dist/tools/contacts.js +149 -0
  50. package/dist/tools/conversations.d.ts +3 -0
  51. package/dist/tools/conversations.js +84 -0
  52. package/dist/tools/discovery.d.ts +3 -0
  53. package/dist/tools/discovery.js +62 -0
  54. package/dist/tools/keys.d.ts +3 -0
  55. package/dist/tools/keys.js +92 -0
  56. package/dist/tools/messages.d.ts +3 -0
  57. package/dist/tools/messages.js +99 -0
  58. package/dist/tools/notifications.d.ts +3 -0
  59. package/dist/tools/notifications.js +64 -0
  60. package/dist/tools/webhooks.d.ts +3 -0
  61. package/dist/tools/webhooks.js +161 -0
  62. package/dist/types.d.ts +256 -0
  63. package/dist/types.js +2 -0
  64. 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
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
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
+ }
@@ -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,3 @@
1
+ import type { Command } from "commander";
2
+ import type { IrisClient } from "../client.js";
3
+ export declare function registerAICommands(program: Command, client: IrisClient): void;
@@ -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,3 @@
1
+ import type { Command } from "commander";
2
+ import type { IrisClient } from "../client.js";
3
+ export declare function registerAnalyticsCommands(program: Command, client: IrisClient): void;
@@ -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,3 @@
1
+ import type { Command } from "commander";
2
+ import type { IrisClient } from "../client.js";
3
+ export declare function registerBillingCommands(program: Command, client: IrisClient): void;
@@ -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,3 @@
1
+ import type { Command } from "commander";
2
+ import type { IrisClient } from "../client.js";
3
+ export declare function registerCampaignCommands(program: Command, client: IrisClient): void;
@@ -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,3 @@
1
+ import type { Command } from "commander";
2
+ import type { IrisClient } from "../client.js";
3
+ export declare function registerChannelCommands(program: Command, client: IrisClient): void;
@@ -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
+ }
@@ -0,0 +1,3 @@
1
+ import type { Command } from "commander";
2
+ import type { IrisClient } from "../client.js";
3
+ export declare function registerContactCommands(program: Command, client: IrisClient): void;