@stacksjs/ts-cloud 0.1.8 → 0.1.9

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 (75) hide show
  1. package/dist/bin/cli.js +1 -1
  2. package/package.json +18 -16
  3. package/src/aws/acm.ts +768 -0
  4. package/src/aws/application-autoscaling.ts +845 -0
  5. package/src/aws/bedrock.ts +4074 -0
  6. package/src/aws/client.ts +891 -0
  7. package/src/aws/cloudformation.ts +896 -0
  8. package/src/aws/cloudfront.ts +1531 -0
  9. package/src/aws/cloudwatch-logs.ts +154 -0
  10. package/src/aws/comprehend.ts +839 -0
  11. package/src/aws/connect.ts +1056 -0
  12. package/src/aws/deploy-imap.ts +384 -0
  13. package/src/aws/dynamodb.ts +340 -0
  14. package/src/aws/ec2.ts +1385 -0
  15. package/src/aws/ecr.ts +621 -0
  16. package/src/aws/ecs.ts +615 -0
  17. package/src/aws/elasticache.ts +301 -0
  18. package/src/aws/elbv2.ts +942 -0
  19. package/src/aws/email.ts +928 -0
  20. package/src/aws/eventbridge.ts +248 -0
  21. package/src/aws/iam.ts +1689 -0
  22. package/src/aws/imap-server.ts +2100 -0
  23. package/src/aws/index.ts +213 -0
  24. package/src/aws/kendra.ts +1097 -0
  25. package/src/aws/lambda.ts +786 -0
  26. package/src/aws/opensearch.ts +158 -0
  27. package/src/aws/personalize.ts +977 -0
  28. package/src/aws/polly.ts +559 -0
  29. package/src/aws/rds.ts +888 -0
  30. package/src/aws/rekognition.ts +846 -0
  31. package/src/aws/route53-domains.ts +359 -0
  32. package/src/aws/route53.ts +1046 -0
  33. package/src/aws/s3.ts +2334 -0
  34. package/src/aws/scheduler.ts +571 -0
  35. package/src/aws/secrets-manager.ts +769 -0
  36. package/src/aws/ses.ts +1081 -0
  37. package/src/aws/setup-phone.ts +104 -0
  38. package/src/aws/setup-sms.ts +580 -0
  39. package/src/aws/sms.ts +1735 -0
  40. package/src/aws/smtp-server.ts +531 -0
  41. package/src/aws/sns.ts +758 -0
  42. package/src/aws/sqs.ts +382 -0
  43. package/src/aws/ssm.ts +807 -0
  44. package/src/aws/sts.ts +92 -0
  45. package/src/aws/support.ts +391 -0
  46. package/src/aws/test-imap.ts +86 -0
  47. package/src/aws/textract.ts +780 -0
  48. package/src/aws/transcribe.ts +108 -0
  49. package/src/aws/translate.ts +641 -0
  50. package/src/aws/voice.ts +1379 -0
  51. package/src/config.ts +35 -0
  52. package/src/deploy/index.ts +7 -0
  53. package/src/deploy/static-site-external-dns.ts +945 -0
  54. package/src/deploy/static-site.ts +1175 -0
  55. package/src/dns/cloudflare.ts +548 -0
  56. package/src/dns/godaddy.ts +412 -0
  57. package/src/dns/index.ts +205 -0
  58. package/src/dns/porkbun.ts +362 -0
  59. package/src/dns/route53-adapter.ts +414 -0
  60. package/src/dns/types.ts +119 -0
  61. package/src/dns/validator.ts +369 -0
  62. package/src/generators/index.ts +5 -0
  63. package/src/generators/infrastructure.ts +1660 -0
  64. package/src/index.ts +163 -0
  65. package/src/push/apns.ts +452 -0
  66. package/src/push/fcm.ts +506 -0
  67. package/src/push/index.ts +58 -0
  68. package/src/security/pre-deploy-scanner.ts +655 -0
  69. package/src/ssl/acme-client.ts +478 -0
  70. package/src/ssl/index.ts +7 -0
  71. package/src/ssl/letsencrypt.ts +747 -0
  72. package/src/types.ts +2 -0
  73. package/src/utils/cli.ts +398 -0
  74. package/src/validation/index.ts +5 -0
  75. package/src/validation/template.ts +405 -0
@@ -0,0 +1,1056 @@
1
+ /**
2
+ * AWS Amazon Connect Operations
3
+ * Direct API calls without AWS SDK dependency
4
+ */
5
+
6
+ import { AWSClient } from './client'
7
+
8
+ export interface ConnectInstance {
9
+ Id?: string
10
+ Arn?: string
11
+ IdentityManagementType?: 'SAML' | 'CONNECT_MANAGED' | 'EXISTING_DIRECTORY'
12
+ InstanceAlias?: string
13
+ CreatedTime?: string
14
+ ServiceRole?: string
15
+ InstanceStatus?: 'CREATION_IN_PROGRESS' | 'ACTIVE' | 'CREATION_FAILED'
16
+ InboundCallsEnabled?: boolean
17
+ OutboundCallsEnabled?: boolean
18
+ }
19
+
20
+ export interface PhoneNumber {
21
+ PhoneNumberId?: string
22
+ PhoneNumberArn?: string
23
+ PhoneNumber?: string
24
+ PhoneNumberCountryCode?: string
25
+ PhoneNumberType?: 'TOLL_FREE' | 'DID' | 'UIFN' | 'SHARED' | 'THIRD_PARTY_TF' | 'THIRD_PARTY_DID'
26
+ PhoneNumberDescription?: string
27
+ TargetArn?: string
28
+ InstanceId?: string
29
+ Tags?: Record<string, string>
30
+ }
31
+
32
+ export interface ContactFlow {
33
+ Id?: string
34
+ Arn?: string
35
+ Name?: string
36
+ Type?: 'CONTACT_FLOW' | 'CUSTOMER_QUEUE' | 'CUSTOMER_HOLD' | 'CUSTOMER_WHISPER' | 'AGENT_HOLD' | 'AGENT_WHISPER' | 'OUTBOUND_WHISPER' | 'AGENT_TRANSFER' | 'QUEUE_TRANSFER'
37
+ State?: 'ACTIVE' | 'ARCHIVED'
38
+ Description?: string
39
+ Content?: string
40
+ Tags?: Record<string, string>
41
+ }
42
+
43
+ export interface Queue {
44
+ QueueId?: string
45
+ QueueArn?: string
46
+ Name?: string
47
+ Description?: string
48
+ HoursOfOperationId?: string
49
+ MaxContacts?: number
50
+ Status?: 'ENABLED' | 'DISABLED'
51
+ Tags?: Record<string, string>
52
+ }
53
+
54
+ export interface AvailablePhoneNumber {
55
+ PhoneNumber?: string
56
+ PhoneNumberCountryCode?: string
57
+ PhoneNumberType?: string
58
+ }
59
+
60
+ /**
61
+ * Amazon Connect client for phone/voice operations
62
+ */
63
+ export class ConnectClient {
64
+ private client: AWSClient
65
+ private region: string
66
+
67
+ constructor(region: string = 'us-east-1') {
68
+ this.region = region
69
+ this.client = new AWSClient()
70
+ }
71
+
72
+ /**
73
+ * Create a new Amazon Connect instance
74
+ */
75
+ async createInstance(params: {
76
+ InstanceAlias: string
77
+ IdentityManagementType?: 'SAML' | 'CONNECT_MANAGED' | 'EXISTING_DIRECTORY'
78
+ InboundCallsEnabled?: boolean
79
+ OutboundCallsEnabled?: boolean
80
+ DirectoryId?: string
81
+ ClientToken?: string
82
+ Tags?: Record<string, string>
83
+ }): Promise<{ Id?: string, Arn?: string }> {
84
+ const body: Record<string, any> = {
85
+ InstanceAlias: params.InstanceAlias,
86
+ IdentityManagementType: params.IdentityManagementType || 'CONNECT_MANAGED',
87
+ InboundCallsEnabled: params.InboundCallsEnabled ?? true,
88
+ OutboundCallsEnabled: params.OutboundCallsEnabled ?? true,
89
+ }
90
+
91
+ if (params.DirectoryId) body.DirectoryId = params.DirectoryId
92
+ if (params.ClientToken) body.ClientToken = params.ClientToken
93
+ if (params.Tags) body.Tags = params.Tags
94
+
95
+ const result = await this.client.request({
96
+ service: 'connect',
97
+ region: this.region,
98
+ method: 'PUT',
99
+ path: '/instance',
100
+ headers: {
101
+ 'Content-Type': 'application/json',
102
+ },
103
+ body: JSON.stringify(body),
104
+ })
105
+
106
+ return result
107
+ }
108
+
109
+ /**
110
+ * Delete an Amazon Connect instance
111
+ */
112
+ async deleteInstance(instanceId: string): Promise<void> {
113
+ await this.client.request({
114
+ service: 'connect',
115
+ region: this.region,
116
+ method: 'DELETE',
117
+ path: `/instance/${instanceId}`,
118
+ })
119
+ }
120
+
121
+ /**
122
+ * Get instance details
123
+ */
124
+ async describeInstance(instanceId: string): Promise<ConnectInstance> {
125
+ const result = await this.client.request({
126
+ service: 'connect',
127
+ region: this.region,
128
+ method: 'GET',
129
+ path: `/instance/${instanceId}`,
130
+ })
131
+
132
+ return result.Instance || result
133
+ }
134
+
135
+ /**
136
+ * List all Connect instances
137
+ */
138
+ async listInstances(params?: {
139
+ MaxResults?: number
140
+ NextToken?: string
141
+ }): Promise<{ InstanceSummaryList?: ConnectInstance[], NextToken?: string }> {
142
+ const queryParams: Record<string, string> = {}
143
+ if (params?.MaxResults) queryParams.maxResults = String(params.MaxResults)
144
+ if (params?.NextToken) queryParams.nextToken = params.NextToken
145
+
146
+ const result = await this.client.request({
147
+ service: 'connect',
148
+ region: this.region,
149
+ method: 'GET',
150
+ path: '/instance',
151
+ queryParams,
152
+ })
153
+
154
+ return result
155
+ }
156
+
157
+ /**
158
+ * Search for available phone numbers
159
+ */
160
+ async searchAvailablePhoneNumbers(params: {
161
+ TargetArn: string
162
+ PhoneNumberCountryCode: string
163
+ PhoneNumberType: 'TOLL_FREE' | 'DID' | 'UIFN'
164
+ PhoneNumberPrefix?: string
165
+ MaxResults?: number
166
+ NextToken?: string
167
+ }): Promise<{ AvailableNumbersList?: AvailablePhoneNumber[], NextToken?: string }> {
168
+ const body: Record<string, any> = {
169
+ TargetArn: params.TargetArn,
170
+ PhoneNumberCountryCode: params.PhoneNumberCountryCode,
171
+ PhoneNumberType: params.PhoneNumberType,
172
+ }
173
+
174
+ if (params.PhoneNumberPrefix) body.PhoneNumberPrefix = params.PhoneNumberPrefix
175
+ if (params.MaxResults) body.MaxResults = params.MaxResults
176
+ if (params.NextToken) body.NextToken = params.NextToken
177
+
178
+ const result = await this.client.request({
179
+ service: 'connect',
180
+ region: this.region,
181
+ method: 'POST',
182
+ path: '/phone-number/search-available',
183
+ headers: {
184
+ 'Content-Type': 'application/json',
185
+ },
186
+ body: JSON.stringify(body),
187
+ })
188
+
189
+ return result
190
+ }
191
+
192
+ /**
193
+ * Claim a phone number
194
+ */
195
+ async claimPhoneNumber(params: {
196
+ TargetArn: string
197
+ PhoneNumber: string
198
+ PhoneNumberDescription?: string
199
+ Tags?: Record<string, string>
200
+ ClientToken?: string
201
+ }): Promise<{ PhoneNumberId?: string, PhoneNumberArn?: string }> {
202
+ const body: Record<string, any> = {
203
+ TargetArn: params.TargetArn,
204
+ PhoneNumber: params.PhoneNumber,
205
+ }
206
+
207
+ if (params.PhoneNumberDescription) body.PhoneNumberDescription = params.PhoneNumberDescription
208
+ if (params.Tags) body.Tags = params.Tags
209
+ if (params.ClientToken) body.ClientToken = params.ClientToken
210
+
211
+ const result = await this.client.request({
212
+ service: 'connect',
213
+ region: this.region,
214
+ method: 'POST',
215
+ path: '/phone-number/claim',
216
+ headers: {
217
+ 'Content-Type': 'application/json',
218
+ },
219
+ body: JSON.stringify(body),
220
+ })
221
+
222
+ return result
223
+ }
224
+
225
+ /**
226
+ * Release a phone number
227
+ */
228
+ async releasePhoneNumber(phoneNumberId: string, clientToken?: string): Promise<void> {
229
+ const queryParams: Record<string, string> = {}
230
+ if (clientToken) queryParams.clientToken = clientToken
231
+
232
+ await this.client.request({
233
+ service: 'connect',
234
+ region: this.region,
235
+ method: 'DELETE',
236
+ path: `/phone-number/${phoneNumberId}`,
237
+ queryParams,
238
+ })
239
+ }
240
+
241
+ /**
242
+ * List phone numbers for an instance
243
+ */
244
+ async listPhoneNumbers(params: {
245
+ TargetArn?: string
246
+ InstanceId?: string
247
+ PhoneNumberTypes?: string[]
248
+ PhoneNumberCountryCodes?: string[]
249
+ MaxResults?: number
250
+ NextToken?: string
251
+ }): Promise<{ ListPhoneNumbersSummaryList?: PhoneNumber[], NextToken?: string }> {
252
+ const body: Record<string, any> = {}
253
+
254
+ if (params.TargetArn) body.TargetArn = params.TargetArn
255
+ if (params.InstanceId) body.InstanceId = params.InstanceId
256
+ if (params.PhoneNumberTypes) body.PhoneNumberTypes = params.PhoneNumberTypes
257
+ if (params.PhoneNumberCountryCodes) body.PhoneNumberCountryCodes = params.PhoneNumberCountryCodes
258
+ if (params.MaxResults) body.MaxResults = params.MaxResults
259
+ if (params.NextToken) body.NextToken = params.NextToken
260
+
261
+ const result = await this.client.request({
262
+ service: 'connect',
263
+ region: this.region,
264
+ method: 'POST',
265
+ path: '/phone-number/list',
266
+ headers: {
267
+ 'Content-Type': 'application/json',
268
+ },
269
+ body: JSON.stringify(body),
270
+ })
271
+
272
+ return result
273
+ }
274
+
275
+ /**
276
+ * Create a contact flow
277
+ */
278
+ async createContactFlow(params: {
279
+ InstanceId: string
280
+ Name: string
281
+ Type: 'CONTACT_FLOW' | 'CUSTOMER_QUEUE' | 'CUSTOMER_HOLD' | 'CUSTOMER_WHISPER' | 'AGENT_HOLD' | 'AGENT_WHISPER' | 'OUTBOUND_WHISPER' | 'AGENT_TRANSFER' | 'QUEUE_TRANSFER'
282
+ Content: string
283
+ Description?: string
284
+ Tags?: Record<string, string>
285
+ }): Promise<{ ContactFlowId?: string, ContactFlowArn?: string }> {
286
+ const body: Record<string, any> = {
287
+ Name: params.Name,
288
+ Type: params.Type,
289
+ Content: params.Content,
290
+ }
291
+
292
+ if (params.Description) body.Description = params.Description
293
+ if (params.Tags) body.Tags = params.Tags
294
+
295
+ const result = await this.client.request({
296
+ service: 'connect',
297
+ region: this.region,
298
+ method: 'PUT',
299
+ path: `/contact-flows/${params.InstanceId}`,
300
+ headers: {
301
+ 'Content-Type': 'application/json',
302
+ },
303
+ body: JSON.stringify(body),
304
+ })
305
+
306
+ return result
307
+ }
308
+
309
+ /**
310
+ * Update contact flow content
311
+ */
312
+ async updateContactFlowContent(params: {
313
+ InstanceId: string
314
+ ContactFlowId: string
315
+ Content: string
316
+ }): Promise<void> {
317
+ await this.client.request({
318
+ service: 'connect',
319
+ region: this.region,
320
+ method: 'POST',
321
+ path: `/contact-flows/${params.InstanceId}/${params.ContactFlowId}/content`,
322
+ headers: {
323
+ 'Content-Type': 'application/json',
324
+ },
325
+ body: JSON.stringify({ Content: params.Content }),
326
+ })
327
+ }
328
+
329
+ /**
330
+ * List contact flows
331
+ */
332
+ async listContactFlows(params: {
333
+ InstanceId: string
334
+ ContactFlowTypes?: string[]
335
+ MaxResults?: number
336
+ NextToken?: string
337
+ }): Promise<{ ContactFlowSummaryList?: ContactFlow[], NextToken?: string }> {
338
+ const queryParams: Record<string, string> = {}
339
+ if (params.ContactFlowTypes) queryParams.contactFlowTypes = params.ContactFlowTypes.join(',')
340
+ if (params.MaxResults) queryParams.maxResults = String(params.MaxResults)
341
+ if (params.NextToken) queryParams.nextToken = params.NextToken
342
+
343
+ const result = await this.client.request({
344
+ service: 'connect',
345
+ region: this.region,
346
+ method: 'GET',
347
+ path: `/contact-flows-summary/${params.InstanceId}`,
348
+ queryParams,
349
+ })
350
+
351
+ return result
352
+ }
353
+
354
+ /**
355
+ * Create a queue
356
+ */
357
+ async createQueue(params: {
358
+ InstanceId: string
359
+ Name: string
360
+ Description?: string
361
+ HoursOfOperationId: string
362
+ MaxContacts?: number
363
+ OutboundCallerConfig?: {
364
+ OutboundCallerIdName?: string
365
+ OutboundCallerIdNumberId?: string
366
+ OutboundFlowId?: string
367
+ }
368
+ Tags?: Record<string, string>
369
+ }): Promise<{ QueueId?: string, QueueArn?: string }> {
370
+ const body: Record<string, any> = {
371
+ Name: params.Name,
372
+ HoursOfOperationId: params.HoursOfOperationId,
373
+ }
374
+
375
+ if (params.Description) body.Description = params.Description
376
+ if (params.MaxContacts) body.MaxContacts = params.MaxContacts
377
+ if (params.OutboundCallerConfig) body.OutboundCallerConfig = params.OutboundCallerConfig
378
+ if (params.Tags) body.Tags = params.Tags
379
+
380
+ const result = await this.client.request({
381
+ service: 'connect',
382
+ region: this.region,
383
+ method: 'PUT',
384
+ path: `/queues/${params.InstanceId}`,
385
+ headers: {
386
+ 'Content-Type': 'application/json',
387
+ },
388
+ body: JSON.stringify(body),
389
+ })
390
+
391
+ return result
392
+ }
393
+
394
+ /**
395
+ * Create hours of operation
396
+ */
397
+ async createHoursOfOperation(params: {
398
+ InstanceId: string
399
+ Name: string
400
+ TimeZone: string
401
+ Config: Array<{
402
+ Day: 'SUNDAY' | 'MONDAY' | 'TUESDAY' | 'WEDNESDAY' | 'THURSDAY' | 'FRIDAY' | 'SATURDAY'
403
+ StartTime: { Hours: number, Minutes: number }
404
+ EndTime: { Hours: number, Minutes: number }
405
+ }>
406
+ Description?: string
407
+ Tags?: Record<string, string>
408
+ }): Promise<{ HoursOfOperationId?: string, HoursOfOperationArn?: string }> {
409
+ const body: Record<string, any> = {
410
+ Name: params.Name,
411
+ TimeZone: params.TimeZone,
412
+ Config: params.Config,
413
+ }
414
+
415
+ if (params.Description) body.Description = params.Description
416
+ if (params.Tags) body.Tags = params.Tags
417
+
418
+ const result = await this.client.request({
419
+ service: 'connect',
420
+ region: this.region,
421
+ method: 'PUT',
422
+ path: `/hours-of-operations/${params.InstanceId}`,
423
+ headers: {
424
+ 'Content-Type': 'application/json',
425
+ },
426
+ body: JSON.stringify(body),
427
+ })
428
+
429
+ return result
430
+ }
431
+
432
+ /**
433
+ * Associate phone number with contact flow
434
+ */
435
+ async associatePhoneNumberContactFlow(params: {
436
+ PhoneNumberId: string
437
+ InstanceId: string
438
+ ContactFlowId: string
439
+ }): Promise<void> {
440
+ await this.client.request({
441
+ service: 'connect',
442
+ region: this.region,
443
+ method: 'PUT',
444
+ path: `/phone-number/${params.PhoneNumberId}/contact-flow`,
445
+ headers: {
446
+ 'Content-Type': 'application/json',
447
+ },
448
+ body: JSON.stringify({
449
+ InstanceId: params.InstanceId,
450
+ ContactFlowId: params.ContactFlowId,
451
+ }),
452
+ })
453
+ }
454
+
455
+ /**
456
+ * Create a routing profile
457
+ */
458
+ async createRoutingProfile(params: {
459
+ InstanceId: string
460
+ Name: string
461
+ Description?: string
462
+ DefaultOutboundQueueId: string
463
+ MediaConcurrencies: Array<{
464
+ Channel: 'VOICE' | 'CHAT' | 'TASK'
465
+ Concurrency: number
466
+ CrossChannelBehavior?: {
467
+ BehaviorType: 'ROUTE_CURRENT_CHANNEL_ONLY' | 'ROUTE_ANY_CHANNEL'
468
+ }
469
+ }>
470
+ QueueConfigs?: Array<{
471
+ QueueReference: {
472
+ QueueId: string
473
+ Channel: 'VOICE' | 'CHAT' | 'TASK'
474
+ }
475
+ Priority: number
476
+ Delay: number
477
+ }>
478
+ Tags?: Record<string, string>
479
+ }): Promise<{ RoutingProfileId?: string, RoutingProfileArn?: string }> {
480
+ const body: Record<string, any> = {
481
+ Name: params.Name,
482
+ DefaultOutboundQueueId: params.DefaultOutboundQueueId,
483
+ MediaConcurrencies: params.MediaConcurrencies,
484
+ }
485
+
486
+ if (params.Description) body.Description = params.Description
487
+ if (params.QueueConfigs) body.QueueConfigs = params.QueueConfigs
488
+ if (params.Tags) body.Tags = params.Tags
489
+
490
+ const result = await this.client.request({
491
+ service: 'connect',
492
+ region: this.region,
493
+ method: 'PUT',
494
+ path: `/routing-profiles/${params.InstanceId}`,
495
+ headers: {
496
+ 'Content-Type': 'application/json',
497
+ },
498
+ body: JSON.stringify(body),
499
+ })
500
+
501
+ return result
502
+ }
503
+
504
+ /**
505
+ * Check if an instance exists by alias
506
+ */
507
+ async instanceExists(instanceAlias: string): Promise<boolean> {
508
+ try {
509
+ const result = await this.listInstances({ MaxResults: 100 })
510
+ return result.InstanceSummaryList?.some(
511
+ instance => instance.InstanceAlias === instanceAlias
512
+ ) || false
513
+ }
514
+ catch {
515
+ return false
516
+ }
517
+ }
518
+
519
+ // ==================== Outbound Calls ====================
520
+
521
+ /**
522
+ * Start an outbound voice contact (make a call)
523
+ */
524
+ async startOutboundVoiceContact(params: {
525
+ InstanceId: string
526
+ ContactFlowId: string
527
+ DestinationPhoneNumber: string
528
+ SourcePhoneNumber?: string
529
+ QueueId?: string
530
+ Attributes?: Record<string, string>
531
+ AnswerMachineDetectionConfig?: {
532
+ EnableAnswerMachineDetection?: boolean
533
+ AwaitAnswerMachinePrompt?: boolean
534
+ }
535
+ CampaignId?: string
536
+ TrafficType?: 'GENERAL' | 'CAMPAIGN'
537
+ ClientToken?: string
538
+ }): Promise<{ ContactId?: string }> {
539
+ const body: Record<string, any> = {
540
+ ContactFlowId: params.ContactFlowId,
541
+ DestinationPhoneNumber: params.DestinationPhoneNumber,
542
+ }
543
+
544
+ if (params.SourcePhoneNumber) body.SourcePhoneNumber = params.SourcePhoneNumber
545
+ if (params.QueueId) body.QueueId = params.QueueId
546
+ if (params.Attributes) body.Attributes = params.Attributes
547
+ if (params.AnswerMachineDetectionConfig) body.AnswerMachineDetectionConfig = params.AnswerMachineDetectionConfig
548
+ if (params.CampaignId) body.CampaignId = params.CampaignId
549
+ if (params.TrafficType) body.TrafficType = params.TrafficType
550
+ if (params.ClientToken) body.ClientToken = params.ClientToken
551
+
552
+ const result = await this.client.request({
553
+ service: 'connect',
554
+ region: this.region,
555
+ method: 'PUT',
556
+ path: `/contact/outbound-voice/${params.InstanceId}`,
557
+ headers: {
558
+ 'Content-Type': 'application/json',
559
+ },
560
+ body: JSON.stringify(body),
561
+ })
562
+
563
+ return result
564
+ }
565
+
566
+ /**
567
+ * Make a simple outbound call (convenience method)
568
+ */
569
+ async makeCall(params: {
570
+ instanceId: string
571
+ contactFlowId: string
572
+ to: string
573
+ from?: string
574
+ attributes?: Record<string, string>
575
+ }): Promise<{ ContactId?: string }> {
576
+ return this.startOutboundVoiceContact({
577
+ InstanceId: params.instanceId,
578
+ ContactFlowId: params.contactFlowId,
579
+ DestinationPhoneNumber: params.to,
580
+ SourcePhoneNumber: params.from,
581
+ Attributes: params.attributes,
582
+ })
583
+ }
584
+
585
+ /**
586
+ * Stop a contact (end a call)
587
+ */
588
+ async stopContact(params: {
589
+ InstanceId: string
590
+ ContactId: string
591
+ }): Promise<void> {
592
+ await this.client.request({
593
+ service: 'connect',
594
+ region: this.region,
595
+ method: 'POST',
596
+ path: `/contact/stop/${params.InstanceId}/${params.ContactId}`,
597
+ })
598
+ }
599
+
600
+ /**
601
+ * Get contact details
602
+ */
603
+ async describeContact(params: {
604
+ InstanceId: string
605
+ ContactId: string
606
+ }): Promise<{
607
+ Contact?: {
608
+ Arn?: string
609
+ Id?: string
610
+ InitialContactId?: string
611
+ PreviousContactId?: string
612
+ InitiationMethod?: 'INBOUND' | 'OUTBOUND' | 'TRANSFER' | 'QUEUE_TRANSFER' | 'CALLBACK' | 'API' | 'DISCONNECT' | 'MONITOR' | 'EXTERNAL_OUTBOUND'
613
+ Name?: string
614
+ Description?: string
615
+ Channel?: 'VOICE' | 'CHAT' | 'TASK'
616
+ QueueInfo?: {
617
+ Id?: string
618
+ EnqueueTimestamp?: string
619
+ }
620
+ AgentInfo?: {
621
+ Id?: string
622
+ ConnectedToAgentTimestamp?: string
623
+ }
624
+ InitiationTimestamp?: string
625
+ DisconnectTimestamp?: string
626
+ ScheduledTimestamp?: string
627
+ }
628
+ }> {
629
+ return this.client.request({
630
+ service: 'connect',
631
+ region: this.region,
632
+ method: 'GET',
633
+ path: `/contacts/${params.InstanceId}/${params.ContactId}`,
634
+ })
635
+ }
636
+
637
+ /**
638
+ * Update contact attributes
639
+ */
640
+ async updateContactAttributes(params: {
641
+ InstanceId: string
642
+ InitialContactId: string
643
+ Attributes: Record<string, string>
644
+ }): Promise<void> {
645
+ await this.client.request({
646
+ service: 'connect',
647
+ region: this.region,
648
+ method: 'POST',
649
+ path: `/contact/attributes/${params.InstanceId}`,
650
+ headers: {
651
+ 'Content-Type': 'application/json',
652
+ },
653
+ body: JSON.stringify({
654
+ InitialContactId: params.InitialContactId,
655
+ Attributes: params.Attributes,
656
+ }),
657
+ })
658
+ }
659
+
660
+ // ==================== Users/Agents ====================
661
+
662
+ /**
663
+ * Create a user (agent)
664
+ */
665
+ async createUser(params: {
666
+ InstanceId: string
667
+ Username: string
668
+ Password?: string
669
+ IdentityInfo?: {
670
+ FirstName?: string
671
+ LastName?: string
672
+ Email?: string
673
+ SecondaryEmail?: string
674
+ Mobile?: string
675
+ }
676
+ PhoneConfig: {
677
+ PhoneType: 'SOFT_PHONE' | 'DESK_PHONE'
678
+ AutoAccept?: boolean
679
+ AfterContactWorkTimeLimit?: number
680
+ DeskPhoneNumber?: string
681
+ }
682
+ DirectoryUserId?: string
683
+ SecurityProfileIds: string[]
684
+ RoutingProfileId: string
685
+ HierarchyGroupId?: string
686
+ Tags?: Record<string, string>
687
+ }): Promise<{ UserId?: string, UserArn?: string }> {
688
+ const body: Record<string, any> = {
689
+ Username: params.Username,
690
+ PhoneConfig: params.PhoneConfig,
691
+ SecurityProfileIds: params.SecurityProfileIds,
692
+ RoutingProfileId: params.RoutingProfileId,
693
+ }
694
+
695
+ if (params.Password) body.Password = params.Password
696
+ if (params.IdentityInfo) body.IdentityInfo = params.IdentityInfo
697
+ if (params.DirectoryUserId) body.DirectoryUserId = params.DirectoryUserId
698
+ if (params.HierarchyGroupId) body.HierarchyGroupId = params.HierarchyGroupId
699
+ if (params.Tags) body.Tags = params.Tags
700
+
701
+ return this.client.request({
702
+ service: 'connect',
703
+ region: this.region,
704
+ method: 'PUT',
705
+ path: `/users/${params.InstanceId}`,
706
+ headers: {
707
+ 'Content-Type': 'application/json',
708
+ },
709
+ body: JSON.stringify(body),
710
+ })
711
+ }
712
+
713
+ /**
714
+ * Delete a user
715
+ */
716
+ async deleteUser(params: {
717
+ InstanceId: string
718
+ UserId: string
719
+ }): Promise<void> {
720
+ await this.client.request({
721
+ service: 'connect',
722
+ region: this.region,
723
+ method: 'DELETE',
724
+ path: `/users/${params.InstanceId}/${params.UserId}`,
725
+ })
726
+ }
727
+
728
+ /**
729
+ * List users
730
+ */
731
+ async listUsers(params: {
732
+ InstanceId: string
733
+ NextToken?: string
734
+ MaxResults?: number
735
+ }): Promise<{
736
+ UserSummaryList?: Array<{
737
+ Id?: string
738
+ Arn?: string
739
+ Username?: string
740
+ }>
741
+ NextToken?: string
742
+ }> {
743
+ const queryParams: Record<string, string> = {}
744
+ if (params.NextToken) queryParams.nextToken = params.NextToken
745
+ if (params.MaxResults) queryParams.maxResults = String(params.MaxResults)
746
+
747
+ return this.client.request({
748
+ service: 'connect',
749
+ region: this.region,
750
+ method: 'GET',
751
+ path: `/users-summary/${params.InstanceId}`,
752
+ queryParams,
753
+ })
754
+ }
755
+
756
+ // ==================== Prompts ====================
757
+
758
+ /**
759
+ * Create a prompt (audio file for IVR)
760
+ */
761
+ async createPrompt(params: {
762
+ InstanceId: string
763
+ Name: string
764
+ S3Uri: string
765
+ Description?: string
766
+ Tags?: Record<string, string>
767
+ }): Promise<{ PromptId?: string, PromptArn?: string }> {
768
+ const body: Record<string, any> = {
769
+ Name: params.Name,
770
+ S3Uri: params.S3Uri,
771
+ }
772
+
773
+ if (params.Description) body.Description = params.Description
774
+ if (params.Tags) body.Tags = params.Tags
775
+
776
+ return this.client.request({
777
+ service: 'connect',
778
+ region: this.region,
779
+ method: 'PUT',
780
+ path: `/prompts/${params.InstanceId}`,
781
+ headers: {
782
+ 'Content-Type': 'application/json',
783
+ },
784
+ body: JSON.stringify(body),
785
+ })
786
+ }
787
+
788
+ /**
789
+ * List prompts
790
+ */
791
+ async listPrompts(params: {
792
+ InstanceId: string
793
+ NextToken?: string
794
+ MaxResults?: number
795
+ }): Promise<{
796
+ PromptSummaryList?: Array<{
797
+ Id?: string
798
+ Arn?: string
799
+ Name?: string
800
+ }>
801
+ NextToken?: string
802
+ }> {
803
+ const queryParams: Record<string, string> = {}
804
+ if (params.NextToken) queryParams.nextToken = params.NextToken
805
+ if (params.MaxResults) queryParams.maxResults = String(params.MaxResults)
806
+
807
+ return this.client.request({
808
+ service: 'connect',
809
+ region: this.region,
810
+ method: 'GET',
811
+ path: `/prompts-summary/${params.InstanceId}`,
812
+ queryParams,
813
+ })
814
+ }
815
+
816
+ // ==================== Quick Connects ====================
817
+
818
+ /**
819
+ * Create a quick connect (for transfers)
820
+ */
821
+ async createQuickConnect(params: {
822
+ InstanceId: string
823
+ Name: string
824
+ Description?: string
825
+ QuickConnectConfig: {
826
+ QuickConnectType: 'USER' | 'QUEUE' | 'PHONE_NUMBER'
827
+ UserConfig?: {
828
+ UserId: string
829
+ ContactFlowId: string
830
+ }
831
+ QueueConfig?: {
832
+ QueueId: string
833
+ ContactFlowId: string
834
+ }
835
+ PhoneConfig?: {
836
+ PhoneNumber: string
837
+ }
838
+ }
839
+ Tags?: Record<string, string>
840
+ }): Promise<{ QuickConnectId?: string, QuickConnectArn?: string }> {
841
+ const body: Record<string, any> = {
842
+ Name: params.Name,
843
+ QuickConnectConfig: params.QuickConnectConfig,
844
+ }
845
+
846
+ if (params.Description) body.Description = params.Description
847
+ if (params.Tags) body.Tags = params.Tags
848
+
849
+ return this.client.request({
850
+ service: 'connect',
851
+ region: this.region,
852
+ method: 'PUT',
853
+ path: `/quick-connects/${params.InstanceId}`,
854
+ headers: {
855
+ 'Content-Type': 'application/json',
856
+ },
857
+ body: JSON.stringify(body),
858
+ })
859
+ }
860
+
861
+ // ==================== Chat/Tasks ====================
862
+
863
+ /**
864
+ * Start a chat contact
865
+ */
866
+ async startChatContact(params: {
867
+ InstanceId: string
868
+ ContactFlowId: string
869
+ ParticipantDetails: {
870
+ DisplayName: string
871
+ }
872
+ Attributes?: Record<string, string>
873
+ InitialMessage?: {
874
+ ContentType: string
875
+ Content: string
876
+ }
877
+ ClientToken?: string
878
+ ChatDurationInMinutes?: number
879
+ SupportedMessagingContentTypes?: string[]
880
+ }): Promise<{
881
+ ContactId?: string
882
+ ParticipantId?: string
883
+ ParticipantToken?: string
884
+ }> {
885
+ return this.client.request({
886
+ service: 'connect',
887
+ region: this.region,
888
+ method: 'PUT',
889
+ path: `/contact/chat/${params.InstanceId}`,
890
+ headers: {
891
+ 'Content-Type': 'application/json',
892
+ },
893
+ body: JSON.stringify(params),
894
+ })
895
+ }
896
+
897
+ /**
898
+ * Start a task contact
899
+ */
900
+ async startTaskContact(params: {
901
+ InstanceId: string
902
+ ContactFlowId?: string
903
+ PreviousContactId?: string
904
+ Attributes?: Record<string, string>
905
+ Name: string
906
+ Description?: string
907
+ References?: Record<string, {
908
+ Value: string
909
+ Type: 'URL' | 'ATTACHMENT' | 'NUMBER' | 'STRING' | 'DATE' | 'EMAIL'
910
+ }>
911
+ ClientToken?: string
912
+ ScheduledTime?: string
913
+ TaskTemplateId?: string
914
+ QuickConnectId?: string
915
+ RelatedContactId?: string
916
+ }): Promise<{ ContactId?: string }> {
917
+ return this.client.request({
918
+ service: 'connect',
919
+ region: this.region,
920
+ method: 'PUT',
921
+ path: `/contact/task/${params.InstanceId}`,
922
+ headers: {
923
+ 'Content-Type': 'application/json',
924
+ },
925
+ body: JSON.stringify(params),
926
+ })
927
+ }
928
+
929
+ // ==================== Contact Flow Modules ====================
930
+
931
+ /**
932
+ * Create a simple IVR contact flow for outbound calls
933
+ */
934
+ createOutboundIvrFlow(params: {
935
+ message: string
936
+ voiceId?: string
937
+ }): string {
938
+ const voiceId = params.voiceId || 'Joanna'
939
+ return JSON.stringify({
940
+ Version: '2019-10-30',
941
+ StartAction: 'play-prompt',
942
+ Actions: {
943
+ 'play-prompt': {
944
+ Type: 'MessageParticipant',
945
+ Parameters: {
946
+ Text: params.message,
947
+ TextToSpeechVoice: voiceId,
948
+ TextToSpeechEngine: 'neural',
949
+ },
950
+ Transitions: {
951
+ NextAction: 'disconnect',
952
+ Errors: [
953
+ { NextAction: 'disconnect', ErrorType: 'NoMatchingError' },
954
+ ],
955
+ },
956
+ },
957
+ 'disconnect': {
958
+ Type: 'DisconnectParticipant',
959
+ Parameters: {},
960
+ Transitions: {},
961
+ },
962
+ },
963
+ })
964
+ }
965
+
966
+ /**
967
+ * Create a contact flow with input collection
968
+ */
969
+ createInputCollectionFlow(params: {
970
+ promptMessage: string
971
+ inputTimeout?: number
972
+ maxDigits?: number
973
+ successNextAction?: string
974
+ voiceId?: string
975
+ }): string {
976
+ const voiceId = params.voiceId || 'Joanna'
977
+ const timeout = params.inputTimeout || 5
978
+ const maxDigits = params.maxDigits || 1
979
+
980
+ return JSON.stringify({
981
+ Version: '2019-10-30',
982
+ StartAction: 'get-input',
983
+ Actions: {
984
+ 'get-input': {
985
+ Type: 'GetParticipantInput',
986
+ Parameters: {
987
+ Text: params.promptMessage,
988
+ TextToSpeechVoice: voiceId,
989
+ TextToSpeechEngine: 'neural',
990
+ InputTimeLimitSeconds: String(timeout),
991
+ MaxDigits: maxDigits,
992
+ EncryptEntry: false,
993
+ },
994
+ Transitions: {
995
+ NextAction: params.successNextAction || 'disconnect',
996
+ Conditions: [],
997
+ Errors: [
998
+ { NextAction: 'disconnect', ErrorType: 'NoMatchingCondition' },
999
+ { NextAction: 'disconnect', ErrorType: 'NoMatchingError' },
1000
+ ],
1001
+ },
1002
+ },
1003
+ 'disconnect': {
1004
+ Type: 'DisconnectParticipant',
1005
+ Parameters: {},
1006
+ Transitions: {},
1007
+ },
1008
+ },
1009
+ })
1010
+ }
1011
+
1012
+ // ==================== Metrics ====================
1013
+
1014
+ /**
1015
+ * Get current metric data
1016
+ */
1017
+ async getCurrentMetricData(params: {
1018
+ InstanceId: string
1019
+ Filters: {
1020
+ Queues?: string[]
1021
+ Channels?: Array<'VOICE' | 'CHAT' | 'TASK'>
1022
+ RoutingProfiles?: string[]
1023
+ }
1024
+ CurrentMetrics: Array<{
1025
+ Name: 'AGENTS_ONLINE' | 'AGENTS_AVAILABLE' | 'AGENTS_ON_CALL' | 'AGENTS_NON_PRODUCTIVE' | 'AGENTS_AFTER_CONTACT_WORK' | 'AGENTS_ERROR' | 'AGENTS_STAFFED' | 'CONTACTS_IN_QUEUE' | 'OLDEST_CONTACT_AGE' | 'CONTACTS_SCHEDULED' | 'AGENTS_ON_CONTACT' | 'SLOTS_ACTIVE' | 'SLOTS_AVAILABLE'
1026
+ Unit?: 'SECONDS' | 'COUNT' | 'PERCENT'
1027
+ }>
1028
+ Groupings?: Array<'QUEUE' | 'CHANNEL' | 'ROUTING_PROFILE'>
1029
+ MaxResults?: number
1030
+ NextToken?: string
1031
+ }): Promise<{
1032
+ MetricResults?: Array<{
1033
+ Dimensions?: {
1034
+ Queue?: { Id?: string; Arn?: string }
1035
+ Channel?: 'VOICE' | 'CHAT' | 'TASK'
1036
+ }
1037
+ Collections?: Array<{
1038
+ Metric?: { Name?: string; Unit?: string }
1039
+ Value?: number
1040
+ }>
1041
+ }>
1042
+ NextToken?: string
1043
+ ApproximateTotalCount?: number
1044
+ }> {
1045
+ return this.client.request({
1046
+ service: 'connect',
1047
+ region: this.region,
1048
+ method: 'POST',
1049
+ path: `/metrics/current/${params.InstanceId}`,
1050
+ headers: {
1051
+ 'Content-Type': 'application/json',
1052
+ },
1053
+ body: JSON.stringify(params),
1054
+ })
1055
+ }
1056
+ }