@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.
- package/README.md +89 -0
- package/docs/SPEC.md +91 -0
- package/logo.svg +1 -0
- package/package.json +21 -0
- package/slate.json +23 -0
- package/src/auth.ts +51 -0
- package/src/config.ts +8 -0
- package/src/index.ts +50 -0
- package/src/lib/aws-signer.ts +158 -0
- package/src/lib/client.ts +919 -0
- package/src/lib/errors.ts +101 -0
- package/src/lib/helpers.ts +15 -0
- package/src/spec.ts +13 -0
- package/src/tools/configure-async-invocation.ts +134 -0
- package/src/tools/create-function.ts +127 -0
- package/src/tools/delete-function.ts +41 -0
- package/src/tools/get-account-settings.ts +45 -0
- package/src/tools/get-function.ts +104 -0
- package/src/tools/index.ts +19 -0
- package/src/tools/invoke-function.ts +94 -0
- package/src/tools/list-functions.ts +88 -0
- package/src/tools/manage-alias.ts +159 -0
- package/src/tools/manage-concurrency.ts +141 -0
- package/src/tools/manage-durable-execution.ts +209 -0
- package/src/tools/manage-event-source-mapping.ts +193 -0
- package/src/tools/manage-function-url.ts +132 -0
- package/src/tools/manage-layer.ts +186 -0
- package/src/tools/manage-permission.ts +94 -0
- package/src/tools/manage-recursion-config.ts +63 -0
- package/src/tools/manage-runtime-management.ts +85 -0
- package/src/tools/manage-tags.ts +72 -0
- package/src/tools/publish-version.ts +83 -0
- package/src/tools/update-function.ts +156 -0
- package/src/triggers/function-changes.ts +113 -0
- package/src/triggers/inbound-webhook.ts +67 -0
- package/src/triggers/index.ts +2 -0
- package/tsconfig.json +23 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { SlateTool } from 'slates';
|
|
2
|
+
import { createClient } from '../lib/helpers';
|
|
3
|
+
import { spec } from '../spec';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
|
|
6
|
+
export let invokeFunction = SlateTool.create(spec, {
|
|
7
|
+
name: 'Invoke Function',
|
|
8
|
+
key: 'invoke_function',
|
|
9
|
+
description: `Invoke a Lambda function synchronously or asynchronously. **Synchronous** (RequestResponse) returns the function's output. **Asynchronous** (Event) queues the event and returns immediately. Use **DryRun** to validate permissions without executing.`,
|
|
10
|
+
instructions: [
|
|
11
|
+
'Synchronous invocations have a 6MB payload limit and return the function result.',
|
|
12
|
+
'Asynchronous invocations have a 1MB payload limit and return a 202 status immediately.',
|
|
13
|
+
'Set logType to "Tail" to get the last 4KB of execution logs in the response.'
|
|
14
|
+
]
|
|
15
|
+
})
|
|
16
|
+
.input(
|
|
17
|
+
z.object({
|
|
18
|
+
functionName: z.string().describe('Function name, ARN, or partial ARN'),
|
|
19
|
+
payload: z.any().optional().describe('JSON payload to send to the function'),
|
|
20
|
+
invocationType: z
|
|
21
|
+
.enum(['RequestResponse', 'Event', 'DryRun'])
|
|
22
|
+
.optional()
|
|
23
|
+
.describe('Invocation type (default: RequestResponse)'),
|
|
24
|
+
qualifier: z.string().optional().describe('Version or alias to invoke'),
|
|
25
|
+
logType: z
|
|
26
|
+
.enum(['None', 'Tail'])
|
|
27
|
+
.optional()
|
|
28
|
+
.describe('Set to "Tail" to include execution logs'),
|
|
29
|
+
clientContext: z
|
|
30
|
+
.string()
|
|
31
|
+
.optional()
|
|
32
|
+
.describe('Base64-encoded client context for synchronous invocations'),
|
|
33
|
+
durableExecutionName: z
|
|
34
|
+
.string()
|
|
35
|
+
.optional()
|
|
36
|
+
.describe('Optional durable execution name for durable functions'),
|
|
37
|
+
tenantId: z.string().optional().describe('Tenant identifier for multi-tenant functions')
|
|
38
|
+
})
|
|
39
|
+
)
|
|
40
|
+
.output(
|
|
41
|
+
z.object({
|
|
42
|
+
statusCode: z
|
|
43
|
+
.number()
|
|
44
|
+
.optional()
|
|
45
|
+
.describe('HTTP status code (200=sync, 202=async, 204=dry run)'),
|
|
46
|
+
response: z.any().optional().describe('Function response payload (synchronous only)'),
|
|
47
|
+
functionError: z
|
|
48
|
+
.string()
|
|
49
|
+
.optional()
|
|
50
|
+
.describe('Error type if the function returned an error'),
|
|
51
|
+
executedVersion: z.string().optional().describe('Version that was executed'),
|
|
52
|
+
logResult: z
|
|
53
|
+
.string()
|
|
54
|
+
.optional()
|
|
55
|
+
.describe('Last 4KB of execution log (when logType is Tail)'),
|
|
56
|
+
durableExecutionArn: z
|
|
57
|
+
.string()
|
|
58
|
+
.optional()
|
|
59
|
+
.describe('Durable execution ARN returned by durable function invocations')
|
|
60
|
+
})
|
|
61
|
+
)
|
|
62
|
+
.handleInvocation(async ctx => {
|
|
63
|
+
let client = createClient(ctx.config, ctx.auth);
|
|
64
|
+
|
|
65
|
+
let result = await client.invokeFunction(ctx.input.functionName, ctx.input.payload, {
|
|
66
|
+
invocationType: ctx.input.invocationType,
|
|
67
|
+
logType: ctx.input.logType,
|
|
68
|
+
qualifier: ctx.input.qualifier,
|
|
69
|
+
clientContext: ctx.input.clientContext,
|
|
70
|
+
durableExecutionName: ctx.input.durableExecutionName,
|
|
71
|
+
tenantId: ctx.input.tenantId
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
let invType = ctx.input.invocationType || 'RequestResponse';
|
|
75
|
+
let msg =
|
|
76
|
+
invType === 'Event'
|
|
77
|
+
? `Function **${ctx.input.functionName}** invoked asynchronously.`
|
|
78
|
+
: invType === 'DryRun'
|
|
79
|
+
? `Dry run completed for **${ctx.input.functionName}** — permissions validated.`
|
|
80
|
+
: `Function **${ctx.input.functionName}** invoked successfully.`;
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
output: {
|
|
84
|
+
statusCode: result?.StatusCode,
|
|
85
|
+
response: result?.Payload,
|
|
86
|
+
functionError: result?.FunctionError,
|
|
87
|
+
executedVersion: result?.ExecutedVersion,
|
|
88
|
+
logResult: result?.LogResult,
|
|
89
|
+
durableExecutionArn: result?.DurableExecutionArn
|
|
90
|
+
},
|
|
91
|
+
message: msg
|
|
92
|
+
};
|
|
93
|
+
})
|
|
94
|
+
.build();
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { SlateTool } from 'slates';
|
|
2
|
+
import { createClient } from '../lib/helpers';
|
|
3
|
+
import { spec } from '../spec';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
|
|
6
|
+
let functionConfigSchema = z.object({
|
|
7
|
+
functionName: z.string().optional().describe('Name of the function'),
|
|
8
|
+
functionArn: z.string().optional().describe('ARN of the function'),
|
|
9
|
+
runtime: z
|
|
10
|
+
.string()
|
|
11
|
+
.optional()
|
|
12
|
+
.describe('Runtime environment (e.g., nodejs22.x, python3.13)'),
|
|
13
|
+
role: z.string().optional().describe('Execution role ARN'),
|
|
14
|
+
handler: z.string().optional().describe('Function handler (e.g., index.handler)'),
|
|
15
|
+
codeSize: z.number().optional().describe('Size of the deployment package in bytes'),
|
|
16
|
+
description: z.string().optional().describe('Function description'),
|
|
17
|
+
timeout: z.number().optional().describe('Execution timeout in seconds'),
|
|
18
|
+
memorySize: z.number().optional().describe('Memory allocated in MB'),
|
|
19
|
+
lastModified: z.string().optional().describe('Last modified timestamp'),
|
|
20
|
+
version: z.string().optional().describe('Function version'),
|
|
21
|
+
state: z.string().optional().describe('Current state of the function'),
|
|
22
|
+
packageType: z.string().optional().describe('Deployment package type (Zip or Image)'),
|
|
23
|
+
architectures: z.array(z.string()).optional().describe('Instruction set architectures')
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
export let listFunctions = SlateTool.create(spec, {
|
|
27
|
+
name: 'List Functions',
|
|
28
|
+
key: 'list_functions',
|
|
29
|
+
description: `List Lambda functions in the configured AWS region. Returns function names, ARNs, runtimes, and key configuration. Use **maxItems** to control page size and **marker** for pagination.`,
|
|
30
|
+
tags: {
|
|
31
|
+
readOnly: true
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
.input(
|
|
35
|
+
z.object({
|
|
36
|
+
maxItems: z
|
|
37
|
+
.number()
|
|
38
|
+
.optional()
|
|
39
|
+
.describe('Maximum number of functions to return (1-10000)'),
|
|
40
|
+
marker: z.string().optional().describe('Pagination token from previous response'),
|
|
41
|
+
includeAllVersions: z
|
|
42
|
+
.boolean()
|
|
43
|
+
.optional()
|
|
44
|
+
.describe('Set to true to include all published versions')
|
|
45
|
+
})
|
|
46
|
+
)
|
|
47
|
+
.output(
|
|
48
|
+
z.object({
|
|
49
|
+
functions: z.array(functionConfigSchema).describe('List of Lambda functions'),
|
|
50
|
+
nextMarker: z.string().optional().describe('Pagination token for the next page')
|
|
51
|
+
})
|
|
52
|
+
)
|
|
53
|
+
.handleInvocation(async ctx => {
|
|
54
|
+
let client = createClient(ctx.config, ctx.auth);
|
|
55
|
+
|
|
56
|
+
let functionVersion = ctx.input.includeAllVersions ? 'ALL' : undefined;
|
|
57
|
+
let result = await client.listFunctions(
|
|
58
|
+
ctx.input.marker,
|
|
59
|
+
ctx.input.maxItems,
|
|
60
|
+
functionVersion
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
let functions = (result.Functions || []).map((f: any) => ({
|
|
64
|
+
functionName: f.FunctionName,
|
|
65
|
+
functionArn: f.FunctionArn,
|
|
66
|
+
runtime: f.Runtime,
|
|
67
|
+
role: f.Role,
|
|
68
|
+
handler: f.Handler,
|
|
69
|
+
codeSize: f.CodeSize,
|
|
70
|
+
description: f.Description,
|
|
71
|
+
timeout: f.Timeout,
|
|
72
|
+
memorySize: f.MemorySize,
|
|
73
|
+
lastModified: f.LastModified,
|
|
74
|
+
version: f.Version,
|
|
75
|
+
state: f.State,
|
|
76
|
+
packageType: f.PackageType,
|
|
77
|
+
architectures: f.Architectures
|
|
78
|
+
}));
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
output: {
|
|
82
|
+
functions,
|
|
83
|
+
nextMarker: result.NextMarker
|
|
84
|
+
},
|
|
85
|
+
message: `Found **${functions.length}** Lambda function(s)${result.NextMarker ? ' (more available)' : ''}.`
|
|
86
|
+
};
|
|
87
|
+
})
|
|
88
|
+
.build();
|
|
@@ -0,0 +1,159 @@
|
|
|
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 routingConfigSchema = z
|
|
8
|
+
.object({
|
|
9
|
+
additionalVersionWeights: z
|
|
10
|
+
.record(z.string(), z.number())
|
|
11
|
+
.optional()
|
|
12
|
+
.describe(
|
|
13
|
+
'Version-to-weight mapping for traffic shifting (e.g., {"2": 0.1} routes 10% to version 2)'
|
|
14
|
+
)
|
|
15
|
+
})
|
|
16
|
+
.optional();
|
|
17
|
+
|
|
18
|
+
export let manageAlias = SlateTool.create(spec, {
|
|
19
|
+
name: 'Manage Alias',
|
|
20
|
+
key: 'manage_alias',
|
|
21
|
+
description: `Create, update, get, delete, or list aliases for a Lambda function. Aliases are named pointers to function versions, enabling canary and blue/green deployments via weighted traffic shifting.`,
|
|
22
|
+
instructions: [
|
|
23
|
+
'Use **action** to specify the operation: "create", "update", "get", "delete", or "list".',
|
|
24
|
+
'For traffic shifting, use routingConfig to split traffic between the primary version and one additional version.'
|
|
25
|
+
]
|
|
26
|
+
})
|
|
27
|
+
.input(
|
|
28
|
+
z.object({
|
|
29
|
+
action: z
|
|
30
|
+
.enum(['create', 'update', 'get', 'delete', 'list'])
|
|
31
|
+
.describe('Operation to perform'),
|
|
32
|
+
functionName: z.string().describe('Function name or ARN'),
|
|
33
|
+
aliasName: z
|
|
34
|
+
.string()
|
|
35
|
+
.optional()
|
|
36
|
+
.describe('Alias name (required for create/update/get/delete)'),
|
|
37
|
+
functionVersion: z
|
|
38
|
+
.string()
|
|
39
|
+
.optional()
|
|
40
|
+
.describe(
|
|
41
|
+
'Function version the alias points to (required for create, optional for update)'
|
|
42
|
+
),
|
|
43
|
+
description: z.string().optional().describe('Alias description'),
|
|
44
|
+
routingConfig: routingConfigSchema.describe(
|
|
45
|
+
'Traffic routing configuration for weighted aliases'
|
|
46
|
+
)
|
|
47
|
+
})
|
|
48
|
+
)
|
|
49
|
+
.output(
|
|
50
|
+
z.object({
|
|
51
|
+
aliasArn: z.string().optional().describe('Alias ARN'),
|
|
52
|
+
aliasName: z.string().optional().describe('Alias name'),
|
|
53
|
+
functionVersion: z.string().optional().describe('Version the alias points to'),
|
|
54
|
+
description: z.string().optional().describe('Alias description'),
|
|
55
|
+
routingConfig: z.any().optional().describe('Traffic routing configuration'),
|
|
56
|
+
aliases: z
|
|
57
|
+
.array(
|
|
58
|
+
z.object({
|
|
59
|
+
aliasArn: z.string().optional(),
|
|
60
|
+
aliasName: z.string().optional(),
|
|
61
|
+
functionVersion: z.string().optional(),
|
|
62
|
+
description: z.string().optional()
|
|
63
|
+
})
|
|
64
|
+
)
|
|
65
|
+
.optional()
|
|
66
|
+
.describe('List of aliases (for list action)'),
|
|
67
|
+
deleted: z.boolean().optional().describe('Whether the alias was deleted')
|
|
68
|
+
})
|
|
69
|
+
)
|
|
70
|
+
.handleInvocation(async ctx => {
|
|
71
|
+
let client = createClient(ctx.config, ctx.auth);
|
|
72
|
+
let { action, functionName, aliasName } = ctx.input;
|
|
73
|
+
|
|
74
|
+
if (action === 'list') {
|
|
75
|
+
let result = await client.listAliases(functionName);
|
|
76
|
+
let aliases = (result.Aliases || []).map((a: any) => ({
|
|
77
|
+
aliasArn: a.AliasArn,
|
|
78
|
+
aliasName: a.Name,
|
|
79
|
+
functionVersion: a.FunctionVersion,
|
|
80
|
+
description: a.Description
|
|
81
|
+
}));
|
|
82
|
+
return {
|
|
83
|
+
output: { aliases },
|
|
84
|
+
message: `Found **${aliases.length}** alias(es) for **${functionName}**.`
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (!aliasName) throw lambdaServiceError('aliasName is required for this action');
|
|
89
|
+
|
|
90
|
+
if (action === 'get') {
|
|
91
|
+
let result = await client.getAlias(functionName, aliasName);
|
|
92
|
+
return {
|
|
93
|
+
output: {
|
|
94
|
+
aliasArn: result.AliasArn,
|
|
95
|
+
aliasName: result.Name,
|
|
96
|
+
functionVersion: result.FunctionVersion,
|
|
97
|
+
description: result.Description,
|
|
98
|
+
routingConfig: result.RoutingConfig
|
|
99
|
+
},
|
|
100
|
+
message: `Alias **${aliasName}** points to version **${result.FunctionVersion}**.`
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (action === 'delete') {
|
|
105
|
+
await client.deleteAlias(functionName, aliasName);
|
|
106
|
+
return {
|
|
107
|
+
output: { deleted: true },
|
|
108
|
+
message: `Deleted alias **${aliasName}** from function **${functionName}**.`
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (action === 'create') {
|
|
113
|
+
if (!ctx.input.functionVersion)
|
|
114
|
+
throw lambdaServiceError('functionVersion is required for creating an alias');
|
|
115
|
+
let params: Record<string, any> = {
|
|
116
|
+
Name: aliasName,
|
|
117
|
+
FunctionVersion: ctx.input.functionVersion
|
|
118
|
+
};
|
|
119
|
+
if (ctx.input.description) params['Description'] = ctx.input.description;
|
|
120
|
+
if (ctx.input.routingConfig?.additionalVersionWeights) {
|
|
121
|
+
params['RoutingConfig'] = {
|
|
122
|
+
AdditionalVersionWeights: ctx.input.routingConfig.additionalVersionWeights
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
let result = await client.createAlias(functionName, params);
|
|
126
|
+
return {
|
|
127
|
+
output: {
|
|
128
|
+
aliasArn: result.AliasArn,
|
|
129
|
+
aliasName: result.Name,
|
|
130
|
+
functionVersion: result.FunctionVersion,
|
|
131
|
+
description: result.Description,
|
|
132
|
+
routingConfig: result.RoutingConfig
|
|
133
|
+
},
|
|
134
|
+
message: `Created alias **${result.Name}** → version **${result.FunctionVersion}**.`
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// update
|
|
139
|
+
let params: Record<string, any> = {};
|
|
140
|
+
if (ctx.input.functionVersion) params['FunctionVersion'] = ctx.input.functionVersion;
|
|
141
|
+
if (ctx.input.description !== undefined) params['Description'] = ctx.input.description;
|
|
142
|
+
if (ctx.input.routingConfig?.additionalVersionWeights) {
|
|
143
|
+
params['RoutingConfig'] = {
|
|
144
|
+
AdditionalVersionWeights: ctx.input.routingConfig.additionalVersionWeights
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
let result = await client.updateAlias(functionName, aliasName, params);
|
|
148
|
+
return {
|
|
149
|
+
output: {
|
|
150
|
+
aliasArn: result.AliasArn,
|
|
151
|
+
aliasName: result.Name,
|
|
152
|
+
functionVersion: result.FunctionVersion,
|
|
153
|
+
description: result.Description,
|
|
154
|
+
routingConfig: result.RoutingConfig
|
|
155
|
+
},
|
|
156
|
+
message: `Updated alias **${result.Name}** → version **${result.FunctionVersion}**.`
|
|
157
|
+
};
|
|
158
|
+
})
|
|
159
|
+
.build();
|
|
@@ -0,0 +1,141 @@
|
|
|
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 manageConcurrency = SlateTool.create(spec, {
|
|
8
|
+
name: 'Manage Concurrency',
|
|
9
|
+
key: 'manage_concurrency',
|
|
10
|
+
description: `Configure reserved and provisioned concurrency for a Lambda function. **Reserved concurrency** guarantees a set amount of concurrent executions. **Provisioned concurrency** keeps execution environments warm to eliminate cold starts.`,
|
|
11
|
+
instructions: [
|
|
12
|
+
'Use concurrencyType "reserved" to manage reserved concurrency, or "provisioned" for provisioned concurrency.',
|
|
13
|
+
'For provisioned concurrency, a qualifier (version number or alias) is required.',
|
|
14
|
+
'Use action "get" to check current settings, "set" to configure, or "delete" to remove.'
|
|
15
|
+
]
|
|
16
|
+
})
|
|
17
|
+
.input(
|
|
18
|
+
z.object({
|
|
19
|
+
action: z.enum(['get', 'set', 'delete']).describe('Operation to perform'),
|
|
20
|
+
functionName: z.string().describe('Function name or ARN'),
|
|
21
|
+
concurrencyType: z
|
|
22
|
+
.enum(['reserved', 'provisioned'])
|
|
23
|
+
.describe('Type of concurrency to manage'),
|
|
24
|
+
concurrentExecutions: z
|
|
25
|
+
.number()
|
|
26
|
+
.optional()
|
|
27
|
+
.describe('Number of concurrent executions to reserve/provision'),
|
|
28
|
+
qualifier: z
|
|
29
|
+
.string()
|
|
30
|
+
.optional()
|
|
31
|
+
.describe('Version or alias (required for provisioned concurrency)')
|
|
32
|
+
})
|
|
33
|
+
)
|
|
34
|
+
.output(
|
|
35
|
+
z.object({
|
|
36
|
+
reservedConcurrentExecutions: z
|
|
37
|
+
.number()
|
|
38
|
+
.optional()
|
|
39
|
+
.describe('Reserved concurrent executions'),
|
|
40
|
+
provisionedConcurrentExecutions: z
|
|
41
|
+
.number()
|
|
42
|
+
.optional()
|
|
43
|
+
.describe('Provisioned concurrent executions'),
|
|
44
|
+
allocatedProvisionedConcurrency: z
|
|
45
|
+
.number()
|
|
46
|
+
.optional()
|
|
47
|
+
.describe('Actually allocated provisioned concurrency'),
|
|
48
|
+
availableProvisionedConcurrency: z
|
|
49
|
+
.number()
|
|
50
|
+
.optional()
|
|
51
|
+
.describe('Available provisioned concurrency'),
|
|
52
|
+
status: z.string().optional().describe('Provisioned concurrency status'),
|
|
53
|
+
deleted: z.boolean().optional()
|
|
54
|
+
})
|
|
55
|
+
)
|
|
56
|
+
.handleInvocation(async ctx => {
|
|
57
|
+
let client = createClient(ctx.config, ctx.auth);
|
|
58
|
+
let { action, functionName, concurrencyType } = ctx.input;
|
|
59
|
+
|
|
60
|
+
if (concurrencyType === 'reserved') {
|
|
61
|
+
if (action === 'get') {
|
|
62
|
+
let result = await client.getFunctionConcurrency(functionName);
|
|
63
|
+
return {
|
|
64
|
+
output: { reservedConcurrentExecutions: result.ReservedConcurrentExecutions },
|
|
65
|
+
message:
|
|
66
|
+
result.ReservedConcurrentExecutions !== undefined
|
|
67
|
+
? `Reserved concurrency for **${functionName}**: **${result.ReservedConcurrentExecutions}**.`
|
|
68
|
+
: `No reserved concurrency configured for **${functionName}**.`
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
if (action === 'delete') {
|
|
72
|
+
await client.deleteFunctionConcurrency(functionName);
|
|
73
|
+
return {
|
|
74
|
+
output: { deleted: true },
|
|
75
|
+
message: `Removed reserved concurrency from **${functionName}**.`
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
if (!ctx.input.concurrentExecutions && ctx.input.concurrentExecutions !== 0) {
|
|
79
|
+
throw lambdaServiceError(
|
|
80
|
+
'concurrentExecutions is required to set reserved concurrency'
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
let result = await client.putFunctionConcurrency(
|
|
84
|
+
functionName,
|
|
85
|
+
ctx.input.concurrentExecutions
|
|
86
|
+
);
|
|
87
|
+
return {
|
|
88
|
+
output: { reservedConcurrentExecutions: result.ReservedConcurrentExecutions },
|
|
89
|
+
message: `Set reserved concurrency for **${functionName}** to **${result.ReservedConcurrentExecutions}**.`
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// provisioned
|
|
94
|
+
if (!ctx.input.qualifier)
|
|
95
|
+
throw lambdaServiceError('qualifier is required for provisioned concurrency');
|
|
96
|
+
|
|
97
|
+
if (action === 'get') {
|
|
98
|
+
let result = await client.getProvisionedConcurrencyConfig(
|
|
99
|
+
functionName,
|
|
100
|
+
ctx.input.qualifier
|
|
101
|
+
);
|
|
102
|
+
return {
|
|
103
|
+
output: {
|
|
104
|
+
provisionedConcurrentExecutions: result.RequestedProvisionedConcurrentExecutions,
|
|
105
|
+
allocatedProvisionedConcurrency: result.AllocatedProvisionedConcurrentExecutions,
|
|
106
|
+
availableProvisionedConcurrency: result.AvailableProvisionedConcurrentExecutions,
|
|
107
|
+
status: result.Status
|
|
108
|
+
},
|
|
109
|
+
message: `Provisioned concurrency for **${functionName}:${ctx.input.qualifier}**: requested=${result.RequestedProvisionedConcurrentExecutions}, status=${result.Status}.`
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (action === 'delete') {
|
|
114
|
+
await client.deleteProvisionedConcurrencyConfig(functionName, ctx.input.qualifier);
|
|
115
|
+
return {
|
|
116
|
+
output: { deleted: true },
|
|
117
|
+
message: `Removed provisioned concurrency from **${functionName}:${ctx.input.qualifier}**.`
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (!ctx.input.concurrentExecutions) {
|
|
122
|
+
throw lambdaServiceError(
|
|
123
|
+
'concurrentExecutions is required to set provisioned concurrency'
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
let result = await client.putProvisionedConcurrencyConfig(
|
|
127
|
+
functionName,
|
|
128
|
+
ctx.input.qualifier,
|
|
129
|
+
ctx.input.concurrentExecutions
|
|
130
|
+
);
|
|
131
|
+
return {
|
|
132
|
+
output: {
|
|
133
|
+
provisionedConcurrentExecutions: result.RequestedProvisionedConcurrentExecutions,
|
|
134
|
+
allocatedProvisionedConcurrency: result.AllocatedProvisionedConcurrentExecutions,
|
|
135
|
+
availableProvisionedConcurrency: result.AvailableProvisionedConcurrentExecutions,
|
|
136
|
+
status: result.Status
|
|
137
|
+
},
|
|
138
|
+
message: `Set provisioned concurrency for **${functionName}:${ctx.input.qualifier}** to **${ctx.input.concurrentExecutions}** (status: ${result.Status}).`
|
|
139
|
+
};
|
|
140
|
+
})
|
|
141
|
+
.build();
|
|
@@ -0,0 +1,209 @@
|
|
|
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 manageDurableExecution = SlateTool.create(spec, {
|
|
8
|
+
name: 'Manage Durable Execution',
|
|
9
|
+
key: 'manage_durable_execution',
|
|
10
|
+
description: `Inspect, list, stop, or send callbacks for Lambda durable executions. Durable executions are long-running, stateful workflows that can be checkpointed and resumed. Supports viewing execution history, state, and sending callback signals for human-in-the-loop patterns.`,
|
|
11
|
+
instructions: [
|
|
12
|
+
'Use **action** to specify: "get", "get_history", "get_state", "list", "stop", "callback_success", "callback_failure", or "callback_heartbeat".',
|
|
13
|
+
'For "list", provide a functionName. For most other actions, provide a durableExecutionArn.',
|
|
14
|
+
'For "get_state", provide checkpointToken.',
|
|
15
|
+
'For callback actions, provide a callbackId.'
|
|
16
|
+
]
|
|
17
|
+
})
|
|
18
|
+
.input(
|
|
19
|
+
z.object({
|
|
20
|
+
action: z
|
|
21
|
+
.enum([
|
|
22
|
+
'get',
|
|
23
|
+
'get_history',
|
|
24
|
+
'get_state',
|
|
25
|
+
'list',
|
|
26
|
+
'stop',
|
|
27
|
+
'callback_success',
|
|
28
|
+
'callback_failure',
|
|
29
|
+
'callback_heartbeat'
|
|
30
|
+
])
|
|
31
|
+
.describe('Operation to perform'),
|
|
32
|
+
durableExecutionArn: z
|
|
33
|
+
.string()
|
|
34
|
+
.optional()
|
|
35
|
+
.describe('Durable execution ARN (for get/get_history/get_state/stop)'),
|
|
36
|
+
functionName: z.string().optional().describe('Function name (required for list)'),
|
|
37
|
+
callbackId: z
|
|
38
|
+
.string()
|
|
39
|
+
.optional()
|
|
40
|
+
.describe('Callback ID (required for callback actions)'),
|
|
41
|
+
checkpointToken: z
|
|
42
|
+
.string()
|
|
43
|
+
.optional()
|
|
44
|
+
.describe('Checkpoint token required when retrieving durable execution state'),
|
|
45
|
+
callbackResult: z.any().optional().describe('Result payload for callback_success'),
|
|
46
|
+
stopErrorMessage: z
|
|
47
|
+
.string()
|
|
48
|
+
.optional()
|
|
49
|
+
.describe('Error message when stopping execution'),
|
|
50
|
+
stopErrorType: z.string().optional().describe('Error type when stopping execution'),
|
|
51
|
+
statusFilter: z
|
|
52
|
+
.enum(['RUNNING', 'SUCCEEDED', 'FAILED', 'TIMED_OUT', 'STOPPED'])
|
|
53
|
+
.optional()
|
|
54
|
+
.describe('Filter by status (for list)'),
|
|
55
|
+
maxItems: z
|
|
56
|
+
.number()
|
|
57
|
+
.optional()
|
|
58
|
+
.describe('Maximum items to return (for list/get_history)')
|
|
59
|
+
})
|
|
60
|
+
)
|
|
61
|
+
.output(
|
|
62
|
+
z.object({
|
|
63
|
+
durableExecutionArn: z.string().optional().describe('Durable execution ARN'),
|
|
64
|
+
durableExecutionName: z.string().optional().describe('Durable execution name'),
|
|
65
|
+
status: z.string().optional().describe('Execution status'),
|
|
66
|
+
startTimestamp: z.string().optional().describe('Start timestamp'),
|
|
67
|
+
endTimestamp: z.string().optional().describe('End timestamp'),
|
|
68
|
+
inputPayload: z.any().optional().describe('Input payload'),
|
|
69
|
+
result: z.any().optional().describe('Execution result'),
|
|
70
|
+
error: z.any().optional().describe('Execution error'),
|
|
71
|
+
functionArn: z.string().optional().describe('Associated function ARN'),
|
|
72
|
+
history: z.any().optional().describe('Execution history events'),
|
|
73
|
+
state: z.any().optional().describe('Current execution state'),
|
|
74
|
+
executions: z
|
|
75
|
+
.array(
|
|
76
|
+
z.object({
|
|
77
|
+
durableExecutionArn: z.string().optional(),
|
|
78
|
+
durableExecutionName: z.string().optional(),
|
|
79
|
+
status: z.string().optional(),
|
|
80
|
+
startTimestamp: z.string().optional()
|
|
81
|
+
})
|
|
82
|
+
)
|
|
83
|
+
.optional()
|
|
84
|
+
.describe('List of executions'),
|
|
85
|
+
stopTimestamp: z.string().optional().describe('Stop timestamp'),
|
|
86
|
+
callbackSent: z.boolean().optional()
|
|
87
|
+
})
|
|
88
|
+
)
|
|
89
|
+
.handleInvocation(async ctx => {
|
|
90
|
+
let client = createClient(ctx.config, ctx.auth);
|
|
91
|
+
let { action } = ctx.input;
|
|
92
|
+
|
|
93
|
+
if (action === 'list') {
|
|
94
|
+
if (!ctx.input.functionName)
|
|
95
|
+
throw lambdaServiceError('functionName is required for list');
|
|
96
|
+
let result = await client.listDurableExecutionsByFunction(
|
|
97
|
+
ctx.input.functionName,
|
|
98
|
+
ctx.input.statusFilter,
|
|
99
|
+
undefined,
|
|
100
|
+
ctx.input.maxItems
|
|
101
|
+
);
|
|
102
|
+
let executions = (result.DurableExecutions || []).map((e: any) => ({
|
|
103
|
+
durableExecutionArn: e.DurableExecutionArn,
|
|
104
|
+
durableExecutionName: e.DurableExecutionName,
|
|
105
|
+
status: e.Status,
|
|
106
|
+
startTimestamp: e.StartTimestamp ? String(e.StartTimestamp) : undefined
|
|
107
|
+
}));
|
|
108
|
+
return {
|
|
109
|
+
output: { executions },
|
|
110
|
+
message: `Found **${executions.length}** durable execution(s) for **${ctx.input.functionName}**.`
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (action === 'get') {
|
|
115
|
+
if (!ctx.input.durableExecutionArn)
|
|
116
|
+
throw lambdaServiceError('durableExecutionArn is required');
|
|
117
|
+
let result = await client.getDurableExecution(ctx.input.durableExecutionArn);
|
|
118
|
+
return {
|
|
119
|
+
output: {
|
|
120
|
+
durableExecutionArn: result.DurableExecutionArn,
|
|
121
|
+
durableExecutionName: result.DurableExecutionName,
|
|
122
|
+
status: result.Status,
|
|
123
|
+
startTimestamp: result.StartTimestamp ? String(result.StartTimestamp) : undefined,
|
|
124
|
+
endTimestamp: result.EndTimestamp ? String(result.EndTimestamp) : undefined,
|
|
125
|
+
inputPayload: result.InputPayload,
|
|
126
|
+
result: result.Result,
|
|
127
|
+
error: result.Error,
|
|
128
|
+
functionArn: result.FunctionArn
|
|
129
|
+
},
|
|
130
|
+
message: `Durable execution **${result.DurableExecutionName || result.DurableExecutionArn}** is **${result.Status}**.`
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (action === 'get_history') {
|
|
135
|
+
if (!ctx.input.durableExecutionArn)
|
|
136
|
+
throw lambdaServiceError('durableExecutionArn is required');
|
|
137
|
+
let result = await client.getDurableExecutionHistory(
|
|
138
|
+
ctx.input.durableExecutionArn,
|
|
139
|
+
undefined,
|
|
140
|
+
ctx.input.maxItems
|
|
141
|
+
);
|
|
142
|
+
return {
|
|
143
|
+
output: { history: result },
|
|
144
|
+
message: `Retrieved history for durable execution.`
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (action === 'get_state') {
|
|
149
|
+
if (!ctx.input.durableExecutionArn)
|
|
150
|
+
throw lambdaServiceError('durableExecutionArn is required');
|
|
151
|
+
if (!ctx.input.checkpointToken)
|
|
152
|
+
throw lambdaServiceError('checkpointToken is required for get_state');
|
|
153
|
+
let result = await client.getDurableExecutionState(
|
|
154
|
+
ctx.input.durableExecutionArn,
|
|
155
|
+
ctx.input.checkpointToken,
|
|
156
|
+
undefined,
|
|
157
|
+
ctx.input.maxItems
|
|
158
|
+
);
|
|
159
|
+
return {
|
|
160
|
+
output: { state: result },
|
|
161
|
+
message: `Retrieved state for durable execution.`
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (action === 'stop') {
|
|
166
|
+
if (!ctx.input.durableExecutionArn)
|
|
167
|
+
throw lambdaServiceError('durableExecutionArn is required');
|
|
168
|
+
let params: Record<string, any> = {};
|
|
169
|
+
if (ctx.input.stopErrorMessage) params['ErrorMessage'] = ctx.input.stopErrorMessage;
|
|
170
|
+
if (ctx.input.stopErrorType) params['ErrorType'] = ctx.input.stopErrorType;
|
|
171
|
+
let result = await client.stopDurableExecution(ctx.input.durableExecutionArn, params);
|
|
172
|
+
return {
|
|
173
|
+
output: {
|
|
174
|
+
stopTimestamp: result.StopTimestamp ? String(result.StopTimestamp) : undefined
|
|
175
|
+
},
|
|
176
|
+
message: `Stopped durable execution.`
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (action === 'callback_success') {
|
|
181
|
+
if (!ctx.input.callbackId) throw lambdaServiceError('callbackId is required');
|
|
182
|
+
await client.sendDurableExecutionCallbackSuccess(
|
|
183
|
+
ctx.input.callbackId,
|
|
184
|
+
ctx.input.callbackResult
|
|
185
|
+
);
|
|
186
|
+
return {
|
|
187
|
+
output: { callbackSent: true },
|
|
188
|
+
message: `Sent success callback for **${ctx.input.callbackId}**.`
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (action === 'callback_failure') {
|
|
193
|
+
if (!ctx.input.callbackId) throw lambdaServiceError('callbackId is required');
|
|
194
|
+
await client.sendDurableExecutionCallbackFailure(ctx.input.callbackId);
|
|
195
|
+
return {
|
|
196
|
+
output: { callbackSent: true },
|
|
197
|
+
message: `Sent failure callback for **${ctx.input.callbackId}**.`
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// callback_heartbeat
|
|
202
|
+
if (!ctx.input.callbackId) throw lambdaServiceError('callbackId is required');
|
|
203
|
+
await client.sendDurableExecutionCallbackHeartbeat(ctx.input.callbackId);
|
|
204
|
+
return {
|
|
205
|
+
output: { callbackSent: true },
|
|
206
|
+
message: `Sent heartbeat for callback **${ctx.input.callbackId}**.`
|
|
207
|
+
};
|
|
208
|
+
})
|
|
209
|
+
.build();
|