@hasna/connectors 0.3.16 → 0.4.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 (127) hide show
  1. package/bin/index.js +71 -1
  2. package/bin/mcp.js +71 -1
  3. package/bin/serve.js +70 -0
  4. package/connectors/connect-asana/.env.example +11 -0
  5. package/connectors/connect-asana/CLAUDE.md +128 -0
  6. package/connectors/connect-asana/README.md +193 -0
  7. package/connectors/connect-asana/package.json +52 -0
  8. package/connectors/connect-asana/src/api/client.ts +119 -0
  9. package/connectors/connect-asana/src/api/index.ts +319 -0
  10. package/connectors/connect-asana/src/cli/index.ts +731 -0
  11. package/connectors/connect-asana/src/index.ts +19 -0
  12. package/connectors/connect-asana/src/types/index.ts +270 -0
  13. package/connectors/connect-asana/src/utils/config.ts +171 -0
  14. package/connectors/connect-asana/src/utils/output.ts +119 -0
  15. package/connectors/connect-asana/tsconfig.json +16 -0
  16. package/connectors/connect-clickup/.env.example +11 -0
  17. package/connectors/connect-clickup/CLAUDE.md +128 -0
  18. package/connectors/connect-clickup/README.md +193 -0
  19. package/connectors/connect-clickup/package.json +52 -0
  20. package/connectors/connect-clickup/src/api/client.ts +116 -0
  21. package/connectors/connect-clickup/src/api/index.ts +400 -0
  22. package/connectors/connect-clickup/src/cli/index.ts +625 -0
  23. package/connectors/connect-clickup/src/index.ts +19 -0
  24. package/connectors/connect-clickup/src/types/index.ts +591 -0
  25. package/connectors/connect-clickup/src/utils/config.ts +157 -0
  26. package/connectors/connect-clickup/src/utils/output.ts +119 -0
  27. package/connectors/connect-clickup/tsconfig.json +16 -0
  28. package/connectors/connect-confluence/.env.example +11 -0
  29. package/connectors/connect-confluence/CLAUDE.md +272 -0
  30. package/connectors/connect-confluence/README.md +193 -0
  31. package/connectors/connect-confluence/package.json +53 -0
  32. package/connectors/connect-confluence/scripts/release.ts +179 -0
  33. package/connectors/connect-confluence/src/api/client.ts +213 -0
  34. package/connectors/connect-confluence/src/api/example.ts +48 -0
  35. package/connectors/connect-confluence/src/api/index.ts +51 -0
  36. package/connectors/connect-confluence/src/cli/index.ts +254 -0
  37. package/connectors/connect-confluence/src/index.ts +103 -0
  38. package/connectors/connect-confluence/src/types/index.ts +237 -0
  39. package/connectors/connect-confluence/src/utils/auth.ts +274 -0
  40. package/connectors/connect-confluence/src/utils/bulk.ts +212 -0
  41. package/connectors/connect-confluence/src/utils/config.ts +326 -0
  42. package/connectors/connect-confluence/src/utils/output.ts +175 -0
  43. package/connectors/connect-confluence/src/utils/settings.ts +114 -0
  44. package/connectors/connect-confluence/src/utils/storage.ts +198 -0
  45. package/connectors/connect-confluence/tsconfig.json +16 -0
  46. package/connectors/connect-jira/.env.example +11 -0
  47. package/connectors/connect-jira/CLAUDE.md +128 -0
  48. package/connectors/connect-jira/README.md +193 -0
  49. package/connectors/connect-jira/package.json +53 -0
  50. package/connectors/connect-jira/src/api/client.ts +131 -0
  51. package/connectors/connect-jira/src/api/index.ts +266 -0
  52. package/connectors/connect-jira/src/cli/index.ts +653 -0
  53. package/connectors/connect-jira/src/index.ts +23 -0
  54. package/connectors/connect-jira/src/types/index.ts +448 -0
  55. package/connectors/connect-jira/src/utils/config.ts +179 -0
  56. package/connectors/connect-jira/src/utils/output.ts +119 -0
  57. package/connectors/connect-jira/tsconfig.json +16 -0
  58. package/connectors/connect-linear/CLAUDE.md +88 -0
  59. package/connectors/connect-linear/README.md +201 -0
  60. package/connectors/connect-linear/package.json +45 -0
  61. package/connectors/connect-linear/src/api/client.ts +62 -0
  62. package/connectors/connect-linear/src/api/index.ts +46 -0
  63. package/connectors/connect-linear/src/api/issues.ts +247 -0
  64. package/connectors/connect-linear/src/api/projects.ts +179 -0
  65. package/connectors/connect-linear/src/api/teams.ts +125 -0
  66. package/connectors/connect-linear/src/api/users.ts +112 -0
  67. package/connectors/connect-linear/src/cli/index.ts +560 -0
  68. package/connectors/connect-linear/src/index.ts +27 -0
  69. package/connectors/connect-linear/src/types/index.ts +275 -0
  70. package/connectors/connect-linear/src/utils/config.ts +249 -0
  71. package/connectors/connect-linear/src/utils/output.ts +119 -0
  72. package/connectors/connect-linear/tsconfig.json +16 -0
  73. package/connectors/connect-slack/.env.example +7 -0
  74. package/connectors/connect-slack/CLAUDE.md +69 -0
  75. package/connectors/connect-slack/README.md +150 -0
  76. package/connectors/connect-slack/package.json +44 -0
  77. package/connectors/connect-slack/src/api/channels.ts +112 -0
  78. package/connectors/connect-slack/src/api/client.ts +97 -0
  79. package/connectors/connect-slack/src/api/index.ts +42 -0
  80. package/connectors/connect-slack/src/api/messages.ts +127 -0
  81. package/connectors/connect-slack/src/api/users.ts +110 -0
  82. package/connectors/connect-slack/src/cli/index.ts +494 -0
  83. package/connectors/connect-slack/src/index.ts +21 -0
  84. package/connectors/connect-slack/src/types/index.ts +263 -0
  85. package/connectors/connect-slack/src/utils/config.ts +297 -0
  86. package/connectors/connect-slack/src/utils/output.ts +119 -0
  87. package/connectors/connect-slack/tsconfig.json +16 -0
  88. package/connectors/connect-telegram/.env.example +2 -0
  89. package/connectors/connect-telegram/package.json +49 -0
  90. package/connectors/connect-todoist/.env.example +11 -0
  91. package/connectors/connect-todoist/CLAUDE.md +104 -0
  92. package/connectors/connect-todoist/README.md +193 -0
  93. package/connectors/connect-todoist/package.json +52 -0
  94. package/connectors/connect-todoist/src/api/client.ts +117 -0
  95. package/connectors/connect-todoist/src/api/index.ts +188 -0
  96. package/connectors/connect-todoist/src/cli/index.ts +990 -0
  97. package/connectors/connect-todoist/src/index.ts +21 -0
  98. package/connectors/connect-todoist/src/types/index.ts +240 -0
  99. package/connectors/connect-todoist/src/utils/config.ts +157 -0
  100. package/connectors/connect-todoist/src/utils/output.ts +119 -0
  101. package/connectors/connect-todoist/tsconfig.json +16 -0
  102. package/connectors/connect-trello/.env.example +11 -0
  103. package/connectors/connect-trello/CLAUDE.md +128 -0
  104. package/connectors/connect-trello/README.md +193 -0
  105. package/connectors/connect-trello/package.json +53 -0
  106. package/connectors/connect-trello/src/api/client.ts +128 -0
  107. package/connectors/connect-trello/src/api/index.ts +278 -0
  108. package/connectors/connect-trello/src/cli/index.ts +737 -0
  109. package/connectors/connect-trello/src/index.ts +21 -0
  110. package/connectors/connect-trello/src/types/index.ts +314 -0
  111. package/connectors/connect-trello/src/utils/config.ts +182 -0
  112. package/connectors/connect-trello/src/utils/output.ts +119 -0
  113. package/connectors/connect-trello/tsconfig.json +16 -0
  114. package/connectors/connect-whatsapp/.env.example +11 -0
  115. package/connectors/connect-whatsapp/CLAUDE.md +113 -0
  116. package/connectors/connect-whatsapp/README.md +193 -0
  117. package/connectors/connect-whatsapp/package.json +53 -0
  118. package/connectors/connect-whatsapp/src/api/client.ts +133 -0
  119. package/connectors/connect-whatsapp/src/api/index.ts +365 -0
  120. package/connectors/connect-whatsapp/src/cli/index.ts +686 -0
  121. package/connectors/connect-whatsapp/src/index.ts +25 -0
  122. package/connectors/connect-whatsapp/src/types/index.ts +502 -0
  123. package/connectors/connect-whatsapp/src/utils/config.ts +179 -0
  124. package/connectors/connect-whatsapp/src/utils/output.ts +119 -0
  125. package/connectors/connect-whatsapp/tsconfig.json +16 -0
  126. package/dist/index.js +70 -0
  127. package/package.json +1 -1
@@ -0,0 +1,502 @@
1
+ // WhatsApp Business Cloud Connector Types
2
+ // Send messages, manage templates, and handle webhooks
3
+
4
+ // ============================================
5
+ // Configuration
6
+ // ============================================
7
+
8
+ export interface WhatsAppConfig {
9
+ accessToken: string;
10
+ phoneNumberId: string;
11
+ businessAccountId?: string;
12
+ baseUrl?: string;
13
+ }
14
+
15
+ // ============================================
16
+ // Common Types
17
+ // ============================================
18
+
19
+ export type OutputFormat = 'json' | 'pretty';
20
+
21
+ export type MessageType = 'text' | 'image' | 'audio' | 'video' | 'document' | 'sticker' | 'location' | 'contacts' | 'interactive' | 'template' | 'reaction';
22
+
23
+ export type MessageStatus = 'sent' | 'delivered' | 'read' | 'failed';
24
+
25
+ // ============================================
26
+ // Contact Types
27
+ // ============================================
28
+
29
+ export interface Contact {
30
+ addresses?: Address[];
31
+ birthday?: string;
32
+ emails?: Email[];
33
+ name: Name;
34
+ org?: Organization;
35
+ phones?: Phone[];
36
+ urls?: Url[];
37
+ }
38
+
39
+ export interface Address {
40
+ street?: string;
41
+ city?: string;
42
+ state?: string;
43
+ zip?: string;
44
+ country?: string;
45
+ country_code?: string;
46
+ type?: 'HOME' | 'WORK';
47
+ }
48
+
49
+ export interface Email {
50
+ email?: string;
51
+ type?: 'HOME' | 'WORK';
52
+ }
53
+
54
+ export interface Name {
55
+ formatted_name: string;
56
+ first_name?: string;
57
+ last_name?: string;
58
+ middle_name?: string;
59
+ suffix?: string;
60
+ prefix?: string;
61
+ }
62
+
63
+ export interface Organization {
64
+ company?: string;
65
+ department?: string;
66
+ title?: string;
67
+ }
68
+
69
+ export interface Phone {
70
+ phone?: string;
71
+ type?: 'CELL' | 'MAIN' | 'IPHONE' | 'HOME' | 'WORK';
72
+ wa_id?: string;
73
+ }
74
+
75
+ export interface Url {
76
+ url?: string;
77
+ type?: 'HOME' | 'WORK';
78
+ }
79
+
80
+ // ============================================
81
+ // Media Types
82
+ // ============================================
83
+
84
+ export interface Media {
85
+ id?: string;
86
+ link?: string;
87
+ caption?: string;
88
+ filename?: string;
89
+ }
90
+
91
+ export interface MediaObject {
92
+ messaging_product: string;
93
+ url: string;
94
+ mime_type: string;
95
+ sha256: string;
96
+ file_size: number;
97
+ id: string;
98
+ }
99
+
100
+ // ============================================
101
+ // Location Types
102
+ // ============================================
103
+
104
+ export interface Location {
105
+ longitude: number;
106
+ latitude: number;
107
+ name?: string;
108
+ address?: string;
109
+ }
110
+
111
+ // ============================================
112
+ // Interactive Message Types
113
+ // ============================================
114
+
115
+ export interface Interactive {
116
+ type: 'button' | 'list' | 'product' | 'product_list' | 'flow' | 'cta_url';
117
+ header?: InteractiveHeader;
118
+ body: InteractiveBody;
119
+ footer?: InteractiveFooter;
120
+ action: InteractiveAction;
121
+ }
122
+
123
+ export interface InteractiveHeader {
124
+ type: 'text' | 'image' | 'video' | 'document';
125
+ text?: string;
126
+ image?: Media;
127
+ video?: Media;
128
+ document?: Media;
129
+ }
130
+
131
+ export interface InteractiveBody {
132
+ text: string;
133
+ }
134
+
135
+ export interface InteractiveFooter {
136
+ text: string;
137
+ }
138
+
139
+ export interface InteractiveAction {
140
+ button?: string;
141
+ buttons?: InteractiveButton[];
142
+ sections?: InteractiveSection[];
143
+ catalog_id?: string;
144
+ product_retailer_id?: string;
145
+ name?: string;
146
+ parameters?: Record<string, unknown>;
147
+ }
148
+
149
+ export interface InteractiveButton {
150
+ type: 'reply';
151
+ reply: {
152
+ id: string;
153
+ title: string;
154
+ };
155
+ }
156
+
157
+ export interface InteractiveSection {
158
+ title?: string;
159
+ rows?: InteractiveRow[];
160
+ product_items?: ProductItem[];
161
+ }
162
+
163
+ export interface InteractiveRow {
164
+ id: string;
165
+ title: string;
166
+ description?: string;
167
+ }
168
+
169
+ export interface ProductItem {
170
+ product_retailer_id: string;
171
+ }
172
+
173
+ // ============================================
174
+ // Template Types
175
+ // ============================================
176
+
177
+ export interface Template {
178
+ name: string;
179
+ language: TemplateLanguage;
180
+ components?: TemplateComponent[];
181
+ }
182
+
183
+ export interface TemplateLanguage {
184
+ code: string;
185
+ }
186
+
187
+ export interface TemplateComponent {
188
+ type: 'header' | 'body' | 'button';
189
+ sub_type?: 'quick_reply' | 'url';
190
+ index?: string;
191
+ parameters?: TemplateParameter[];
192
+ }
193
+
194
+ export interface TemplateParameter {
195
+ type: 'text' | 'currency' | 'date_time' | 'image' | 'document' | 'video' | 'payload';
196
+ text?: string;
197
+ currency?: TemplateCurrency;
198
+ date_time?: TemplateDateTime;
199
+ image?: Media;
200
+ document?: Media;
201
+ video?: Media;
202
+ payload?: string;
203
+ }
204
+
205
+ export interface TemplateCurrency {
206
+ fallback_value: string;
207
+ code: string;
208
+ amount_1000: number;
209
+ }
210
+
211
+ export interface TemplateDateTime {
212
+ fallback_value: string;
213
+ }
214
+
215
+ // ============================================
216
+ // Reaction Types
217
+ // ============================================
218
+
219
+ export interface Reaction {
220
+ message_id: string;
221
+ emoji: string;
222
+ }
223
+
224
+ // ============================================
225
+ // Message Types
226
+ // ============================================
227
+
228
+ export interface TextMessage {
229
+ body: string;
230
+ preview_url?: boolean;
231
+ }
232
+
233
+ export interface SendMessageInput {
234
+ messaging_product: 'whatsapp';
235
+ recipient_type?: 'individual';
236
+ to: string;
237
+ type: MessageType;
238
+ text?: TextMessage;
239
+ image?: Media;
240
+ audio?: Media;
241
+ video?: Media;
242
+ document?: Media;
243
+ sticker?: Media;
244
+ location?: Location;
245
+ contacts?: Contact[];
246
+ interactive?: Interactive;
247
+ template?: Template;
248
+ reaction?: Reaction;
249
+ context?: {
250
+ message_id: string;
251
+ };
252
+ }
253
+
254
+ export interface SendMessageResponse {
255
+ messaging_product: string;
256
+ contacts: {
257
+ input: string;
258
+ wa_id: string;
259
+ }[];
260
+ messages: {
261
+ id: string;
262
+ message_status?: string;
263
+ }[];
264
+ }
265
+
266
+ // ============================================
267
+ // Business Profile Types
268
+ // ============================================
269
+
270
+ export interface BusinessProfile {
271
+ messaging_product: string;
272
+ address?: string;
273
+ description?: string;
274
+ email?: string;
275
+ profile_picture_url?: string;
276
+ websites?: string[];
277
+ vertical?: string;
278
+ about?: string;
279
+ }
280
+
281
+ // ============================================
282
+ // Phone Number Types
283
+ // ============================================
284
+
285
+ export interface PhoneNumber {
286
+ id: string;
287
+ verified_name: string;
288
+ code_verification_status: string;
289
+ display_phone_number: string;
290
+ quality_rating: string;
291
+ platform_type?: string;
292
+ throughput?: {
293
+ level: string;
294
+ };
295
+ }
296
+
297
+ export interface PhoneNumbersResponse {
298
+ data: PhoneNumber[];
299
+ paging?: {
300
+ cursors: {
301
+ before: string;
302
+ after: string;
303
+ };
304
+ };
305
+ }
306
+
307
+ // ============================================
308
+ // Template Management Types
309
+ // ============================================
310
+
311
+ export interface MessageTemplate {
312
+ id: string;
313
+ name: string;
314
+ status: 'APPROVED' | 'PENDING' | 'REJECTED' | 'DISABLED';
315
+ category: 'MARKETING' | 'UTILITY' | 'AUTHENTICATION';
316
+ language: string;
317
+ components?: MessageTemplateComponent[];
318
+ }
319
+
320
+ export interface MessageTemplateComponent {
321
+ type: 'HEADER' | 'BODY' | 'FOOTER' | 'BUTTONS';
322
+ format?: 'TEXT' | 'IMAGE' | 'VIDEO' | 'DOCUMENT';
323
+ text?: string;
324
+ example?: {
325
+ header_text?: string[];
326
+ body_text?: string[][];
327
+ };
328
+ buttons?: MessageTemplateButton[];
329
+ }
330
+
331
+ export interface MessageTemplateButton {
332
+ type: 'PHONE_NUMBER' | 'URL' | 'QUICK_REPLY';
333
+ text: string;
334
+ phone_number?: string;
335
+ url?: string;
336
+ }
337
+
338
+ export interface MessageTemplatesResponse {
339
+ data: MessageTemplate[];
340
+ paging?: {
341
+ cursors: {
342
+ before: string;
343
+ after: string;
344
+ };
345
+ next?: string;
346
+ };
347
+ }
348
+
349
+ // ============================================
350
+ // Webhook Types
351
+ // ============================================
352
+
353
+ export interface WebhookPayload {
354
+ object: string;
355
+ entry: WebhookEntry[];
356
+ }
357
+
358
+ export interface WebhookEntry {
359
+ id: string;
360
+ changes: WebhookChange[];
361
+ }
362
+
363
+ export interface WebhookChange {
364
+ value: WebhookValue;
365
+ field: string;
366
+ }
367
+
368
+ export interface WebhookValue {
369
+ messaging_product: string;
370
+ metadata: {
371
+ display_phone_number: string;
372
+ phone_number_id: string;
373
+ };
374
+ contacts?: WebhookContact[];
375
+ messages?: WebhookMessage[];
376
+ statuses?: WebhookStatus[];
377
+ errors?: WebhookError[];
378
+ }
379
+
380
+ export interface WebhookContact {
381
+ profile: {
382
+ name: string;
383
+ };
384
+ wa_id: string;
385
+ }
386
+
387
+ export interface WebhookMessage {
388
+ from: string;
389
+ id: string;
390
+ timestamp: string;
391
+ type: MessageType;
392
+ text?: TextMessage;
393
+ image?: MediaWebhook;
394
+ audio?: MediaWebhook;
395
+ video?: MediaWebhook;
396
+ document?: MediaWebhook;
397
+ sticker?: MediaWebhook;
398
+ location?: Location;
399
+ contacts?: Contact[];
400
+ interactive?: InteractiveWebhook;
401
+ button?: ButtonWebhook;
402
+ context?: {
403
+ from: string;
404
+ id: string;
405
+ };
406
+ }
407
+
408
+ export interface MediaWebhook {
409
+ id: string;
410
+ mime_type: string;
411
+ sha256?: string;
412
+ caption?: string;
413
+ filename?: string;
414
+ }
415
+
416
+ export interface InteractiveWebhook {
417
+ type: string;
418
+ button_reply?: {
419
+ id: string;
420
+ title: string;
421
+ };
422
+ list_reply?: {
423
+ id: string;
424
+ title: string;
425
+ description?: string;
426
+ };
427
+ }
428
+
429
+ export interface ButtonWebhook {
430
+ text: string;
431
+ payload: string;
432
+ }
433
+
434
+ export interface WebhookStatus {
435
+ id: string;
436
+ status: MessageStatus;
437
+ timestamp: string;
438
+ recipient_id: string;
439
+ conversation?: {
440
+ id: string;
441
+ expiration_timestamp?: string;
442
+ origin?: {
443
+ type: string;
444
+ };
445
+ };
446
+ pricing?: {
447
+ billable: boolean;
448
+ pricing_model: string;
449
+ category: string;
450
+ };
451
+ }
452
+
453
+ export interface WebhookError {
454
+ code: number;
455
+ title: string;
456
+ message: string;
457
+ error_data?: {
458
+ details: string;
459
+ };
460
+ }
461
+
462
+ // ============================================
463
+ // API Error Types
464
+ // ============================================
465
+
466
+ export interface WhatsAppError {
467
+ message: string;
468
+ type: string;
469
+ code: number;
470
+ error_subcode?: number;
471
+ error_data?: {
472
+ messaging_product: string;
473
+ details: string;
474
+ };
475
+ fbtrace_id: string;
476
+ }
477
+
478
+ export interface WhatsAppErrorResponse {
479
+ error: WhatsAppError;
480
+ }
481
+
482
+ export class WhatsAppApiError extends Error {
483
+ public readonly statusCode: number;
484
+ public readonly errorCode: number;
485
+ public readonly errorSubcode?: number;
486
+ public readonly fbtraceId: string;
487
+
488
+ constructor(
489
+ message: string,
490
+ statusCode: number,
491
+ errorCode: number,
492
+ fbtraceId: string,
493
+ errorSubcode?: number
494
+ ) {
495
+ super(message);
496
+ this.name = 'WhatsAppApiError';
497
+ this.statusCode = statusCode;
498
+ this.errorCode = errorCode;
499
+ this.errorSubcode = errorSubcode;
500
+ this.fbtraceId = fbtraceId;
501
+ }
502
+ }
@@ -0,0 +1,179 @@
1
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, rmSync } from 'fs';
2
+ import { homedir } from 'os';
3
+ import { join } from 'path';
4
+
5
+ const CONNECTOR_NAME = 'connect-whatsapp';
6
+ const DEFAULT_PROFILE = 'default';
7
+
8
+ export interface ProfileConfig {
9
+ accessToken?: string;
10
+ phoneNumberId?: string;
11
+ businessAccountId?: string;
12
+ }
13
+
14
+ let profileOverride: string | undefined;
15
+
16
+ const CONFIG_DIR = join(homedir(), '.connect', CONNECTOR_NAME);
17
+ const PROFILES_DIR = join(CONFIG_DIR, 'profiles');
18
+ const CURRENT_PROFILE_FILE = join(CONFIG_DIR, 'current_profile');
19
+
20
+ export function setProfileOverride(profile: string | undefined): void {
21
+ profileOverride = profile;
22
+ }
23
+
24
+ export function ensureConfigDir(): void {
25
+ if (!existsSync(CONFIG_DIR)) {
26
+ mkdirSync(CONFIG_DIR, { recursive: true });
27
+ }
28
+ if (!existsSync(PROFILES_DIR)) {
29
+ mkdirSync(PROFILES_DIR, { recursive: true });
30
+ }
31
+ }
32
+
33
+ function getProfilePath(profile: string): string {
34
+ return join(PROFILES_DIR, `${profile}.json`);
35
+ }
36
+
37
+ export function getCurrentProfile(): string {
38
+ if (profileOverride) {
39
+ return profileOverride;
40
+ }
41
+
42
+ ensureConfigDir();
43
+
44
+ if (existsSync(CURRENT_PROFILE_FILE)) {
45
+ try {
46
+ const profile = readFileSync(CURRENT_PROFILE_FILE, 'utf-8').trim();
47
+ if (profile && profileExists(profile)) {
48
+ return profile;
49
+ }
50
+ } catch {
51
+ // Fall through to default
52
+ }
53
+ }
54
+
55
+ return DEFAULT_PROFILE;
56
+ }
57
+
58
+ export function setCurrentProfile(profile: string): void {
59
+ ensureConfigDir();
60
+
61
+ if (!profileExists(profile) && profile !== DEFAULT_PROFILE) {
62
+ throw new Error(`Profile "${profile}" does not exist`);
63
+ }
64
+
65
+ writeFileSync(CURRENT_PROFILE_FILE, profile);
66
+ }
67
+
68
+ export function profileExists(profile: string): boolean {
69
+ return existsSync(getProfilePath(profile));
70
+ }
71
+
72
+ export function listProfiles(): string[] {
73
+ ensureConfigDir();
74
+
75
+ if (!existsSync(PROFILES_DIR)) {
76
+ return [];
77
+ }
78
+
79
+ return readdirSync(PROFILES_DIR)
80
+ .filter(f => f.endsWith('.json'))
81
+ .map(f => f.replace('.json', ''))
82
+ .sort();
83
+ }
84
+
85
+ export function createProfile(profile: string, config: ProfileConfig = {}): boolean {
86
+ ensureConfigDir();
87
+
88
+ if (profileExists(profile)) {
89
+ return false;
90
+ }
91
+
92
+ if (!/^[a-zA-Z0-9_-]+$/.test(profile)) {
93
+ throw new Error('Profile name can only contain letters, numbers, hyphens, and underscores');
94
+ }
95
+
96
+ writeFileSync(getProfilePath(profile), JSON.stringify(config, null, 2));
97
+ return true;
98
+ }
99
+
100
+ export function deleteProfile(profile: string): boolean {
101
+ if (profile === DEFAULT_PROFILE) {
102
+ return false;
103
+ }
104
+
105
+ if (!profileExists(profile)) {
106
+ return false;
107
+ }
108
+
109
+ if (getCurrentProfile() === profile) {
110
+ setCurrentProfile(DEFAULT_PROFILE);
111
+ }
112
+
113
+ rmSync(getProfilePath(profile));
114
+ return true;
115
+ }
116
+
117
+ export function loadProfile(profile?: string): ProfileConfig {
118
+ ensureConfigDir();
119
+ const profileName = profile || getCurrentProfile();
120
+ const profilePath = getProfilePath(profileName);
121
+
122
+ if (!existsSync(profilePath)) {
123
+ return {};
124
+ }
125
+
126
+ try {
127
+ return JSON.parse(readFileSync(profilePath, 'utf-8'));
128
+ } catch {
129
+ return {};
130
+ }
131
+ }
132
+
133
+ export function saveProfile(config: ProfileConfig, profile?: string): void {
134
+ ensureConfigDir();
135
+ const profileName = profile || getCurrentProfile();
136
+ writeFileSync(getProfilePath(profileName), JSON.stringify(config, null, 2));
137
+ }
138
+
139
+ export function getAccessToken(): string | undefined {
140
+ return process.env.WHATSAPP_ACCESS_TOKEN || loadProfile().accessToken;
141
+ }
142
+
143
+ export function setAccessToken(accessToken: string): void {
144
+ const config = loadProfile();
145
+ config.accessToken = accessToken;
146
+ saveProfile(config);
147
+ }
148
+
149
+ export function getPhoneNumberId(): string | undefined {
150
+ return process.env.WHATSAPP_PHONE_NUMBER_ID || loadProfile().phoneNumberId;
151
+ }
152
+
153
+ export function setPhoneNumberId(phoneNumberId: string): void {
154
+ const config = loadProfile();
155
+ config.phoneNumberId = phoneNumberId;
156
+ saveProfile(config);
157
+ }
158
+
159
+ export function getBusinessAccountId(): string | undefined {
160
+ return process.env.WHATSAPP_BUSINESS_ACCOUNT_ID || loadProfile().businessAccountId;
161
+ }
162
+
163
+ export function setBusinessAccountId(businessAccountId: string): void {
164
+ const config = loadProfile();
165
+ config.businessAccountId = businessAccountId;
166
+ saveProfile(config);
167
+ }
168
+
169
+ export function clearConfig(): void {
170
+ saveProfile({});
171
+ }
172
+
173
+ export function getConfigDir(): string {
174
+ return CONFIG_DIR;
175
+ }
176
+
177
+ export function getActiveProfileName(): string {
178
+ return getCurrentProfile();
179
+ }