feat(codepipeline): add AWS CodePipeline integration with tools and block (#4945)

* feat(codepipeline): add AWS CodePipeline integration with tools and block

* fix(codepipeline): address review feedback on input coercion and error statuses

* chore(hooks): restore use-inline-rename onSave type accidentally swept into previous commit
This commit is contained in:
Theodore Li
2026-06-10 14:51:24 -04:00
committed by GitHub
parent 3cedac8e82
commit 1ff445ae80
40 changed files with 3227 additions and 2 deletions
+27
View File
@@ -5512,6 +5512,33 @@ export function CloudWatchIcon(props: SVGProps<SVGSVGElement>) {
)
}
export function CodePipelineIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
{...props}
viewBox='5 5 70.002 70.002'
version='1.1'
xmlns='http://www.w3.org/2000/svg'
xmlnsXlink='http://www.w3.org/1999/xlink'
>
<g
id='Icon-Architecture/64/Arch_AWS-CodePipeline_64'
stroke='none'
strokeWidth='1'
fill='none'
fillRule='evenodd'
transform='translate(40, 40) scale(1.25) translate(-40, -40)'
>
<path
d='M30,31 L35,31 L35,29 L30,29 L30,31 Z M36.667,61.166 L34.817,60.407 L42.409,41.885 L44.259,42.644 L36.667,61.166 Z M45.571,55.78 L51.068,50.958 L45.575,46.197 L46.886,44.685 L53.245,50.198 C53.463,50.387 53.589,50.662 53.590006,50.951 C53.591,51.24 53.467,51.515 53.249,51.706 L46.89,57.284 L45.571,55.78 Z M25.508976,51.048 C25.507,50.759 25.63,50.484 25.847,50.293 L32.184,44.691 L33.508,46.19 L28.029,51.033 L33.478,55.73 L32.171,57.245 L25.855,51.799 C25.637,51.611 25.511,51.337 25.508976,51.048 L25.508976,51.048 Z M62.535,35 L18.465,35 C16.555,35 15,33.446 15,31.536 L15,31 L27,31 L27,29 L15,29 L15,18.464 C15,16.554 16.555,15 18.465,15 L62.535,15 C64.445,15 66,16.554 66,18.464 L66,29 L38,29 L38,31 L66,31 L66,31.536 C66,33.446 64.445,35 62.535,35 L62.535,35 Z M22,66 L58,66 L58,37 L22,37 L22,66 Z M62.535,13 L18.465,13 C15.451,13 13,15.451 13,18.464 L13,31.536 C13,34.549 15.451,37 18.465,37 L20,37 L20,67 C20,67.552 20.447,68 21,68 L59,68 C59.553,68 60,67.552 60,67 L60,37 L62.535,37 C65.549,37 68,34.549 68,31.536 L68,18.464 C68,15.451 65.549,13 62.535,13 L62.535,13 Z'
id='AWS-CodePipeline_Icon_64_Squid'
fill='currentColor'
/>
</g>
</svg>
)
}
export function TextractIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
+2
View File
@@ -35,6 +35,7 @@ import {
CloudFormationIcon,
CloudflareIcon,
CloudWatchIcon,
CodePipelineIcon,
ConfluenceIcon,
CrowdStrikeIcon,
CursorIcon,
@@ -249,6 +250,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
cloudflare: CloudflareIcon,
cloudformation: CloudFormationIcon,
cloudwatch: CloudWatchIcon,
codepipeline: CodePipelineIcon,
confluence: ConfluenceIcon,
confluence_v2: ConfluenceIcon,
crowdstrike: CrowdStrikeIcon,
@@ -0,0 +1,237 @@
---
title: CodePipeline
description: Run, monitor, and approve AWS CodePipeline pipelines
---
import { BlockInfoCard } from "@/components/ui/block-info-card"
<BlockInfoCard
type="codepipeline"
color="linear-gradient(45deg, #2E27AD 0%, #527FFF 100%)"
/>
## Usage Instructions
Integrate AWS CodePipeline into workflows. Start, stop, and monitor pipeline executions, retry failed stages, and approve or reject manual approval actions. Requires AWS access key and secret access key.
## Tools
### `codepipeline_list_pipelines`
List all CodePipeline pipelines in an AWS account and region
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `awsRegion` | string | Yes | AWS region \(e.g., us-east-1\) |
| `awsAccessKeyId` | string | Yes | AWS access key ID |
| `awsSecretAccessKey` | string | Yes | AWS secret access key |
| `maxResults` | number | No | Maximum number of pipelines to return \(1-1000\) |
| `nextToken` | string | No | Pagination token from a previous call |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `pipelines` | array | List of pipelines with name, version, type, and timestamps |
| ↳ `name` | string | Pipeline name |
| ↳ `version` | number | Pipeline version number |
| ↳ `pipelineType` | string | Pipeline type \(V1 or V2\) |
| ↳ `executionMode` | string | Execution mode \(QUEUED, SUPERSEDED, PARALLEL\) |
| ↳ `created` | number | Epoch ms when the pipeline was created |
| ↳ `updated` | number | Epoch ms when the pipeline was last updated |
| `nextToken` | string | Pagination token for the next page of results |
### `codepipeline_get_pipeline_state`
Get the current state of a CodePipeline pipeline, including stage and action status and pending approval tokens
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `awsRegion` | string | Yes | AWS region \(e.g., us-east-1\) |
| `awsAccessKeyId` | string | Yes | AWS access key ID |
| `awsSecretAccessKey` | string | Yes | AWS secret access key |
| `pipelineName` | string | Yes | Name of the pipeline |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `pipelineName` | string | Pipeline name |
| `pipelineVersion` | number | Pipeline version number |
| `created` | number | Epoch ms when the pipeline was created |
| `updated` | number | Epoch ms when the pipeline was last updated |
| `stageStates` | array | Per-stage state including latest execution status and action details |
| ↳ `stageName` | string | Stage name |
| ↳ `status` | string | Latest stage execution status \(InProgress, Succeeded, Failed, Stopped, Cancelled\) |
| ↳ `pipelineExecutionId` | string | Pipeline execution ID currently in the stage |
| ↳ `inboundTransitionEnabled` | boolean | Whether the inbound transition into the stage is enabled |
| ↳ `actionStates` | array | Per-action state with status, summary, error details, and approval token \(for pending manual approvals\) |
### `codepipeline_get_pipeline_execution`
Get details of a CodePipeline execution, including status, trigger, source revisions, and resolved variables
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `awsRegion` | string | Yes | AWS region \(e.g., us-east-1\) |
| `awsAccessKeyId` | string | Yes | AWS access key ID |
| `awsSecretAccessKey` | string | Yes | AWS secret access key |
| `pipelineName` | string | Yes | Name of the pipeline |
| `pipelineExecutionId` | string | Yes | ID of the pipeline execution |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `pipelineExecutionId` | string | Pipeline execution ID |
| `pipelineName` | string | Pipeline name |
| `pipelineVersion` | number | Pipeline version number |
| `status` | string | Execution status \(Cancelled, InProgress, Stopped, Stopping, Succeeded, Superseded, Failed\) |
| `statusSummary` | string | Status summary for the execution |
| `executionMode` | string | Execution mode \(QUEUED, SUPERSEDED, PARALLEL\) |
| `executionType` | string | Execution type \(STANDARD or ROLLBACK\) |
| `triggerType` | string | What triggered the execution \(e.g., Webhook, StartPipelineExecution\) |
| `triggerDetail` | string | Detail about the trigger \(e.g., user ARN\) |
| `artifactRevisions` | array | Source artifact revisions for the execution |
| ↳ `name` | string | Artifact name |
| ↳ `revisionId` | string | Revision ID \(e.g., commit SHA\) |
| ↳ `revisionSummary` | string | Revision summary \(e.g., commit message\) |
| ↳ `revisionUrl` | string | URL of the revision |
| ↳ `created` | number | Epoch ms when the revision was created |
| `variables` | array | Resolved pipeline variables for the execution |
| ↳ `name` | string | Variable name |
| ↳ `resolvedValue` | string | Resolved variable value |
### `codepipeline_list_pipeline_executions`
List recent executions of a CodePipeline pipeline with status and source revisions
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `awsRegion` | string | Yes | AWS region \(e.g., us-east-1\) |
| `awsAccessKeyId` | string | Yes | AWS access key ID |
| `awsSecretAccessKey` | string | Yes | AWS secret access key |
| `pipelineName` | string | Yes | Name of the pipeline |
| `maxResults` | number | No | Maximum number of executions to return \(1-100, default 100\) |
| `nextToken` | string | No | Pagination token from a previous call |
| `succeededInStage` | string | No | Only return executions that succeeded in this stage |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `executions` | array | Pipeline execution summaries, most recent first |
| ↳ `pipelineExecutionId` | string | Pipeline execution ID |
| ↳ `status` | string | Execution status \(Cancelled, InProgress, Stopped, Stopping, Succeeded, Superseded, Failed\) |
| ↳ `statusSummary` | string | Status summary for the execution |
| ↳ `startTime` | number | Epoch ms when the execution started |
| ↳ `lastUpdateTime` | number | Epoch ms when the execution was last updated |
| ↳ `executionMode` | string | Execution mode \(QUEUED, SUPERSEDED, PARALLEL\) |
| ↳ `executionType` | string | Execution type \(STANDARD or ROLLBACK\) |
| ↳ `stopTriggerReason` | string | Reason the execution was stopped, if applicable |
| ↳ `triggerType` | string | What triggered the execution |
| ↳ `triggerDetail` | string | Detail about the trigger |
| ↳ `sourceRevisions` | array | Source revisions \(commit IDs, summaries, URLs\) for the execution |
| `nextToken` | string | Pagination token for the next page of results |
### `codepipeline_start_execution`
Start a CodePipeline pipeline execution, optionally overriding pipeline variables
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `awsRegion` | string | Yes | AWS region \(e.g., us-east-1\) |
| `awsAccessKeyId` | string | Yes | AWS access key ID |
| `awsSecretAccessKey` | string | Yes | AWS secret access key |
| `pipelineName` | string | Yes | Name of the pipeline to start |
| `clientRequestToken` | string | No | Idempotency token to identify a unique execution request |
| `variables` | json | No | Pipeline variable overrides as an array of \{ name, value \} objects |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `pipelineExecutionId` | string | ID of the pipeline execution that was started |
### `codepipeline_stop_execution`
Stop a CodePipeline pipeline execution, either finishing in-progress actions or abandoning them
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `awsRegion` | string | Yes | AWS region \(e.g., us-east-1\) |
| `awsAccessKeyId` | string | Yes | AWS access key ID |
| `awsSecretAccessKey` | string | Yes | AWS secret access key |
| `pipelineName` | string | Yes | Name of the pipeline |
| `pipelineExecutionId` | string | Yes | ID of the pipeline execution to stop |
| `abandon` | boolean | No | Abandon in-progress actions instead of letting them finish \(default false\) |
| `reason` | string | No | Reason for stopping the execution \(max 200 characters\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `pipelineExecutionId` | string | ID of the pipeline execution that was stopped |
### `codepipeline_retry_stage_execution`
Retry the failed actions (or all actions) of a failed CodePipeline stage
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `awsRegion` | string | Yes | AWS region \(e.g., us-east-1\) |
| `awsAccessKeyId` | string | Yes | AWS access key ID |
| `awsSecretAccessKey` | string | Yes | AWS secret access key |
| `pipelineName` | string | Yes | Name of the pipeline |
| `stageName` | string | Yes | Name of the failed stage to retry |
| `pipelineExecutionId` | string | Yes | ID of the pipeline execution in the failed stage |
| `retryMode` | string | Yes | Scope of the retry: FAILED_ACTIONS or ALL_ACTIONS |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `pipelineExecutionId` | string | ID of the pipeline execution with the retried stage |
### `codepipeline_put_approval_result`
Approve or reject a pending CodePipeline manual approval action. The approval token is available from Get Pipeline State on the pending approval action
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `awsRegion` | string | Yes | AWS region \(e.g., us-east-1\) |
| `awsAccessKeyId` | string | Yes | AWS access key ID |
| `awsSecretAccessKey` | string | Yes | AWS secret access key |
| `pipelineName` | string | Yes | Name of the pipeline |
| `stageName` | string | Yes | Name of the stage containing the approval action |
| `actionName` | string | Yes | Name of the manual approval action |
| `token` | string | Yes | Approval token from Get Pipeline State for the pending approval |
| `status` | string | Yes | Approval decision: Approved or Rejected |
| `summary` | string | Yes | Summary explaining the approval decision \(max 512 characters\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `approvedAt` | number | Epoch ms when the approval or rejection was submitted |
| `status` | string | The submitted approval decision \(Approved or Rejected\) |
@@ -30,6 +30,7 @@
"cloudflare",
"cloudformation",
"cloudwatch",
"codepipeline",
"confluence",
"crowdstrike",
"cursor",
@@ -0,0 +1,87 @@
import { CodePipelineClient, GetPipelineExecutionCommand } from '@aws-sdk/client-codepipeline'
import { createLogger } from '@sim/logger'
import { toError } from '@sim/utils/errors'
import { type NextRequest, NextResponse } from 'next/server'
import { awsCodepipelineGetPipelineExecutionContract } from '@/lib/api/contracts/tools/aws/codepipeline-get-pipeline-execution'
import { parseToolRequest } from '@/lib/api/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
import { awsErrorStatus } from '@/app/api/tools/codepipeline/utils'
const logger = createLogger('CodePipelineGetPipelineExecution')
export const POST = withRouteHandler(async (request: NextRequest) => {
try {
const auth = await checkInternalAuth(request)
if (!auth.success || !auth.userId) {
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
}
const parsed = await parseToolRequest(awsCodepipelineGetPipelineExecutionContract, request, {
errorFormat: 'details',
logger,
})
if (!parsed.success) return parsed.response
const validatedData = parsed.data.body
logger.info('Getting CodePipeline pipeline execution')
const client = new CodePipelineClient({
region: validatedData.region,
credentials: {
accessKeyId: validatedData.accessKeyId,
secretAccessKey: validatedData.secretAccessKey,
},
})
try {
const command = new GetPipelineExecutionCommand({
pipelineName: validatedData.pipelineName,
pipelineExecutionId: validatedData.pipelineExecutionId,
})
const response = await client.send(command)
const execution = response.pipelineExecution
if (!execution) {
throw new Error('Pipeline execution not found in response')
}
logger.info('Successfully got pipeline execution')
return NextResponse.json({
success: true,
output: {
pipelineExecutionId: execution.pipelineExecutionId ?? validatedData.pipelineExecutionId,
pipelineName: execution.pipelineName ?? validatedData.pipelineName,
pipelineVersion: execution.pipelineVersion,
status: execution.status ?? 'Unknown',
statusSummary: execution.statusSummary,
executionMode: execution.executionMode,
executionType: execution.executionType,
triggerType: execution.trigger?.triggerType,
triggerDetail: execution.trigger?.triggerDetail,
artifactRevisions: (execution.artifactRevisions ?? []).map((r) => ({
name: r.name ?? '',
revisionId: r.revisionId,
revisionSummary: r.revisionSummary,
revisionUrl: r.revisionUrl,
created: r.created?.getTime(),
})),
variables: (execution.variables ?? []).map((v) => ({
name: v.name ?? '',
resolvedValue: v.resolvedValue ?? '',
})),
},
})
} finally {
client.destroy()
}
} catch (error) {
logger.error('GetPipelineExecution failed', { error: toError(error).message })
return NextResponse.json(
{ error: `Failed to get CodePipeline pipeline execution: ${toError(error).message}` },
{ status: awsErrorStatus(error) }
)
}
})
@@ -0,0 +1,84 @@
import { CodePipelineClient, GetPipelineStateCommand } from '@aws-sdk/client-codepipeline'
import { createLogger } from '@sim/logger'
import { toError } from '@sim/utils/errors'
import { type NextRequest, NextResponse } from 'next/server'
import { awsCodepipelineGetPipelineStateContract } from '@/lib/api/contracts/tools/aws/codepipeline-get-pipeline-state'
import { parseToolRequest } from '@/lib/api/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
import { awsErrorStatus } from '@/app/api/tools/codepipeline/utils'
const logger = createLogger('CodePipelineGetPipelineState')
export const POST = withRouteHandler(async (request: NextRequest) => {
try {
const auth = await checkInternalAuth(request)
if (!auth.success || !auth.userId) {
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
}
const parsed = await parseToolRequest(awsCodepipelineGetPipelineStateContract, request, {
errorFormat: 'details',
logger,
})
if (!parsed.success) return parsed.response
const validatedData = parsed.data.body
logger.info('Getting CodePipeline pipeline state')
const client = new CodePipelineClient({
region: validatedData.region,
credentials: {
accessKeyId: validatedData.accessKeyId,
secretAccessKey: validatedData.secretAccessKey,
},
})
try {
const command = new GetPipelineStateCommand({ name: validatedData.pipelineName })
const response = await client.send(command)
const stageStates = (response.stageStates ?? []).map((stage) => ({
stageName: stage.stageName ?? '',
status: stage.latestExecution?.status,
pipelineExecutionId: stage.latestExecution?.pipelineExecutionId,
inboundTransitionEnabled: stage.inboundTransitionState?.enabled,
actionStates: (stage.actionStates ?? []).map((action) => ({
actionName: action.actionName ?? '',
status: action.latestExecution?.status,
summary: action.latestExecution?.summary,
lastStatusChange: action.latestExecution?.lastStatusChange?.getTime(),
externalExecutionId: action.latestExecution?.externalExecutionId,
externalExecutionUrl: action.latestExecution?.externalExecutionUrl,
errorCode: action.latestExecution?.errorDetails?.code,
errorMessage: action.latestExecution?.errorDetails?.message,
percentComplete: action.latestExecution?.percentComplete,
token: action.latestExecution?.token,
revisionId: action.currentRevision?.revisionId,
entityUrl: action.entityUrl,
})),
}))
logger.info(`Successfully got pipeline state with ${stageStates.length} stages`)
return NextResponse.json({
success: true,
output: {
pipelineName: response.pipelineName ?? validatedData.pipelineName,
pipelineVersion: response.pipelineVersion,
created: response.created?.getTime(),
updated: response.updated?.getTime(),
stageStates,
},
})
} finally {
client.destroy()
}
} catch (error) {
logger.error('GetPipelineState failed', { error: toError(error).message })
return NextResponse.json(
{ error: `Failed to get CodePipeline pipeline state: ${toError(error).message}` },
{ status: awsErrorStatus(error) }
)
}
})
@@ -0,0 +1,87 @@
import { CodePipelineClient, ListPipelineExecutionsCommand } from '@aws-sdk/client-codepipeline'
import { createLogger } from '@sim/logger'
import { toError } from '@sim/utils/errors'
import { type NextRequest, NextResponse } from 'next/server'
import { awsCodepipelineListPipelineExecutionsContract } from '@/lib/api/contracts/tools/aws/codepipeline-list-pipeline-executions'
import { parseToolRequest } from '@/lib/api/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
import { awsErrorStatus } from '@/app/api/tools/codepipeline/utils'
const logger = createLogger('CodePipelineListPipelineExecutions')
export const POST = withRouteHandler(async (request: NextRequest) => {
try {
const auth = await checkInternalAuth(request)
if (!auth.success || !auth.userId) {
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
}
const parsed = await parseToolRequest(awsCodepipelineListPipelineExecutionsContract, request, {
errorFormat: 'details',
logger,
})
if (!parsed.success) return parsed.response
const validatedData = parsed.data.body
logger.info('Listing CodePipeline pipeline executions')
const client = new CodePipelineClient({
region: validatedData.region,
credentials: {
accessKeyId: validatedData.accessKeyId,
secretAccessKey: validatedData.secretAccessKey,
},
})
try {
const command = new ListPipelineExecutionsCommand({
pipelineName: validatedData.pipelineName,
...(validatedData.maxResults !== undefined && { maxResults: validatedData.maxResults }),
...(validatedData.nextToken && { nextToken: validatedData.nextToken }),
...(validatedData.succeededInStage && {
filter: { succeededInStage: { stageName: validatedData.succeededInStage } },
}),
})
const response = await client.send(command)
const executions = (response.pipelineExecutionSummaries ?? []).map((e) => ({
pipelineExecutionId: e.pipelineExecutionId ?? '',
status: e.status ?? 'Unknown',
statusSummary: e.statusSummary,
startTime: e.startTime?.getTime(),
lastUpdateTime: e.lastUpdateTime?.getTime(),
executionMode: e.executionMode,
executionType: e.executionType,
stopTriggerReason: e.stopTrigger?.reason,
triggerType: e.trigger?.triggerType,
triggerDetail: e.trigger?.triggerDetail,
sourceRevisions: (e.sourceRevisions ?? []).map((r) => ({
actionName: r.actionName ?? '',
revisionId: r.revisionId,
revisionSummary: r.revisionSummary,
revisionUrl: r.revisionUrl,
})),
}))
logger.info(`Successfully listed ${executions.length} pipeline executions`)
return NextResponse.json({
success: true,
output: {
executions,
...(response.nextToken && { nextToken: response.nextToken }),
},
})
} finally {
client.destroy()
}
} catch (error) {
logger.error('ListPipelineExecutions failed', { error: toError(error).message })
return NextResponse.json(
{ error: `Failed to list CodePipeline pipeline executions: ${toError(error).message}` },
{ status: awsErrorStatus(error) }
)
}
})
@@ -0,0 +1,73 @@
import { CodePipelineClient, ListPipelinesCommand } from '@aws-sdk/client-codepipeline'
import { createLogger } from '@sim/logger'
import { toError } from '@sim/utils/errors'
import { type NextRequest, NextResponse } from 'next/server'
import { awsCodepipelineListPipelinesContract } from '@/lib/api/contracts/tools/aws/codepipeline-list-pipelines'
import { parseToolRequest } from '@/lib/api/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
import { awsErrorStatus } from '@/app/api/tools/codepipeline/utils'
const logger = createLogger('CodePipelineListPipelines')
export const POST = withRouteHandler(async (request: NextRequest) => {
try {
const auth = await checkInternalAuth(request)
if (!auth.success || !auth.userId) {
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
}
const parsed = await parseToolRequest(awsCodepipelineListPipelinesContract, request, {
errorFormat: 'details',
logger,
})
if (!parsed.success) return parsed.response
const validatedData = parsed.data.body
logger.info('Listing CodePipeline pipelines')
const client = new CodePipelineClient({
region: validatedData.region,
credentials: {
accessKeyId: validatedData.accessKeyId,
secretAccessKey: validatedData.secretAccessKey,
},
})
try {
const command = new ListPipelinesCommand({
...(validatedData.maxResults !== undefined && { maxResults: validatedData.maxResults }),
...(validatedData.nextToken && { nextToken: validatedData.nextToken }),
})
const response = await client.send(command)
const pipelines = (response.pipelines ?? []).map((p) => ({
name: p.name ?? '',
version: p.version,
pipelineType: p.pipelineType,
executionMode: p.executionMode,
created: p.created?.getTime(),
updated: p.updated?.getTime(),
}))
logger.info(`Successfully listed ${pipelines.length} pipelines`)
return NextResponse.json({
success: true,
output: {
pipelines,
...(response.nextToken && { nextToken: response.nextToken }),
},
})
} finally {
client.destroy()
}
} catch (error) {
logger.error('ListPipelines failed', { error: toError(error).message })
return NextResponse.json(
{ error: `Failed to list CodePipeline pipelines: ${toError(error).message}` },
{ status: awsErrorStatus(error) }
)
}
})
@@ -0,0 +1,74 @@
import {
type ApprovalStatus,
CodePipelineClient,
PutApprovalResultCommand,
} from '@aws-sdk/client-codepipeline'
import { createLogger } from '@sim/logger'
import { toError } from '@sim/utils/errors'
import { type NextRequest, NextResponse } from 'next/server'
import { awsCodepipelinePutApprovalResultContract } from '@/lib/api/contracts/tools/aws/codepipeline-put-approval-result'
import { parseToolRequest } from '@/lib/api/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
import { awsErrorStatus } from '@/app/api/tools/codepipeline/utils'
const logger = createLogger('CodePipelinePutApprovalResult')
export const POST = withRouteHandler(async (request: NextRequest) => {
try {
const auth = await checkInternalAuth(request)
if (!auth.success || !auth.userId) {
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
}
const parsed = await parseToolRequest(awsCodepipelinePutApprovalResultContract, request, {
errorFormat: 'details',
logger,
})
if (!parsed.success) return parsed.response
const validatedData = parsed.data.body
logger.info('Submitting CodePipeline approval result')
const client = new CodePipelineClient({
region: validatedData.region,
credentials: {
accessKeyId: validatedData.accessKeyId,
secretAccessKey: validatedData.secretAccessKey,
},
})
try {
const command = new PutApprovalResultCommand({
pipelineName: validatedData.pipelineName,
stageName: validatedData.stageName,
actionName: validatedData.actionName,
token: validatedData.token,
result: {
status: validatedData.status as ApprovalStatus,
summary: validatedData.summary,
},
})
const response = await client.send(command)
logger.info('Successfully submitted approval result')
return NextResponse.json({
success: true,
output: {
approvedAt: response.approvedAt?.getTime(),
status: validatedData.status,
},
})
} finally {
client.destroy()
}
} catch (error) {
logger.error('PutApprovalResult failed', { error: toError(error).message })
return NextResponse.json(
{ error: `Failed to submit CodePipeline approval result: ${toError(error).message}` },
{ status: awsErrorStatus(error) }
)
}
})
@@ -0,0 +1,69 @@
import {
CodePipelineClient,
RetryStageExecutionCommand,
type StageRetryMode,
} from '@aws-sdk/client-codepipeline'
import { createLogger } from '@sim/logger'
import { toError } from '@sim/utils/errors'
import { type NextRequest, NextResponse } from 'next/server'
import { awsCodepipelineRetryStageExecutionContract } from '@/lib/api/contracts/tools/aws/codepipeline-retry-stage-execution'
import { parseToolRequest } from '@/lib/api/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
import { awsErrorStatus } from '@/app/api/tools/codepipeline/utils'
const logger = createLogger('CodePipelineRetryStageExecution')
export const POST = withRouteHandler(async (request: NextRequest) => {
try {
const auth = await checkInternalAuth(request)
if (!auth.success || !auth.userId) {
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
}
const parsed = await parseToolRequest(awsCodepipelineRetryStageExecutionContract, request, {
errorFormat: 'details',
logger,
})
if (!parsed.success) return parsed.response
const validatedData = parsed.data.body
logger.info('Retrying CodePipeline stage execution')
const client = new CodePipelineClient({
region: validatedData.region,
credentials: {
accessKeyId: validatedData.accessKeyId,
secretAccessKey: validatedData.secretAccessKey,
},
})
try {
const command = new RetryStageExecutionCommand({
pipelineName: validatedData.pipelineName,
stageName: validatedData.stageName,
pipelineExecutionId: validatedData.pipelineExecutionId,
retryMode: validatedData.retryMode as StageRetryMode,
})
const response = await client.send(command)
logger.info('Successfully retried stage execution')
return NextResponse.json({
success: true,
output: {
pipelineExecutionId: response.pipelineExecutionId ?? validatedData.pipelineExecutionId,
},
})
} finally {
client.destroy()
}
} catch (error) {
logger.error('RetryStageExecution failed', { error: toError(error).message })
return NextResponse.json(
{ error: `Failed to retry CodePipeline stage execution: ${toError(error).message}` },
{ status: awsErrorStatus(error) }
)
}
})
@@ -0,0 +1,69 @@
import { CodePipelineClient, StartPipelineExecutionCommand } from '@aws-sdk/client-codepipeline'
import { createLogger } from '@sim/logger'
import { toError } from '@sim/utils/errors'
import { type NextRequest, NextResponse } from 'next/server'
import { awsCodepipelineStartExecutionContract } from '@/lib/api/contracts/tools/aws/codepipeline-start-execution'
import { parseToolRequest } from '@/lib/api/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
import { awsErrorStatus } from '@/app/api/tools/codepipeline/utils'
const logger = createLogger('CodePipelineStartExecution')
export const POST = withRouteHandler(async (request: NextRequest) => {
try {
const auth = await checkInternalAuth(request)
if (!auth.success || !auth.userId) {
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
}
const parsed = await parseToolRequest(awsCodepipelineStartExecutionContract, request, {
errorFormat: 'details',
logger,
})
if (!parsed.success) return parsed.response
const validatedData = parsed.data.body
logger.info('Starting CodePipeline pipeline execution')
const client = new CodePipelineClient({
region: validatedData.region,
credentials: {
accessKeyId: validatedData.accessKeyId,
secretAccessKey: validatedData.secretAccessKey,
},
})
try {
const command = new StartPipelineExecutionCommand({
name: validatedData.pipelineName,
...(validatedData.clientRequestToken && {
clientRequestToken: validatedData.clientRequestToken,
}),
...(validatedData.variables &&
validatedData.variables.length > 0 && { variables: validatedData.variables }),
})
const response = await client.send(command)
if (!response.pipelineExecutionId) {
throw new Error('No pipeline execution ID returned')
}
logger.info('Successfully started pipeline execution')
return NextResponse.json({
success: true,
output: { pipelineExecutionId: response.pipelineExecutionId },
})
} finally {
client.destroy()
}
} catch (error) {
logger.error('StartPipelineExecution failed', { error: toError(error).message })
return NextResponse.json(
{ error: `Failed to start CodePipeline pipeline execution: ${toError(error).message}` },
{ status: awsErrorStatus(error) }
)
}
})
@@ -0,0 +1,65 @@
import { CodePipelineClient, StopPipelineExecutionCommand } from '@aws-sdk/client-codepipeline'
import { createLogger } from '@sim/logger'
import { toError } from '@sim/utils/errors'
import { type NextRequest, NextResponse } from 'next/server'
import { awsCodepipelineStopExecutionContract } from '@/lib/api/contracts/tools/aws/codepipeline-stop-execution'
import { parseToolRequest } from '@/lib/api/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
import { awsErrorStatus } from '@/app/api/tools/codepipeline/utils'
const logger = createLogger('CodePipelineStopExecution')
export const POST = withRouteHandler(async (request: NextRequest) => {
try {
const auth = await checkInternalAuth(request)
if (!auth.success || !auth.userId) {
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
}
const parsed = await parseToolRequest(awsCodepipelineStopExecutionContract, request, {
errorFormat: 'details',
logger,
})
if (!parsed.success) return parsed.response
const validatedData = parsed.data.body
logger.info('Stopping CodePipeline pipeline execution')
const client = new CodePipelineClient({
region: validatedData.region,
credentials: {
accessKeyId: validatedData.accessKeyId,
secretAccessKey: validatedData.secretAccessKey,
},
})
try {
const command = new StopPipelineExecutionCommand({
pipelineName: validatedData.pipelineName,
pipelineExecutionId: validatedData.pipelineExecutionId,
...(validatedData.abandon !== undefined && { abandon: validatedData.abandon }),
...(validatedData.reason && { reason: validatedData.reason }),
})
const response = await client.send(command)
logger.info('Successfully stopped pipeline execution')
return NextResponse.json({
success: true,
output: {
pipelineExecutionId: response.pipelineExecutionId ?? validatedData.pipelineExecutionId,
},
})
} finally {
client.destroy()
}
} catch (error) {
logger.error('StopPipelineExecution failed', { error: toError(error).message })
return NextResponse.json(
{ error: `Failed to stop CodePipeline pipeline execution: ${toError(error).message}` },
{ status: awsErrorStatus(error) }
)
}
})
@@ -0,0 +1,10 @@
/**
* Maps an AWS SDK error to a response status. Client faults (e.g.
* PipelineNotFoundException, InvalidApprovalTokenException) keep the 4xx
* status AWS reports via `$metadata`; everything else maps to 500.
*/
export function awsErrorStatus(error: unknown): number {
const status = (error as { $metadata?: { httpStatusCode?: number } } | null)?.$metadata
?.httpStatusCode
return typeof status === 'number' && status >= 400 && status < 500 ? status : 500
}
+608
View File
@@ -0,0 +1,608 @@
import { CodePipelineIcon } from '@/components/icons'
import type { BlockConfig, BlockMeta } from '@/blocks/types'
import { AuthMode, IntegrationType } from '@/blocks/types'
import {
parseOptionalBooleanInput,
parseOptionalJsonInput,
parseOptionalNumberInput,
} from '@/blocks/utils'
import type {
CodePipelineGetPipelineExecutionResponse,
CodePipelineGetPipelineStateResponse,
CodePipelineListPipelineExecutionsResponse,
CodePipelineListPipelinesResponse,
CodePipelinePutApprovalResultResponse,
CodePipelineRetryStageExecutionResponse,
CodePipelineStartExecutionResponse,
CodePipelineStopExecutionResponse,
} from '@/tools/codepipeline/types'
export const CodePipelineBlock: BlockConfig<
| CodePipelineListPipelinesResponse
| CodePipelineGetPipelineStateResponse
| CodePipelineGetPipelineExecutionResponse
| CodePipelineListPipelineExecutionsResponse
| CodePipelineStartExecutionResponse
| CodePipelineStopExecutionResponse
| CodePipelineRetryStageExecutionResponse
| CodePipelinePutApprovalResultResponse
> = {
type: 'codepipeline',
name: 'CodePipeline',
description: 'Run, monitor, and approve AWS CodePipeline pipelines',
authMode: AuthMode.ApiKey,
longDescription:
'Integrate AWS CodePipeline into workflows. Start, stop, and monitor pipeline executions, retry failed stages, and approve or reject manual approval actions. Requires AWS access key and secret access key.',
docsLink: 'https://docs.sim.ai/tools/codepipeline',
category: 'tools',
integrationType: IntegrationType.DevOps,
bgColor: 'linear-gradient(45deg, #2E27AD 0%, #527FFF 100%)',
iconColor: '#527FFF',
icon: CodePipelineIcon,
subBlocks: [
{
id: 'operation',
title: 'Operation',
type: 'dropdown',
options: [
{ label: 'Start Execution', id: 'start_execution' },
{ label: 'Get Pipeline State', id: 'get_pipeline_state' },
{ label: 'List Pipelines', id: 'list_pipelines' },
{ label: 'List Executions', id: 'list_pipeline_executions' },
{ label: 'Get Execution', id: 'get_pipeline_execution' },
{ label: 'Stop Execution', id: 'stop_execution' },
{ label: 'Retry Stage', id: 'retry_stage_execution' },
{ label: 'Approve / Reject Approval', id: 'put_approval_result' },
],
value: () => 'start_execution',
},
{
id: 'awsRegion',
title: 'AWS Region',
type: 'short-input',
placeholder: 'us-east-1',
required: true,
},
{
id: 'awsAccessKeyId',
title: 'AWS Access Key ID',
type: 'short-input',
placeholder: 'AKIA...',
password: true,
required: true,
},
{
id: 'awsSecretAccessKey',
title: 'AWS Secret Access Key',
type: 'short-input',
placeholder: 'Your secret access key',
password: true,
required: true,
},
{
id: 'pipelineName',
title: 'Pipeline Name',
type: 'short-input',
placeholder: 'my-pipeline',
condition: {
field: 'operation',
value: [
'start_execution',
'get_pipeline_state',
'list_pipeline_executions',
'get_pipeline_execution',
'stop_execution',
'retry_stage_execution',
'put_approval_result',
],
},
required: {
field: 'operation',
value: [
'start_execution',
'get_pipeline_state',
'list_pipeline_executions',
'get_pipeline_execution',
'stop_execution',
'retry_stage_execution',
'put_approval_result',
],
},
},
{
id: 'pipelineExecutionId',
title: 'Execution ID',
type: 'short-input',
placeholder: 'e.g., 3137f7cb-7cf7-4abc-9f1d-d7eu3471a2b1',
condition: {
field: 'operation',
value: ['get_pipeline_execution', 'stop_execution', 'retry_stage_execution'],
},
required: {
field: 'operation',
value: ['get_pipeline_execution', 'stop_execution', 'retry_stage_execution'],
},
},
{
id: 'stageName',
title: 'Stage Name',
type: 'short-input',
placeholder: 'e.g., Deploy',
condition: { field: 'operation', value: ['retry_stage_execution', 'put_approval_result'] },
required: { field: 'operation', value: ['retry_stage_execution', 'put_approval_result'] },
},
{
id: 'retryMode',
title: 'Retry Mode',
type: 'dropdown',
options: [
{ label: 'Failed Actions Only', id: 'FAILED_ACTIONS' },
{ label: 'All Actions', id: 'ALL_ACTIONS' },
],
value: () => 'FAILED_ACTIONS',
condition: { field: 'operation', value: 'retry_stage_execution' },
required: { field: 'operation', value: 'retry_stage_execution' },
},
{
id: 'actionName',
title: 'Approval Action Name',
type: 'short-input',
placeholder: 'e.g., ManualApproval',
condition: { field: 'operation', value: 'put_approval_result' },
required: { field: 'operation', value: 'put_approval_result' },
},
{
id: 'approvalToken',
title: 'Approval Token',
type: 'short-input',
placeholder: 'Token from Get Pipeline State',
condition: { field: 'operation', value: 'put_approval_result' },
required: { field: 'operation', value: 'put_approval_result' },
},
{
id: 'approvalStatus',
title: 'Decision',
type: 'dropdown',
options: [
{ label: 'Approve', id: 'Approved' },
{ label: 'Reject', id: 'Rejected' },
],
value: () => 'Approved',
condition: { field: 'operation', value: 'put_approval_result' },
required: { field: 'operation', value: 'put_approval_result' },
},
{
id: 'approvalSummary',
title: 'Summary',
type: 'short-input',
placeholder: 'Why the change is approved or rejected',
condition: { field: 'operation', value: 'put_approval_result' },
required: { field: 'operation', value: 'put_approval_result' },
},
{
id: 'pipelineVariables',
title: 'Pipeline Variables',
type: 'table',
columns: ['name', 'value'],
condition: { field: 'operation', value: 'start_execution' },
},
{
id: 'clientRequestToken',
title: 'Client Request Token',
type: 'short-input',
placeholder: 'Idempotency token (letters, digits, hyphens)',
condition: { field: 'operation', value: 'start_execution' },
mode: 'advanced',
},
{
id: 'abandon',
title: 'Abandon In-Progress Actions',
type: 'switch',
condition: { field: 'operation', value: 'stop_execution' },
mode: 'advanced',
},
{
id: 'stopReason',
title: 'Stop Reason',
type: 'short-input',
placeholder: 'Why the execution is being stopped',
condition: { field: 'operation', value: 'stop_execution' },
mode: 'advanced',
},
{
id: 'succeededInStage',
title: 'Succeeded In Stage',
type: 'short-input',
placeholder: 'Only executions that succeeded in this stage',
condition: { field: 'operation', value: 'list_pipeline_executions' },
mode: 'advanced',
},
{
id: 'maxResults',
title: 'Max Results',
type: 'short-input',
placeholder: '100',
condition: { field: 'operation', value: ['list_pipelines', 'list_pipeline_executions'] },
mode: 'advanced',
},
{
id: 'nextToken',
title: 'Next Token',
type: 'short-input',
placeholder: 'Pagination token from a previous call',
condition: { field: 'operation', value: ['list_pipelines', 'list_pipeline_executions'] },
mode: 'advanced',
},
],
tools: {
access: [
'codepipeline_list_pipelines',
'codepipeline_get_pipeline_state',
'codepipeline_get_pipeline_execution',
'codepipeline_list_pipeline_executions',
'codepipeline_start_execution',
'codepipeline_stop_execution',
'codepipeline_retry_stage_execution',
'codepipeline_put_approval_result',
],
config: {
tool: (params) => {
switch (params.operation) {
case 'list_pipelines':
return 'codepipeline_list_pipelines'
case 'get_pipeline_state':
return 'codepipeline_get_pipeline_state'
case 'get_pipeline_execution':
return 'codepipeline_get_pipeline_execution'
case 'list_pipeline_executions':
return 'codepipeline_list_pipeline_executions'
case 'start_execution':
return 'codepipeline_start_execution'
case 'stop_execution':
return 'codepipeline_stop_execution'
case 'retry_stage_execution':
return 'codepipeline_retry_stage_execution'
case 'put_approval_result':
return 'codepipeline_put_approval_result'
default:
throw new Error(`Invalid CodePipeline operation: ${params.operation}`)
}
},
params: (params) => {
const { operation, maxResults, ...rest } = params
const awsRegion = rest.awsRegion
const awsAccessKeyId = rest.awsAccessKeyId
const awsSecretAccessKey = rest.awsSecretAccessKey
const parsedMaxResults = parseOptionalNumberInput(maxResults, 'Max results', {
integer: true,
min: 1,
})
switch (operation) {
case 'list_pipelines':
return {
awsRegion,
awsAccessKeyId,
awsSecretAccessKey,
...(parsedMaxResults !== undefined && { maxResults: parsedMaxResults }),
...(rest.nextToken && { nextToken: rest.nextToken }),
}
case 'get_pipeline_state':
return {
awsRegion,
awsAccessKeyId,
awsSecretAccessKey,
pipelineName: rest.pipelineName,
}
case 'get_pipeline_execution':
return {
awsRegion,
awsAccessKeyId,
awsSecretAccessKey,
pipelineName: rest.pipelineName,
pipelineExecutionId: rest.pipelineExecutionId,
}
case 'list_pipeline_executions':
return {
awsRegion,
awsAccessKeyId,
awsSecretAccessKey,
pipelineName: rest.pipelineName,
...(parsedMaxResults !== undefined && { maxResults: parsedMaxResults }),
...(rest.nextToken && { nextToken: rest.nextToken }),
...(rest.succeededInStage && { succeededInStage: rest.succeededInStage }),
}
case 'start_execution': {
const rows = parseOptionalJsonInput(rest.pipelineVariables, 'Pipeline variables')
const variables = (() => {
if (rows === undefined) return undefined
if (!Array.isArray(rows)) {
throw new Error('Pipeline variables must be an array of { name, value } objects')
}
const entries = rows
.map((row) => ({
name: row?.cells?.name ?? row?.name,
value: row?.cells?.value ?? row?.value,
}))
.filter((entry) => entry.name && entry.value !== undefined && entry.value !== '')
.map((entry) => ({ name: String(entry.name), value: String(entry.value) }))
return entries.length > 0 ? entries : undefined
})()
return {
awsRegion,
awsAccessKeyId,
awsSecretAccessKey,
pipelineName: rest.pipelineName,
...(rest.clientRequestToken && { clientRequestToken: rest.clientRequestToken }),
...(variables && { variables }),
}
}
case 'stop_execution': {
const abandon = parseOptionalBooleanInput(rest.abandon)
return {
awsRegion,
awsAccessKeyId,
awsSecretAccessKey,
pipelineName: rest.pipelineName,
pipelineExecutionId: rest.pipelineExecutionId,
...(abandon !== undefined && { abandon }),
...(rest.stopReason && { reason: rest.stopReason }),
}
}
case 'retry_stage_execution':
return {
awsRegion,
awsAccessKeyId,
awsSecretAccessKey,
pipelineName: rest.pipelineName,
stageName: rest.stageName,
pipelineExecutionId: rest.pipelineExecutionId,
retryMode: rest.retryMode,
}
case 'put_approval_result':
return {
awsRegion,
awsAccessKeyId,
awsSecretAccessKey,
pipelineName: rest.pipelineName,
stageName: rest.stageName,
actionName: rest.actionName,
token: rest.approvalToken,
status: rest.approvalStatus,
summary: rest.approvalSummary,
}
default:
throw new Error(`Invalid CodePipeline operation: ${operation}`)
}
},
},
},
inputs: {
operation: { type: 'string', description: 'CodePipeline operation to perform' },
awsRegion: { type: 'string', description: 'AWS region' },
awsAccessKeyId: { type: 'string', description: 'AWS access key ID' },
awsSecretAccessKey: { type: 'string', description: 'AWS secret access key' },
pipelineName: { type: 'string', description: 'Pipeline name' },
pipelineExecutionId: { type: 'string', description: 'Pipeline execution ID' },
stageName: { type: 'string', description: 'Stage name for retry or approval' },
retryMode: {
type: 'string',
description: 'Retry scope: FAILED_ACTIONS or ALL_ACTIONS',
},
actionName: { type: 'string', description: 'Manual approval action name' },
approvalToken: {
type: 'string',
description: 'Approval token from Get Pipeline State for the pending approval',
},
approvalStatus: { type: 'string', description: 'Approval decision: Approved or Rejected' },
approvalSummary: { type: 'string', description: 'Summary explaining the approval decision' },
pipelineVariables: {
type: 'json',
description: 'Pipeline variable overrides (name/value pairs)',
},
clientRequestToken: {
type: 'string',
description: 'Idempotency token for starting an execution',
},
abandon: {
type: 'boolean',
description: 'Abandon in-progress actions instead of letting them finish',
},
stopReason: { type: 'string', description: 'Reason for stopping the execution' },
succeededInStage: {
type: 'string',
description: 'Only list executions that succeeded in this stage',
},
maxResults: { type: 'number', description: 'Maximum number of results' },
nextToken: { type: 'string', description: 'Pagination token from a previous call' },
},
outputs: {
pipelines: {
type: 'array',
description: 'List of pipelines with name, version, type, and timestamps',
},
nextToken: {
type: 'string',
description: 'Pagination token for the next page of results',
},
pipelineName: {
type: 'string',
description: 'Pipeline name',
},
pipelineVersion: {
type: 'number',
description: 'Pipeline version number',
},
created: {
type: 'number',
description: 'Epoch ms when the pipeline was created',
},
updated: {
type: 'number',
description: 'Epoch ms when the pipeline was last updated',
},
stageStates: {
type: 'array',
description: 'Per-stage state including action status and pending approval tokens',
},
pipelineExecutionId: {
type: 'string',
description: 'Pipeline execution ID',
},
status: {
type: 'string',
description: 'Execution status or submitted approval decision',
},
statusSummary: {
type: 'string',
description: 'Status summary for the execution',
},
executionMode: {
type: 'string',
description: 'Execution mode (QUEUED, SUPERSEDED, PARALLEL)',
},
executionType: {
type: 'string',
description: 'Execution type (STANDARD or ROLLBACK)',
},
triggerType: {
type: 'string',
description: 'What triggered the execution',
},
triggerDetail: {
type: 'string',
description: 'Detail about the trigger',
},
artifactRevisions: {
type: 'array',
description: 'Source artifact revisions for the execution',
},
variables: {
type: 'array',
description: 'Resolved pipeline variables for the execution',
},
executions: {
type: 'array',
description: 'Pipeline execution summaries, most recent first',
},
approvedAt: {
type: 'number',
description: 'Epoch ms when the approval or rejection was submitted',
},
},
}
export const CodePipelineBlockMeta = {
tags: ['cloud', 'ci-cd'],
templates: [
{
icon: CodePipelineIcon,
title: 'CodePipeline deploy approver',
prompt:
'Build a workflow that checks a CodePipeline pipeline for pending manual approvals, posts the change summary and source revisions to Slack, and approves or rejects the deployment based on the team lead reply.',
modules: ['agent', 'workflows'],
category: 'engineering',
tags: ['devops', 'ci-cd', 'automation'],
alsoIntegrations: ['slack'],
},
{
icon: CodePipelineIcon,
title: 'CodePipeline failure triage',
prompt:
'Create a scheduled workflow that polls CodePipeline executions every few minutes, and when one fails, pulls the pipeline state to find the failing stage and action error, opens a Linear issue, and alerts the on-call channel in Slack.',
modules: ['scheduled', 'agent', 'workflows'],
category: 'engineering',
tags: ['devops', 'monitoring', 'automation'],
alsoIntegrations: ['linear', 'slack'],
},
{
icon: CodePipelineIcon,
title: 'CodePipeline release train',
prompt:
'Build a scheduled workflow that starts the release CodePipeline pipeline every weekday at 9am with the release version as a pipeline variable, then posts the execution ID and a link to Slack.',
modules: ['scheduled', 'workflows'],
category: 'engineering',
tags: ['devops', 'ci-cd', 'automation'],
alsoIntegrations: ['slack'],
},
{
icon: CodePipelineIcon,
title: 'CodePipeline deploy digest',
prompt:
'Create a scheduled daily workflow that lists executions across the team CodePipeline pipelines, summarizes successes, failures, and rollbacks with their source revisions, and posts a digest to Slack.',
modules: ['scheduled', 'agent', 'workflows'],
category: 'engineering',
tags: ['devops', 'reporting'],
alsoIntegrations: ['slack'],
},
{
icon: CodePipelineIcon,
title: 'CodePipeline flaky-stage retrier',
prompt:
'Build a scheduled workflow that finds failed CodePipeline executions, retries the failed stage once with failed-actions mode, and escalates to PagerDuty if the retry fails again.',
modules: ['scheduled', 'agent', 'workflows'],
category: 'engineering',
tags: ['devops', 'automation', 'monitoring'],
alsoIntegrations: ['pagerduty'],
},
{
icon: CodePipelineIcon,
title: 'CodePipeline rollback brake',
prompt:
'Create a workflow that watches CloudWatch alarms after a deployment, and when an error-rate alarm fires while a CodePipeline execution is in progress, stops the execution with a reason and notifies the release channel.',
modules: ['agent', 'workflows'],
category: 'engineering',
tags: ['devops', 'monitoring', 'automation'],
alsoIntegrations: ['cloudwatch', 'slack'],
},
{
icon: CodePipelineIcon,
title: 'CodePipeline deployment audit log',
prompt:
'Build a scheduled workflow that records every CodePipeline execution — pipeline, status, trigger, source revisions, and timing — into a table for compliance and deployment-frequency reporting.',
modules: ['scheduled', 'tables', 'workflows'],
category: 'operations',
tags: ['devops', 'enterprise', 'reporting'],
},
],
skills: [
{
name: 'approve-pending-deployment',
description:
'Find a pending CodePipeline manual approval, summarize the change, and approve or reject it.',
content:
'# Approve Pending CodePipeline Deployment\n\nHandle a manual approval gate in a pipeline.\n\n## Steps\n1. Get the pipeline state and locate the action awaiting approval (status InProgress on a manual approval action) and its approval token.\n2. Pull the execution details for the stage to summarize what is being deployed (source revisions, trigger).\n3. Submit the approval result with the token, the Approved or Rejected decision, and a summary explaining the decision.\n\n## Output\nThe decision that was submitted, the approval summary, and the pipeline/stage/action it applied to.',
},
{
name: 'investigate-failed-pipeline',
description:
'Find the failing stage and action of a CodePipeline execution and report the error details.',
content:
'# Investigate Failed CodePipeline Execution\n\nDiagnose why a pipeline run failed.\n\n## Steps\n1. List recent executions for the pipeline and identify the failed one (or use the provided execution ID).\n2. Get the pipeline state and find the stage and action with a Failed status.\n3. Capture the action error code, error message, and external execution URL, plus the source revisions that were being deployed.\n\n## Output\nThe failing stage and action, the error details, the commit/revision involved, and a link to the external execution.',
},
{
name: 'trigger-pipeline-release',
description:
'Start a CodePipeline execution, optionally with variable overrides, and report the execution ID.',
content:
'# Trigger CodePipeline Release\n\nKick off a pipeline run.\n\n## Steps\n1. Confirm the pipeline name (list pipelines if unsure).\n2. Start the execution, passing any pipeline variable overrides (e.g. version or environment) and an idempotency token if retries are possible.\n3. Optionally poll the pipeline state to confirm the execution entered the first stage.\n\n## Output\nThe pipeline execution ID that was started and the variables it ran with.',
},
{
name: 'retry-failed-stage',
description:
'Retry the failed actions of a CodePipeline stage and confirm the stage re-entered execution.',
content:
'# Retry Failed CodePipeline Stage\n\nRe-run a failed stage without restarting the whole pipeline.\n\n## Steps\n1. Get the pipeline state and identify the failed stage and the execution ID stuck in it.\n2. Retry the stage with FAILED_ACTIONS mode (or ALL_ACTIONS if the whole stage should re-run).\n3. Check the pipeline state again to confirm the stage is InProgress.\n\n## Output\nThe stage that was retried, the retry mode used, and the current stage status.',
},
],
} as const satisfies BlockMeta
+3
View File
@@ -34,6 +34,7 @@ import { ClickHouseBlock, ClickHouseBlockMeta } from '@/blocks/blocks/clickhouse
import { CloudflareBlock, CloudflareBlockMeta } from '@/blocks/blocks/cloudflare'
import { CloudFormationBlock, CloudFormationBlockMeta } from '@/blocks/blocks/cloudformation'
import { CloudWatchBlock, CloudWatchBlockMeta } from '@/blocks/blocks/cloudwatch'
import { CodePipelineBlock, CodePipelineBlockMeta } from '@/blocks/blocks/codepipeline'
import { ConditionBlock } from '@/blocks/blocks/condition'
import { ConfluenceBlock, ConfluenceBlockMeta, ConfluenceV2Block } from '@/blocks/blocks/confluence'
import { CredentialBlock } from '@/blocks/blocks/credential'
@@ -352,6 +353,7 @@ const BLOCK_REGISTRY: Record<string, BlockConfig> = {
cloudflare: CloudflareBlock,
cloudformation: CloudFormationBlock,
cloudwatch: CloudWatchBlock,
codepipeline: CodePipelineBlock,
condition: ConditionBlock,
confluence: ConfluenceBlock,
confluence_v2: ConfluenceV2Block,
@@ -642,6 +644,7 @@ const BLOCK_META_REGISTRY: Record<string, BlockMeta> = {
cloudflare: CloudflareBlockMeta,
cloudformation: CloudFormationBlockMeta,
cloudwatch: CloudWatchBlockMeta,
codepipeline: CodePipelineBlockMeta,
confluence: ConfluenceBlockMeta,
crowdstrike: CrowdStrikeBlockMeta,
cursor: CursorBlockMeta,
+27
View File
@@ -5512,6 +5512,33 @@ export function CloudWatchIcon(props: SVGProps<SVGSVGElement>) {
)
}
export function CodePipelineIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
{...props}
viewBox='5 5 70.002 70.002'
version='1.1'
xmlns='http://www.w3.org/2000/svg'
xmlnsXlink='http://www.w3.org/1999/xlink'
>
<g
id='Icon-Architecture/64/Arch_AWS-CodePipeline_64'
stroke='none'
strokeWidth='1'
fill='none'
fillRule='evenodd'
transform='translate(40, 40) scale(1.25) translate(-40, -40)'
>
<path
d='M30,31 L35,31 L35,29 L30,29 L30,31 Z M36.667,61.166 L34.817,60.407 L42.409,41.885 L44.259,42.644 L36.667,61.166 Z M45.571,55.78 L51.068,50.958 L45.575,46.197 L46.886,44.685 L53.245,50.198 C53.463,50.387 53.589,50.662 53.590006,50.951 C53.591,51.24 53.467,51.515 53.249,51.706 L46.89,57.284 L45.571,55.78 Z M25.508976,51.048 C25.507,50.759 25.63,50.484 25.847,50.293 L32.184,44.691 L33.508,46.19 L28.029,51.033 L33.478,55.73 L32.171,57.245 L25.855,51.799 C25.637,51.611 25.511,51.337 25.508976,51.048 L25.508976,51.048 Z M62.535,35 L18.465,35 C16.555,35 15,33.446 15,31.536 L15,31 L27,31 L27,29 L15,29 L15,18.464 C15,16.554 16.555,15 18.465,15 L62.535,15 C64.445,15 66,16.554 66,18.464 L66,29 L38,29 L38,31 L66,31 L66,31.536 C66,33.446 64.445,35 62.535,35 L62.535,35 Z M22,66 L58,66 L58,37 L22,37 L22,66 Z M62.535,13 L18.465,13 C15.451,13 13,15.451 13,18.464 L13,31.536 C13,34.549 15.451,37 18.465,37 L20,37 L20,67 C20,67.552 20.447,68 21,68 L59,68 C59.553,68 60,67.552 60,67 L60,37 L62.535,37 C65.549,37 68,34.549 68,31.536 L68,18.464 C68,15.451 65.549,13 62.535,13 L62.535,13 Z'
id='AWS-CodePipeline_Icon_64_Squid'
fill='currentColor'
/>
</g>
</svg>
)
}
export function TextractIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
@@ -0,0 +1,70 @@
import { z } from 'zod'
import type {
ContractBody,
ContractBodyInput,
ContractJsonResponse,
} from '@/lib/api/contracts/types'
import { defineRouteContract } from '@/lib/api/contracts/types'
import { validateAwsRegion } from '@/lib/core/security/input-validation'
const GetPipelineExecutionSchema = z.object({
region: z
.string()
.min(1, 'AWS region is required')
.refine((v) => validateAwsRegion(v).isValid, {
message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)',
}),
accessKeyId: z.string().min(1, 'AWS access key ID is required'),
secretAccessKey: z.string().min(1, 'AWS secret access key is required'),
pipelineName: z
.string()
.min(1, 'Pipeline name is required')
.max(100, 'Pipeline name must be at most 100 characters'),
pipelineExecutionId: z.string().min(1, 'Pipeline execution ID is required'),
})
const GetPipelineExecutionResponseSchema = z.object({
success: z.literal(true),
output: z.object({
pipelineExecutionId: z.string(),
pipelineName: z.string(),
pipelineVersion: z.number().optional(),
status: z.string(),
statusSummary: z.string().optional(),
executionMode: z.string().optional(),
executionType: z.string().optional(),
triggerType: z.string().optional(),
triggerDetail: z.string().optional(),
artifactRevisions: z.array(
z.object({
name: z.string(),
revisionId: z.string().optional(),
revisionSummary: z.string().optional(),
revisionUrl: z.string().optional(),
created: z.number().optional(),
})
),
variables: z.array(
z.object({
name: z.string(),
resolvedValue: z.string(),
})
),
}),
})
export const awsCodepipelineGetPipelineExecutionContract = defineRouteContract({
method: 'POST',
path: '/api/tools/codepipeline/get-pipeline-execution',
body: GetPipelineExecutionSchema,
response: { mode: 'json', schema: GetPipelineExecutionResponseSchema },
})
export type AwsCodepipelineGetPipelineExecutionRequest = ContractBodyInput<
typeof awsCodepipelineGetPipelineExecutionContract
>
export type AwsCodepipelineGetPipelineExecutionBody = ContractBody<
typeof awsCodepipelineGetPipelineExecutionContract
>
export type AwsCodepipelineGetPipelineExecutionResponse = ContractJsonResponse<
typeof awsCodepipelineGetPipelineExecutionContract
>
@@ -0,0 +1,73 @@
import { z } from 'zod'
import type {
ContractBody,
ContractBodyInput,
ContractJsonResponse,
} from '@/lib/api/contracts/types'
import { defineRouteContract } from '@/lib/api/contracts/types'
import { validateAwsRegion } from '@/lib/core/security/input-validation'
const GetPipelineStateSchema = z.object({
region: z
.string()
.min(1, 'AWS region is required')
.refine((v) => validateAwsRegion(v).isValid, {
message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)',
}),
accessKeyId: z.string().min(1, 'AWS access key ID is required'),
secretAccessKey: z.string().min(1, 'AWS secret access key is required'),
pipelineName: z
.string()
.min(1, 'Pipeline name is required')
.max(100, 'Pipeline name must be at most 100 characters'),
})
const ActionStateSchema = z.object({
actionName: z.string(),
status: z.string().optional(),
summary: z.string().optional(),
lastStatusChange: z.number().optional(),
externalExecutionId: z.string().optional(),
externalExecutionUrl: z.string().optional(),
errorCode: z.string().optional(),
errorMessage: z.string().optional(),
percentComplete: z.number().optional(),
token: z.string().optional(),
revisionId: z.string().optional(),
entityUrl: z.string().optional(),
})
const StageStateSchema = z.object({
stageName: z.string(),
status: z.string().optional(),
pipelineExecutionId: z.string().optional(),
inboundTransitionEnabled: z.boolean().optional(),
actionStates: z.array(ActionStateSchema),
})
const GetPipelineStateResponseSchema = z.object({
success: z.literal(true),
output: z.object({
pipelineName: z.string(),
pipelineVersion: z.number().optional(),
created: z.number().optional(),
updated: z.number().optional(),
stageStates: z.array(StageStateSchema),
}),
})
export const awsCodepipelineGetPipelineStateContract = defineRouteContract({
method: 'POST',
path: '/api/tools/codepipeline/get-pipeline-state',
body: GetPipelineStateSchema,
response: { mode: 'json', schema: GetPipelineStateResponseSchema },
})
export type AwsCodepipelineGetPipelineStateRequest = ContractBodyInput<
typeof awsCodepipelineGetPipelineStateContract
>
export type AwsCodepipelineGetPipelineStateBody = ContractBody<
typeof awsCodepipelineGetPipelineStateContract
>
export type AwsCodepipelineGetPipelineStateResponse = ContractJsonResponse<
typeof awsCodepipelineGetPipelineStateContract
>
@@ -0,0 +1,74 @@
import { z } from 'zod'
import type {
ContractBody,
ContractBodyInput,
ContractJsonResponse,
} from '@/lib/api/contracts/types'
import { defineRouteContract } from '@/lib/api/contracts/types'
import { validateAwsRegion } from '@/lib/core/security/input-validation'
const ListPipelineExecutionsSchema = z.object({
region: z
.string()
.min(1, 'AWS region is required')
.refine((v) => validateAwsRegion(v).isValid, {
message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)',
}),
accessKeyId: z.string().min(1, 'AWS access key ID is required'),
secretAccessKey: z.string().min(1, 'AWS secret access key is required'),
pipelineName: z
.string()
.min(1, 'Pipeline name is required')
.max(100, 'Pipeline name must be at most 100 characters'),
maxResults: z.preprocess(
(v) => (v === '' || v === undefined || v === null ? undefined : v),
z.coerce.number().int().min(1).max(100).optional()
),
nextToken: z.string().min(1).max(2048).optional(),
succeededInStage: z.string().min(1).max(100).optional(),
})
const PipelineExecutionSummarySchema = z.object({
pipelineExecutionId: z.string(),
status: z.string(),
statusSummary: z.string().optional(),
startTime: z.number().optional(),
lastUpdateTime: z.number().optional(),
executionMode: z.string().optional(),
executionType: z.string().optional(),
stopTriggerReason: z.string().optional(),
triggerType: z.string().optional(),
triggerDetail: z.string().optional(),
sourceRevisions: z.array(
z.object({
actionName: z.string(),
revisionId: z.string().optional(),
revisionSummary: z.string().optional(),
revisionUrl: z.string().optional(),
})
),
})
const ListPipelineExecutionsResponseSchema = z.object({
success: z.literal(true),
output: z.object({
executions: z.array(PipelineExecutionSummarySchema),
nextToken: z.string().optional(),
}),
})
export const awsCodepipelineListPipelineExecutionsContract = defineRouteContract({
method: 'POST',
path: '/api/tools/codepipeline/list-pipeline-executions',
body: ListPipelineExecutionsSchema,
response: { mode: 'json', schema: ListPipelineExecutionsResponseSchema },
})
export type AwsCodepipelineListPipelineExecutionsRequest = ContractBodyInput<
typeof awsCodepipelineListPipelineExecutionsContract
>
export type AwsCodepipelineListPipelineExecutionsBody = ContractBody<
typeof awsCodepipelineListPipelineExecutionsContract
>
export type AwsCodepipelineListPipelineExecutionsResponse = ContractJsonResponse<
typeof awsCodepipelineListPipelineExecutionsContract
>
@@ -0,0 +1,57 @@
import { z } from 'zod'
import type {
ContractBody,
ContractBodyInput,
ContractJsonResponse,
} from '@/lib/api/contracts/types'
import { defineRouteContract } from '@/lib/api/contracts/types'
import { validateAwsRegion } from '@/lib/core/security/input-validation'
const ListPipelinesSchema = z.object({
region: z
.string()
.min(1, 'AWS region is required')
.refine((v) => validateAwsRegion(v).isValid, {
message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)',
}),
accessKeyId: z.string().min(1, 'AWS access key ID is required'),
secretAccessKey: z.string().min(1, 'AWS secret access key is required'),
maxResults: z.preprocess(
(v) => (v === '' || v === undefined || v === null ? undefined : v),
z.coerce.number().int().min(1).max(1000).optional()
),
nextToken: z.string().min(1).max(2048).optional(),
})
const ListPipelinesResponseSchema = z.object({
success: z.literal(true),
output: z.object({
pipelines: z.array(
z.object({
name: z.string(),
version: z.number().optional(),
pipelineType: z.string().optional(),
executionMode: z.string().optional(),
created: z.number().optional(),
updated: z.number().optional(),
})
),
nextToken: z.string().optional(),
}),
})
export const awsCodepipelineListPipelinesContract = defineRouteContract({
method: 'POST',
path: '/api/tools/codepipeline/list-pipelines',
body: ListPipelinesSchema,
response: { mode: 'json', schema: ListPipelinesResponseSchema },
})
export type AwsCodepipelineListPipelinesRequest = ContractBodyInput<
typeof awsCodepipelineListPipelinesContract
>
export type AwsCodepipelineListPipelinesBody = ContractBody<
typeof awsCodepipelineListPipelinesContract
>
export type AwsCodepipelineListPipelinesResponse = ContractJsonResponse<
typeof awsCodepipelineListPipelinesContract
>
@@ -0,0 +1,61 @@
import { z } from 'zod'
import type {
ContractBody,
ContractBodyInput,
ContractJsonResponse,
} from '@/lib/api/contracts/types'
import { defineRouteContract } from '@/lib/api/contracts/types'
import { validateAwsRegion } from '@/lib/core/security/input-validation'
const PutApprovalResultSchema = z.object({
region: z
.string()
.min(1, 'AWS region is required')
.refine((v) => validateAwsRegion(v).isValid, {
message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)',
}),
accessKeyId: z.string().min(1, 'AWS access key ID is required'),
secretAccessKey: z.string().min(1, 'AWS secret access key is required'),
pipelineName: z
.string()
.min(1, 'Pipeline name is required')
.max(100, 'Pipeline name must be at most 100 characters'),
stageName: z
.string()
.min(1, 'Stage name is required')
.max(100, 'Stage name must be at most 100 characters'),
actionName: z
.string()
.min(1, 'Action name is required')
.max(100, 'Action name must be at most 100 characters'),
token: z.string().min(1, 'Approval token is required'),
status: z.enum(['Approved', 'Rejected']),
summary: z
.string()
.min(1, 'Approval summary is required')
.max(512, 'Approval summary must be at most 512 characters'),
})
const PutApprovalResultResponseSchema = z.object({
success: z.literal(true),
output: z.object({
approvedAt: z.number().optional(),
status: z.string(),
}),
})
export const awsCodepipelinePutApprovalResultContract = defineRouteContract({
method: 'POST',
path: '/api/tools/codepipeline/put-approval-result',
body: PutApprovalResultSchema,
response: { mode: 'json', schema: PutApprovalResultResponseSchema },
})
export type AwsCodepipelinePutApprovalResultRequest = ContractBodyInput<
typeof awsCodepipelinePutApprovalResultContract
>
export type AwsCodepipelinePutApprovalResultBody = ContractBody<
typeof awsCodepipelinePutApprovalResultContract
>
export type AwsCodepipelinePutApprovalResultResponse = ContractJsonResponse<
typeof awsCodepipelinePutApprovalResultContract
>
@@ -0,0 +1,52 @@
import { z } from 'zod'
import type {
ContractBody,
ContractBodyInput,
ContractJsonResponse,
} from '@/lib/api/contracts/types'
import { defineRouteContract } from '@/lib/api/contracts/types'
import { validateAwsRegion } from '@/lib/core/security/input-validation'
const RetryStageExecutionSchema = z.object({
region: z
.string()
.min(1, 'AWS region is required')
.refine((v) => validateAwsRegion(v).isValid, {
message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)',
}),
accessKeyId: z.string().min(1, 'AWS access key ID is required'),
secretAccessKey: z.string().min(1, 'AWS secret access key is required'),
pipelineName: z
.string()
.min(1, 'Pipeline name is required')
.max(100, 'Pipeline name must be at most 100 characters'),
stageName: z
.string()
.min(1, 'Stage name is required')
.max(100, 'Stage name must be at most 100 characters'),
pipelineExecutionId: z.string().min(1, 'Pipeline execution ID is required'),
retryMode: z.enum(['FAILED_ACTIONS', 'ALL_ACTIONS']),
})
const RetryStageExecutionResponseSchema = z.object({
success: z.literal(true),
output: z.object({
pipelineExecutionId: z.string(),
}),
})
export const awsCodepipelineRetryStageExecutionContract = defineRouteContract({
method: 'POST',
path: '/api/tools/codepipeline/retry-stage-execution',
body: RetryStageExecutionSchema,
response: { mode: 'json', schema: RetryStageExecutionResponseSchema },
})
export type AwsCodepipelineRetryStageExecutionRequest = ContractBodyInput<
typeof awsCodepipelineRetryStageExecutionContract
>
export type AwsCodepipelineRetryStageExecutionBody = ContractBody<
typeof awsCodepipelineRetryStageExecutionContract
>
export type AwsCodepipelineRetryStageExecutionResponse = ContractJsonResponse<
typeof awsCodepipelineRetryStageExecutionContract
>
@@ -0,0 +1,62 @@
import { z } from 'zod'
import type {
ContractBody,
ContractBodyInput,
ContractJsonResponse,
} from '@/lib/api/contracts/types'
import { defineRouteContract } from '@/lib/api/contracts/types'
import { validateAwsRegion } from '@/lib/core/security/input-validation'
const StartExecutionSchema = z.object({
region: z
.string()
.min(1, 'AWS region is required')
.refine((v) => validateAwsRegion(v).isValid, {
message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)',
}),
accessKeyId: z.string().min(1, 'AWS access key ID is required'),
secretAccessKey: z.string().min(1, 'AWS secret access key is required'),
pipelineName: z
.string()
.min(1, 'Pipeline name is required')
.max(100, 'Pipeline name must be at most 100 characters'),
clientRequestToken: z
.string()
.min(1)
.max(128)
.regex(/^[a-zA-Z0-9-]+$/, 'Client request token may only contain letters, digits, and hyphens')
.optional(),
variables: z
.array(
z.object({
name: z.string().min(1, 'Variable name is required'),
value: z.string().min(1, 'Variable value cannot be empty'),
})
)
.min(1)
.max(50)
.optional(),
})
const StartExecutionResponseSchema = z.object({
success: z.literal(true),
output: z.object({
pipelineExecutionId: z.string(),
}),
})
export const awsCodepipelineStartExecutionContract = defineRouteContract({
method: 'POST',
path: '/api/tools/codepipeline/start-execution',
body: StartExecutionSchema,
response: { mode: 'json', schema: StartExecutionResponseSchema },
})
export type AwsCodepipelineStartExecutionRequest = ContractBodyInput<
typeof awsCodepipelineStartExecutionContract
>
export type AwsCodepipelineStartExecutionBody = ContractBody<
typeof awsCodepipelineStartExecutionContract
>
export type AwsCodepipelineStartExecutionResponse = ContractJsonResponse<
typeof awsCodepipelineStartExecutionContract
>
@@ -0,0 +1,49 @@
import { z } from 'zod'
import type {
ContractBody,
ContractBodyInput,
ContractJsonResponse,
} from '@/lib/api/contracts/types'
import { defineRouteContract } from '@/lib/api/contracts/types'
import { validateAwsRegion } from '@/lib/core/security/input-validation'
const StopExecutionSchema = z.object({
region: z
.string()
.min(1, 'AWS region is required')
.refine((v) => validateAwsRegion(v).isValid, {
message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)',
}),
accessKeyId: z.string().min(1, 'AWS access key ID is required'),
secretAccessKey: z.string().min(1, 'AWS secret access key is required'),
pipelineName: z
.string()
.min(1, 'Pipeline name is required')
.max(100, 'Pipeline name must be at most 100 characters'),
pipelineExecutionId: z.string().min(1, 'Pipeline execution ID is required'),
abandon: z.boolean().optional(),
reason: z.string().max(200, 'Stop reason must be at most 200 characters').optional(),
})
const StopExecutionResponseSchema = z.object({
success: z.literal(true),
output: z.object({
pipelineExecutionId: z.string(),
}),
})
export const awsCodepipelineStopExecutionContract = defineRouteContract({
method: 'POST',
path: '/api/tools/codepipeline/stop-execution',
body: StopExecutionSchema,
response: { mode: 'json', schema: StopExecutionResponseSchema },
})
export type AwsCodepipelineStopExecutionRequest = ContractBodyInput<
typeof awsCodepipelineStopExecutionContract
>
export type AwsCodepipelineStopExecutionBody = ContractBody<
typeof awsCodepipelineStopExecutionContract
>
export type AwsCodepipelineStopExecutionResponse = ContractJsonResponse<
typeof awsCodepipelineStopExecutionContract
>
@@ -35,6 +35,7 @@ import {
CloudFormationIcon,
CloudflareIcon,
CloudWatchIcon,
CodePipelineIcon,
ConfluenceIcon,
CrowdStrikeIcon,
CursorIcon,
@@ -247,6 +248,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
cloudflare: CloudflareIcon,
cloudformation: CloudFormationIcon,
cloudwatch: CloudWatchIcon,
codepipeline: CodePipelineIcon,
confluence_v2: ConfluenceIcon,
crowdstrike: CrowdStrikeIcon,
cursor_v2: CursorIcon,
@@ -2826,6 +2826,57 @@
"integrationType": "observability",
"tags": ["cloud", "monitoring"]
},
{
"type": "codepipeline",
"slug": "codepipeline",
"name": "CodePipeline",
"description": "Run, monitor, and approve AWS CodePipeline pipelines",
"longDescription": "Integrate AWS CodePipeline into workflows. Start, stop, and monitor pipeline executions, retry failed stages, and approve or reject manual approval actions. Requires AWS access key and secret access key.",
"bgColor": "linear-gradient(45deg, #2E27AD 0%, #527FFF 100%)",
"iconName": "CodePipelineIcon",
"docsUrl": "https://docs.sim.ai/tools/codepipeline",
"operations": [
{
"name": "Start Execution",
"description": "Start a CodePipeline pipeline execution, optionally overriding pipeline variables"
},
{
"name": "Get Pipeline State",
"description": "Get the current state of a CodePipeline pipeline, including stage and action status and pending approval tokens"
},
{
"name": "List Pipelines",
"description": "List all CodePipeline pipelines in an AWS account and region"
},
{
"name": "List Executions",
"description": "List recent executions of a CodePipeline pipeline with status and source revisions"
},
{
"name": "Get Execution",
"description": "Get details of a CodePipeline execution, including status, trigger, source revisions, and resolved variables"
},
{
"name": "Stop Execution",
"description": "Stop a CodePipeline pipeline execution, either finishing in-progress actions or abandoning them"
},
{
"name": "Retry Stage",
"description": "Retry the failed actions (or all actions) of a failed CodePipeline stage"
},
{
"name": "Approve / Reject Approval",
"description": "Approve or reject a pending CodePipeline manual approval action. The approval token is available from Get Pipeline State on the pending approval action"
}
],
"operationCount": 8,
"triggers": [],
"triggerCount": 0,
"authType": "api-key",
"category": "tools",
"integrationType": "devops",
"tags": ["cloud", "ci-cd"]
},
{
"type": "confluence_v2",
"slug": "confluence",
+1
View File
@@ -40,6 +40,7 @@
"@aws-sdk/client-cloudformation": "3.1032.0",
"@aws-sdk/client-cloudwatch": "3.1032.0",
"@aws-sdk/client-cloudwatch-logs": "3.1032.0",
"@aws-sdk/client-codepipeline": "3.1032.0",
"@aws-sdk/client-dynamodb": "3.1032.0",
"@aws-sdk/client-iam": "3.1032.0",
"@aws-sdk/client-identitystore": "3.1032.0",
@@ -0,0 +1,153 @@
import type {
CodePipelineGetPipelineExecutionParams,
CodePipelineGetPipelineExecutionResponse,
} from '@/tools/codepipeline/types'
import type { ToolConfig } from '@/tools/types'
export const getPipelineExecutionTool: ToolConfig<
CodePipelineGetPipelineExecutionParams,
CodePipelineGetPipelineExecutionResponse
> = {
id: 'codepipeline_get_pipeline_execution',
name: 'CodePipeline Get Pipeline Execution',
description:
'Get details of a CodePipeline execution, including status, trigger, source revisions, and resolved variables',
version: '1.0.0',
params: {
awsRegion: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AWS region (e.g., us-east-1)',
},
awsAccessKeyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AWS access key ID',
},
awsSecretAccessKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AWS secret access key',
},
pipelineName: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Name of the pipeline',
},
pipelineExecutionId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the pipeline execution',
},
},
request: {
url: '/api/tools/codepipeline/get-pipeline-execution',
method: 'POST',
headers: () => ({
'Content-Type': 'application/json',
}),
body: (params) => ({
region: params.awsRegion,
accessKeyId: params.awsAccessKeyId,
secretAccessKey: params.awsSecretAccessKey,
pipelineName: params.pipelineName,
pipelineExecutionId: params.pipelineExecutionId,
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to get CodePipeline pipeline execution')
}
return {
success: true,
output: {
pipelineExecutionId: data.output.pipelineExecutionId,
pipelineName: data.output.pipelineName,
pipelineVersion: data.output.pipelineVersion,
status: data.output.status,
statusSummary: data.output.statusSummary,
executionMode: data.output.executionMode,
executionType: data.output.executionType,
triggerType: data.output.triggerType,
triggerDetail: data.output.triggerDetail,
artifactRevisions: data.output.artifactRevisions,
variables: data.output.variables,
},
}
},
outputs: {
pipelineExecutionId: { type: 'string', description: 'Pipeline execution ID' },
pipelineName: { type: 'string', description: 'Pipeline name' },
pipelineVersion: { type: 'number', description: 'Pipeline version number', optional: true },
status: {
type: 'string',
description:
'Execution status (Cancelled, InProgress, Stopped, Stopping, Succeeded, Superseded, Failed)',
},
statusSummary: {
type: 'string',
description: 'Status summary for the execution',
optional: true,
},
executionMode: {
type: 'string',
description: 'Execution mode (QUEUED, SUPERSEDED, PARALLEL)',
optional: true,
},
executionType: {
type: 'string',
description: 'Execution type (STANDARD or ROLLBACK)',
optional: true,
},
triggerType: {
type: 'string',
description: 'What triggered the execution (e.g., Webhook, StartPipelineExecution)',
optional: true,
},
triggerDetail: {
type: 'string',
description: 'Detail about the trigger (e.g., user ARN)',
optional: true,
},
artifactRevisions: {
type: 'array',
description: 'Source artifact revisions for the execution',
items: {
type: 'object',
properties: {
name: { type: 'string', description: 'Artifact name' },
revisionId: { type: 'string', description: 'Revision ID (e.g., commit SHA)' },
revisionSummary: {
type: 'string',
description: 'Revision summary (e.g., commit message)',
},
revisionUrl: { type: 'string', description: 'URL of the revision' },
created: { type: 'number', description: 'Epoch ms when the revision was created' },
},
},
},
variables: {
type: 'array',
description: 'Resolved pipeline variables for the execution',
items: {
type: 'object',
properties: {
name: { type: 'string', description: 'Variable name' },
resolvedValue: { type: 'string', description: 'Resolved variable value' },
},
},
},
},
}
@@ -0,0 +1,119 @@
import type {
CodePipelineGetPipelineStateParams,
CodePipelineGetPipelineStateResponse,
} from '@/tools/codepipeline/types'
import type { ToolConfig } from '@/tools/types'
export const getPipelineStateTool: ToolConfig<
CodePipelineGetPipelineStateParams,
CodePipelineGetPipelineStateResponse
> = {
id: 'codepipeline_get_pipeline_state',
name: 'CodePipeline Get Pipeline State',
description:
'Get the current state of a CodePipeline pipeline, including stage and action status and pending approval tokens',
version: '1.0.0',
params: {
awsRegion: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AWS region (e.g., us-east-1)',
},
awsAccessKeyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AWS access key ID',
},
awsSecretAccessKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AWS secret access key',
},
pipelineName: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Name of the pipeline',
},
},
request: {
url: '/api/tools/codepipeline/get-pipeline-state',
method: 'POST',
headers: () => ({
'Content-Type': 'application/json',
}),
body: (params) => ({
region: params.awsRegion,
accessKeyId: params.awsAccessKeyId,
secretAccessKey: params.awsSecretAccessKey,
pipelineName: params.pipelineName,
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to get CodePipeline pipeline state')
}
return {
success: true,
output: {
pipelineName: data.output.pipelineName,
pipelineVersion: data.output.pipelineVersion,
created: data.output.created,
updated: data.output.updated,
stageStates: data.output.stageStates,
},
}
},
outputs: {
pipelineName: { type: 'string', description: 'Pipeline name' },
pipelineVersion: { type: 'number', description: 'Pipeline version number', optional: true },
created: {
type: 'number',
description: 'Epoch ms when the pipeline was created',
optional: true,
},
updated: {
type: 'number',
description: 'Epoch ms when the pipeline was last updated',
optional: true,
},
stageStates: {
type: 'array',
description: 'Per-stage state including latest execution status and action details',
items: {
type: 'object',
properties: {
stageName: { type: 'string', description: 'Stage name' },
status: {
type: 'string',
description:
'Latest stage execution status (InProgress, Succeeded, Failed, Stopped, Cancelled)',
},
pipelineExecutionId: {
type: 'string',
description: 'Pipeline execution ID currently in the stage',
},
inboundTransitionEnabled: {
type: 'boolean',
description: 'Whether the inbound transition into the stage is enabled',
},
actionStates: {
type: 'array',
description:
'Per-action state with status, summary, error details, and approval token (for pending manual approvals)',
},
},
},
},
},
}
+19
View File
@@ -0,0 +1,19 @@
import { getPipelineExecutionTool } from '@/tools/codepipeline/get_pipeline_execution'
import { getPipelineStateTool } from '@/tools/codepipeline/get_pipeline_state'
import { listPipelineExecutionsTool } from '@/tools/codepipeline/list_pipeline_executions'
import { listPipelinesTool } from '@/tools/codepipeline/list_pipelines'
import { putApprovalResultTool } from '@/tools/codepipeline/put_approval_result'
import { retryStageExecutionTool } from '@/tools/codepipeline/retry_stage_execution'
import { startExecutionTool } from '@/tools/codepipeline/start_execution'
import { stopExecutionTool } from '@/tools/codepipeline/stop_execution'
export * from './types'
export const codepipelineGetPipelineExecutionTool = getPipelineExecutionTool
export const codepipelineGetPipelineStateTool = getPipelineStateTool
export const codepipelineListPipelineExecutionsTool = listPipelineExecutionsTool
export const codepipelineListPipelinesTool = listPipelinesTool
export const codepipelinePutApprovalResultTool = putApprovalResultTool
export const codepipelineRetryStageExecutionTool = retryStageExecutionTool
export const codepipelineStartExecutionTool = startExecutionTool
export const codepipelineStopExecutionTool = stopExecutionTool
@@ -0,0 +1,140 @@
import type {
CodePipelineListPipelineExecutionsParams,
CodePipelineListPipelineExecutionsResponse,
} from '@/tools/codepipeline/types'
import type { ToolConfig } from '@/tools/types'
export const listPipelineExecutionsTool: ToolConfig<
CodePipelineListPipelineExecutionsParams,
CodePipelineListPipelineExecutionsResponse
> = {
id: 'codepipeline_list_pipeline_executions',
name: 'CodePipeline List Pipeline Executions',
description: 'List recent executions of a CodePipeline pipeline with status and source revisions',
version: '1.0.0',
params: {
awsRegion: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AWS region (e.g., us-east-1)',
},
awsAccessKeyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AWS access key ID',
},
awsSecretAccessKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AWS secret access key',
},
pipelineName: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Name of the pipeline',
},
maxResults: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of executions to return (1-100, default 100)',
},
nextToken: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination token from a previous call',
},
succeededInStage: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Only return executions that succeeded in this stage',
},
},
request: {
url: '/api/tools/codepipeline/list-pipeline-executions',
method: 'POST',
headers: () => ({
'Content-Type': 'application/json',
}),
body: (params) => ({
region: params.awsRegion,
accessKeyId: params.awsAccessKeyId,
secretAccessKey: params.awsSecretAccessKey,
pipelineName: params.pipelineName,
...(params.maxResults !== undefined && { maxResults: params.maxResults }),
...(params.nextToken && { nextToken: params.nextToken }),
...(params.succeededInStage && { succeededInStage: params.succeededInStage }),
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to list CodePipeline pipeline executions')
}
return {
success: true,
output: {
executions: data.output.executions,
nextToken: data.output.nextToken,
},
}
},
outputs: {
executions: {
type: 'array',
description: 'Pipeline execution summaries, most recent first',
items: {
type: 'object',
properties: {
pipelineExecutionId: { type: 'string', description: 'Pipeline execution ID' },
status: {
type: 'string',
description:
'Execution status (Cancelled, InProgress, Stopped, Stopping, Succeeded, Superseded, Failed)',
},
statusSummary: { type: 'string', description: 'Status summary for the execution' },
startTime: { type: 'number', description: 'Epoch ms when the execution started' },
lastUpdateTime: {
type: 'number',
description: 'Epoch ms when the execution was last updated',
},
executionMode: {
type: 'string',
description: 'Execution mode (QUEUED, SUPERSEDED, PARALLEL)',
},
executionType: {
type: 'string',
description: 'Execution type (STANDARD or ROLLBACK)',
},
stopTriggerReason: {
type: 'string',
description: 'Reason the execution was stopped, if applicable',
},
triggerType: { type: 'string', description: 'What triggered the execution' },
triggerDetail: { type: 'string', description: 'Detail about the trigger' },
sourceRevisions: {
type: 'array',
description: 'Source revisions (commit IDs, summaries, URLs) for the execution',
},
},
},
},
nextToken: {
type: 'string',
description: 'Pagination token for the next page of results',
optional: true,
},
},
}
@@ -0,0 +1,105 @@
import type {
CodePipelineListPipelinesParams,
CodePipelineListPipelinesResponse,
} from '@/tools/codepipeline/types'
import type { ToolConfig } from '@/tools/types'
export const listPipelinesTool: ToolConfig<
CodePipelineListPipelinesParams,
CodePipelineListPipelinesResponse
> = {
id: 'codepipeline_list_pipelines',
name: 'CodePipeline List Pipelines',
description: 'List all CodePipeline pipelines in an AWS account and region',
version: '1.0.0',
params: {
awsRegion: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AWS region (e.g., us-east-1)',
},
awsAccessKeyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AWS access key ID',
},
awsSecretAccessKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AWS secret access key',
},
maxResults: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of pipelines to return (1-1000)',
},
nextToken: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination token from a previous call',
},
},
request: {
url: '/api/tools/codepipeline/list-pipelines',
method: 'POST',
headers: () => ({
'Content-Type': 'application/json',
}),
body: (params) => ({
region: params.awsRegion,
accessKeyId: params.awsAccessKeyId,
secretAccessKey: params.awsSecretAccessKey,
...(params.maxResults !== undefined && { maxResults: params.maxResults }),
...(params.nextToken && { nextToken: params.nextToken }),
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to list CodePipeline pipelines')
}
return {
success: true,
output: {
pipelines: data.output.pipelines,
nextToken: data.output.nextToken,
},
}
},
outputs: {
pipelines: {
type: 'array',
description: 'List of pipelines with name, version, type, and timestamps',
items: {
type: 'object',
properties: {
name: { type: 'string', description: 'Pipeline name' },
version: { type: 'number', description: 'Pipeline version number' },
pipelineType: { type: 'string', description: 'Pipeline type (V1 or V2)' },
executionMode: {
type: 'string',
description: 'Execution mode (QUEUED, SUPERSEDED, PARALLEL)',
},
created: { type: 'number', description: 'Epoch ms when the pipeline was created' },
updated: { type: 'number', description: 'Epoch ms when the pipeline was last updated' },
},
},
},
nextToken: {
type: 'string',
description: 'Pagination token for the next page of results',
optional: true,
},
},
}
@@ -0,0 +1,120 @@
import type {
CodePipelinePutApprovalResultParams,
CodePipelinePutApprovalResultResponse,
} from '@/tools/codepipeline/types'
import type { ToolConfig } from '@/tools/types'
export const putApprovalResultTool: ToolConfig<
CodePipelinePutApprovalResultParams,
CodePipelinePutApprovalResultResponse
> = {
id: 'codepipeline_put_approval_result',
name: 'CodePipeline Put Approval Result',
description:
'Approve or reject a pending CodePipeline manual approval action. The approval token is available from Get Pipeline State on the pending approval action',
version: '1.0.0',
params: {
awsRegion: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AWS region (e.g., us-east-1)',
},
awsAccessKeyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AWS access key ID',
},
awsSecretAccessKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AWS secret access key',
},
pipelineName: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Name of the pipeline',
},
stageName: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Name of the stage containing the approval action',
},
actionName: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Name of the manual approval action',
},
token: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Approval token from Get Pipeline State for the pending approval',
},
status: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Approval decision: Approved or Rejected',
},
summary: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Summary explaining the approval decision (max 512 characters)',
},
},
request: {
url: '/api/tools/codepipeline/put-approval-result',
method: 'POST',
headers: () => ({
'Content-Type': 'application/json',
}),
body: (params) => ({
region: params.awsRegion,
accessKeyId: params.awsAccessKeyId,
secretAccessKey: params.awsSecretAccessKey,
pipelineName: params.pipelineName,
stageName: params.stageName,
actionName: params.actionName,
token: params.token,
status: params.status,
summary: params.summary,
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to submit CodePipeline approval result')
}
return {
success: true,
output: {
approvedAt: data.output.approvedAt,
status: data.output.status,
},
}
},
outputs: {
approvedAt: {
type: 'number',
description: 'Epoch ms when the approval or rejection was submitted',
optional: true,
},
status: {
type: 'string',
description: 'The submitted approval decision (Approved or Rejected)',
},
},
}
@@ -0,0 +1,99 @@
import type {
CodePipelineRetryStageExecutionParams,
CodePipelineRetryStageExecutionResponse,
} from '@/tools/codepipeline/types'
import type { ToolConfig } from '@/tools/types'
export const retryStageExecutionTool: ToolConfig<
CodePipelineRetryStageExecutionParams,
CodePipelineRetryStageExecutionResponse
> = {
id: 'codepipeline_retry_stage_execution',
name: 'CodePipeline Retry Stage Execution',
description: 'Retry the failed actions (or all actions) of a failed CodePipeline stage',
version: '1.0.0',
params: {
awsRegion: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AWS region (e.g., us-east-1)',
},
awsAccessKeyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AWS access key ID',
},
awsSecretAccessKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AWS secret access key',
},
pipelineName: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Name of the pipeline',
},
stageName: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Name of the failed stage to retry',
},
pipelineExecutionId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the pipeline execution in the failed stage',
},
retryMode: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Scope of the retry: FAILED_ACTIONS or ALL_ACTIONS',
},
},
request: {
url: '/api/tools/codepipeline/retry-stage-execution',
method: 'POST',
headers: () => ({
'Content-Type': 'application/json',
}),
body: (params) => ({
region: params.awsRegion,
accessKeyId: params.awsAccessKeyId,
secretAccessKey: params.awsSecretAccessKey,
pipelineName: params.pipelineName,
stageName: params.stageName,
pipelineExecutionId: params.pipelineExecutionId,
retryMode: params.retryMode,
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to retry CodePipeline stage execution')
}
return {
success: true,
output: {
pipelineExecutionId: data.output.pipelineExecutionId,
},
}
},
outputs: {
pipelineExecutionId: {
type: 'string',
description: 'ID of the pipeline execution with the retried stage',
},
},
}
@@ -0,0 +1,92 @@
import type {
CodePipelineStartExecutionParams,
CodePipelineStartExecutionResponse,
} from '@/tools/codepipeline/types'
import type { ToolConfig } from '@/tools/types'
export const startExecutionTool: ToolConfig<
CodePipelineStartExecutionParams,
CodePipelineStartExecutionResponse
> = {
id: 'codepipeline_start_execution',
name: 'CodePipeline Start Execution',
description: 'Start a CodePipeline pipeline execution, optionally overriding pipeline variables',
version: '1.0.0',
params: {
awsRegion: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AWS region (e.g., us-east-1)',
},
awsAccessKeyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AWS access key ID',
},
awsSecretAccessKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AWS secret access key',
},
pipelineName: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Name of the pipeline to start',
},
clientRequestToken: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Idempotency token to identify a unique execution request',
},
variables: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description: 'Pipeline variable overrides as an array of { name, value } objects',
},
},
request: {
url: '/api/tools/codepipeline/start-execution',
method: 'POST',
headers: () => ({
'Content-Type': 'application/json',
}),
body: (params) => ({
region: params.awsRegion,
accessKeyId: params.awsAccessKeyId,
secretAccessKey: params.awsSecretAccessKey,
pipelineName: params.pipelineName,
...(params.clientRequestToken && { clientRequestToken: params.clientRequestToken }),
...(params.variables && params.variables.length > 0 && { variables: params.variables }),
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to start CodePipeline pipeline execution')
}
return {
success: true,
output: {
pipelineExecutionId: data.output.pipelineExecutionId,
},
}
},
outputs: {
pipelineExecutionId: {
type: 'string',
description: 'ID of the pipeline execution that was started',
},
},
}
@@ -0,0 +1,100 @@
import type {
CodePipelineStopExecutionParams,
CodePipelineStopExecutionResponse,
} from '@/tools/codepipeline/types'
import type { ToolConfig } from '@/tools/types'
export const stopExecutionTool: ToolConfig<
CodePipelineStopExecutionParams,
CodePipelineStopExecutionResponse
> = {
id: 'codepipeline_stop_execution',
name: 'CodePipeline Stop Execution',
description:
'Stop a CodePipeline pipeline execution, either finishing in-progress actions or abandoning them',
version: '1.0.0',
params: {
awsRegion: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AWS region (e.g., us-east-1)',
},
awsAccessKeyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AWS access key ID',
},
awsSecretAccessKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AWS secret access key',
},
pipelineName: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Name of the pipeline',
},
pipelineExecutionId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the pipeline execution to stop',
},
abandon: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Abandon in-progress actions instead of letting them finish (default false)',
},
reason: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Reason for stopping the execution (max 200 characters)',
},
},
request: {
url: '/api/tools/codepipeline/stop-execution',
method: 'POST',
headers: () => ({
'Content-Type': 'application/json',
}),
body: (params) => ({
region: params.awsRegion,
accessKeyId: params.awsAccessKeyId,
secretAccessKey: params.awsSecretAccessKey,
pipelineName: params.pipelineName,
pipelineExecutionId: params.pipelineExecutionId,
...(params.abandon !== undefined && { abandon: params.abandon }),
...(params.reason && { reason: params.reason }),
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || 'Failed to stop CodePipeline pipeline execution')
}
return {
success: true,
output: {
pipelineExecutionId: data.output.pipelineExecutionId,
},
}
},
outputs: {
pipelineExecutionId: {
type: 'string',
description: 'ID of the pipeline execution that was stopped',
},
},
}
+182
View File
@@ -0,0 +1,182 @@
import type { ToolResponse } from '@/tools/types'
interface CodePipelineConnectionConfig {
awsRegion: string
awsAccessKeyId: string
awsSecretAccessKey: string
}
export interface CodePipelineListPipelinesParams extends CodePipelineConnectionConfig {
maxResults?: number
nextToken?: string
}
export interface CodePipelineListPipelinesResponse extends ToolResponse {
output: {
pipelines: {
name: string
version: number | undefined
pipelineType: string | undefined
executionMode: string | undefined
created: number | undefined
updated: number | undefined
}[]
nextToken?: string
}
}
export interface CodePipelineGetPipelineStateParams extends CodePipelineConnectionConfig {
pipelineName: string
}
export interface CodePipelineActionState {
actionName: string
status: string | undefined
summary: string | undefined
lastStatusChange: number | undefined
externalExecutionId: string | undefined
externalExecutionUrl: string | undefined
errorCode: string | undefined
errorMessage: string | undefined
percentComplete: number | undefined
token: string | undefined
revisionId: string | undefined
entityUrl: string | undefined
}
export interface CodePipelineStageState {
stageName: string
status: string | undefined
pipelineExecutionId: string | undefined
inboundTransitionEnabled: boolean | undefined
actionStates: CodePipelineActionState[]
}
export interface CodePipelineGetPipelineStateResponse extends ToolResponse {
output: {
pipelineName: string
pipelineVersion: number | undefined
created: number | undefined
updated: number | undefined
stageStates: CodePipelineStageState[]
}
}
export interface CodePipelineGetPipelineExecutionParams extends CodePipelineConnectionConfig {
pipelineName: string
pipelineExecutionId: string
}
export interface CodePipelineGetPipelineExecutionResponse extends ToolResponse {
output: {
pipelineExecutionId: string
pipelineName: string
pipelineVersion: number | undefined
status: string
statusSummary: string | undefined
executionMode: string | undefined
executionType: string | undefined
triggerType: string | undefined
triggerDetail: string | undefined
artifactRevisions: {
name: string
revisionId: string | undefined
revisionSummary: string | undefined
revisionUrl: string | undefined
created: number | undefined
}[]
variables: {
name: string
resolvedValue: string
}[]
}
}
export interface CodePipelineListPipelineExecutionsParams extends CodePipelineConnectionConfig {
pipelineName: string
maxResults?: number
nextToken?: string
succeededInStage?: string
}
export interface CodePipelineListPipelineExecutionsResponse extends ToolResponse {
output: {
executions: {
pipelineExecutionId: string
status: string
statusSummary: string | undefined
startTime: number | undefined
lastUpdateTime: number | undefined
executionMode: string | undefined
executionType: string | undefined
stopTriggerReason: string | undefined
triggerType: string | undefined
triggerDetail: string | undefined
sourceRevisions: {
actionName: string
revisionId: string | undefined
revisionSummary: string | undefined
revisionUrl: string | undefined
}[]
}[]
nextToken?: string
}
}
export interface CodePipelineStartExecutionParams extends CodePipelineConnectionConfig {
pipelineName: string
clientRequestToken?: string
variables?: { name: string; value: string }[]
}
export interface CodePipelineStartExecutionResponse extends ToolResponse {
output: {
pipelineExecutionId: string
}
}
export interface CodePipelineStopExecutionParams extends CodePipelineConnectionConfig {
pipelineName: string
pipelineExecutionId: string
abandon?: boolean
reason?: string
}
export interface CodePipelineStopExecutionResponse extends ToolResponse {
output: {
pipelineExecutionId: string
}
}
export type CodePipelineRetryMode = 'FAILED_ACTIONS' | 'ALL_ACTIONS'
export interface CodePipelineRetryStageExecutionParams extends CodePipelineConnectionConfig {
pipelineName: string
stageName: string
pipelineExecutionId: string
retryMode: CodePipelineRetryMode
}
export interface CodePipelineRetryStageExecutionResponse extends ToolResponse {
output: {
pipelineExecutionId: string
}
}
export type CodePipelineApprovalStatus = 'Approved' | 'Rejected'
export interface CodePipelinePutApprovalResultParams extends CodePipelineConnectionConfig {
pipelineName: string
stageName: string
actionName: string
token: string
status: CodePipelineApprovalStatus
summary: string
}
export interface CodePipelinePutApprovalResultResponse extends ToolResponse {
output: {
approvedAt: number | undefined
status: string
}
}
+18
View File
@@ -434,6 +434,16 @@ import {
cloudwatchQueryLogsTool,
cloudwatchUnmuteAlarmTool,
} from '@/tools/cloudwatch'
import {
codepipelineGetPipelineExecutionTool,
codepipelineGetPipelineStateTool,
codepipelineListPipelineExecutionsTool,
codepipelineListPipelinesTool,
codepipelinePutApprovalResultTool,
codepipelineRetryStageExecutionTool,
codepipelineStartExecutionTool,
codepipelineStopExecutionTool,
} from '@/tools/codepipeline'
import {
confluenceAddLabelTool,
confluenceCreateBlogPostTool,
@@ -4302,6 +4312,14 @@ export const tools: Record<string, ToolConfig> = {
cloudwatch_put_metric_data: cloudwatchPutMetricDataTool,
cloudwatch_query_logs: cloudwatchQueryLogsTool,
cloudwatch_unmute_alarm: cloudwatchUnmuteAlarmTool,
codepipeline_get_pipeline_execution: codepipelineGetPipelineExecutionTool,
codepipeline_get_pipeline_state: codepipelineGetPipelineStateTool,
codepipeline_list_pipeline_executions: codepipelineListPipelineExecutionsTool,
codepipeline_list_pipelines: codepipelineListPipelinesTool,
codepipeline_put_approval_result: codepipelinePutApprovalResultTool,
codepipeline_retry_stage_execution: codepipelineRetryStageExecutionTool,
codepipeline_start_execution: codepipelineStartExecutionTool,
codepipeline_stop_execution: codepipelineStopExecutionTool,
crowdstrike_get_sensor_aggregates: crowdstrikeGetSensorAggregatesTool,
crowdstrike_get_sensor_details: crowdstrikeGetSensorDetailsTool,
crowdstrike_query_sensors: crowdstrikeQuerySensorsTool,
+3
View File
@@ -96,6 +96,7 @@
"@aws-sdk/client-cloudformation": "3.1032.0",
"@aws-sdk/client-cloudwatch": "3.1032.0",
"@aws-sdk/client-cloudwatch-logs": "3.1032.0",
"@aws-sdk/client-codepipeline": "3.1032.0",
"@aws-sdk/client-dynamodb": "3.1032.0",
"@aws-sdk/client-iam": "3.1032.0",
"@aws-sdk/client-identitystore": "3.1032.0",
@@ -579,6 +580,8 @@
"@aws-sdk/client-cloudwatch-logs": ["@aws-sdk/client-cloudwatch-logs@3.1032.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.1", "@aws-sdk/credential-provider-node": "^3.972.32", "@aws-sdk/middleware-host-header": "^3.972.10", "@aws-sdk/middleware-logger": "^3.972.10", "@aws-sdk/middleware-recursion-detection": "^3.972.11", "@aws-sdk/middleware-user-agent": "^3.972.31", "@aws-sdk/region-config-resolver": "^3.972.12", "@aws-sdk/types": "^3.973.8", "@aws-sdk/util-endpoints": "^3.996.7", "@aws-sdk/util-user-agent-browser": "^3.972.10", "@aws-sdk/util-user-agent-node": "^3.973.17", "@smithy/config-resolver": "^4.4.16", "@smithy/core": "^3.23.15", "@smithy/eventstream-serde-browser": "^4.2.14", "@smithy/eventstream-serde-config-resolver": "^4.3.14", "@smithy/eventstream-serde-node": "^4.2.14", "@smithy/fetch-http-handler": "^5.3.17", "@smithy/hash-node": "^4.2.14", "@smithy/invalid-dependency": "^4.2.14", "@smithy/middleware-content-length": "^4.2.14", "@smithy/middleware-endpoint": "^4.4.30", "@smithy/middleware-retry": "^4.5.3", "@smithy/middleware-serde": "^4.2.18", "@smithy/middleware-stack": "^4.2.14", "@smithy/node-config-provider": "^4.3.14", "@smithy/node-http-handler": "^4.5.3", "@smithy/protocol-http": "^5.3.14", "@smithy/smithy-client": "^4.12.11", "@smithy/types": "^4.14.1", "@smithy/url-parser": "^4.2.14", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-body-length-node": "^4.2.3", "@smithy/util-defaults-mode-browser": "^4.3.47", "@smithy/util-defaults-mode-node": "^4.2.52", "@smithy/util-endpoints": "^3.4.1", "@smithy/util-middleware": "^4.2.14", "@smithy/util-retry": "^4.3.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-pzvsXRUrlq5q0HTmpEUF07koRw1cikeWY4M5brPQimMBZx5VahiIVyacNwD1tr40rKwo72SyFDToBWSnXFVYKA=="],
"@aws-sdk/client-codepipeline": ["@aws-sdk/client-codepipeline@3.1032.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.1", "@aws-sdk/credential-provider-node": "^3.972.32", "@aws-sdk/middleware-host-header": "^3.972.10", "@aws-sdk/middleware-logger": "^3.972.10", "@aws-sdk/middleware-recursion-detection": "^3.972.11", "@aws-sdk/middleware-user-agent": "^3.972.31", "@aws-sdk/region-config-resolver": "^3.972.12", "@aws-sdk/types": "^3.973.8", "@aws-sdk/util-endpoints": "^3.996.7", "@aws-sdk/util-user-agent-browser": "^3.972.10", "@aws-sdk/util-user-agent-node": "^3.973.17", "@smithy/config-resolver": "^4.4.16", "@smithy/core": "^3.23.15", "@smithy/fetch-http-handler": "^5.3.17", "@smithy/hash-node": "^4.2.14", "@smithy/invalid-dependency": "^4.2.14", "@smithy/middleware-content-length": "^4.2.14", "@smithy/middleware-endpoint": "^4.4.30", "@smithy/middleware-retry": "^4.5.3", "@smithy/middleware-serde": "^4.2.18", "@smithy/middleware-stack": "^4.2.14", "@smithy/node-config-provider": "^4.3.14", "@smithy/node-http-handler": "^4.5.3", "@smithy/protocol-http": "^5.3.14", "@smithy/smithy-client": "^4.12.11", "@smithy/types": "^4.14.1", "@smithy/url-parser": "^4.2.14", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-body-length-node": "^4.2.3", "@smithy/util-defaults-mode-browser": "^4.3.47", "@smithy/util-defaults-mode-node": "^4.2.52", "@smithy/util-endpoints": "^3.4.1", "@smithy/util-middleware": "^4.2.14", "@smithy/util-retry": "^4.3.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-VNoKgaIrzM+kQxJTDt+rXshkrj4pf9wgfwSi23DNE1cmaeDGJQJEoScD0vPqry8B8Qaj0omS7UzZyVCVv1ZOFw=="],
"@aws-sdk/client-dynamodb": ["@aws-sdk/client-dynamodb@3.1032.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.1", "@aws-sdk/credential-provider-node": "^3.972.32", "@aws-sdk/dynamodb-codec": "^3.973.1", "@aws-sdk/middleware-endpoint-discovery": "^3.972.11", "@aws-sdk/middleware-host-header": "^3.972.10", "@aws-sdk/middleware-logger": "^3.972.10", "@aws-sdk/middleware-recursion-detection": "^3.972.11", "@aws-sdk/middleware-user-agent": "^3.972.31", "@aws-sdk/region-config-resolver": "^3.972.12", "@aws-sdk/types": "^3.973.8", "@aws-sdk/util-endpoints": "^3.996.7", "@aws-sdk/util-user-agent-browser": "^3.972.10", "@aws-sdk/util-user-agent-node": "^3.973.17", "@smithy/config-resolver": "^4.4.16", "@smithy/core": "^3.23.15", "@smithy/fetch-http-handler": "^5.3.17", "@smithy/hash-node": "^4.2.14", "@smithy/invalid-dependency": "^4.2.14", "@smithy/middleware-content-length": "^4.2.14", "@smithy/middleware-endpoint": "^4.4.30", "@smithy/middleware-retry": "^4.5.3", "@smithy/middleware-serde": "^4.2.18", "@smithy/middleware-stack": "^4.2.14", "@smithy/node-config-provider": "^4.3.14", "@smithy/node-http-handler": "^4.5.3", "@smithy/protocol-http": "^5.3.14", "@smithy/smithy-client": "^4.12.11", "@smithy/types": "^4.14.1", "@smithy/url-parser": "^4.2.14", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-body-length-node": "^4.2.3", "@smithy/util-defaults-mode-browser": "^4.3.47", "@smithy/util-defaults-mode-node": "^4.2.52", "@smithy/util-endpoints": "^3.4.1", "@smithy/util-middleware": "^4.2.14", "@smithy/util-retry": "^4.3.2", "@smithy/util-utf8": "^4.2.2", "@smithy/util-waiter": "^4.2.16", "tslib": "^2.6.2" } }, "sha512-kkXiZBNdWCQAg/8opqAu10TxzdpqMkcGrNAT2ScdfWhCpzYZ2pmSpP8W7BOlA32jYIWnYrEdb808UZsNWYBPAA=="],
"@aws-sdk/client-iam": ["@aws-sdk/client-iam@3.1032.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.1", "@aws-sdk/credential-provider-node": "^3.972.32", "@aws-sdk/middleware-host-header": "^3.972.10", "@aws-sdk/middleware-logger": "^3.972.10", "@aws-sdk/middleware-recursion-detection": "^3.972.11", "@aws-sdk/middleware-user-agent": "^3.972.31", "@aws-sdk/region-config-resolver": "^3.972.12", "@aws-sdk/types": "^3.973.8", "@aws-sdk/util-endpoints": "^3.996.7", "@aws-sdk/util-user-agent-browser": "^3.972.10", "@aws-sdk/util-user-agent-node": "^3.973.17", "@smithy/config-resolver": "^4.4.16", "@smithy/core": "^3.23.15", "@smithy/fetch-http-handler": "^5.3.17", "@smithy/hash-node": "^4.2.14", "@smithy/invalid-dependency": "^4.2.14", "@smithy/middleware-content-length": "^4.2.14", "@smithy/middleware-endpoint": "^4.4.30", "@smithy/middleware-retry": "^4.5.3", "@smithy/middleware-serde": "^4.2.18", "@smithy/middleware-stack": "^4.2.14", "@smithy/node-config-provider": "^4.3.14", "@smithy/node-http-handler": "^4.5.3", "@smithy/protocol-http": "^5.3.14", "@smithy/smithy-client": "^4.12.11", "@smithy/types": "^4.14.1", "@smithy/url-parser": "^4.2.14", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-body-length-node": "^4.2.3", "@smithy/util-defaults-mode-browser": "^4.3.47", "@smithy/util-defaults-mode-node": "^4.2.52", "@smithy/util-endpoints": "^3.4.1", "@smithy/util-middleware": "^4.2.14", "@smithy/util-retry": "^4.3.2", "@smithy/util-utf8": "^4.2.2", "@smithy/util-waiter": "^4.2.16", "tslib": "^2.6.2" } }, "sha512-dzLygZx+PIUJ1Iob2l6a3ToqRtF1FQzF+Ps8lPeFaJSibslUt12hmBGUJ7uIVvoXhGzRRsRwtXTCH++XZpVYag=="],
+2 -2
View File
@@ -9,8 +9,8 @@ const QUERY_HOOKS_DIR = path.join(ROOT, 'apps/sim/hooks/queries')
const SELECTOR_HOOKS_DIR = path.join(ROOT, 'apps/sim/hooks/selectors')
const BASELINE = {
totalRoutes: 804,
zodRoutes: 804,
totalRoutes: 811,
zodRoutes: 811,
nonZodRoutes: 0,
} as const