matimo-examples 0.1.0-alpha.11
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/.env.example +49 -0
- package/LICENSE +21 -0
- package/README.md +525 -0
- package/agents/decorator-pattern-agent.ts +368 -0
- package/agents/factory-pattern-agent.ts +253 -0
- package/agents/langchain-agent.ts +146 -0
- package/edit/edit-decorator.ts +178 -0
- package/edit/edit-factory.ts +138 -0
- package/edit/edit-langchain.ts +292 -0
- package/execute/execute-decorator.ts +49 -0
- package/execute/execute-factory.ts +46 -0
- package/execute/execute-langchain.ts +232 -0
- package/github/github-decorator.ts +326 -0
- package/github/github-factory.ts +355 -0
- package/github/github-langchain.ts +206 -0
- package/github/github-with-approval.ts +228 -0
- package/gmail/README.md +345 -0
- package/gmail/gmail-decorator.ts +216 -0
- package/gmail/gmail-factory.ts +231 -0
- package/gmail/gmail-langchain.ts +201 -0
- package/hubspot/README.md +316 -0
- package/hubspot/hubspot-decorator.ts +180 -0
- package/hubspot/hubspot-factory.ts +188 -0
- package/hubspot/hubspot-langchain.ts +222 -0
- package/logger-example.ts +40 -0
- package/mailchimp/README.md +321 -0
- package/mailchimp/mailchimp-decorator.ts +277 -0
- package/mailchimp/mailchimp-factory.ts +187 -0
- package/mailchimp/mailchimp-langchain.ts +155 -0
- package/notion/README.md +293 -0
- package/notion/notion-decorator.ts +275 -0
- package/notion/notion-factory.ts +256 -0
- package/notion/notion-langchain.ts +237 -0
- package/package.json +79 -0
- package/postgres/README.md +188 -0
- package/postgres/postgres-decorator.ts +198 -0
- package/postgres/postgres-factory.ts +180 -0
- package/postgres/postgres-langchain.ts +213 -0
- package/postgres/postgres-with-approval.ts +344 -0
- package/read/read-decorator.ts +154 -0
- package/read/read-factory.ts +121 -0
- package/read/read-langchain.ts +273 -0
- package/search/search-decorator.ts +206 -0
- package/search/search-factory.ts +146 -0
- package/search/search-langchain.ts +255 -0
- package/slack/README.md +339 -0
- package/slack/slack-decorator.ts +245 -0
- package/slack/slack-factory.ts +226 -0
- package/slack/slack-langchain.ts +242 -0
- package/tsconfig.json +20 -0
- package/twilio/README.md +309 -0
- package/twilio/twilio-decorator.ts +288 -0
- package/twilio/twilio-factory.ts +238 -0
- package/twilio/twilio-langchain.ts +218 -0
- package/web/web-decorator.ts +52 -0
- package/web/web-factory.ts +70 -0
- package/web/web-langchain.ts +163 -0
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* ============================================================================
|
|
4
|
+
* MAILCHIMP TOOLS - DECORATOR PATTERN EXAMPLE
|
|
5
|
+
* ============================================================================
|
|
6
|
+
*
|
|
7
|
+
* PATTERN: Decorator Pattern — Complete CRUD Workflow
|
|
8
|
+
* ─────────────────────────────────────────────────────────────────────────
|
|
9
|
+
* Same end-to-end flow as the factory example, but using TypeScript @tool
|
|
10
|
+
* decorators on a class. The decorator intercepts each method call and
|
|
11
|
+
* routes it through MatimoInstance automatically.
|
|
12
|
+
*
|
|
13
|
+
* FLOW:
|
|
14
|
+
* 1. GET — Fetch audiences, pick the first one automatically
|
|
15
|
+
* 2. READ — List subscribers, pick the first real contact
|
|
16
|
+
* 3. UPDATE — Update that contact's name
|
|
17
|
+
* 4. CREATE — Build a campaign targeting the audience
|
|
18
|
+
*
|
|
19
|
+
* SETUP:
|
|
20
|
+
* ─────────────────────────────────────────────────────────────────────────
|
|
21
|
+
* export MAILCHIMP_API_KEY=abc123def456-us6
|
|
22
|
+
* pnpm mailchimp:decorator
|
|
23
|
+
*
|
|
24
|
+
* ============================================================================
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import 'dotenv/config';
|
|
28
|
+
import { MatimoInstance, tool, setGlobalMatimoInstance } from 'matimo';
|
|
29
|
+
|
|
30
|
+
class MailchimpAgent {
|
|
31
|
+
constructor(private readonly sp: string) {}
|
|
32
|
+
|
|
33
|
+
@tool('mailchimp-get-lists')
|
|
34
|
+
async getLists(server_prefix: string, count?: number): Promise<unknown> {
|
|
35
|
+
// Decorator auto-executes via matimo
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@tool('mailchimp-add-list-member')
|
|
40
|
+
async addMember(
|
|
41
|
+
server_prefix: string,
|
|
42
|
+
list_id: string,
|
|
43
|
+
email_address: string,
|
|
44
|
+
status: string,
|
|
45
|
+
merge_fields?: Record<string, string>,
|
|
46
|
+
tags?: string[]
|
|
47
|
+
): Promise<unknown> {
|
|
48
|
+
// Decorator auto-executes via matimo
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@tool('mailchimp-get-list-members')
|
|
53
|
+
async getMembers(
|
|
54
|
+
server_prefix: string,
|
|
55
|
+
list_id: string,
|
|
56
|
+
status?: string,
|
|
57
|
+
count?: number,
|
|
58
|
+
offset?: number
|
|
59
|
+
): Promise<unknown> {
|
|
60
|
+
// Decorator auto-executes via matimo
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@tool('mailchimp-update-list-member')
|
|
65
|
+
async updateMember(
|
|
66
|
+
server_prefix: string,
|
|
67
|
+
list_id: string,
|
|
68
|
+
subscriber_hash: string,
|
|
69
|
+
merge_fields?: Record<string, string>
|
|
70
|
+
): Promise<unknown> {
|
|
71
|
+
// Decorator auto-executes via matimo
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@tool('mailchimp-create-campaign')
|
|
76
|
+
async createCampaign(
|
|
77
|
+
server_prefix: string,
|
|
78
|
+
type: string,
|
|
79
|
+
list_id: string,
|
|
80
|
+
subject_line: string,
|
|
81
|
+
preview_text?: string,
|
|
82
|
+
title?: string,
|
|
83
|
+
from_name?: string,
|
|
84
|
+
reply_to?: string
|
|
85
|
+
): Promise<unknown> {
|
|
86
|
+
// Decorator auto-executes via matimo
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ── Convenience helpers that bind server_prefix automatically ────────────
|
|
91
|
+
|
|
92
|
+
async fetchAudiences(count = 10) {
|
|
93
|
+
return this.getLists(this.sp, count);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async subscribe(
|
|
97
|
+
listId: string,
|
|
98
|
+
email: string,
|
|
99
|
+
firstName: string,
|
|
100
|
+
lastName: string,
|
|
101
|
+
tags: string[]
|
|
102
|
+
) {
|
|
103
|
+
return this.addMember(
|
|
104
|
+
this.sp,
|
|
105
|
+
listId,
|
|
106
|
+
email,
|
|
107
|
+
'subscribed',
|
|
108
|
+
{ FNAME: firstName, LNAME: lastName },
|
|
109
|
+
tags
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async listSubscribers(listId: string, count = 5) {
|
|
114
|
+
return this.getMembers(this.sp, listId, 'subscribed', count, 0);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async patchMember(listId: string, hash: string, mergeFields: Record<string, string>) {
|
|
118
|
+
return this.updateMember(this.sp, listId, hash, mergeFields);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async buildCampaign(listId: string, subjectLine: string, fromName: string, replyTo: string) {
|
|
122
|
+
return this.createCampaign(
|
|
123
|
+
this.sp,
|
|
124
|
+
'regular',
|
|
125
|
+
listId,
|
|
126
|
+
subjectLine,
|
|
127
|
+
'Built automatically by Matimo decorator example.', // preview_text
|
|
128
|
+
`Matimo Decorator Demo — ${new Date().toISOString().split('T')[0]}`, // title
|
|
129
|
+
fromName, // from_name
|
|
130
|
+
replyTo // reply_to
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function runMailchimpDecoratorFlow() {
|
|
136
|
+
console.info('\n╔════════════════════════════════════════════════════════╗');
|
|
137
|
+
console.info('║ Mailchimp - Decorator Pattern (Full CRUD Flow) ║');
|
|
138
|
+
console.info('╚════════════════════════════════════════════════════════╝\n');
|
|
139
|
+
|
|
140
|
+
const apiKey = process.env.MAILCHIMP_API_KEY;
|
|
141
|
+
if (!apiKey) {
|
|
142
|
+
console.error('❌ MAILCHIMP_API_KEY not set.');
|
|
143
|
+
console.info(' export MAILCHIMP_API_KEY="abc123def456-us6"');
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const serverPrefix = apiKey.split('-').pop()!;
|
|
148
|
+
console.info(`🔑 API Key is configured.. 📍 Server \n`);
|
|
149
|
+
|
|
150
|
+
console.info('🚀 Initializing Matimo...');
|
|
151
|
+
const matimo = await MatimoInstance.init({ autoDiscover: true });
|
|
152
|
+
setGlobalMatimoInstance(matimo);
|
|
153
|
+
console.info(
|
|
154
|
+
`✅ ${matimo.listTools().filter((t) => t.name.startsWith('mailchimp-')).length} Mailchimp tools loaded\n`
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
const agent = new MailchimpAgent(serverPrefix);
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
// ── STEP 1: GET AUDIENCES ─────────────────────────────────────────────
|
|
161
|
+
console.info('─'.repeat(60));
|
|
162
|
+
console.info('STEP 1 — Get Audiences [@tool: mailchimp-get-lists]');
|
|
163
|
+
console.info('─'.repeat(60));
|
|
164
|
+
|
|
165
|
+
const listsResult = await agent.fetchAudiences();
|
|
166
|
+
const listsData = (listsResult as any).data || listsResult;
|
|
167
|
+
|
|
168
|
+
if (!listsData.lists || listsData.lists.length === 0) {
|
|
169
|
+
console.error('❌ No audiences found. Create one at mailchimp.com first.');
|
|
170
|
+
process.exit(1);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
console.info(`✅ Found ${listsData.total_items} audience(s):`);
|
|
174
|
+
listsData.lists.forEach((list: any) => {
|
|
175
|
+
console.info(
|
|
176
|
+
` • "${list.name}" (${list.id}) — ${list.stats?.member_count ?? 0} subscribers`
|
|
177
|
+
);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
const audience = listsData.lists[0];
|
|
181
|
+
const listId: string = audience.id;
|
|
182
|
+
console.info(`\n ➡️ Using: "${audience.name}" (${listId})\n`);
|
|
183
|
+
|
|
184
|
+
// ── STEP 2: READ SUBSCRIBERS ─ pick a real contact ───────────────────
|
|
185
|
+
console.info('─'.repeat(60));
|
|
186
|
+
console.info('STEP 2 — Read Subscribers [@tool: mailchimp-get-list-members]');
|
|
187
|
+
console.info('─'.repeat(60));
|
|
188
|
+
|
|
189
|
+
const membersResult = await agent.listSubscribers(listId, 5);
|
|
190
|
+
const membersData = (membersResult as any).data || membersResult;
|
|
191
|
+
|
|
192
|
+
if (!membersData.members || membersData.members.length === 0) {
|
|
193
|
+
console.error('❌ No subscribed members found in this audience. Add a contact first.');
|
|
194
|
+
process.exit(1);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
console.info(`✅ ${membersData.total_items} total subscribed member(s) — showing up to 5:`);
|
|
198
|
+
membersData.members.forEach((m: any) => console.info(` • ${m.email_address} (${m.status})`));
|
|
199
|
+
|
|
200
|
+
// Use the first real contact from the list — no fake emails needed
|
|
201
|
+
const contact = membersData.members[0];
|
|
202
|
+
const subscriberHash: string = contact.id;
|
|
203
|
+
console.info(`\n ➡️ Using contact: ${contact.email_address} (hash: ${subscriberHash})\n`);
|
|
204
|
+
|
|
205
|
+
// ── STEP 3: UPDATE THE SUBSCRIBER ──────────────────────────────────────────
|
|
206
|
+
console.info('─'.repeat(60));
|
|
207
|
+
console.info('STEP 3 — Update Subscriber [@tool: mailchimp-update-list-member]');
|
|
208
|
+
console.info('─'.repeat(60));
|
|
209
|
+
|
|
210
|
+
try {
|
|
211
|
+
const updateResult = await agent.patchMember(
|
|
212
|
+
listId,
|
|
213
|
+
subscriberHash,
|
|
214
|
+
contact.merge_fields ?? {}
|
|
215
|
+
);
|
|
216
|
+
const updateData = (updateResult as any).data || updateResult;
|
|
217
|
+
|
|
218
|
+
if (updateData.id || updateData.email_address) {
|
|
219
|
+
console.info(`✅ Subscriber confirmed/updated:`);
|
|
220
|
+
console.info(` Email: ${updateData.email_address}`);
|
|
221
|
+
console.info(` Status: ${updateData.status}\n`);
|
|
222
|
+
} else {
|
|
223
|
+
console.info(`⚠️ Update response: ${JSON.stringify(updateData)}\n`);
|
|
224
|
+
}
|
|
225
|
+
} catch (updateError) {
|
|
226
|
+
const msg = updateError instanceof Error ? updateError.message : String(updateError);
|
|
227
|
+
console.info(`⚠️ Update skipped: ${msg}`);
|
|
228
|
+
console.info(` (This contact may have merge field validation issues in Mailchimp.)\n`);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// ── STEP 4: CREATE A CAMPAIGN ─────────────────────────────────────────
|
|
232
|
+
console.info('─'.repeat(60));
|
|
233
|
+
console.info('STEP 4 — Create Campaign [@tool: mailchimp-create-campaign]');
|
|
234
|
+
console.info('─'.repeat(60));
|
|
235
|
+
|
|
236
|
+
const campaignResult = await agent.buildCampaign(
|
|
237
|
+
listId,
|
|
238
|
+
'Welcome — created by Matimo Decorator',
|
|
239
|
+
'Matimo Demo',
|
|
240
|
+
contact.email_address
|
|
241
|
+
);
|
|
242
|
+
const campaignData = (campaignResult as any).data || campaignResult;
|
|
243
|
+
|
|
244
|
+
if (campaignData.id) {
|
|
245
|
+
console.info(`✅ Campaign created (not sent):`);
|
|
246
|
+
console.info(` Campaign ID: ${campaignData.id}`);
|
|
247
|
+
console.info(` Status: ${campaignData.status}`);
|
|
248
|
+
console.info(` Audience: "${audience.name}"\n`);
|
|
249
|
+
} else {
|
|
250
|
+
console.info(`⚠️ Campaign response: ${JSON.stringify(campaignData)}\n`);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// ── NEXT STEPS (approval required) ───────────────────────────────────
|
|
254
|
+
console.info('─'.repeat(60));
|
|
255
|
+
console.info('NEXT STEPS (these tools require requires_approval: true)');
|
|
256
|
+
console.info('─'.repeat(60));
|
|
257
|
+
console.info(
|
|
258
|
+
` • Send campaign: mailchimp-send-campaign { campaign_id: "${campaignData.id ?? '<id>'}" }`
|
|
259
|
+
);
|
|
260
|
+
console.info(
|
|
261
|
+
` • Remove subscriber: mailchimp-remove-list-member { list_id: "${listId}", subscriber_hash: "${subscriberHash}" }`
|
|
262
|
+
);
|
|
263
|
+
console.info();
|
|
264
|
+
} catch (error) {
|
|
265
|
+
console.error('❌ Error:', error);
|
|
266
|
+
process.exit(1);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
console.info('════════════════════════════════════════════════════════════');
|
|
270
|
+
console.info('✨ Full CRUD Flow Complete!');
|
|
271
|
+
console.info('════════════════════════════════════════════════════════════\n');
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
runMailchimpDecoratorFlow().catch((error) => {
|
|
275
|
+
console.error('❌ Fatal error:', error);
|
|
276
|
+
process.exit(1);
|
|
277
|
+
});
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* ============================================================================
|
|
4
|
+
* MAILCHIMP TOOLS - FACTORY PATTERN EXAMPLE
|
|
5
|
+
* ============================================================================
|
|
6
|
+
*
|
|
7
|
+
* PATTERN: SDK Factory Pattern — Complete CRUD Workflow
|
|
8
|
+
* ─────────────────────────────────────────────────────────────────────────
|
|
9
|
+
* This example runs a full end-to-end Mailchimp workflow using only the
|
|
10
|
+
* API key. No other configuration is needed — IDs are discovered at runtime
|
|
11
|
+
* by calling the tools themselves.
|
|
12
|
+
*
|
|
13
|
+
* FLOW:
|
|
14
|
+
* 1. GET — Fetch your audiences, pick the first one automatically
|
|
15
|
+
* 2. READ — List subscribers in that audience, pick the first real contact
|
|
16
|
+
* 3. UPDATE — Update that contact's name
|
|
17
|
+
* 4. CREATE — Build a campaign targeting the audience
|
|
18
|
+
* (send-campaign and remove-member require approval — shown as next steps)
|
|
19
|
+
*
|
|
20
|
+
* SETUP:
|
|
21
|
+
* ─────────────────────────────────────────────────────────────────────────
|
|
22
|
+
* export MAILCHIMP_API_KEY=abc123def456-us6
|
|
23
|
+
* pnpm mailchimp:factory
|
|
24
|
+
*
|
|
25
|
+
* Only MAILCHIMP_API_KEY is required. The server prefix and audience ID are
|
|
26
|
+
* discovered automatically by the tools.
|
|
27
|
+
*
|
|
28
|
+
* ============================================================================
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import 'dotenv/config';
|
|
32
|
+
import { MatimoInstance } from 'matimo';
|
|
33
|
+
|
|
34
|
+
async function runMailchimpFactoryFlow() {
|
|
35
|
+
console.info('\n╔════════════════════════════════════════════════════════╗');
|
|
36
|
+
console.info('║ Mailchimp - Factory Pattern (Full CRUD Flow) ║');
|
|
37
|
+
console.info('╚════════════════════════════════════════════════════════╝\n');
|
|
38
|
+
|
|
39
|
+
const apiKey = process.env.MAILCHIMP_API_KEY;
|
|
40
|
+
if (!apiKey) {
|
|
41
|
+
console.error('❌ MAILCHIMP_API_KEY not set.');
|
|
42
|
+
console.info(' export MAILCHIMP_API_KEY="abc123def456-us6"');
|
|
43
|
+
console.info(' Get one from: https://us1.admin.mailchimp.com/account/api/');
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Server prefix is the last segment of the API key (e.g. "us6" from "abc123-us6")
|
|
48
|
+
const serverPrefix = apiKey.split('-').pop()!;
|
|
49
|
+
console.info(`🔑 API Key: is configured... 📍 Server: \n`);
|
|
50
|
+
|
|
51
|
+
console.info('🚀 Initializing Matimo...');
|
|
52
|
+
const matimo = await MatimoInstance.init({ autoDiscover: true });
|
|
53
|
+
const mailchimpTools = matimo.listTools().filter((t) => t.name.startsWith('mailchimp-'));
|
|
54
|
+
console.info(`✅ ${mailchimpTools.length} Mailchimp tools loaded\n`);
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
// ── STEP 1: GET AUDIENCES ─────────────────────────────────────────────
|
|
58
|
+
console.info('─'.repeat(60));
|
|
59
|
+
console.info('STEP 1 — Get Audiences [mailchimp-get-lists]');
|
|
60
|
+
console.info('─'.repeat(60));
|
|
61
|
+
|
|
62
|
+
const listsResult = await matimo.execute('mailchimp-get-lists', {
|
|
63
|
+
server_prefix: serverPrefix,
|
|
64
|
+
count: 10,
|
|
65
|
+
});
|
|
66
|
+
const listsData = (listsResult as any).data || listsResult;
|
|
67
|
+
|
|
68
|
+
if (!listsData.lists || listsData.lists.length === 0) {
|
|
69
|
+
console.error('❌ No audiences found. Create one at mailchimp.com first.');
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
console.info(`✅ Found ${listsData.total_items} audience(s):`);
|
|
74
|
+
listsData.lists.forEach((list: any) => {
|
|
75
|
+
console.info(
|
|
76
|
+
` • "${list.name}" (${list.id}) — ${list.stats?.member_count ?? 0} subscribers`
|
|
77
|
+
);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Automatically pick the first audience — no manual ID needed
|
|
81
|
+
const audience = listsData.lists[0];
|
|
82
|
+
const listId: string = audience.id;
|
|
83
|
+
console.info(`\n ➡️ Using audience: "${audience.name}" (${listId})\n`);
|
|
84
|
+
|
|
85
|
+
// ── STEP 2: READ SUBSCRIBERS ─ pick a real contact ───────────────────
|
|
86
|
+
console.info('─'.repeat(60));
|
|
87
|
+
console.info('STEP 2 — Read Subscribers [mailchimp-get-list-members]');
|
|
88
|
+
console.info('─'.repeat(60));
|
|
89
|
+
|
|
90
|
+
const membersResult = await matimo.execute('mailchimp-get-list-members', {
|
|
91
|
+
server_prefix: serverPrefix,
|
|
92
|
+
list_id: listId,
|
|
93
|
+
status: 'subscribed',
|
|
94
|
+
count: 5,
|
|
95
|
+
offset: 0,
|
|
96
|
+
});
|
|
97
|
+
const membersData = (membersResult as any).data || membersResult;
|
|
98
|
+
|
|
99
|
+
if (!membersData.members || membersData.members.length === 0) {
|
|
100
|
+
console.error('❌ No subscribed members found in this audience. Add a contact first.');
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
console.info(`✅ ${membersData.total_items} total subscribed member(s) — showing up to 5:`);
|
|
105
|
+
membersData.members.forEach((m: any) => console.info(` • ${m.email_address} (${m.status})`));
|
|
106
|
+
|
|
107
|
+
// Use the first real contact from the list — no fake emails needed
|
|
108
|
+
const contact = membersData.members[0];
|
|
109
|
+
const subscriberHash: string = contact.id;
|
|
110
|
+
console.info(`\n ➡️ Using contact: ${contact.email_address} (hash: ${subscriberHash})\n`);
|
|
111
|
+
|
|
112
|
+
// ── STEP 3: UPDATE THE SUBSCRIBER ─────────────────────────────────────
|
|
113
|
+
console.info('─'.repeat(60));
|
|
114
|
+
console.info('STEP 3 — Update Subscriber [mailchimp-update-list-member]');
|
|
115
|
+
console.info('─'.repeat(60));
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
const updateResult = await matimo.execute('mailchimp-update-list-member', {
|
|
119
|
+
server_prefix: serverPrefix,
|
|
120
|
+
list_id: listId,
|
|
121
|
+
subscriber_hash: subscriberHash,
|
|
122
|
+
status: 'subscribed',
|
|
123
|
+
merge_fields: contact.merge_fields ?? {},
|
|
124
|
+
});
|
|
125
|
+
const updateData = (updateResult as any).data || updateResult;
|
|
126
|
+
|
|
127
|
+
if (updateData.id || updateData.email_address) {
|
|
128
|
+
console.info(`✅ Subscriber confirmed/updated:`);
|
|
129
|
+
console.info(` Email: ${updateData.email_address}`);
|
|
130
|
+
console.info(` Status: ${updateData.status}\n`);
|
|
131
|
+
} else {
|
|
132
|
+
console.info(`⚠️ Update response: ${JSON.stringify(updateData)}\n`);
|
|
133
|
+
}
|
|
134
|
+
} catch (updateError) {
|
|
135
|
+
const msg = updateError instanceof Error ? updateError.message : String(updateError);
|
|
136
|
+
console.info(`⚠️ Update skipped: ${msg}`);
|
|
137
|
+
console.info(` (This contact may have merge field validation issues in Mailchimp.)\n`);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// ── STEP 4: CREATE A CAMPAIGN ─────────────────────────────────────────
|
|
141
|
+
console.info('─'.repeat(60));
|
|
142
|
+
console.info('STEP 4 — Create Campaign [mailchimp-create-campaign]');
|
|
143
|
+
console.info('─'.repeat(60));
|
|
144
|
+
|
|
145
|
+
const campaignResult = await matimo.execute('mailchimp-create-campaign', {
|
|
146
|
+
server_prefix: serverPrefix,
|
|
147
|
+
type: 'regular',
|
|
148
|
+
list_id: listId,
|
|
149
|
+
subject_line: 'Welcome — created by Matimo',
|
|
150
|
+
preview_text: 'This campaign was built automatically by the Matimo factory example.',
|
|
151
|
+
title: `Matimo Factory Demo — ${new Date().toISOString().split('T')[0]}`,
|
|
152
|
+
from_name: 'Matimo Demo',
|
|
153
|
+
reply_to: contact.email_address,
|
|
154
|
+
});
|
|
155
|
+
const campaignData = (campaignResult as any).data || campaignResult;
|
|
156
|
+
|
|
157
|
+
if (campaignData.id) {
|
|
158
|
+
console.info(`✅ Campaign created (not sent):`);
|
|
159
|
+
console.info(` Campaign ID: ${campaignData.id}`);
|
|
160
|
+
console.info(` Status: ${campaignData.status}`);
|
|
161
|
+
console.info(` Audience: "${audience.name}"\n`);
|
|
162
|
+
} else {
|
|
163
|
+
console.info(`⚠️ Campaign response: ${JSON.stringify(campaignData)}\n`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// ── NEXT STEPS (approval required) ───────────────────────────────────
|
|
167
|
+
console.info('─'.repeat(60));
|
|
168
|
+
console.info('NEXT STEPS (these tools require approval flag: true)');
|
|
169
|
+
console.info('─'.repeat(60));
|
|
170
|
+
console.info(
|
|
171
|
+
` • Send campaign: mailchimp-send-campaign { campaign_id: "${campaignData.id ?? '<id>'}" }`
|
|
172
|
+
);
|
|
173
|
+
console.info(
|
|
174
|
+
` • Remove subscriber: mailchimp-remove-list-member { list_id: "${listId}", subscriber_hash: "${subscriberHash}" }`
|
|
175
|
+
);
|
|
176
|
+
console.info();
|
|
177
|
+
} catch (error) {
|
|
178
|
+
console.error('❌ Error:', error);
|
|
179
|
+
process.exit(1);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
console.info('════════════════════════════════════════════════════════════');
|
|
183
|
+
console.info('✨ Full CRUD Flow Complete!');
|
|
184
|
+
console.info('════════════════════════════════════════════════════════════\n');
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
runMailchimpFactoryFlow().catch(console.error);
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* ============================================================================
|
|
4
|
+
* MAILCHIMP TOOLS - LANGCHAIN AI AGENT EXAMPLE
|
|
5
|
+
* ============================================================================
|
|
6
|
+
*
|
|
7
|
+
* PATTERN: Autonomous AI Agent — Goal-Driven Workflow
|
|
8
|
+
* ─────────────────────────────────────────────────────────────────────────
|
|
9
|
+
* The agent receives BUSINESS GOALS in natural language — it decides which
|
|
10
|
+
* Mailchimp tools to call, in what order, and how to chain results between
|
|
11
|
+
* them. No tool names, no API concepts, no IDs are given in the prompts.
|
|
12
|
+
*
|
|
13
|
+
* This is the point of Matimo + LangChain: define tools once, let the
|
|
14
|
+
* agent figure out how to use them.
|
|
15
|
+
*
|
|
16
|
+
* GOALS given to the agent (purely business language):
|
|
17
|
+
* 1. "Show me all my mailing lists"
|
|
18
|
+
* 2. "Who are the subscribers in my first list?"
|
|
19
|
+
* 3. "Update the first contact's profile"
|
|
20
|
+
* 4. "Create a new email campaign for my first list"
|
|
21
|
+
*
|
|
22
|
+
* SETUP:
|
|
23
|
+
* ─────────────────────────────────────────────────────────────────────────
|
|
24
|
+
* export MAILCHIMP_API_KEY=abc123def456-us6
|
|
25
|
+
* export OPENAI_API_KEY=sk-xxxxxxxxxxxxx
|
|
26
|
+
* pnpm mailchimp:langchain
|
|
27
|
+
*
|
|
28
|
+
* ============================================================================
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import 'dotenv/config';
|
|
32
|
+
import { createAgent } from 'langchain';
|
|
33
|
+
import { ChatOpenAI } from '@langchain/openai';
|
|
34
|
+
import { MatimoInstance, convertToolsToLangChain } from 'matimo';
|
|
35
|
+
|
|
36
|
+
async function runMailchimpAIAgent() {
|
|
37
|
+
console.info('\n╔════════════════════════════════════════════════════════╗');
|
|
38
|
+
console.info('║ Mailchimp - LangChain Autonomous Agent ║');
|
|
39
|
+
console.info('╚════════════════════════════════════════════════════════╝\n');
|
|
40
|
+
|
|
41
|
+
const apiKey = process.env.MAILCHIMP_API_KEY;
|
|
42
|
+
if (!apiKey) {
|
|
43
|
+
console.error('❌ MAILCHIMP_API_KEY not set.');
|
|
44
|
+
console.info(' export MAILCHIMP_API_KEY="abc123def456-us6"');
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!process.env.OPENAI_API_KEY) {
|
|
49
|
+
console.error('❌ OPENAI_API_KEY not set.');
|
|
50
|
+
console.info(' export OPENAI_API_KEY="sk-..."');
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// The server prefix is infrastructure context required by every Mailchimp
|
|
55
|
+
// API call — it is derived from the API key, not a business concept.
|
|
56
|
+
const serverPrefix = apiKey.split('-').pop()!;
|
|
57
|
+
console.info(`🔑 API Key is configured... 📍 Server`);
|
|
58
|
+
console.info(`🤖 Using GPT-4o-mini\n`);
|
|
59
|
+
|
|
60
|
+
// ── Initialize Matimo ────────────────────────────────────────────────────
|
|
61
|
+
console.info('🚀 Initializing Matimo...');
|
|
62
|
+
const matimo = await MatimoInstance.init({ autoDiscover: true });
|
|
63
|
+
|
|
64
|
+
const mailchimpToolDefs = matimo.listTools().filter((t) => t.name.startsWith('mailchimp-'));
|
|
65
|
+
|
|
66
|
+
console.info(`✅ ${mailchimpToolDefs.length} Mailchimp tools registered:`);
|
|
67
|
+
mailchimpToolDefs.forEach((t) => console.info(` 🔧 ${t.name}`));
|
|
68
|
+
console.info();
|
|
69
|
+
|
|
70
|
+
// Convert Matimo tools to LangChain format — inject the API key once
|
|
71
|
+
const langchainTools = await convertToolsToLangChain(mailchimpToolDefs as any[], matimo, {
|
|
72
|
+
MAILCHIMP_API_KEY: apiKey,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// ── Build the agent ──────────────────────────────────────────────────────
|
|
76
|
+
const model = new ChatOpenAI({ modelName: 'gpt-4o-mini', temperature: 0 });
|
|
77
|
+
|
|
78
|
+
console.info('🔧 Creating agent...\n');
|
|
79
|
+
const agent = await createAgent({ model, tools: langchainTools as any[] });
|
|
80
|
+
|
|
81
|
+
// ── Business goals — agent decides which tools to use ───────────────────
|
|
82
|
+
//
|
|
83
|
+
// The prompts contain ONLY business intent. The agent reads the tool
|
|
84
|
+
// descriptions and autonomously chooses, orders, and chains tool calls.
|
|
85
|
+
//
|
|
86
|
+
// The server_prefix is passed as infrastructure context (every Mailchimp
|
|
87
|
+
// API call requires it), not as a hint about which tool to use.
|
|
88
|
+
//
|
|
89
|
+
const context = `My Mailchimp server prefix is "${serverPrefix}".`;
|
|
90
|
+
|
|
91
|
+
const goals = [
|
|
92
|
+
{
|
|
93
|
+
label: 'Goal 1 — What mailing lists do I have?',
|
|
94
|
+
prompt: `${context} Show me all my mailing lists with their names and subscriber counts.`,
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
label: 'Goal 2 — Who are my subscribers?',
|
|
98
|
+
prompt:
|
|
99
|
+
`${context} Show me the 5 most recent active subscribers in my first mailing list. ` +
|
|
100
|
+
`Include their email addresses and statuses.`,
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
label: "Goal 3 — Update a contact's profile",
|
|
104
|
+
prompt:
|
|
105
|
+
`${context} Get my first mailing list and find the first subscriber. ` +
|
|
106
|
+
`Confirm they are still subscribed by updating their status to subscribed. Confirm the update.`,
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
label: 'Goal 4 — Draft a new email campaign',
|
|
110
|
+
prompt:
|
|
111
|
+
`${context} Create a draft email campaign for my first mailing list. ` +
|
|
112
|
+
`Use the first subscriber's email as the reply-to address. ` +
|
|
113
|
+
`Subject: "Welcome from Matimo Agent", sender name: "Matimo Demo". ` +
|
|
114
|
+
`Do not send it yet. Give me the campaign ID.`,
|
|
115
|
+
},
|
|
116
|
+
];
|
|
117
|
+
|
|
118
|
+
console.info('🧠 Running Goals — Agent Decides Which Tools to Use');
|
|
119
|
+
console.info('═'.repeat(60));
|
|
120
|
+
|
|
121
|
+
for (const goal of goals) {
|
|
122
|
+
console.info(`\n📋 ${goal.label}`);
|
|
123
|
+
console.info('─'.repeat(60));
|
|
124
|
+
const userPrompt = goal.prompt.replace(`${serverPrefix}`, '<server-prefix>');
|
|
125
|
+
console.info(`👤 User: "${userPrompt}"\n`);
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
const response = await agent.invoke({
|
|
129
|
+
messages: [{ role: 'user', content: goal.prompt }],
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const lastMessage = response.messages[response.messages.length - 1];
|
|
133
|
+
if (lastMessage) {
|
|
134
|
+
const content =
|
|
135
|
+
typeof lastMessage.content === 'string'
|
|
136
|
+
? lastMessage.content
|
|
137
|
+
: JSON.stringify(lastMessage.content);
|
|
138
|
+
console.info(`🤖 Agent: ${content}\n`);
|
|
139
|
+
}
|
|
140
|
+
} catch (error) {
|
|
141
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
142
|
+
console.info(`⚠️ Agent error: ${msg}\n`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
console.info('═'.repeat(60));
|
|
147
|
+
console.info('✨ Autonomous Agent Workflow Complete!\n');
|
|
148
|
+
console.info('What the agent did on its own:');
|
|
149
|
+
console.info(' ✅ Chose the right tool for each business goal');
|
|
150
|
+
console.info(' ✅ Discovered audience IDs without being told to');
|
|
151
|
+
console.info(' ✅ Chained tool outputs (IDs, hashes) into subsequent calls');
|
|
152
|
+
console.info(' ✅ Only MAILCHIMP_API_KEY was required from the user\n');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
runMailchimpAIAgent().catch(console.error);
|