chore: import upstream snapshot with attribution
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
import crypto from 'crypto'
|
||||
import { getErrorMessage } from '@sim/utils/errors'
|
||||
import {
|
||||
encodeS3PathComponent,
|
||||
generatePresignedUrl,
|
||||
getSignatureKey,
|
||||
parseS3Uri,
|
||||
} from '@/tools/s3/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const s3GetObjectTool: ToolConfig = {
|
||||
id: 's3_get_object',
|
||||
name: 'S3 Get Object',
|
||||
description: 'Retrieve an object from an AWS S3 bucket',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
accessKeyId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Your AWS Access Key ID',
|
||||
},
|
||||
secretAccessKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Your AWS Secret Access Key',
|
||||
},
|
||||
region: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description:
|
||||
'Optional region override when URL does not include region (e.g., us-east-1, eu-west-1)',
|
||||
},
|
||||
s3Uri: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'S3 Object URL (e.g., https://bucket.s3.region.amazonaws.com/path/to/file)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
try {
|
||||
const { bucketName, region, objectKey } = parseS3Uri(params.s3Uri, params.region)
|
||||
|
||||
params.bucketName = bucketName
|
||||
params.region = region
|
||||
params.objectKey = objectKey
|
||||
|
||||
return `https://${bucketName}.s3.${region}.amazonaws.com/${encodeS3PathComponent(objectKey)}`
|
||||
} catch (_error) {
|
||||
throw new Error(
|
||||
'Invalid S3 Object URL. Use a valid S3 URL and optionally provide region if the URL omits it.'
|
||||
)
|
||||
}
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => {
|
||||
try {
|
||||
// Parse S3 URI if not already parsed
|
||||
if (!params.bucketName || !params.region || !params.objectKey) {
|
||||
const { bucketName, region, objectKey } = parseS3Uri(params.s3Uri, params.region)
|
||||
params.bucketName = bucketName
|
||||
params.region = region
|
||||
params.objectKey = objectKey
|
||||
}
|
||||
|
||||
// Use UTC time explicitly
|
||||
const date = new Date()
|
||||
const amzDate = date.toISOString().replace(/[:-]|\.\d{3}/g, '')
|
||||
const dateStamp = amzDate.slice(0, 8)
|
||||
|
||||
const method = 'GET'
|
||||
const encodedPath = encodeS3PathComponent(params.objectKey)
|
||||
const canonicalUri = `/${encodedPath}`
|
||||
const canonicalQueryString = ''
|
||||
const payloadHash = crypto.createHash('sha256').update('').digest('hex')
|
||||
const host = `${params.bucketName}.s3.${params.region}.amazonaws.com`
|
||||
const canonicalHeaders =
|
||||
`host:${host}\n` + `x-amz-content-sha256:${payloadHash}\n` + `x-amz-date:${amzDate}\n`
|
||||
const signedHeaders = 'host;x-amz-content-sha256;x-amz-date'
|
||||
const canonicalRequest = `${method}\n${canonicalUri}\n${canonicalQueryString}\n${canonicalHeaders}\n${signedHeaders}\n${payloadHash}`
|
||||
|
||||
const algorithm = 'AWS4-HMAC-SHA256'
|
||||
const credentialScope = `${dateStamp}/${params.region}/s3/aws4_request`
|
||||
const stringToSign = `${algorithm}\n${amzDate}\n${credentialScope}\n${crypto.createHash('sha256').update(canonicalRequest).digest('hex')}`
|
||||
|
||||
const signingKey = getSignatureKey(params.secretAccessKey, dateStamp, params.region, 's3')
|
||||
const signature = crypto.createHmac('sha256', signingKey).update(stringToSign).digest('hex')
|
||||
|
||||
const authorizationHeader = `${algorithm} Credential=${params.accessKeyId}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signature}`
|
||||
|
||||
return {
|
||||
Host: host,
|
||||
'X-Amz-Content-Sha256': payloadHash,
|
||||
'X-Amz-Date': amzDate,
|
||||
Authorization: authorizationHeader,
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMessage = getErrorMessage(error, 'Unknown error')
|
||||
throw new Error(`Failed to generate request headers: ${errorMessage}`)
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params) => {
|
||||
// Parse S3 URI if not already parsed
|
||||
if (!params.bucketName || !params.region || !params.objectKey) {
|
||||
const { bucketName, region, objectKey } = parseS3Uri(params.s3Uri, params.region)
|
||||
params.bucketName = bucketName
|
||||
params.region = region
|
||||
params.objectKey = objectKey
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text()
|
||||
throw new Error(
|
||||
`Failed to download S3 object: ${response.status} ${response.statusText} ${errorText}`
|
||||
)
|
||||
}
|
||||
|
||||
const contentType = response.headers.get('content-type') || 'application/octet-stream'
|
||||
const lastModified = response.headers.get('last-modified') || new Date().toISOString()
|
||||
const fileName = params.objectKey.split('/').pop() || params.objectKey
|
||||
const arrayBuffer = await response.arrayBuffer()
|
||||
const buffer = Buffer.from(arrayBuffer)
|
||||
|
||||
// Generate pre-signed URL for download
|
||||
const url = generatePresignedUrl(params, 3600)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
url,
|
||||
file: {
|
||||
name: fileName,
|
||||
mimeType: contentType,
|
||||
data: buffer.toString('base64'),
|
||||
size: buffer.length,
|
||||
},
|
||||
metadata: {
|
||||
fileType: contentType,
|
||||
size: buffer.length,
|
||||
name: fileName,
|
||||
lastModified: lastModified,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
url: {
|
||||
type: 'string',
|
||||
description: 'Pre-signed URL for downloading the S3 object',
|
||||
},
|
||||
file: {
|
||||
type: 'file',
|
||||
description: 'Downloaded file stored in execution files',
|
||||
},
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'File metadata including type, size, name, and last modified date',
|
||||
},
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user