Skip to content

feat: add S3 / object storage action (#38) - #85

Open
raphael-goetz wants to merge 6 commits into
mainfrom
feat/#38-s3-object-storage-action
Open

raphael-goetz wants to merge 6 commits into
mainfrom
feat/#38-s3-object-storage-action

Conversation

@raphael-goetz

Copy link
Copy Markdown
Member

What

Adds a new s3-action to the monorepo, exposing AWS S3 (or any S3-compatible provider, via a custom endpoint) as Hercules runtime functions: putObject, getObject, deleteObject, listObjects, and createPresignedUrl. Object content is exchanged as hercules' standard FILE value (base64 + content type), so files interop with other flow functions.

Why

Closes #38.

How

  • actions/s3-action/src/index.ts — action definition, config (access_key_id/secret_access_key optional, falling back to the AWS SDK's default credential provider chain; region, endpoint, bucket), data types and function registration.
  • actions/s3-action/src/helpers.ts — cached S3Client factory, bucket resolution, error mapping.
  • actions/s3-action/src/functions/* — the five runtime functions, backed by @aws-sdk/client-s3 / @aws-sdk/s3-request-presigner.
  • actions/s3-action/src/data_types/* — zod schemas for S3_OBJECT, S3_OBJECT_CONTENT (wraps the standard FILE type), S3_DELETE_RESULT, S3_OBJECT_LIST, S3_PRESIGNED_URL.
  • .gitlab-ci.yml — added s3-action to the .node-actions matrix so it's typechecked/tested/built/published like the other Node actions.
  • Design decisions (binary content via FILE, optional credentials) were cross-checked against an earlier abandoned WIP attempt at this same issue (#38-s3-action), which didn't typecheck against the current SDK; the sound parts of its design were carried over and fixed up.

Testing

  • npm run typecheck — clean
  • npm run test (vitest) — 10/10 passing
  • npm run build (vite) — builds dist/index.js
  • Verified end-to-end with the real hercules SDK's hercules export CLI: all 5 functions, 5 data types, and 5 configs register and serialize correctly
  • node scripts/for-each-action.mjs typecheck|test from repo root — all 7 Node actions pass

New centaurus action exposing AWS S3 (and any S3-compatible provider,
via a custom endpoint) as Hercules runtime functions: putObject,
getObject, deleteObject, listObjects, and createPresignedUrl.

Refs #38
Avoids returning a stale client with the old secret after credential
rotation that keeps the same access key ID, region, and endpoint.

Refs #38
Brings two design decisions over from an earlier WIP attempt at this
action (origin/#38-s3-action, same author, never finished — it
doesn't typecheck against the current hercules SDK):

- putObject/getObject now exchange content as hercules' standard FILE
  value (base64 + content type) instead of UTF-8 text only, so
  binary objects work and files interop with other flow functions.
- access_key_id/secret_access_key are now optional, falling back to
  the AWS SDK's default credential provider chain (env vars, shared
  config, or an IAM role) when unset.

Also adds StorageClass to listObjects results and an explicit
Deleted flag to deleteObject's result, both present in that WIP.

Refs #38
@raphael-goetz raphael-goetz self-assigned this Nov 1, 2026
@github-actions

github-actions Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

GitLab Pipeline Action

General information

Link to pipeline: https://gitlab.com/code0-tech/development/centaurus/-/pipelines/3018518499

Status: Passed
Duration: 9 minutes

Job summaries

docs:preview

Documentation preview available at https://code0-tech.gitlab.io/-/development/telescopium/-/jobs/17670073741/artifacts/out/index.html

@nicosammito nicosammito left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The put function should be able to upload multiply files


@Identifier("putObject")
@DisplayIcon("simple:amazons3")
@Signature("(Key: string, File: FILE, Bucket?: string): S3_OBJECT")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FILE is wrongly declared should be FILE<"/">

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — the parameter is now declared as FILE<"*/*">[] (also switched to accepting multiple files, see below).

Address review feedback on #85:

- putObject now accepts `Files: FILE<"*/*">[]` and uploads each file,
  returning `S3_OBJECT[]`. Each object's key is derived from the file's
  own name, optionally under an optional `KeyPrefix` (folder).
- Fix the FILE parameter type: `FILE` -> `FILE<"*/*">`.
- Add a `resolveObjectKey` helper (prefix + file name joining) with tests.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@raphael-goetz

Copy link
Copy Markdown
Member Author

Addressed the requested changes (@nicosammito):

putObject now uploads multiple files. Signature is now:

(Files: FILE<"*/*">[], KeyPrefix?: string, Bucket?: string): S3_OBJECT[]
  • Files takes one or more standard FILE values; each is uploaded and the function returns one S3_OBJECT per file.
  • Each object's key is the file's own fileName, optionally placed under KeyPrefix (e.g. reports/ → reports/<name>). Deriving the key from the file name is what makes a per-file key well-defined for the multi-file case, and it round-trips cleanly with getObject (which already sets fileName).
  • The File param type is fixed to FILE<"*/*"> as requested.

Also added a resolveObjectKey helper (prefix + file-name joining, no double slashes) with unit tests. npm run typecheck, npm run test (14 passing), and npm run build all pass locally.

@nicosammito nicosammito left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The icon used does not exist: simple:amazons3. We are using simple-icon here.
Also it should not be named object but instead upload files, delete file, list file, etc.

optional: true,
})
export class PutObjectFunction {
async run(context: FunctionContext, Files: File<"*/*">[], KeyPrefix?: string, Bucket?: string): Promise<S3Object[]> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should File also in the signature. This function should not be named Upload objects but upload files. Also there is no such icon simple:amazons3

Comment thread actions/s3-action/src/helpers.ts Outdated
* S3-compatible provider (MinIO, Cloudflare R2, DigitalOcean Spaces, ...) by
* pointing `endpoint` at that provider.
*/
let cached: { accessKeyId: string; secretAccessKey: string; region: string; endpoint: string; client: S3Client } | null =

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can't cache clients. This is generally not possible. Because you have infinite possible client connections.

@Taucher2003

Copy link
Copy Markdown
Member

Also it should not be named object but instead upload files, delete file, list file, etc.

I would disagree with that. S3 (and object storage in general) has no concept of a file. It only knows objects. There are no directories, objects can only have a key with a prefix.

S3 Files does exist, but that is a separate service which provides a filesystem backed by a S3 bucket. Naming things as "file" here could create confusion.

@nicosammito

Copy link
Copy Markdown
Member

Also it should not be named object but instead upload files, delete file, list file, etc.

I would disagree with that. S3 (and object storage in general) has no concept of a file. It only knows objects. There are no directories, objects can only have a key with a prefix.

S3 Files does exist, but that is a separate service which provides a filesystem backed by a S3 bucket. Naming things as "file" here could create confusion.

Partially agree here, I would prefer functions for managing files and data. So e.g. Upload data and Upload files

- Replace invalid `simple:amazons3` icon (AWS icons were pulled from Simple
  Icons) with `tabler:bucket`.
- Stop caching the S3 client: a single action process serves many flows with
  different matched configs, so build a fresh client per call.
- Split the functions into two mirrored sets (per review): an object set
  (raw data at an explicit key) and a file set (hercules FILE values keyed by
  file name), each with upload/download/delete/list/presigned. Adds the
  S3_OBJECT_DATA data type for raw-text object bodies.
- Update README and tests accordingly.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

S3 / Object Storage Action

3 participants