Merge branch 'main' into nicer-app-emails

# Conflicts:
#	pnpm-lock.yaml
This commit is contained in:
James Ritchie
2025-09-07 13:15:37 +01:00
678 changed files with 28563 additions and 18647 deletions
-5
View File
@@ -1,5 +0,0 @@
---
"trigger.dev": patch
---
Added experimental_devProcessCwdInBuildDir config option to opt-in to new process.cwd behavior when executing tasks in the dev CLI. Currently process.cwd maps to the "root" of your trigger.dev project (the directory that contains your trigger.config.ts file). Setting experimental_devProcessCwdInBuildDir to true changes process.cwd to instead be the temporary build directory inside of the .trigger directory.
-5
View File
@@ -1,5 +0,0 @@
---
"trigger.dev": patch
---
Fix dev runs
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/sdk": patch
---
fix: importing from runEngine/index.js breaks non-node runtimes
-6
View File
@@ -1,6 +0,0 @@
---
"trigger.dev": patch
"@trigger.dev/core": patch
---
The dev command will now use the platform-provided engine URL
-10
View File
@@ -1,10 +0,0 @@
---
"@trigger.dev/react-hooks": patch
"@trigger.dev/sdk": patch
"trigger.dev": patch
"@trigger.dev/build": patch
"@trigger.dev/core": patch
"@trigger.dev/rsc": patch
---
Run Engine 2.0 (alpha)
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/sdk": patch
---
fix: Logging large objects is now much more performant and uses less memory
-5
View File
@@ -1,5 +0,0 @@
---
"trigger.dev": patch
---
Fix update command version mismatch detection
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/sdk": patch
---
New internal idempotency implementation for trigger and batch trigger to prevent request retries from duplicating work
-5
View File
@@ -1,5 +0,0 @@
---
"trigger.dev": patch
---
Add external log exporters and fix missing external trace exporters in deployed tasks
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/sdk": patch
---
When you create a Waitpoint token using `wait.createToken()` you get a URL back that can be used to complete it by making an HTTP POST request.
-5
View File
@@ -1,5 +0,0 @@
---
"trigger.dev": patch
---
Allow any runs to finish after SIGTERM but disable warm starts
-5
View File
@@ -1,5 +0,0 @@
---
"trigger.dev": patch
---
Gracefully shutdown task run processes using SIGTERM followed by SIGKILL after a 1s timeout. This also prevents cancelled or completed runs from leaving orphaned Ttask run processes behind
-5
View File
@@ -1,5 +0,0 @@
---
"trigger.dev": patch
---
Enhance deploy command output to better distinguish between local and remote builds
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/core": patch
---
Configurable queue consumer count in supervisor session
-5
View File
@@ -1,5 +0,0 @@
---
"trigger.dev": patch
---
Switch to profile after successful login
-5
View File
@@ -1,5 +0,0 @@
---
"trigger.dev": patch
---
Fixes a bug that would allow processes that had OOM errors to be incorrectly reused when experimental_processKeepAlive was enabled
-6
View File
@@ -1,6 +0,0 @@
---
"trigger.dev": patch
"@trigger.dev/core": patch
---
Runtime agnostic SDK config via env vars
-12
View File
@@ -1,12 +0,0 @@
---
"@trigger.dev/redis-worker": major
"@trigger.dev/react-hooks": major
"@trigger.dev/sdk": major
"trigger.dev": major
"@trigger.dev/python": major
"@trigger.dev/build": major
"@trigger.dev/core": major
"@trigger.dev/rsc": major
---
Trigger.dev v4 release. Please see our upgrade to v4 docs to view the full changelog: https://trigger.dev/docs/upgrade-to-v4
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/core": patch
---
Add supervisor http client option to disable debug logs
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/sdk": patch
---
Fixed an issue with realtime streams that timeout and resume streaming dropping chunks
-18
View File
@@ -1,18 +0,0 @@
---
"@trigger.dev/sdk": patch
---
Added and cleaned up the run ctx param:
- New optional properties `ctx.run.parentTaskRunId` and `ctx.run.rootTaskRunId` reference the current run's root/parent ID.
- Removed deprecated properties from `ctx`
- Added a new `ctx.deployment` object that contains information about the deployment associated with the run.
We also update `metadata.root` and `metadata.parent` to work even when the run is a "root" run (meaning it doesn't have a parent or a root associated run). This now works:
```ts
metadata.root.set("foo", "bar");
metadata.parent.set("baz", 1);
metadata.current().foo // "bar"
metadata.current().baz // 1
```
-6
View File
@@ -1,6 +0,0 @@
---
"trigger.dev": patch
"@trigger.dev/core": patch
---
Expose esbuild `keepNames` option (experimental)
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/sdk": patch
---
The envvars.list() and retrieve() functions receive isSecret for each value. Secret values are always redacted.
-6
View File
@@ -1,6 +0,0 @@
---
"trigger.dev": patch
"@trigger.dev/core": patch
---
Add `experimental_autoDetectExternal` trigger config option
-6
View File
@@ -1,6 +0,0 @@
---
"trigger.dev": patch
"@trigger.dev/core": patch
---
Add project details to the whoami command
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/sdk": patch
---
Fix issue where realtime streams would cut off after 5 minutes
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/sdk": patch
---
Deprecate toolTask and replace with `ai.tool(mySchemaTask)`
-5
View File
@@ -1,5 +0,0 @@
---
"trigger.dev": patch
---
Log images sizes for self-hosted deploys
-6
View File
@@ -1,6 +0,0 @@
---
"@trigger.dev/sdk": patch
"trigger.dev": patch
---
Display clickable links in Cursor terminal
-5
View File
@@ -1,5 +0,0 @@
---
"trigger.dev": patch
---
Fix init.ts in custom trigger dirs
-6
View File
@@ -1,6 +0,0 @@
---
"trigger.dev": patch
"@trigger.dev/core": patch
---
Add import timings and bundle size analysis, the dev command will now warn about slow imports
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/core": patch
---
Improve structured logs
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/build": patch
---
syncVercelEnvVars() fix for syncing the wrong preview branch env vars
-5
View File
@@ -1,5 +0,0 @@
---
"trigger.dev": patch
---
Fix update command version range handling
-45
View File
@@ -1,45 +0,0 @@
---
"@trigger.dev/sdk": patch
---
Removes the `releaseConcurrencyOnWaitpoint` option on queues and the `releaseConcurrency` option on various wait functions. Replaced with the following default behavior:
- Concurrency is never released when a run is first blocked via a waitpoint, at either the env or queue level.
- Concurrency is always released when a run is checkpointed and shutdown, at both the env and queue level.
Additionally, environment concurrency limits now have a new "Burst Factor", defaulting to 2.0x. The "Burst Factor" allows the environment-wide concurrency limit to be higher than any individual queue's concurrency limit. For example, if you have an environment concurrency limit of 100, and a Burst Factor of 2.0x, then you can execute up to 200 runs concurrently, but any one task/queue can still only execute 100 runs concurrently.
We've done some work cleaning up the run statuses. The new statuses are:
- `PENDING_VERSION`: Task is waiting for a version update because it cannot execute without additional information (task, queue, etc.)
- `QUEUED`: Task is waiting to be executed by a worker
- `DEQUEUED`: Task has been dequeued and is being sent to a worker to start executing.
- `EXECUTING`: Task is currently being executed by a worker
- `WAITING`: Task has been paused by the system, and will be resumed by the system
- `COMPLETED`: Task has been completed successfully
- `CANCELED`: Task has been canceled by the user
- `FAILED`: Task has failed to complete, due to an error in the system
- `CRASHED`: Task has crashed and won't be retried, most likely the worker ran out of resources, e.g. memory or storage
- `SYSTEM_FAILURE`: Task has failed to complete, due to an error in the system
- `DELAYED`: Task has been scheduled to run at a specific time
- `EXPIRED`: Task has expired and won't be executed
- `TIMED_OUT`: Task has reached it's maxDuration and has been stopped
We've removed the following statuses:
- `WAITING_FOR_DEPLOY`: This is no longer used, and is replaced by `PENDING_VERSION`
- `FROZEN`: This is no longer used, and is replaced by `WAITING`
- `INTERRUPTED`: This is no longer used
- `REATTEMPTING`: This is no longer used, and is replaced by `EXECUTING`
We've also added "boolean" helpers to runs returned via the API and from Realtime:
- `isQueued`: Returns true when the status is `QUEUED`, `PENDING_VERSION`, or `DELAYED`
- `isExecuting`: Returns true when the status is `EXECUTING`, `DEQUEUED`. These count against your concurrency limits.
- `isWaiting`: Returns true when the status is `WAITING`. These do not count against your concurrency limits.
- `isCompleted`: Returns true when the status is any of the completed statuses.
- `isCanceled`: Returns true when the status is `CANCELED`
- `isFailed`: Returns true when the status is any of the failed statuses.
- `isSuccess`: Returns true when the status is `COMPLETED`
This change adds the ability to easily detect which runs are being counted against your concurrency limit by filtering for both `EXECUTING` or `DEQUEUED`.
-5
View File
@@ -1,5 +0,0 @@
---
"trigger.dev": patch
---
Init command will now correctly install v4-beta packages
-5
View File
@@ -1,5 +0,0 @@
---
"trigger.dev": patch
---
Fix metadata collapsing correctness
-5
View File
@@ -1,5 +0,0 @@
---
"trigger.dev": patch
---
Improve warm start times by eagerly creating the child TaskRunProcess when a previous run as completed
-11
View File
@@ -1,11 +0,0 @@
---
"trigger.dev": patch
"@trigger.dev/core": patch
---
- Resolve issue where CLI could get stuck during deploy finalization
- Unify local and remote build logic, with multi-platform build support
- Improve switch command; now accepts profile name as an argument
- Registry configuration is now fully managed by the webapp
- The deploy `--self-hosted` flag is no longer required
- Enhance deployment error reporting and image digest retrieval
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/redis-worker": patch
---
Now each worker gets it's own pLimit concurrency limiter, and we will only ever dequeue items where there is concurrency capacity, preventing incorrectly retried jobs due to visibility timeout expiry
-5
View File
@@ -1,5 +0,0 @@
---
"trigger.dev": patch
---
Fix init.ts detection when using the sentry esbuild plugin
-8
View File
@@ -1,8 +0,0 @@
---
"trigger.dev": patch
"@trigger.dev/core": patch
---
- Correctly resolve waitpoints that come in early
- Ensure correct state before requesting suspension
- Fix race conditions in snapshot processing
-5
View File
@@ -1,5 +0,0 @@
---
"trigger.dev": patch
---
experimental processKeepAlive
-5
View File
@@ -1,5 +0,0 @@
---
"trigger.dev": patch
---
Fixes runLimiter check on #dequeueRuns
-5
View File
@@ -1,5 +0,0 @@
---
"trigger.dev": patch
---
Update nypm package to support test-based bun.lock files
-104
View File
@@ -1,104 +0,0 @@
{
"mode": "pre",
"tag": "v4-beta",
"initialVersions": {
"coordinator": "0.0.1",
"docker-provider": "0.0.1",
"kubernetes-provider": "0.0.1",
"supervisor": "0.0.1",
"webapp": "1.0.0",
"@trigger.dev/build": "3.3.17",
"trigger.dev": "3.3.17",
"@trigger.dev/core": "3.3.17",
"@trigger.dev/python": "3.3.17",
"@trigger.dev/react-hooks": "3.3.17",
"@trigger.dev/redis-worker": "3.3.17",
"@trigger.dev/rsc": "3.3.17",
"@trigger.dev/sdk": "3.3.17"
},
"changesets": [
"beige-horses-juggle",
"big-carrots-fail",
"big-garlics-own",
"blue-eyes-tickle",
"breezy-turtles-talk",
"chatty-snakes-hope",
"chilled-weeks-switch",
"clean-beans-compete",
"cuddly-boats-press",
"curvy-dogs-share",
"cyan-news-design",
"early-points-jam",
"eight-ligers-help",
"eighty-rings-divide",
"empty-dolls-judge",
"fifty-beers-bake",
"flat-pianos-live",
"four-needles-add",
"fuzzy-snakes-beg",
"gentle-waves-suffer",
"giant-plums-smash",
"gold-insects-invite",
"green-lions-relate",
"grumpy-wasps-fold",
"healthy-apricots-drop",
"hip-cups-wave",
"honest-files-decide",
"itchy-frogs-care",
"itchy-games-sort",
"late-chairs-ring",
"late-dancers-smile",
"lazy-panthers-shop",
"lazy-plums-fetch",
"light-peas-melt",
"little-birds-appear",
"moody-squids-count",
"nasty-cobras-wonder",
"nice-colts-boil",
"ninety-games-grow",
"orange-pens-smile",
"orange-rocks-grow",
"plenty-dolphins-act",
"polite-badgers-suffer",
"polite-impalas-care",
"polite-lies-fix",
"rare-beds-accept",
"rare-mails-fail",
"real-rats-drop",
"red-chairs-begin",
"red-rings-marry",
"red-wasps-cover",
"shiny-kiwis-beam",
"silly-cows-serve",
"silly-timers-repair",
"sixty-beers-share",
"small-birds-arrive",
"small-dancers-smell",
"smart-coins-hammer",
"smooth-planets-flow",
"soft-candles-do",
"sour-mirrors-accept",
"spotty-ducks-punch",
"spotty-pants-wink",
"strong-pianos-provide",
"sweet-dolphins-invent",
"tender-jobs-collect",
"thick-bikes-laugh",
"tidy-books-smell",
"tiny-buckets-teach",
"tricky-houses-invite",
"twelve-actors-hide",
"two-eagles-report",
"two-tigers-dream",
"weak-jobs-hide",
"weak-parents-sip",
"wet-deers-think",
"wet-dragons-boil",
"wet-steaks-reflect",
"wicked-ads-walk",
"wild-mirrors-return",
"witty-cherries-tan",
"witty-donkeys-unite",
"yellow-cars-tell"
]
}
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/core": patch
---
Add verbose structured log level
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/build": patch
---
Add Lightpanda extension
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/sdk": patch
---
Add onCancel lifecycle hook
-5
View File
@@ -1,5 +0,0 @@
---
"trigger.dev": patch
---
Added AI assistance link when you have build errors
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/sdk": patch
---
Provide realtime skipColumns option via untamperable public access tokens
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/core": patch
---
Suppress external instrumentation for fetch calls from ApiClient
-5
View File
@@ -1,5 +0,0 @@
---
"trigger.dev": patch
---
Handle flush errors gracefully in dev
-5
View File
@@ -1,5 +0,0 @@
---
"trigger.dev": patch
---
Added support for Preview branches in v4 projects
-5
View File
@@ -1,5 +0,0 @@
---
"trigger.dev": patch
---
Can now set project ref using the TRIGGER_PROJECT_REF env var
-6
View File
@@ -1,6 +0,0 @@
---
"trigger.dev": patch
"@trigger.dev/core": patch
---
Add runtime version detection for display in the dashboard
-16
View File
@@ -1,16 +0,0 @@
---
"@trigger.dev/react-hooks": patch
---
Added the ability to specify a "createdAt" filter when subscribing to tags in our useRealtime hooks:
```tsx
// Only subscribe to runs created in the last 10 hours
useRealtimeRunWithTags("my-tag", { createdAt: "10h" })
```
You can also now choose to skip subscribing to specific columns by specifying the `skipColumns` option:
```tsx
useRealtimeRun(run.id, { skipColumns: ["usageDurationMs"] });
```
-6
View File
@@ -1,6 +0,0 @@
---
"@trigger.dev/sdk": patch
"@trigger.dev/core": patch
---
Improve metadata flushing efficiency by collapsing operations
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/core": patch
---
fix: Realtime streams: prevent enqueuing into closed ReadableStream
-5
View File
@@ -1,5 +0,0 @@
---
"trigger.dev": patch
---
Update profile switcher
-5
View File
@@ -1,5 +0,0 @@
---
"trigger.dev": patch
---
Update base images to latest compatible versions. The `node-22` runtime now uses v22.16.0 and `bun` uses the latest v1.2.18 release. The default `node` runtime is unchanged and points at v21.7.3.
-6
View File
@@ -1,6 +0,0 @@
---
"trigger.dev": patch
"@trigger.dev/core": patch
---
Improve usage flushing
-5
View File
@@ -1,5 +0,0 @@
---
"trigger.dev": patch
---
fix: default machine config indexing now works
-6
View File
@@ -1,6 +0,0 @@
---
"trigger.dev": patch
"@trigger.dev/core": patch
---
Prevent large outputs from overwriting each other
-6
View File
@@ -1,6 +0,0 @@
---
"trigger.dev": patch
"@trigger.dev/core": patch
---
Fail fast in CI when running deploy with missing `TRIGGER_ACCESS_TOKEN` and add useful error message with link to docs
-5
View File
@@ -1,5 +0,0 @@
---
"trigger.dev": patch
---
Always print full deploy logs in CI
-5
View File
@@ -1,5 +0,0 @@
---
"trigger.dev": patch
---
TriggerApiError 4xx errors will no longer cause tasks to be retried
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/build": patch
---
Add ffmpeg v7 support to existing extension: `ffmpeg({ version: "7" })`
-8
View File
@@ -1,8 +0,0 @@
---
"trigger.dev": patch
"@trigger.dev/core": patch
---
- Fix polling interval reset bug that could create duplicate intervals
- Protect against unexpected attempt number changes
- Prevent run execution zombies after warm starts
-5
View File
@@ -1,5 +0,0 @@
---
"trigger.dev": patch
---
Fix stalled run detection
-6
View File
@@ -1,6 +0,0 @@
---
"trigger.dev": patch
"@trigger.dev/core": patch
---
Managed run controller performance and reliability improvements
-5
View File
@@ -1,5 +0,0 @@
---
"trigger.dev": patch
---
Fix init.ts auto-import for deployed workers
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/sdk": patch
---
Added runs.list filtering for queue and machine
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/sdk": patch
---
maintain proper context in metadata.root and parent getters
-7
View File
@@ -1,7 +0,0 @@
---
"@trigger.dev/sdk": patch
"trigger.dev": patch
"@trigger.dev/core": patch
---
v4: New lifecycle hooks
-5
View File
@@ -1,5 +0,0 @@
---
"trigger.dev": patch
---
Output esbuild metafile, can be inspected after `deploy --dry run`
-6
View File
@@ -1,6 +0,0 @@
---
"trigger.dev": patch
"@trigger.dev/core": patch
---
Fix QUEUED status snapshot handler
-5
View File
@@ -1,5 +0,0 @@
---
"trigger.dev": patch
---
Serialize metadata to prevent invalid metadata from breaking run completions
-5
View File
@@ -1,5 +0,0 @@
---
"trigger.dev": patch
---
If you pass a directory when calling deploy we validate it exists and give helpful hints
-6
View File
@@ -1,6 +0,0 @@
---
"@trigger.dev/react-hooks": patch
"@trigger.dev/core": patch
---
Fixes an issue with realtime when re-subscribing to a run, that would temporarily display stale data and the changes. Now when re-subscribing to a run only the latest changes will be vended
-6
View File
@@ -1,6 +0,0 @@
---
"trigger.dev": patch
"@trigger.dev/core": patch
---
Expose esbuild `minify` option (experimental)
-5
View File
@@ -1,5 +0,0 @@
---
"trigger.dev": patch
---
Fix `syncEnvVars` for non-preview deployments
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/build": patch
---
Add playwright extension
-5
View File
@@ -1,5 +0,0 @@
---
"@trigger.dev/core": patch
---
Fix Bun runtime path resolution fallback
+2 -6
View File
@@ -1,7 +1,3 @@
{
"mcpServers": {
"trigger.dev": {
"url": "http://localhost:3333/sse"
}
}
}
"mcpServers": {}
}
+6 -3
View File
@@ -6,7 +6,7 @@ alwaysApply: false
The main trigger.dev webapp, which powers it's API and dashboard and makes up the docker image that is produced as an OSS image, is a Remix 2.1.0 app that uses an express server, written in TypeScript. The following subsystems are either included in the webapp or are used by the webapp in another part of the monorepo:
- `@trigger.dev/database` exports a Prisma 5.4.1 client that is used extensively in the webapp to access a PostgreSQL instance. The schema file is [schema.prisma](mdc:internal-packages/database/prisma/schema.prisma)
- `@trigger.dev/database` exports a Prisma 6.14.0 client that is used extensively in the webapp to access a PostgreSQL instance. The schema file is [schema.prisma](mdc:internal-packages/database/prisma/schema.prisma)
- `@trigger.dev/core` is a published package and is used to share code between the `@trigger.dev/sdk` and the webapp. It includes functionality but also a load of Zod schemas for data validation. When importing from `@trigger.dev/core` in the webapp, we never import the root `@trigger.dev/core` path, instead we favor one of the subpath exports that you can find in [package.json](mdc:packages/core/package.json)
- `@internal/run-engine` has all the code needed to trigger a run and take it through it's lifecycle to completion.
- `@trigger.dev/redis-worker` is a custom redis based background job/worker system that's used in the webapp and also used inside the run engine.
@@ -31,7 +31,10 @@ We originally the Trigger.dev "Run Engine" not as a single system, but just spre
- The batch trigger API endpoint is [api.v1.tasks.batch.ts](mdc:apps/webapp/app/routes/api.v1.tasks.batch.ts)
- Setup code for the prisma client is in [db.server.ts](mdc:apps/webapp/app/db.server.ts)
- The run engine is configured in [runEngine.server.ts](mdc:apps/webapp/app/v3/runEngine.server.ts)
- All the "services" that are found in app/v3/services/**/*.server.ts
- All the "services" that are found in app/v3/services/\*_/_.server.ts
- The code for the TaskEvent data, which is the otel data sent from tasks to our servers, is in both the [eventRepository.server.ts](mdc:apps/webapp/app/v3/eventRepository.server.ts) and also the [otlpExporter.server.ts](mdc:apps/webapp/app/v3/otlpExporter.server.ts). The otel endpoints which are hit from production and development otel exporters is [otel.v1.logs.ts](mdc:apps/webapp/app/routes/otel.v1.logs.ts) and [otel.v1.traces.ts](mdc:apps/webapp/app/routes/otel.v1.traces.ts)
- We use "presenters" to move more complex loader code into a class, and you can find those are app/v3/presenters/**/*.server.ts
- We use "presenters" to move more complex loader code into a class, and you can find those are app/v3/presenters/\*_/_.server.ts
- All the "services" that are found in app/v3/services/\*_/_.server.ts
- The code for the TaskEvent data, which is the otel data sent from tasks to our servers, is in both the [eventRepository.server.ts](mdc:apps/webapp/app/v3/eventRepository.server.ts) and also the [otlpExporter.server.ts](mdc:apps/webapp/app/v3/otlpExporter.server.ts). The otel endpoints which are hit from production and development otel exporters is [otel.v1.logs.ts](mdc:apps/webapp/app/routes/otel.v1.logs.ts) and [otel.v1.traces.ts](mdc:apps/webapp/app/routes/otel.v1.traces.ts)
- We use "presenters" to move more complex loader code into a class, and you can find those are app/v3/presenters/\*_/_.server.ts
-22
View File
@@ -431,28 +431,6 @@ export async function POST(request: Request) {
}
```
### tasks.triggerAndPoll()
Triggers a task and polls until completion. Not recommended for web requests as it blocks until the run completes. Consider using Realtime docs for better alternatives.
```ts
import { tasks } from "@trigger.dev/sdk/v3";
import type { emailSequence } from "~/trigger/emails";
export async function POST(request: Request) {
const data = await request.json();
const result = await tasks.triggerAndPoll<typeof emailSequence>(
"email-sequence",
{
to: data.email,
name: data.name,
},
{ pollIntervalMs: 5000 }
);
return Response.json(result);
}
```
### batch.trigger()
Triggers multiple runs of different tasks at once, useful when you need to execute multiple tasks simultaneously.
+2
View File
@@ -16,6 +16,8 @@
**/dist
**/node_modules
**/generated/prisma
apps/webapp/build
apps/webapp/public/build
+1 -1
View File
@@ -85,7 +85,7 @@ jobs:
DIRECT_URL: postgresql://postgres:postgres@localhost:5432/postgres
SESSION_SECRET: "secret"
MAGIC_LINK_SECRET: "secret"
ENCRYPTION_KEY: "secret"
ENCRYPTION_KEY: "dummy-encryption-keeeey-32-bytes"
DEPLOY_REGISTRY_HOST: "docker.io"
CLICKHOUSE_URL: "http://default:password@localhost:8123"
+2 -1
View File
@@ -63,4 +63,5 @@ apps/**/public/build
/packages/core/src/package.json
/packages/trigger-sdk/src/package.json
/packages/python/src/package.json
.claude
.claude
.mcp.log
+5 -5
View File
@@ -59,7 +59,7 @@
"request": "launch",
"name": "Debug V3 Dev CLI",
"command": "pnpm exec trigger dev",
"cwd": "${workspaceFolder}/references/v3-catalog",
"cwd": "${workspaceFolder}/references/hello-world",
"sourceMaps": true
},
{
@@ -83,7 +83,7 @@
"request": "launch",
"name": "Debug V3 Deploy CLI",
"command": "pnpm exec trigger deploy --self-hosted --load-image",
"cwd": "${workspaceFolder}/references/v3-catalog",
"cwd": "${workspaceFolder}/references/hello-world",
"sourceMaps": true
},
{
@@ -91,7 +91,7 @@
"request": "launch",
"name": "Debug V3 list-profiles CLI",
"command": "pnpm exec trigger list-profiles --log-level debug",
"cwd": "${workspaceFolder}/references/v3-catalog",
"cwd": "${workspaceFolder}/references/hello-world",
"sourceMaps": true
},
{
@@ -99,7 +99,7 @@
"request": "launch",
"name": "Debug V3 update CLI",
"command": "pnpm exec trigger update",
"cwd": "${workspaceFolder}/references/v3-catalog",
"cwd": "${workspaceFolder}/references/hello-world",
"sourceMaps": true
},
{
@@ -107,7 +107,7 @@
"request": "launch",
"name": "Debug V3 Management",
"command": "pnpm run management",
"cwd": "${workspaceFolder}/references/v3-catalog",
"cwd": "${workspaceFolder}/references/hello-world",
"sourceMaps": true
},
{
+15 -70
View File
@@ -84,17 +84,17 @@ branch are tagged into a release periodically.
2. Once the app is running click the magic link button and enter your email. You will automatically be logged in, since you are running locally. Create an Org and your first project in the dashboard.
## Manual testing using v3-catalog
## Manual testing using hello-world
We use the `<root>/references/v3-catalog` subdirectory as a staging ground for testing changes to the SDK (`@trigger.dev/sdk` at `<root>/packages/trigger-sdk`), the Core package (`@trigger.dev/core` at `<root>packages/core`), the CLI (`trigger.dev` at `<root>/packages/cli-v3`) and the platform (The remix app at `<root>/apps/webapp`). The instructions below will get you started on using the `v3-catalog` for local development of Trigger.dev (v3).
We use the `<root>/references/hello-world` subdirectory as a staging ground for testing changes to the SDK (`@trigger.dev/sdk` at `<root>/packages/trigger-sdk`), the Core package (`@trigger.dev/core` at `<root>packages/core`), the CLI (`trigger.dev` at `<root>/packages/cli-v3`) and the platform (The remix app at `<root>/apps/webapp`). The instructions below will get you started on using the `hello-world` for local development of Trigger.dev.
### First-time setup
First, make sure you are running the webapp according to the instructions above. Then:
1. Visit http://localhost:3030 in your browser and create a new V3 project called "v3-catalog".
1. Visit http://localhost:3030 in your browser and create a new V3 project called "hello-world".
2. In Postgres go to the "Projects" table and for the project you create change the `externalRef` to `yubjwjsfkxnylobaqvqz`.
2. In Postgres go to the "Projects" table and for the project you create change the `externalRef` to `proj_rrkpdguyagvsoktglnod`.
3. Build the CLI
@@ -105,10 +105,10 @@ pnpm run build --filter trigger.dev
pnpm i
```
4. Change into the `<root>/references/v3-catalog` directory and authorize the CLI to the local server:
4. Change into the `<root>/references/hello-world` directory and authorize the CLI to the local server:
```sh
cd references/v3-catalog
cd references/hello-world
cp .env.example .env
pnpm exec trigger login -a http://localhost:3030
```
@@ -118,7 +118,7 @@ This will open a new browser window and authorize the CLI against your local use
You can optionally pass a `--profile` flag to the `login` command, which will allow you to use the CLI with separate accounts/servers. We suggest using a profile called `local` for your local development:
```sh
cd references/v3-catalog
cd references/hello-world
pnpm exec trigger login -a http://localhost:3030 --profile local
# later when you run the dev or deploy command:
pnpm exec trigger dev --profile local
@@ -137,84 +137,29 @@ The following steps should be followed any time you start working on a new featu
pnpm run dev --filter trigger.dev --filter "@trigger.dev/*"
```
3. Open another terminal window, and change into the `<root>/references/v3-catalog` directory.
3. Open another terminal window, and change into the `<root>/references/hello-world` directory.
4. You'll need to run the following commands to setup prisma and migrate the database:
4. Run the `dev` command, which will register all the local tasks with the platform and allow you to start testing task execution:
```sh
pnpm exec prisma migrate deploy
pnpm run generate:prisma
```
5. Run the `dev` command, which will register all the local tasks with the platform and allow you to start testing task execution:
```sh
# in <root>/references/v3-catalog
# in <root>/references/hello-world
pnpm exec trigger dev
```
If you want additional debug logging, you can use the `--log-level debug` flag:
```sh
# in <root>/references/v3-catalog
# in <root>/references/hello-world
pnpm exec trigger dev --log-level debug
```
6. If you make any changes in the CLI/Core/SDK, you'll need to `CTRL+C` to exit the `dev` command and restart it to pickup changes. Any changes to the files inside of the `v3-catalog/src/trigger` dir will automatically be rebuilt by the `dev` command.
6. If you make any changes in the CLI/Core/SDK, you'll need to `CTRL+C` to exit the `dev` command and restart it to pickup changes. Any changes to the files inside of the `hello-world/src/trigger` dir will automatically be rebuilt by the `dev` command.
7. Navigate to the `v3-catalog` project in your local dashboard at localhost:3030 and you should see the list of tasks.
7. Navigate to the `hello-world` project in your local dashboard at localhost:3030 and you should see the list of tasks.
8. Go to the "Test" page in the sidebar and select a task. Then enter a payload and click "Run test". You can tell what the payloads should be by looking at the relevant task file inside the `/references/v3-catalog/src/trigger` folder. Many of them accept an empty payload.
8. Go to the "Test" page in the sidebar and select a task. Then enter a payload and click "Run test". You can tell what the payloads should be by looking at the relevant task file inside the `/references/hello-world/src/trigger` folder. Many of them accept an empty payload.
9. Feel free to add additional files in `v3-catalog/src/trigger` to test out specific aspects of the system, or add in edge cases.
## Running end-to-end webapp tests (deprecated)
To run the end-to-end tests, follow the steps below:
1. Set up environment variables (copy example envs into the correct place)
```sh
cp ./.env.example ./.env
cp ./references/nextjs-test/.env.example ./references/nextjs-test/.env.local
```
2. Set up dependencies
```sh
# Build packages
pnpm run build --filter @references/nextjs-test^...
pnpm --filter @trigger.dev/database generate
# Move trigger-cli bin to correct place
pnpm install --frozen-lockfile
# Install playwrite browsers (ONE TIME ONLY)
npx playwright install
```
3. Set up the database
```sh
pnpm run docker
pnpm run db:migrate
pnpm run db:seed
```
4. Run the end-to-end tests
```sh
pnpm run test:e2e
```
### Cleanup
The end-to-end tests use a `setup` and `teardown` script to seed the database with test data. If the test runner doesn't exit cleanly, then the database can be left in a state where the tests can't run because the `setup` script will try to create data that already exists. If this happens, you can manually delete the `users` and `organizations` from the database using prisma studio:
```sh
# With the database running (i.e. pnpm run docker)
pnpm run db:studio
```
9. Feel free to add additional files in `hello-world/src/trigger` to test out specific aspects of the system, or add in edge cases.
## Adding and running migrations
+67 -31
View File
@@ -1,44 +1,75 @@
<div align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://imagedelivery.net/3TbraffuDZ4aEf8KWOmI_w/a45d1fa2-0ae8-4a39-4409-f4f934bfae00/public">
<source media="(prefers-color-scheme: light)" srcset="https://imagedelivery.net/3TbraffuDZ4aEf8KWOmI_w/3f5ad4c1-c4c8-4277-b622-290e7f37bd00/public">
<img alt="Trigger.dev logo" src="https://imagedelivery.net/3TbraffuDZ4aEf8KWOmI_w/a45d1fa2-0ae8-4a39-4409-f4f934bfae00/public">
</picture>
### Open source background jobs and AI infrastructure
[Discord](https://trigger.dev/discord) | [Website](https://trigger.dev) | [Issues](https://github.com/triggerdotdev/trigger.dev/issues) | [Docs](https://trigger.dev/docs)
![Trigger.dev logo](https://content.trigger.dev/github-header-banner.jpg)
[![Twitter](https://img.shields.io/twitter/url/https/twitter.com/triggerdotdev.svg?style=social&label=Follow%20%40trigger.dev)](https://twitter.com/triggerdotdev)
### Build and deploy fullymanaged AI agents and workflows
[Website](https://trigger.dev) | [Docs](https://trigger.dev/docs) | [Issues](https://github.com/triggerdotdev/trigger.dev/issues) | [Feature requests](https://triggerdev.featurebase.app/) | [Public roadmap](https://triggerdev.featurebase.app/roadmap) | [Self-hosting](https://trigger.dev/docs/self-hosting/overview)
[![Open Source](https://img.shields.io/badge/Open%20Source-%E2%9D%A4-red.svg)](https://github.com/triggerdotdev/trigger.dev)
[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://github.com/triggerdotdev/trigger.dev/blob/main/LICENSE)
[![npm](https://img.shields.io/npm/v/@trigger.dev/sdk.svg?label=npm)](https://www.npmjs.com/package/@trigger.dev/sdk)
[![SDK downloads](https://img.shields.io/npm/dm/@trigger.dev/sdk.svg?label=SDK%20downloads)](https://www.npmjs.com/package/@trigger.dev/sdk)
[![Twitter Follow](https://img.shields.io/twitter/follow/triggerdotdev?style=social)](https://twitter.com/triggerdotdev)
[![Discord](https://img.shields.io/discord/1066956501299777596?logo=discord&logoColor=white&color=7289da)](https://discord.gg/nkqV9xBYWy)
[![GitHub stars](https://img.shields.io/github/stars/triggerdotdev/trigger.dev?style=social)](https://github.com/triggerdotdev/trigger.dev)
</div>
## About Trigger.dev
Trigger.dev is an open source platform and SDK which allows you to create long-running background jobs. Write normal async code, deploy, and never hit a timeout.
Trigger.dev is the open-source platform for building AI workflows in TypeScript. Long-running tasks with retries, queues, observability, and elastic scaling.
### Key features:
## The platform designed for building AI agents
- JavaScript and TypeScript SDK
- No timeouts
- Retries (with exponential backoff)
- Queues and concurrency controls
- Schedules and crons
- Full Observability; logs, live trace views, advanced filtering
- React hooks to interact with the Trigger API from your React app
- Pipe LLM streams straight to your users through the Realtime API
- Trigger tasks and display the run status and metadata anywhere in your app
- Custom alerts, get notified by email, Slack or webhooks
- No infrastructure to manage
- Elastic (scaling)
- Works with your existing tech stack
Build [AI agents](https://trigger.dev/product/ai-agents) using all the frameworks, services and LLMs you're used to, deploy them to Trigger.dev and get durable, long-running tasks with retries, queues, observability, and elastic scaling out of the box.
## In your codebase
- **Long-running without timeouts**: Execute your tasks with absolutely no timeouts, unlike AWS Lambda, Vercel, and other serverless platforms.
- **Durability, retries & queues**: Build rock solid agents and AI applications using our durable tasks, retries, queues and idempotency.
- **True runtime freedom**: Customize your deployed tasks with system packages run browsers, Python scripts, FFmpeg and more.
- **Human-in-the-loop**: Programmatically pause your tasks until a human can approve, reject or give feedback.
- **Realtime apps & streaming**: Move your background jobs to the foreground by subscribing to runs or streaming AI responses to your app.
- **Observability & monitoring**: Each run has full tracing and logs. Configure error alerts to catch bugs fast.
## Key features:
- **[JavaScript and TypeScript SDK](https://trigger.dev/docs/tasks/overview)** - Build background tasks using familiar programming models
- **[Long-running tasks](https://trigger.dev/docs/runs/max-duration)** - Handle resource-heavy tasks without timeouts
- **[Durable cron schedules](https://trigger.dev/docs/tasks/scheduled#scheduled-tasks-cron)** - Create and attach recurring schedules of up to a year
- **[Trigger.dev Realtime](https://trigger.dev/docs/realtime/overview)** - Trigger, subscribe to, and get real-time updates for runs, with LLM streaming support
- **[Build extensions](https://trigger.dev/docs/config/extensions/overview#build-extensions)** - Hook directly into the build system and customize the build process. Run Python scripts, FFmpeg, browsers, and more.
- **[React hooks](https://trigger.dev/docs/frontend/react-hooks#react-hooks)** - Interact with the Trigger.dev API on your frontend using our React hooks package
- **[Batch triggering](https://trigger.dev/docs/triggering#tasks-batchtrigger)** - Use batchTrigger() to initiate multiple runs of a task with custom payloads and options
- **[Structured inputs / outputs](https://trigger.dev/docs/tasks/schemaTask#schematask)** - Define precise data schemas for your tasks with runtime payload validation
- **[Waits](https://trigger.dev/docs/wait)** - Add waits to your tasks to pause execution for a specified duration
- **[Preview branches](https://trigger.dev/docs/deployment/preview-branches)** - Create isolated environments for testing and development. Integrates with Vercel and git workflows
- **[Waitpoints](https://trigger.dev/docs/wait-for-token#wait-for-token)** - Add human-in-the-loop judgment at critical decision points without disrupting workflow
- **[Concurrency & queues](https://trigger.dev/docs/queue-concurrency#concurrency-and-queues)** - Set concurrency rules to manage how multiple tasks execute
- **[Multiple environments](https://trigger.dev/docs/how-it-works#dev-mode)** - Support for DEV, PREVIEW, STAGING, and PROD environments
- **[No infrastructure to manage](https://trigger.dev/docs/how-it-works#trigger-dev-architecture)** - Auto-scaling infrastructure that eliminates timeouts and server management
- **[Automatic retries](https://trigger.dev/docs/errors-retrying)** - If your task encounters an uncaught error, we automatically attempt to run it again
- **[Checkpointing](https://trigger.dev/docs/how-it-works#the-checkpoint-resume-system)** - Tasks are inherently durable, thanks to our checkpointing feature
- **[Versioning](https://trigger.dev/docs/versioning)** - Atomic versioning allows you to deploy new versions without affecting running tasks
- **[Machines](https://trigger.dev/docs/machines)** - Configure the number of vCPUs and GBs of RAM you want the task to use
- **[Observability & monitoring](https://trigger.dev/product/observability-and-monitoring)** - Monitor every aspect of your tasks' performance with comprehensive logging and visualization tools
- **[Logging & tracing](https://trigger.dev/docs/logging)** - Comprehensive logging and tracing for all your tasks
- **[Tags](https://trigger.dev/docs/tags#tags)** - Attach up to ten tags to each run, allowing you to filter via the dashboard, realtime, and the SDK
- **[Run metadata](https://trigger.dev/docs/runs/metadata#run-metadata)** - Attach metadata to runs which updates as the run progresses and is available to use in your frontend for live updates
- **[Bulk actions](https://trigger.dev/docs/bulk-actions)** - Perform actions on multiple runs simultaneously, including replaying and cancelling
- **[Real-time alerts](https://trigger.dev/docs/troubleshooting-alerts#alerts)** - Choose your preferred notification method for run failures and deployments
## Write tasks in your codebase
Create tasks where they belong: in your codebase. Version control, localhost, test and review like you're already used to.
```ts
import { task } from "@trigger.dev/sdk/v3";
import { task } from "@trigger.dev/sdk";
//1. You need to export each task
export const helloWorld = task({
@@ -58,13 +89,13 @@ Use our SDK to write tasks in your codebase. There's no infrastructure to manage
## Environments
We support `Development`, `Staging`, and `Production` environments, allowing you to test your tasks before deploying them to production.
We support `Development`, `Staging`, `Preview`, and `Production` environments, allowing you to test your tasks before deploying them to production.
## Full visibility of every job run
View every task in every run so you can tell exactly what happened. We provide a full trace view of every task run so you can see what happened at every step.
![Trace view image](https://imagedelivery.net/3TbraffuDZ4aEf8KWOmI_w/7c1b347f-004c-4482-38a7-3f6fa9c00d00/public)
![Trace view image](https://content.trigger.dev/trace-view.png)
# Getting started
@@ -73,14 +104,19 @@ The quickest way to get started is to create an account and project in our [web
### Useful links:
- [Quick start](https://trigger.dev/docs/quick-start) - get up and running in minutes
- [How it works](https://trigger.dev/docs/v3/how-it-works) - understand how Trigger.dev works under the hood
- [How it works](https://trigger.dev/docs/how-it-works) - understand how Trigger.dev works under the hood
- [Guides and examples](https://trigger.dev/docs/guides/introduction) - walk-through guides and code examples for popular frameworks and use cases
## Self-hosting
If you prefer to self-host Trigger.dev, you can follow our [self-hosting guide](https://trigger.dev/docs/v3/open-source-self-hosting#overview).
If you prefer to self-host Trigger.dev, you can follow our [self-hosting guides](https://trigger.dev/docs/self-hosting/overview):
We also have a dedicated self-hosting channel in our [Discord server](https://trigger.dev/discord) for support.
- [Docker self-hosting guide](https://trigger.dev/docs/self-hosting/docker) - use Docker Compose to spin up a Trigger.dev instance
- [Kubernetes self-hosting guide](https://trigger.dev/docs/self-hosting/kubernetes) - use our official Helm chart to deploy Trigger.dev to your Kubernetes cluster
## Support and community
We have a large active community in our official [Discord server](https://trigger.dev/discord) for support, including a dedicated channel for self-hosting.
## Development
+2 -1
View File
@@ -13,13 +13,14 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@aws-sdk/client-ecr": "^3.839.0",
"@kubernetes/client-node": "^1.0.0",
"@trigger.dev/core": "workspace:*",
"dockerode": "^4.0.6",
"prom-client": "^15.1.0",
"socket.io": "4.7.4",
"std-env": "^3.8.0",
"zod": "3.23.8"
"zod": "3.25.76"
},
"devDependencies": {
"@types/dockerode": "^3.3.33"
+7 -2
View File
@@ -15,7 +15,7 @@ const Env = z.object({
OTEL_EXPORTER_OTLP_ENDPOINT: z.string().url(), // set on the runners
// Workload API settings (coordinator mode) - the workload API is what the run controller connects to
TRIGGER_WORKLOAD_API_ENABLED: BoolEnv.default("true"),
TRIGGER_WORKLOAD_API_ENABLED: BoolEnv.default(true),
TRIGGER_WORKLOAD_API_PROTOCOL: z
.string()
.transform((s) => z.enum(["http", "https"]).parse(s.toLowerCase()))
@@ -32,7 +32,7 @@ const Env = z.object({
RUNNER_PRETTY_LOGS: BoolEnv.default(false),
// Dequeue settings (provider mode)
TRIGGER_DEQUEUE_ENABLED: BoolEnv.default("true"),
TRIGGER_DEQUEUE_ENABLED: BoolEnv.default(true),
TRIGGER_DEQUEUE_INTERVAL_MS: z.coerce.number().int().default(250),
TRIGGER_DEQUEUE_IDLE_INTERVAL_MS: z.coerce.number().int().default(1000),
TRIGGER_DEQUEUE_MAX_RUN_COUNT: z.coerce.number().int().default(10),
@@ -76,6 +76,11 @@ const Env = z.object({
KUBERNETES_IMAGE_PULL_SECRETS: z.string().optional(), // csv
KUBERNETES_EPHEMERAL_STORAGE_SIZE_LIMIT: z.string().default("10Gi"),
KUBERNETES_EPHEMERAL_STORAGE_SIZE_REQUEST: z.string().default("2Gi"),
KUBERNETES_STRIP_IMAGE_DIGEST: BoolEnv.default(false),
// Placement tags settings
PLACEMENT_TAGS_ENABLED: BoolEnv.default(false),
PLACEMENT_TAGS_PREFIX: z.string().default("node.cluster.x-k8s.io"),
// Metrics
METRICS_ENABLED: BoolEnv.default(true),
+6 -1
View File
@@ -3,7 +3,7 @@ import { SimpleStructuredLogger } from "@trigger.dev/core/v3/utils/structuredLog
const logger = new SimpleStructuredLogger("env-util");
export const BoolEnv = z.preprocess((val) => {
const baseBoolEnv = z.preprocess((val) => {
if (typeof val !== "string") {
return val;
}
@@ -11,6 +11,11 @@ export const BoolEnv = z.preprocess((val) => {
return ["true", "1"].includes(val.toLowerCase().trim());
}, z.boolean());
// Create a type-safe version that only accepts boolean defaults
export const BoolEnv = baseBoolEnv as Omit<typeof baseBoolEnv, "default"> & {
default: (value: boolean) => z.ZodDefault<typeof baseBoolEnv>;
};
export const AdditionalEnvVars = z.preprocess((val) => {
if (typeof val !== "string") {
return val;
+1
View File
@@ -247,6 +247,7 @@ class ManagedSupervisor {
nextAttemptNumber: message.run.attemptNumber,
snapshotId: message.snapshot.id,
snapshotFriendlyId: message.snapshot.friendlyId,
placementTags: message.placementTags,
});
// Disabled for now
+34 -4
View File
@@ -8,14 +8,16 @@ import { env } from "../env.js";
import { getDockerHostDomain, getRunnerId, normalizeDockerHostUrl } from "../util.js";
import Docker from "dockerode";
import { tryCatch } from "@trigger.dev/core";
import { ECRAuthService } from "./ecrAuth.js";
export class DockerWorkloadManager implements WorkloadManager {
private readonly logger = new SimpleStructuredLogger("docker-workload-manager");
private readonly docker: Docker;
private readonly runnerNetworks: string[];
private readonly auth?: Docker.AuthConfig;
private readonly staticAuth?: Docker.AuthConfig;
private readonly platformOverride?: string;
private readonly ecrAuthService?: ECRAuthService;
constructor(private opts: WorkloadManagerOptions) {
this.docker = new Docker({
@@ -44,13 +46,18 @@ export class DockerWorkloadManager implements WorkloadManager {
url: env.DOCKER_REGISTRY_URL,
});
this.auth = {
this.staticAuth = {
username: env.DOCKER_REGISTRY_USERNAME,
password: env.DOCKER_REGISTRY_PASSWORD,
serveraddress: env.DOCKER_REGISTRY_URL,
};
} else if (ECRAuthService.hasAWSCredentials()) {
this.logger.info("🐋 AWS credentials found, initializing ECR auth service");
this.ecrAuthService = new ECRAuthService();
} else {
this.logger.warn("🐋 No Docker registry credentials provided, skipping auth");
this.logger.warn(
"🐋 No Docker registry credentials or AWS credentials provided, skipping auth"
);
}
}
@@ -160,9 +167,12 @@ export class DockerWorkloadManager implements WorkloadManager {
imageArchitecture: inspectResult?.Architecture,
});
// Get auth config (static or ECR)
const authConfig = await this.getAuthConfig();
// Ensure the image is present
const [createImageError, imageResponseReader] = await tryCatch(
this.docker.createImage(this.auth, {
this.docker.createImage(authConfig, {
fromImage: imageRef,
...(this.platformOverride ? { platform: this.platformOverride } : {}),
})
@@ -216,6 +226,26 @@ export class DockerWorkloadManager implements WorkloadManager {
logger.debug("create succeeded", { startResult, containerId: container.id });
}
/**
* Get authentication config for Docker operations
* Uses static credentials if available, otherwise attempts ECR auth
*/
private async getAuthConfig(): Promise<Docker.AuthConfig | undefined> {
// Use static credentials if available
if (this.staticAuth) {
return this.staticAuth;
}
// Use ECR auth if service is available
if (this.ecrAuthService) {
const ecrAuth = await this.ecrAuthService.getAuthConfig();
return ecrAuth || undefined;
}
// No auth available
return undefined;
}
private async attachContainerToNetworks({
containerId,
networkNames,
@@ -0,0 +1,144 @@
import { ECRClient, GetAuthorizationTokenCommand } from "@aws-sdk/client-ecr";
import { SimpleStructuredLogger } from "@trigger.dev/core/v3/utils/structuredLogger";
import { tryCatch } from "@trigger.dev/core";
import Docker from "dockerode";
interface ECRTokenCache {
token: string;
username: string;
serverAddress: string;
expiresAt: Date;
}
export class ECRAuthService {
private readonly logger = new SimpleStructuredLogger("ecr-auth-service");
private readonly ecrClient: ECRClient;
private tokenCache: ECRTokenCache | null = null;
constructor() {
this.ecrClient = new ECRClient();
this.logger.info("🔐 ECR Auth Service initialized", {
region: this.ecrClient.config.region,
});
}
/**
* Check if we have AWS credentials configured
*/
static hasAWSCredentials(): boolean {
if (process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY) {
return true;
}
if (
process.env.AWS_PROFILE ||
process.env.AWS_ROLE_ARN ||
process.env.AWS_WEB_IDENTITY_TOKEN_FILE
) {
return true;
}
return false;
}
/**
* Check if the current token is still valid with a 10-minute buffer
*/
private isTokenValid(): boolean {
if (!this.tokenCache) {
return false;
}
const now = new Date();
const bufferMs = 10 * 60 * 1000; // 10 minute buffer before expiration
return now < new Date(this.tokenCache.expiresAt.getTime() - bufferMs);
}
/**
* Get a fresh ECR authorization token from AWS
*/
private async fetchNewToken(): Promise<ECRTokenCache | null> {
const [error, response] = await tryCatch(
this.ecrClient.send(new GetAuthorizationTokenCommand({}))
);
if (error) {
this.logger.error("Failed to get ECR authorization token", { error });
return null;
}
const authData = response.authorizationData?.[0];
if (!authData?.authorizationToken || !authData.proxyEndpoint) {
this.logger.error("Invalid ECR authorization response", { authData });
return null;
}
// Decode the base64 token to get username:password
const decoded = Buffer.from(authData.authorizationToken, "base64").toString("utf-8");
const [username, password] = decoded.split(":", 2);
if (!username || !password) {
this.logger.error("Failed to parse ECR authorization token");
return null;
}
const expiresAt = authData.expiresAt || new Date(Date.now() + 12 * 60 * 60 * 1000); // Default 12 hours
const tokenCache: ECRTokenCache = {
token: password,
username,
serverAddress: authData.proxyEndpoint,
expiresAt,
};
this.logger.info("🔐 Successfully fetched ECR token", {
username,
serverAddress: authData.proxyEndpoint,
expiresAt: expiresAt.toISOString(),
});
return tokenCache;
}
/**
* Get ECR auth config for Docker operations
* Returns cached token if valid, otherwise fetches a new one
*/
async getAuthConfig(): Promise<Docker.AuthConfig | null> {
// Check if cached token is still valid
if (this.isTokenValid()) {
this.logger.debug("Using cached ECR token");
return {
username: this.tokenCache!.username,
password: this.tokenCache!.token,
serveraddress: this.tokenCache!.serverAddress,
};
}
// Fetch new token
this.logger.info("Fetching new ECR authorization token");
const newToken = await this.fetchNewToken();
if (!newToken) {
return null;
}
// Cache the new token
this.tokenCache = newToken;
return {
username: newToken.username,
password: newToken.token,
serveraddress: newToken.serverAddress,
};
}
/**
* Clear the cached token (useful for testing or forcing refresh)
*/
clearCache(): void {
this.tokenCache = null;
this.logger.debug("ECR token cache cleared");
}
}
@@ -4,7 +4,8 @@ import {
type WorkloadManagerCreateOptions,
type WorkloadManagerOptions,
} from "./types.js";
import type { EnvironmentType, MachinePreset } from "@trigger.dev/core/v3";
import type { EnvironmentType, MachinePreset, PlacementTag } from "@trigger.dev/core/v3";
import { PlacementTagProcessor } from "@trigger.dev/core/v3/serverOnly";
import { env } from "../env.js";
import { type K8sApi, createK8sApi, type k8s } from "../clients/kubernetes.js";
import { getRunnerId } from "../util.js";
@@ -17,9 +18,14 @@ export class KubernetesWorkloadManager implements WorkloadManager {
private readonly logger = new SimpleStructuredLogger("kubernetes-workload-provider");
private k8s: K8sApi;
private namespace = env.KUBERNETES_NAMESPACE;
private placementTagProcessor: PlacementTagProcessor;
constructor(private opts: WorkloadManagerOptions) {
this.k8s = createK8sApi();
this.placementTagProcessor = new PlacementTagProcessor({
enabled: env.PLACEMENT_TAGS_ENABLED,
prefix: env.PLACEMENT_TAGS_PREFIX,
});
if (opts.workloadApiDomain) {
this.logger.warn("[KubernetesWorkloadManager] ⚠️ Custom workload API domain", {
@@ -28,6 +34,35 @@ export class KubernetesWorkloadManager implements WorkloadManager {
}
}
private addPlacementTags(
podSpec: Omit<k8s.V1PodSpec, "containers">,
placementTags?: PlacementTag[]
): Omit<k8s.V1PodSpec, "containers"> {
const nodeSelector = this.placementTagProcessor.convertToNodeSelector(
placementTags,
podSpec.nodeSelector
);
return {
...podSpec,
nodeSelector,
};
}
private stripImageDigest(imageRef: string): string {
if (!env.KUBERNETES_STRIP_IMAGE_DIGEST) {
return imageRef;
}
const atIndex = imageRef.lastIndexOf("@");
if (atIndex === -1) {
return imageRef;
}
return imageRef.substring(0, atIndex);
}
async create(opts: WorkloadManagerCreateOptions) {
this.logger.log("[KubernetesWorkloadManager] Creating container", { opts });
@@ -48,12 +83,12 @@ export class KubernetesWorkloadManager implements WorkloadManager {
},
},
spec: {
...this.#defaultPodSpec,
...this.addPlacementTags(this.#defaultPodSpec, opts.placementTags),
terminationGracePeriodSeconds: 60 * 60,
containers: [
{
name: "run-controller",
image: opts.image,
image: this.stripImageDigest(opts.image),
ports: [
{
containerPort: 8000,

Some files were not shown because too many files have changed in this diff Show More