@slates-integrations/aws-lambda 0.2.0-rc.6

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 (37) hide show
  1. package/README.md +89 -0
  2. package/docs/SPEC.md +91 -0
  3. package/logo.svg +1 -0
  4. package/package.json +21 -0
  5. package/slate.json +23 -0
  6. package/src/auth.ts +51 -0
  7. package/src/config.ts +8 -0
  8. package/src/index.ts +50 -0
  9. package/src/lib/aws-signer.ts +158 -0
  10. package/src/lib/client.ts +919 -0
  11. package/src/lib/errors.ts +101 -0
  12. package/src/lib/helpers.ts +15 -0
  13. package/src/spec.ts +13 -0
  14. package/src/tools/configure-async-invocation.ts +134 -0
  15. package/src/tools/create-function.ts +127 -0
  16. package/src/tools/delete-function.ts +41 -0
  17. package/src/tools/get-account-settings.ts +45 -0
  18. package/src/tools/get-function.ts +104 -0
  19. package/src/tools/index.ts +19 -0
  20. package/src/tools/invoke-function.ts +94 -0
  21. package/src/tools/list-functions.ts +88 -0
  22. package/src/tools/manage-alias.ts +159 -0
  23. package/src/tools/manage-concurrency.ts +141 -0
  24. package/src/tools/manage-durable-execution.ts +209 -0
  25. package/src/tools/manage-event-source-mapping.ts +193 -0
  26. package/src/tools/manage-function-url.ts +132 -0
  27. package/src/tools/manage-layer.ts +186 -0
  28. package/src/tools/manage-permission.ts +94 -0
  29. package/src/tools/manage-recursion-config.ts +63 -0
  30. package/src/tools/manage-runtime-management.ts +85 -0
  31. package/src/tools/manage-tags.ts +72 -0
  32. package/src/tools/publish-version.ts +83 -0
  33. package/src/tools/update-function.ts +156 -0
  34. package/src/triggers/function-changes.ts +113 -0
  35. package/src/triggers/inbound-webhook.ts +67 -0
  36. package/src/triggers/index.ts +2 -0
  37. package/tsconfig.json +23 -0
@@ -0,0 +1,193 @@
1
+ import { SlateTool } from 'slates';
2
+ import { createClient } from '../lib/helpers';
3
+ import { lambdaServiceError } from '../lib/errors';
4
+ import { spec } from '../spec';
5
+ import { z } from 'zod';
6
+
7
+ export let manageEventSourceMapping = SlateTool.create(spec, {
8
+ name: 'Manage Event Source Mapping',
9
+ key: 'manage_event_source_mapping',
10
+ description: `Create, update, get, delete, or list event source mappings that connect Lambda to streaming/queue services (SQS, Kinesis, DynamoDB Streams, Kafka, Amazon MQ). Lambda automatically polls the source and invokes the function.`,
11
+ instructions: [
12
+ 'Use **action** to specify: "create", "update", "get", "delete", or "list".',
13
+ 'For create, provide the eventSourceArn and functionName at minimum.',
14
+ 'For Kinesis/DynamoDB Streams, a startingPosition is required (TRIM_HORIZON, LATEST, or AT_TIMESTAMP).'
15
+ ]
16
+ })
17
+ .input(
18
+ z.object({
19
+ action: z
20
+ .enum(['create', 'update', 'get', 'delete', 'list'])
21
+ .describe('Operation to perform'),
22
+ mappingUuid: z
23
+ .string()
24
+ .optional()
25
+ .describe('Event source mapping UUID (required for get/update/delete)'),
26
+ functionName: z.string().optional().describe('Function name or ARN'),
27
+ eventSourceArn: z
28
+ .string()
29
+ .optional()
30
+ .describe('ARN of the event source (SQS, Kinesis, DynamoDB, Kafka, MQ)'),
31
+ enabled: z.boolean().optional().describe('Whether the mapping is active'),
32
+ batchSize: z.number().optional().describe('Maximum records per batch (1-10000)'),
33
+ startingPosition: z
34
+ .enum(['TRIM_HORIZON', 'LATEST', 'AT_TIMESTAMP'])
35
+ .optional()
36
+ .describe('Starting position for stream sources'),
37
+ startingPositionTimestamp: z
38
+ .string()
39
+ .optional()
40
+ .describe('Timestamp for AT_TIMESTAMP starting position (ISO 8601)'),
41
+ maximumBatchingWindowInSeconds: z
42
+ .number()
43
+ .optional()
44
+ .describe('Maximum batching window in seconds'),
45
+ maximumRetryAttempts: z
46
+ .number()
47
+ .optional()
48
+ .describe('Maximum retry attempts (-1 for infinite)'),
49
+ parallelizationFactor: z
50
+ .number()
51
+ .optional()
52
+ .describe('Concurrent batches per shard (1-10)'),
53
+ filterPatterns: z.array(z.string()).optional().describe('Event filter patterns'),
54
+ onFailureDestinationArn: z
55
+ .string()
56
+ .optional()
57
+ .describe('Destination ARN for failed records'),
58
+ bisectBatchOnFunctionError: z
59
+ .boolean()
60
+ .optional()
61
+ .describe('Split batch on error for retry'),
62
+ topics: z.array(z.string()).optional().describe('Kafka topics'),
63
+ queues: z.array(z.string()).optional().describe('MQ queue names'),
64
+ maximumConcurrency: z
65
+ .number()
66
+ .optional()
67
+ .describe('Maximum concurrent function invocations (2-1000)')
68
+ })
69
+ )
70
+ .output(
71
+ z.object({
72
+ mappingUuid: z.string().optional().describe('Event source mapping UUID'),
73
+ mappingArn: z.string().optional().describe('Event source mapping ARN'),
74
+ functionArn: z.string().optional().describe('Function ARN'),
75
+ eventSourceArn: z.string().optional().describe('Event source ARN'),
76
+ state: z.string().optional().describe('Mapping state'),
77
+ batchSize: z.number().optional().describe('Batch size'),
78
+ lastModified: z.string().optional().describe('Last modified timestamp'),
79
+ mappings: z
80
+ .array(
81
+ z.object({
82
+ mappingUuid: z.string().optional(),
83
+ functionArn: z.string().optional(),
84
+ eventSourceArn: z.string().optional(),
85
+ state: z.string().optional(),
86
+ batchSize: z.number().optional()
87
+ })
88
+ )
89
+ .optional()
90
+ .describe('List of mappings (for list action)'),
91
+ deleted: z.boolean().optional()
92
+ })
93
+ )
94
+ .handleInvocation(async ctx => {
95
+ let client = createClient(ctx.config, ctx.auth);
96
+ let { action } = ctx.input;
97
+
98
+ let mapResult = (r: any) => ({
99
+ mappingUuid: r.UUID,
100
+ mappingArn: r.EventSourceMappingArn,
101
+ functionArn: r.FunctionArn,
102
+ eventSourceArn: r.EventSourceArn,
103
+ state: r.State,
104
+ batchSize: r.BatchSize,
105
+ lastModified: r.LastModified ? String(r.LastModified) : undefined
106
+ });
107
+
108
+ if (action === 'list') {
109
+ let result = await client.listEventSourceMappings(
110
+ ctx.input.functionName,
111
+ ctx.input.eventSourceArn
112
+ );
113
+ let mappings = (result.EventSourceMappings || []).map((m: any) => ({
114
+ mappingUuid: m.UUID,
115
+ functionArn: m.FunctionArn,
116
+ eventSourceArn: m.EventSourceArn,
117
+ state: m.State,
118
+ batchSize: m.BatchSize
119
+ }));
120
+ return {
121
+ output: { mappings },
122
+ message: `Found **${mappings.length}** event source mapping(s).`
123
+ };
124
+ }
125
+
126
+ if (action === 'get') {
127
+ if (!ctx.input.mappingUuid) throw lambdaServiceError('mappingUuid is required');
128
+ let result = await client.getEventSourceMapping(ctx.input.mappingUuid);
129
+ return {
130
+ output: mapResult(result),
131
+ message: `Event source mapping **${result.UUID}** is in state **${result.State}**.`
132
+ };
133
+ }
134
+
135
+ if (action === 'delete') {
136
+ if (!ctx.input.mappingUuid) throw lambdaServiceError('mappingUuid is required');
137
+ let result = await client.deleteEventSourceMapping(ctx.input.mappingUuid);
138
+ return {
139
+ output: { ...mapResult(result), deleted: true },
140
+ message: `Deleted event source mapping **${ctx.input.mappingUuid}**.`
141
+ };
142
+ }
143
+
144
+ let buildParams = (): Record<string, any> => {
145
+ let params: Record<string, any> = {};
146
+ if (ctx.input.functionName) params['FunctionName'] = ctx.input.functionName;
147
+ if (ctx.input.eventSourceArn) params['EventSourceArn'] = ctx.input.eventSourceArn;
148
+ if (ctx.input.enabled !== undefined) params['Enabled'] = ctx.input.enabled;
149
+ if (ctx.input.batchSize) params['BatchSize'] = ctx.input.batchSize;
150
+ if (ctx.input.startingPosition) params['StartingPosition'] = ctx.input.startingPosition;
151
+ if (ctx.input.startingPositionTimestamp)
152
+ params['StartingPositionTimestamp'] = ctx.input.startingPositionTimestamp;
153
+ if (ctx.input.maximumBatchingWindowInSeconds !== undefined)
154
+ params['MaximumBatchingWindowInSeconds'] = ctx.input.maximumBatchingWindowInSeconds;
155
+ if (ctx.input.maximumRetryAttempts !== undefined)
156
+ params['MaximumRetryAttempts'] = ctx.input.maximumRetryAttempts;
157
+ if (ctx.input.parallelizationFactor)
158
+ params['ParallelizationFactor'] = ctx.input.parallelizationFactor;
159
+ if (ctx.input.filterPatterns)
160
+ params['FilterCriteria'] = {
161
+ Filters: ctx.input.filterPatterns.map(p => ({ Pattern: p }))
162
+ };
163
+ if (ctx.input.onFailureDestinationArn)
164
+ params['DestinationConfig'] = {
165
+ OnFailure: { Destination: ctx.input.onFailureDestinationArn }
166
+ };
167
+ if (ctx.input.bisectBatchOnFunctionError !== undefined)
168
+ params['BisectBatchOnFunctionError'] = ctx.input.bisectBatchOnFunctionError;
169
+ if (ctx.input.topics) params['Topics'] = ctx.input.topics;
170
+ if (ctx.input.queues) params['Queues'] = ctx.input.queues;
171
+ if (ctx.input.maximumConcurrency)
172
+ params['ScalingConfig'] = { MaximumConcurrency: ctx.input.maximumConcurrency };
173
+ return params;
174
+ };
175
+
176
+ if (action === 'create') {
177
+ let result = await client.createEventSourceMapping(buildParams());
178
+ return {
179
+ output: mapResult(result),
180
+ message: `Created event source mapping **${result.UUID}** (${result.State}).`
181
+ };
182
+ }
183
+
184
+ // update
185
+ if (!ctx.input.mappingUuid)
186
+ throw lambdaServiceError('mappingUuid is required for update');
187
+ let result = await client.updateEventSourceMapping(ctx.input.mappingUuid, buildParams());
188
+ return {
189
+ output: mapResult(result),
190
+ message: `Updated event source mapping **${result.UUID}** (${result.State}).`
191
+ };
192
+ })
193
+ .build();
@@ -0,0 +1,132 @@
1
+ import { SlateTool } from 'slates';
2
+ import { createClient } from '../lib/helpers';
3
+ import { lambdaServiceError } from '../lib/errors';
4
+ import { spec } from '../spec';
5
+ import { z } from 'zod';
6
+
7
+ let corsConfigSchema = z
8
+ .object({
9
+ allowCredentials: z.boolean().optional().describe('Allow credentials in CORS requests'),
10
+ allowHeaders: z.array(z.string()).optional().describe('Allowed headers'),
11
+ allowMethods: z.array(z.string()).optional().describe('Allowed HTTP methods'),
12
+ allowOrigins: z.array(z.string()).optional().describe('Allowed origins'),
13
+ exposeHeaders: z.array(z.string()).optional().describe('Headers to expose in responses'),
14
+ maxAge: z.number().optional().describe('Cache duration for preflight requests in seconds')
15
+ })
16
+ .optional();
17
+
18
+ export let manageFunctionUrl = SlateTool.create(spec, {
19
+ name: 'Manage Function URL',
20
+ key: 'manage_function_url',
21
+ description: `Create, update, get, or delete a dedicated HTTPS endpoint (function URL) for a Lambda function. Function URLs provide public API access without needing API Gateway. Supports IAM authentication or open access, and configurable CORS.`,
22
+ instructions: [
23
+ 'Use **action** to specify: "create", "update", "get", or "delete".',
24
+ 'Set authType to "NONE" for public access or "AWS_IAM" to require SigV4 signatures.'
25
+ ]
26
+ })
27
+ .input(
28
+ z.object({
29
+ action: z.enum(['create', 'update', 'get', 'delete']).describe('Operation to perform'),
30
+ functionName: z.string().describe('Function name or ARN'),
31
+ qualifier: z.string().optional().describe('Alias name to associate the URL with'),
32
+ authType: z
33
+ .enum(['AWS_IAM', 'NONE'])
34
+ .optional()
35
+ .describe('Authentication type (required for create)'),
36
+ cors: corsConfigSchema.describe('CORS configuration'),
37
+ invokeMode: z.enum(['BUFFERED', 'RESPONSE_STREAM']).optional().describe('Response mode')
38
+ })
39
+ )
40
+ .output(
41
+ z.object({
42
+ functionUrl: z.string().optional().describe('The HTTPS endpoint URL'),
43
+ functionArn: z.string().optional().describe('Function ARN'),
44
+ authType: z.string().optional().describe('Authentication type'),
45
+ cors: z.any().optional().describe('CORS configuration'),
46
+ invokeMode: z.string().optional().describe('Response mode'),
47
+ creationTime: z.string().optional().describe('Creation timestamp'),
48
+ deleted: z.boolean().optional()
49
+ })
50
+ )
51
+ .handleInvocation(async ctx => {
52
+ let client = createClient(ctx.config, ctx.auth);
53
+ let { action, functionName, qualifier } = ctx.input;
54
+
55
+ if (action === 'get') {
56
+ let result = await client.getFunctionUrlConfig(functionName, qualifier);
57
+ return {
58
+ output: {
59
+ functionUrl: result.FunctionUrl,
60
+ functionArn: result.FunctionArn,
61
+ authType: result.AuthType,
62
+ cors: result.Cors,
63
+ invokeMode: result.InvokeMode,
64
+ creationTime: result.CreationTime
65
+ },
66
+ message: `Function URL: **${result.FunctionUrl}** (auth: ${result.AuthType}).`
67
+ };
68
+ }
69
+
70
+ if (action === 'delete') {
71
+ await client.deleteFunctionUrlConfig(functionName, qualifier);
72
+ return {
73
+ output: { deleted: true },
74
+ message: `Deleted function URL for **${functionName}**.`
75
+ };
76
+ }
77
+
78
+ let buildParams = (): Record<string, any> => {
79
+ let params: Record<string, any> = {};
80
+ if (ctx.input.authType) params['AuthType'] = ctx.input.authType;
81
+ if (ctx.input.invokeMode) params['InvokeMode'] = ctx.input.invokeMode;
82
+ if (ctx.input.cors) {
83
+ let corsObj: Record<string, any> = {};
84
+ if (ctx.input.cors.allowCredentials !== undefined)
85
+ corsObj['AllowCredentials'] = ctx.input.cors.allowCredentials;
86
+ if (ctx.input.cors.allowHeaders) corsObj['AllowHeaders'] = ctx.input.cors.allowHeaders;
87
+ if (ctx.input.cors.allowMethods) corsObj['AllowMethods'] = ctx.input.cors.allowMethods;
88
+ if (ctx.input.cors.allowOrigins) corsObj['AllowOrigins'] = ctx.input.cors.allowOrigins;
89
+ if (ctx.input.cors.exposeHeaders)
90
+ corsObj['ExposeHeaders'] = ctx.input.cors.exposeHeaders;
91
+ if (ctx.input.cors.maxAge !== undefined) corsObj['MaxAge'] = ctx.input.cors.maxAge;
92
+ params['Cors'] = corsObj;
93
+ }
94
+ return params;
95
+ };
96
+
97
+ if (action === 'create') {
98
+ if (!ctx.input.authType)
99
+ throw lambdaServiceError('authType is required for creating a function URL');
100
+ let result = await client.createFunctionUrlConfig(
101
+ functionName,
102
+ buildParams(),
103
+ qualifier
104
+ );
105
+ return {
106
+ output: {
107
+ functionUrl: result.FunctionUrl,
108
+ functionArn: result.FunctionArn,
109
+ authType: result.AuthType,
110
+ cors: result.Cors,
111
+ invokeMode: result.InvokeMode,
112
+ creationTime: result.CreationTime
113
+ },
114
+ message: `Created function URL: **${result.FunctionUrl}** (auth: ${result.AuthType}).`
115
+ };
116
+ }
117
+
118
+ // update
119
+ let result = await client.updateFunctionUrlConfig(functionName, buildParams(), qualifier);
120
+ return {
121
+ output: {
122
+ functionUrl: result.FunctionUrl,
123
+ functionArn: result.FunctionArn,
124
+ authType: result.AuthType,
125
+ cors: result.Cors,
126
+ invokeMode: result.InvokeMode,
127
+ creationTime: result.CreationTime
128
+ },
129
+ message: `Updated function URL: **${result.FunctionUrl}**.`
130
+ };
131
+ })
132
+ .build();
@@ -0,0 +1,186 @@
1
+ import { SlateTool } from 'slates';
2
+ import { createClient } from '../lib/helpers';
3
+ import { lambdaServiceError } from '../lib/errors';
4
+ import { spec } from '../spec';
5
+ import { z } from 'zod';
6
+
7
+ export let manageLayer = SlateTool.create(spec, {
8
+ name: 'Manage Layer',
9
+ key: 'manage_layer',
10
+ description: `Publish, get, delete, or list Lambda layers and their versions. Layers are reusable packages of libraries, dependencies, or custom runtimes that can be attached to functions.`,
11
+ instructions: [
12
+ 'Use **action** to specify the operation: "publish", "get", "delete", "list_layers", or "list_versions".',
13
+ 'When publishing, provide the layer content via an S3 location or base64-encoded ZIP.'
14
+ ]
15
+ })
16
+ .input(
17
+ z.object({
18
+ action: z
19
+ .enum(['publish', 'get', 'delete', 'list_layers', 'list_versions'])
20
+ .describe('Operation to perform'),
21
+ layerName: z
22
+ .string()
23
+ .optional()
24
+ .describe('Layer name (required for publish/get/delete/list_versions)'),
25
+ versionNumber: z
26
+ .number()
27
+ .optional()
28
+ .describe('Layer version number (required for get/delete)'),
29
+ content: z
30
+ .object({
31
+ s3Bucket: z.string().optional().describe('S3 bucket with the layer archive'),
32
+ s3Key: z.string().optional().describe('S3 object key'),
33
+ s3ObjectVersion: z.string().optional().describe('S3 object version'),
34
+ zipFile: z.string().optional().describe('Base64-encoded ZIP file')
35
+ })
36
+ .optional()
37
+ .describe('Layer content (required for publish)'),
38
+ compatibleRuntimes: z
39
+ .array(z.string())
40
+ .optional()
41
+ .describe('Compatible runtimes (e.g., ["nodejs22.x", "python3.13"])'),
42
+ compatibleArchitectures: z
43
+ .array(z.enum(['x86_64', 'arm64']))
44
+ .optional()
45
+ .describe('Compatible architectures'),
46
+ description: z.string().optional().describe('Layer version description'),
47
+ licenseInfo: z.string().optional().describe('Layer license information'),
48
+ compatibleRuntimeFilter: z
49
+ .string()
50
+ .optional()
51
+ .describe('Filter layers by compatible runtime (for list_layers)')
52
+ })
53
+ )
54
+ .output(
55
+ z.object({
56
+ layerArn: z.string().optional().describe('Layer ARN'),
57
+ layerVersionArn: z.string().optional().describe('Layer version ARN'),
58
+ versionNumber: z.number().optional().describe('Layer version number'),
59
+ description: z.string().optional().describe('Layer description'),
60
+ codeSize: z.number().optional().describe('Layer code size in bytes'),
61
+ codeSha256: z.string().optional().describe('SHA256 of the layer code'),
62
+ compatibleRuntimes: z.array(z.string()).optional().describe('Compatible runtimes'),
63
+ layers: z
64
+ .array(
65
+ z.object({
66
+ layerName: z.string().optional(),
67
+ layerArn: z.string().optional(),
68
+ latestVersion: z.number().optional(),
69
+ latestDescription: z.string().optional()
70
+ })
71
+ )
72
+ .optional()
73
+ .describe('List of layers (for list_layers)'),
74
+ versions: z
75
+ .array(
76
+ z.object({
77
+ layerVersionArn: z.string().optional(),
78
+ versionNumber: z.number().optional(),
79
+ description: z.string().optional(),
80
+ compatibleRuntimes: z.array(z.string()).optional()
81
+ })
82
+ )
83
+ .optional()
84
+ .describe('List of layer versions (for list_versions)'),
85
+ deleted: z.boolean().optional()
86
+ })
87
+ )
88
+ .handleInvocation(async ctx => {
89
+ let client = createClient(ctx.config, ctx.auth);
90
+ let { action, layerName } = ctx.input;
91
+
92
+ if (action === 'list_layers') {
93
+ let result = await client.listLayers(
94
+ undefined,
95
+ undefined,
96
+ ctx.input.compatibleRuntimeFilter
97
+ );
98
+ let layers = (result.Layers || []).map((l: any) => ({
99
+ layerName: l.LayerName,
100
+ layerArn: l.LayerArn,
101
+ latestVersion: l.LatestMatchingVersion?.Version,
102
+ latestDescription: l.LatestMatchingVersion?.Description
103
+ }));
104
+ return {
105
+ output: { layers },
106
+ message: `Found **${layers.length}** layer(s).`
107
+ };
108
+ }
109
+
110
+ if (!layerName) throw lambdaServiceError('layerName is required for this action');
111
+
112
+ if (action === 'list_versions') {
113
+ let result = await client.listLayerVersions(layerName);
114
+ let versions = (result.LayerVersions || []).map((v: any) => ({
115
+ layerVersionArn: v.LayerVersionArn,
116
+ versionNumber: v.Version,
117
+ description: v.Description,
118
+ compatibleRuntimes: v.CompatibleRuntimes
119
+ }));
120
+ return {
121
+ output: { versions },
122
+ message: `Found **${versions.length}** version(s) of layer **${layerName}**.`
123
+ };
124
+ }
125
+
126
+ if (action === 'delete') {
127
+ if (!ctx.input.versionNumber)
128
+ throw lambdaServiceError('versionNumber is required for delete');
129
+ await client.deleteLayerVersion(layerName, ctx.input.versionNumber);
130
+ return {
131
+ output: { deleted: true },
132
+ message: `Deleted version **${ctx.input.versionNumber}** of layer **${layerName}**.`
133
+ };
134
+ }
135
+
136
+ if (action === 'get') {
137
+ if (!ctx.input.versionNumber)
138
+ throw lambdaServiceError('versionNumber is required for get');
139
+ let result = await client.getLayerVersion(layerName, ctx.input.versionNumber);
140
+ return {
141
+ output: {
142
+ layerArn: result.LayerArn,
143
+ layerVersionArn: result.LayerVersionArn,
144
+ versionNumber: result.Version,
145
+ description: result.Description,
146
+ codeSize: result.Content?.CodeSize,
147
+ codeSha256: result.Content?.CodeSha256,
148
+ compatibleRuntimes: result.CompatibleRuntimes
149
+ },
150
+ message: `Layer **${layerName}** version **${result.Version}** (${result.Content?.CodeSize || 0} bytes).`
151
+ };
152
+ }
153
+
154
+ // publish
155
+ if (!ctx.input.content)
156
+ throw lambdaServiceError('content is required for publishing a layer');
157
+ let contentObj: Record<string, any> = {};
158
+ if (ctx.input.content.s3Bucket) contentObj['S3Bucket'] = ctx.input.content.s3Bucket;
159
+ if (ctx.input.content.s3Key) contentObj['S3Key'] = ctx.input.content.s3Key;
160
+ if (ctx.input.content.s3ObjectVersion)
161
+ contentObj['S3ObjectVersion'] = ctx.input.content.s3ObjectVersion;
162
+ if (ctx.input.content.zipFile) contentObj['ZipFile'] = ctx.input.content.zipFile;
163
+
164
+ let params: Record<string, any> = { Content: contentObj };
165
+ if (ctx.input.compatibleRuntimes)
166
+ params['CompatibleRuntimes'] = ctx.input.compatibleRuntimes;
167
+ if (ctx.input.compatibleArchitectures)
168
+ params['CompatibleArchitectures'] = ctx.input.compatibleArchitectures;
169
+ if (ctx.input.description) params['Description'] = ctx.input.description;
170
+ if (ctx.input.licenseInfo) params['LicenseInfo'] = ctx.input.licenseInfo;
171
+
172
+ let result = await client.publishLayerVersion(layerName, params);
173
+ return {
174
+ output: {
175
+ layerArn: result.LayerArn,
176
+ layerVersionArn: result.LayerVersionArn,
177
+ versionNumber: result.Version,
178
+ description: result.Description,
179
+ codeSize: result.Content?.CodeSize,
180
+ codeSha256: result.Content?.CodeSha256,
181
+ compatibleRuntimes: result.CompatibleRuntimes
182
+ },
183
+ message: `Published layer **${layerName}** version **${result.Version}**.`
184
+ };
185
+ })
186
+ .build();
@@ -0,0 +1,94 @@
1
+ import { SlateTool } from 'slates';
2
+ import { createClient } from '../lib/helpers';
3
+ import { lambdaServiceError } from '../lib/errors';
4
+ import { spec } from '../spec';
5
+ import { z } from 'zod';
6
+
7
+ export let managePermission = SlateTool.create(spec, {
8
+ name: 'Manage Permission',
9
+ key: 'manage_permission',
10
+ description: `Add, remove, or view resource-based policy statements on a Lambda function. These policies grant other AWS accounts or services (e.g., S3, API Gateway, EventBridge) permission to invoke the function.`,
11
+ instructions: [
12
+ 'Use **action** "add" to grant access, "remove" to revoke, or "get" to view the current policy.',
13
+ 'For "add", provide a unique statementId, the allowed action (e.g., lambda:InvokeFunction), and the principal.'
14
+ ]
15
+ })
16
+ .input(
17
+ z.object({
18
+ action: z.enum(['add', 'remove', 'get']).describe('Operation to perform'),
19
+ functionName: z.string().describe('Function name or ARN'),
20
+ qualifier: z.string().optional().describe('Version or alias'),
21
+ statementId: z
22
+ .string()
23
+ .optional()
24
+ .describe('Unique statement identifier (required for add/remove)'),
25
+ permissionAction: z
26
+ .string()
27
+ .optional()
28
+ .describe('Lambda action to allow (e.g., "lambda:InvokeFunction")'),
29
+ principal: z
30
+ .string()
31
+ .optional()
32
+ .describe('AWS service or account (e.g., "s3.amazonaws.com" or account ID)'),
33
+ sourceArn: z.string().optional().describe('ARN of the source triggering the function'),
34
+ sourceAccount: z.string().optional().describe('AWS account ID of the source'),
35
+ principalOrgId: z.string().optional().describe('AWS Organizations ID for the principal')
36
+ })
37
+ )
38
+ .output(
39
+ z.object({
40
+ statement: z.string().optional().describe('Policy statement JSON'),
41
+ policy: z.string().optional().describe('Full policy JSON (for get action)'),
42
+ revisionId: z.string().optional().describe('Policy revision ID'),
43
+ removed: z.boolean().optional()
44
+ })
45
+ )
46
+ .handleInvocation(async ctx => {
47
+ let client = createClient(ctx.config, ctx.auth);
48
+ let { action, functionName, qualifier } = ctx.input;
49
+
50
+ if (action === 'get') {
51
+ let result = await client.getPolicy(functionName, qualifier);
52
+ return {
53
+ output: {
54
+ policy: result.Policy,
55
+ revisionId: result.RevisionId
56
+ },
57
+ message: `Retrieved resource policy for **${functionName}**.`
58
+ };
59
+ }
60
+
61
+ if (action === 'remove') {
62
+ if (!ctx.input.statementId)
63
+ throw lambdaServiceError('statementId is required for remove');
64
+ await client.removePermission(functionName, ctx.input.statementId, qualifier);
65
+ return {
66
+ output: { removed: true },
67
+ message: `Removed permission statement **${ctx.input.statementId}** from **${functionName}**.`
68
+ };
69
+ }
70
+
71
+ // add
72
+ if (!ctx.input.statementId) throw lambdaServiceError('statementId is required for add');
73
+ if (!ctx.input.permissionAction)
74
+ throw lambdaServiceError('permissionAction is required for add');
75
+ if (!ctx.input.principal) throw lambdaServiceError('principal is required for add');
76
+
77
+ let params: Record<string, any> = {
78
+ StatementId: ctx.input.statementId,
79
+ Action: ctx.input.permissionAction,
80
+ Principal: ctx.input.principal
81
+ };
82
+ if (ctx.input.sourceArn) params['SourceArn'] = ctx.input.sourceArn;
83
+ if (ctx.input.sourceAccount) params['SourceAccount'] = ctx.input.sourceAccount;
84
+ if (ctx.input.principalOrgId) params['PrincipalOrgID'] = ctx.input.principalOrgId;
85
+
86
+ let result = await client.addPermission(functionName, params, qualifier);
87
+ return {
88
+ output: {
89
+ statement: result.Statement
90
+ },
91
+ message: `Added permission for **${ctx.input.principal}** to invoke **${functionName}**.`
92
+ };
93
+ })
94
+ .build();
@@ -0,0 +1,63 @@
1
+ import { SlateTool } from 'slates';
2
+ import { createClient } from '../lib/helpers';
3
+ import { lambdaServiceError } from '../lib/errors';
4
+ import { spec } from '../spec';
5
+ import { z } from 'zod';
6
+
7
+ export let manageRecursionConfig = SlateTool.create(spec, {
8
+ name: 'Manage Recursion Config',
9
+ key: 'manage_recursion_config',
10
+ description: `Get or set recursive loop detection for a Lambda function. Lambda defaults to terminating detected recursive invocation loops; only use Allow for intentional recursive designs with safeguards.`,
11
+ instructions: [
12
+ 'Use action "get" to read the current recursive loop setting.',
13
+ 'Use action "set" with recursiveLoop set to "Terminate" or "Allow".'
14
+ ]
15
+ })
16
+ .input(
17
+ z.object({
18
+ action: z.enum(['get', 'set']).describe('Operation to perform'),
19
+ functionName: z.string().describe('Function name or ARN'),
20
+ recursiveLoop: z
21
+ .enum(['Allow', 'Terminate'])
22
+ .optional()
23
+ .describe('Recursive loop handling to set')
24
+ })
25
+ )
26
+ .output(
27
+ z.object({
28
+ recursiveLoop: z
29
+ .string()
30
+ .optional()
31
+ .describe('Recursive loop handling: Allow or Terminate')
32
+ })
33
+ )
34
+ .handleInvocation(async ctx => {
35
+ let client = createClient(ctx.config, ctx.auth);
36
+ let { action, functionName } = ctx.input;
37
+
38
+ if (action === 'get') {
39
+ let result = await client.getFunctionRecursionConfig(functionName);
40
+ return {
41
+ output: {
42
+ recursiveLoop: result.RecursiveLoop
43
+ },
44
+ message: `Recursive loop detection for **${functionName}** is **${result.RecursiveLoop}**.`
45
+ };
46
+ }
47
+
48
+ if (!ctx.input.recursiveLoop) {
49
+ throw lambdaServiceError('recursiveLoop is required for set.');
50
+ }
51
+
52
+ let result = await client.putFunctionRecursionConfig(
53
+ functionName,
54
+ ctx.input.recursiveLoop
55
+ );
56
+ return {
57
+ output: {
58
+ recursiveLoop: result.RecursiveLoop
59
+ },
60
+ message: `Set recursive loop detection for **${functionName}** to **${result.RecursiveLoop}**.`
61
+ };
62
+ })
63
+ .build();