trigger.dev 0.0.0-v3-prerelease-20240813130521 → 0.0.0-v3-prerelease-20240912191953

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (231) hide show
  1. package/dist/esm/apiClient.d.ts +190 -0
  2. package/dist/esm/apiClient.js +239 -0
  3. package/dist/esm/apiClient.js.map +1 -0
  4. package/dist/esm/build/buildWorker.d.ts +68 -0
  5. package/dist/esm/build/buildWorker.js +159 -0
  6. package/dist/esm/build/buildWorker.js.map +1 -0
  7. package/dist/esm/build/bundle.d.ts +36 -0
  8. package/dist/esm/build/bundle.js +197 -0
  9. package/dist/esm/build/bundle.js.map +1 -0
  10. package/dist/esm/build/extensions.d.ts +15 -0
  11. package/dist/esm/build/extensions.js +158 -0
  12. package/dist/esm/build/extensions.js.map +1 -0
  13. package/dist/esm/build/externals.d.ts +15 -0
  14. package/dist/esm/build/externals.js +285 -0
  15. package/dist/esm/build/externals.js.map +1 -0
  16. package/dist/esm/build/manifests.d.ts +2 -0
  17. package/dist/esm/build/manifests.js +22 -0
  18. package/dist/esm/build/manifests.js.map +1 -0
  19. package/dist/esm/build/packageModules.d.ts +18 -0
  20. package/dist/esm/build/packageModules.js +84 -0
  21. package/dist/esm/build/packageModules.js.map +1 -0
  22. package/dist/esm/build/plugins.d.ts +6 -0
  23. package/dist/esm/build/plugins.js +67 -0
  24. package/dist/esm/build/plugins.js.map +1 -0
  25. package/dist/esm/build/resolveModule.d.ts +1 -0
  26. package/dist/esm/build/resolveModule.js +9 -0
  27. package/dist/esm/build/resolveModule.js.map +1 -0
  28. package/dist/esm/cli/common.d.ts +30 -0
  29. package/dist/esm/cli/common.js +85 -0
  30. package/dist/esm/cli/common.js.map +1 -0
  31. package/dist/esm/cli/index.d.ts +2 -0
  32. package/dist/esm/cli/index.js +27 -0
  33. package/dist/esm/cli/index.js.map +1 -0
  34. package/dist/esm/commands/deploy.d.ts +5 -0
  35. package/dist/esm/commands/deploy.js +402 -0
  36. package/dist/esm/commands/deploy.js.map +1 -0
  37. package/dist/esm/commands/dev.d.ts +37 -0
  38. package/dist/esm/commands/dev.js +128 -0
  39. package/dist/esm/commands/dev.js.map +1 -0
  40. package/dist/esm/commands/init.d.ts +3 -0
  41. package/dist/esm/commands/init.js +417 -0
  42. package/dist/esm/commands/init.js.map +1 -0
  43. package/dist/esm/commands/list-profiles.d.ts +23 -0
  44. package/dist/esm/commands/list-profiles.js +40 -0
  45. package/dist/esm/commands/list-profiles.js.map +1 -0
  46. package/dist/esm/commands/login.d.ts +28 -0
  47. package/dist/esm/commands/login.js +275 -0
  48. package/dist/esm/commands/login.js.map +1 -0
  49. package/dist/esm/commands/logout.d.ts +23 -0
  50. package/dist/esm/commands/logout.js +28 -0
  51. package/dist/esm/commands/logout.js.map +1 -0
  52. package/dist/esm/commands/update.d.ts +24 -0
  53. package/dist/esm/commands/update.js +233 -0
  54. package/dist/esm/commands/update.js.map +1 -0
  55. package/dist/esm/commands/whoami.d.ts +34 -0
  56. package/dist/esm/commands/whoami.js +70 -0
  57. package/dist/esm/commands/whoami.js.map +1 -0
  58. package/dist/esm/config.d.ts +22 -0
  59. package/dist/esm/config.js +204 -0
  60. package/dist/esm/config.js.map +1 -0
  61. package/dist/esm/consts.d.ts +4 -0
  62. package/dist/esm/consts.js +5 -0
  63. package/dist/esm/consts.js.map +1 -0
  64. package/dist/esm/deploy/buildImage.d.ts +69 -0
  65. package/dist/esm/deploy/buildImage.js +472 -0
  66. package/dist/esm/deploy/buildImage.js.map +1 -0
  67. package/dist/esm/deploy/logs.d.ts +19 -0
  68. package/dist/esm/deploy/logs.js +96 -0
  69. package/dist/esm/deploy/logs.js.map +1 -0
  70. package/dist/esm/dev/backgroundWorker.d.ts +114 -0
  71. package/dist/esm/dev/backgroundWorker.js +412 -0
  72. package/dist/esm/dev/backgroundWorker.js.map +1 -0
  73. package/dist/esm/dev/devOutput.d.ts +9 -0
  74. package/dist/esm/dev/devOutput.js +137 -0
  75. package/dist/esm/dev/devOutput.js.map +1 -0
  76. package/dist/esm/dev/devSession.d.ts +17 -0
  77. package/dist/esm/dev/devSession.js +156 -0
  78. package/dist/esm/dev/devSession.js.map +1 -0
  79. package/dist/esm/dev/workerRuntime.d.ts +16 -0
  80. package/dist/esm/dev/workerRuntime.js +258 -0
  81. package/dist/esm/dev/workerRuntime.js.map +1 -0
  82. package/dist/esm/entryPoints/deploy-index-controller.d.ts +1 -0
  83. package/dist/esm/entryPoints/deploy-index-controller.js +85 -0
  84. package/dist/esm/entryPoints/deploy-index-controller.js.map +1 -0
  85. package/dist/esm/entryPoints/deploy-index-worker.d.ts +1 -0
  86. package/dist/esm/entryPoints/deploy-index-worker.js +104 -0
  87. package/dist/esm/entryPoints/deploy-index-worker.js.map +1 -0
  88. package/dist/esm/entryPoints/deploy-run-controller.d.ts +1 -0
  89. package/dist/esm/entryPoints/deploy-run-controller.js +996 -0
  90. package/dist/esm/entryPoints/deploy-run-controller.js.map +1 -0
  91. package/dist/esm/entryPoints/deploy-run-worker.d.ts +1 -0
  92. package/dist/esm/entryPoints/deploy-run-worker.js +297 -0
  93. package/dist/esm/entryPoints/deploy-run-worker.js.map +1 -0
  94. package/dist/esm/entryPoints/dev-index-worker.d.ts +1 -0
  95. package/dist/esm/entryPoints/dev-index-worker.js +104 -0
  96. package/dist/esm/entryPoints/dev-index-worker.js.map +1 -0
  97. package/dist/esm/entryPoints/dev-run-worker.d.ts +1 -0
  98. package/dist/esm/entryPoints/dev-run-worker.js +254 -0
  99. package/dist/esm/entryPoints/dev-run-worker.js.map +1 -0
  100. package/dist/esm/entryPoints/loader.d.ts +1 -0
  101. package/dist/esm/entryPoints/loader.js +16 -0
  102. package/dist/esm/entryPoints/loader.js.map +1 -0
  103. package/dist/esm/executions/taskRunProcess.d.ts +115 -0
  104. package/dist/esm/executions/taskRunProcess.js +254 -0
  105. package/dist/esm/executions/taskRunProcess.js.map +1 -0
  106. package/dist/esm/imports/magicast.d.ts +2 -0
  107. package/dist/esm/imports/magicast.js +5 -0
  108. package/dist/esm/imports/magicast.js.map +1 -0
  109. package/dist/esm/imports/xdg-app-paths.d.ts +2 -0
  110. package/dist/esm/imports/xdg-app-paths.js +3 -0
  111. package/dist/esm/imports/xdg-app-paths.js.map +1 -0
  112. package/dist/{index.d.ts → esm/index.d.ts} +1 -0
  113. package/dist/esm/index.js +17 -0
  114. package/dist/esm/index.js.map +1 -0
  115. package/dist/esm/indexing/indexWorkerManifest.d.ts +85 -0
  116. package/dist/esm/indexing/indexWorkerManifest.js +76 -0
  117. package/dist/esm/indexing/indexWorkerManifest.js.map +1 -0
  118. package/dist/esm/indexing/registerTasks.d.ts +2 -0
  119. package/dist/esm/indexing/registerTasks.js +62 -0
  120. package/dist/esm/indexing/registerTasks.js.map +1 -0
  121. package/dist/esm/package.json +3 -0
  122. package/dist/esm/runtimes/bun.d.ts +2 -0
  123. package/dist/esm/runtimes/bun.js +11 -0
  124. package/dist/esm/runtimes/bun.js.map +1 -0
  125. package/dist/esm/shims/esm.d.ts +1 -0
  126. package/dist/esm/shims/esm.js +9 -0
  127. package/dist/esm/shims/esm.js.map +1 -0
  128. package/dist/esm/sourceDir.d.ts +1 -0
  129. package/dist/esm/sourceDir.js +4 -0
  130. package/dist/esm/sourceDir.js.map +1 -0
  131. package/dist/esm/telemetry/tracing.d.ts +3 -0
  132. package/dist/esm/telemetry/tracing.js +58 -0
  133. package/dist/esm/telemetry/tracing.js.map +1 -0
  134. package/dist/esm/types.d.ts +6 -0
  135. package/dist/esm/types.js +2 -0
  136. package/dist/esm/types.js.map +1 -0
  137. package/dist/esm/utilities/assertExhaustive.d.ts +1 -0
  138. package/dist/esm/utilities/assertExhaustive.js +4 -0
  139. package/dist/esm/utilities/assertExhaustive.js.map +1 -0
  140. package/dist/esm/utilities/buildManifest.d.ts +2 -0
  141. package/dist/esm/utilities/buildManifest.js +9 -0
  142. package/dist/esm/utilities/buildManifest.js.map +1 -0
  143. package/dist/esm/utilities/cliOutput.d.ts +20 -0
  144. package/dist/esm/utilities/cliOutput.js +88 -0
  145. package/dist/esm/utilities/cliOutput.js.map +1 -0
  146. package/dist/esm/utilities/configFiles.d.ts +29 -0
  147. package/dist/esm/utilities/configFiles.js +65 -0
  148. package/dist/esm/utilities/configFiles.js.map +1 -0
  149. package/dist/esm/utilities/createFileFromTemplate.d.ts +16 -0
  150. package/dist/esm/utilities/createFileFromTemplate.js +50 -0
  151. package/dist/esm/utilities/createFileFromTemplate.js.map +1 -0
  152. package/dist/esm/utilities/deployErrors.d.ts +19 -0
  153. package/dist/esm/utilities/deployErrors.js +117 -0
  154. package/dist/esm/utilities/deployErrors.js.map +1 -0
  155. package/dist/esm/utilities/dotEnv.d.ts +4 -0
  156. package/dist/esm/utilities/dotEnv.js +28 -0
  157. package/dist/esm/utilities/dotEnv.js.map +1 -0
  158. package/dist/esm/utilities/eventBus.d.ts +14 -0
  159. package/dist/esm/utilities/eventBus.js +3 -0
  160. package/dist/esm/utilities/eventBus.js.map +1 -0
  161. package/dist/esm/utilities/fileSystem.d.ts +12 -0
  162. package/dist/esm/utilities/fileSystem.js +81 -0
  163. package/dist/esm/utilities/fileSystem.js.map +1 -0
  164. package/dist/esm/utilities/getApiKeyType.d.ts +13 -0
  165. package/dist/esm/utilities/getApiKeyType.js +44 -0
  166. package/dist/esm/utilities/getApiKeyType.js.map +1 -0
  167. package/dist/esm/utilities/initialBanner.d.ts +4 -0
  168. package/dist/esm/utilities/initialBanner.js +82 -0
  169. package/dist/esm/utilities/initialBanner.js.map +1 -0
  170. package/dist/esm/utilities/keyValueBy.d.ts +8 -0
  171. package/dist/esm/utilities/keyValueBy.js +19 -0
  172. package/dist/esm/utilities/keyValueBy.js.map +1 -0
  173. package/dist/esm/utilities/links.d.ts +9 -0
  174. package/dist/esm/utilities/links.js +10 -0
  175. package/dist/esm/utilities/links.js.map +1 -0
  176. package/dist/esm/utilities/linux.d.ts +1 -0
  177. package/dist/esm/utilities/linux.js +24 -0
  178. package/dist/esm/utilities/linux.js.map +1 -0
  179. package/dist/esm/utilities/logger.d.ts +40 -0
  180. package/dist/esm/utilities/logger.js +109 -0
  181. package/dist/esm/utilities/logger.js.map +1 -0
  182. package/dist/esm/utilities/normalizeImportPath.d.ts +1 -0
  183. package/dist/esm/utilities/normalizeImportPath.js +5 -0
  184. package/dist/esm/utilities/normalizeImportPath.js.map +1 -0
  185. package/dist/esm/utilities/obfuscateApiKey.d.ts +1 -0
  186. package/dist/esm/utilities/obfuscateApiKey.js +5 -0
  187. package/dist/esm/utilities/obfuscateApiKey.js.map +1 -0
  188. package/dist/esm/utilities/parseNameAndPath.d.ts +2 -0
  189. package/dist/esm/utilities/parseNameAndPath.js +10 -0
  190. package/dist/esm/utilities/parseNameAndPath.js.map +1 -0
  191. package/dist/esm/utilities/resolveInternalFilePath.d.ts +1 -0
  192. package/dist/esm/utilities/resolveInternalFilePath.js +8 -0
  193. package/dist/esm/utilities/resolveInternalFilePath.js.map +1 -0
  194. package/dist/esm/utilities/runtimeCheck.d.ts +8 -0
  195. package/dist/esm/utilities/runtimeCheck.js +52 -0
  196. package/dist/esm/utilities/runtimeCheck.js.map +1 -0
  197. package/dist/esm/utilities/safeJsonParse.d.ts +1 -0
  198. package/dist/esm/utilities/safeJsonParse.js +12 -0
  199. package/dist/esm/utilities/safeJsonParse.js.map +1 -0
  200. package/dist/esm/utilities/sanitizeEnvVars.d.ts +3 -0
  201. package/dist/esm/utilities/sanitizeEnvVars.js +4 -0
  202. package/dist/esm/utilities/sanitizeEnvVars.js.map +1 -0
  203. package/dist/esm/utilities/session.d.ts +33 -0
  204. package/dist/esm/utilities/session.js +82 -0
  205. package/dist/esm/utilities/session.js.map +1 -0
  206. package/dist/esm/utilities/sourceFiles.d.ts +12 -0
  207. package/dist/esm/utilities/sourceFiles.js +76 -0
  208. package/dist/esm/utilities/sourceFiles.js.map +1 -0
  209. package/dist/esm/utilities/taskFiles.d.ts +6 -0
  210. package/dist/esm/utilities/taskFiles.js +69 -0
  211. package/dist/esm/utilities/taskFiles.js.map +1 -0
  212. package/dist/esm/utilities/tempDirectories.d.ts +16 -0
  213. package/dist/esm/utilities/tempDirectories.js +27 -0
  214. package/dist/esm/utilities/tempDirectories.js.map +1 -0
  215. package/dist/esm/utilities/windows.d.ts +7 -0
  216. package/dist/esm/utilities/windows.js +21 -0
  217. package/dist/esm/utilities/windows.js.map +1 -0
  218. package/dist/esm/version.d.ts +1 -0
  219. package/dist/esm/version.js +2 -0
  220. package/dist/esm/version.js.map +1 -0
  221. package/package.json +82 -78
  222. package/dist/Containerfile.prod +0 -58
  223. package/dist/index.js +0 -7780
  224. package/dist/index.js.map +0 -1
  225. package/dist/templates/examples/simple.ts.template +0 -14
  226. package/dist/templates/trigger.config.ts.template +0 -16
  227. package/dist/workers/dev/worker-facade.js +0 -192
  228. package/dist/workers/dev/worker-setup.js +0 -35
  229. package/dist/workers/prod/entry-point.js +0 -2170
  230. package/dist/workers/prod/worker-facade.js +0 -223
  231. package/dist/workers/prod/worker-setup.js +0 -24
@@ -1,2170 +0,0 @@
1
- // src/workers/prod/entry-point.ts
2
- import {
3
- CoordinatorToProdWorkerMessages,
4
- PostStartCauses,
5
- PreStopCauses,
6
- ProdWorkerToCoordinatorMessages,
7
- TaskRunErrorCodes as TaskRunErrorCodes2
8
- } from "@trigger.dev/core/v3";
9
- import { ZodSocketConnection } from "@trigger.dev/core/v3/zodSocket";
10
-
11
- // ../core-apps/src/http.ts
12
- var HttpReply = class {
13
- constructor(response) {
14
- this.response = response;
15
- }
16
- empty(status) {
17
- return this.response.writeHead(status ?? 200).end();
18
- }
19
- text(text, status, contentType) {
20
- return this.response.writeHead(status ?? 200, { "Content-Type": contentType || "text/plain" }).end(text.endsWith("\n") ? text : `${text}
21
- `);
22
- }
23
- json(value, pretty) {
24
- return this.text(
25
- JSON.stringify(value, void 0, pretty ? 2 : void 0),
26
- 200,
27
- "application/json"
28
- );
29
- }
30
- };
31
- function getRandomInteger(min, max) {
32
- const intMin = Math.ceil(min);
33
- const intMax = Math.floor(max);
34
- return Math.floor(Math.random() * (intMax - intMin + 1)) + intMin;
35
- }
36
- function getRandomPortNumber() {
37
- return getRandomInteger(8e3, 9999);
38
- }
39
-
40
- // ../core-apps/src/logger.ts
41
- var SimpleLogger = class {
42
- constructor(prefix) {
43
- this.prefix = prefix;
44
- }
45
- #debugEnabled = ["1", "true"].includes(process.env.DEBUG ?? "");
46
- log(arg0, ...argN) {
47
- console.log(...this.#getPrefixedArgs(arg0, ...argN));
48
- return arg0;
49
- }
50
- debug(arg0, ...argN) {
51
- if (!this.#debugEnabled) {
52
- return arg0;
53
- }
54
- console.debug(...this.#getPrefixedArgs("DEBUG", arg0, ...argN));
55
- return arg0;
56
- }
57
- error(arg0, ...argN) {
58
- console.error(...this.#getPrefixedArgs(arg0, ...argN));
59
- return arg0;
60
- }
61
- #getPrefixedArgs(...args) {
62
- if (!this.prefix) {
63
- return args;
64
- }
65
- return [this.prefix, ...args];
66
- }
67
- };
68
-
69
- // ../core-apps/src/process.ts
70
- var EXIT_CODE_ALREADY_HANDLED = 111;
71
- var EXIT_CODE_CHILD_NONZERO = 112;
72
-
73
- // ../core-apps/src/backoff.ts
74
- import { setTimeout as timeout } from "node:timers/promises";
75
- var StopRetrying = class extends Error {
76
- constructor(message) {
77
- super(message);
78
- this.name = "StopRetrying";
79
- }
80
- };
81
- var AttemptTimeout = class extends Error {
82
- constructor(message) {
83
- super(message);
84
- this.name = "AttemptTimeout";
85
- }
86
- };
87
- var RetryLimitExceeded = class extends Error {
88
- constructor(message) {
89
- super(message);
90
- this.name = "RetryLimitExceeded";
91
- }
92
- };
93
- var ExponentialBackoff = class _ExponentialBackoff {
94
- #retries = 0;
95
- #type;
96
- #base;
97
- #factor;
98
- #min;
99
- #max;
100
- #maxRetries;
101
- #maxElapsed;
102
- constructor(type, opts = {}) {
103
- this.#type = type ?? "NoJitter";
104
- this.#base = opts.base ?? 2;
105
- this.#factor = opts.factor ?? 1;
106
- this.#min = opts.min ?? -Infinity;
107
- this.#max = opts.max ?? Infinity;
108
- this.#maxRetries = opts.maxRetries ?? Infinity;
109
- this.#maxElapsed = opts.maxElapsed ?? Infinity;
110
- }
111
- #clone(type, opts = {}) {
112
- return new _ExponentialBackoff(type ?? this.#type, {
113
- base: opts.base ?? this.#base,
114
- factor: opts.factor ?? this.#factor,
115
- min: opts.min ?? this.#min,
116
- max: opts.max ?? this.#max,
117
- maxRetries: opts.maxRetries ?? this.#maxRetries,
118
- maxElapsed: opts.maxElapsed ?? this.#maxElapsed
119
- });
120
- }
121
- type(type) {
122
- return this.#clone(type);
123
- }
124
- base(base) {
125
- return this.#clone(void 0, { base });
126
- }
127
- factor(factor) {
128
- return this.#clone(void 0, { factor });
129
- }
130
- min(min) {
131
- return this.#clone(void 0, { min });
132
- }
133
- max(max) {
134
- return this.#clone(void 0, { max });
135
- }
136
- maxRetries(maxRetries) {
137
- return this.#clone(void 0, { maxRetries });
138
- }
139
- // TODO: With .execute(), should this also include the time it takes to execute the callback?
140
- maxElapsed(maxElapsed) {
141
- return this.#clone(void 0, { maxElapsed });
142
- }
143
- retries(retries) {
144
- if (typeof retries !== "undefined") {
145
- if (retries > this.#maxRetries) {
146
- console.error(
147
- `Can't set retries ${retries} higher than maxRetries (${this.#maxRetries}), setting to maxRetries instead.`
148
- );
149
- this.#retries = this.#maxRetries;
150
- } else {
151
- this.#retries = retries;
152
- }
153
- }
154
- return this.#clone();
155
- }
156
- async *retryAsync(maxRetries = this.#maxRetries ?? Infinity) {
157
- let elapsed = 0;
158
- let retry = 0;
159
- while (retry <= maxRetries) {
160
- const delay = this.delay(retry);
161
- elapsed += delay;
162
- if (elapsed > this.#maxElapsed) {
163
- break;
164
- }
165
- yield {
166
- delay: {
167
- seconds: delay,
168
- milliseconds: delay * 1e3
169
- },
170
- retry
171
- };
172
- retry++;
173
- }
174
- }
175
- async *[Symbol.asyncIterator]() {
176
- yield* this.retryAsync();
177
- }
178
- /** Returns the delay for the current retry in seconds. */
179
- delay(retries = this.#retries, jitter = true) {
180
- if (retries > this.#maxRetries) {
181
- console.error(
182
- `Can't set retries ${retries} higher than maxRetries (${this.#maxRetries}), setting to maxRetries instead.`
183
- );
184
- retries = this.#maxRetries;
185
- }
186
- let delay = this.#factor * this.#base ** retries;
187
- switch (this.#type) {
188
- case "NoJitter": {
189
- break;
190
- }
191
- case "FullJitter": {
192
- if (!jitter) {
193
- delay = 0;
194
- break;
195
- }
196
- delay *= Math.random();
197
- break;
198
- }
199
- case "EqualJitter": {
200
- if (!jitter) {
201
- delay *= 0.5;
202
- break;
203
- }
204
- delay *= 0.5 * (1 + Math.random());
205
- break;
206
- }
207
- default: {
208
- throw new Error(`Unknown backoff type: ${this.#type}`);
209
- }
210
- }
211
- if (delay < this.#min) {
212
- delay = this.#min + Math.random() * (this.#min * 0.2);
213
- }
214
- if (delay > this.#max) {
215
- delay = this.#max - Math.random() * (this.#max * 0.2);
216
- }
217
- delay = Math.round(delay);
218
- return delay;
219
- }
220
- /** Waits with the appropriate delay for the current retry. */
221
- async wait(retries = this.#retries, jitter = true) {
222
- if (retries > this.#maxRetries) {
223
- console.error(`Retry limit exceeded: ${retries} > ${this.#maxRetries}`);
224
- throw new RetryLimitExceeded();
225
- }
226
- const delay = this.delay(retries, jitter);
227
- return await timeout(delay * 1e3);
228
- }
229
- elapsed(retries = this.#retries, jitter = true) {
230
- let elapsed = 0;
231
- for (let i = 0; i <= retries; i++) {
232
- elapsed += this.delay(i, jitter);
233
- }
234
- const total = elapsed;
235
- let days = 0;
236
- if (elapsed > 3600 * 24) {
237
- days = Math.floor(elapsed / 3600 / 24);
238
- elapsed -= days * 3600 * 24;
239
- }
240
- let hours = 0;
241
- if (elapsed > 3600) {
242
- hours = Math.floor(elapsed / 3600);
243
- elapsed -= hours * 3600;
244
- }
245
- let minutes = 0;
246
- if (elapsed > 60) {
247
- minutes = Math.floor(elapsed / 60);
248
- elapsed -= minutes * 60;
249
- }
250
- const seconds = elapsed;
251
- return {
252
- seconds,
253
- minutes,
254
- hours,
255
- days,
256
- total
257
- };
258
- }
259
- reset() {
260
- this.#retries = 0;
261
- return this;
262
- }
263
- next() {
264
- this.#retries++;
265
- return this.delay();
266
- }
267
- stop() {
268
- throw new StopRetrying();
269
- }
270
- get state() {
271
- return {
272
- retries: this.#retries,
273
- type: this.#type,
274
- base: this.#base,
275
- factor: this.#factor,
276
- min: this.#min,
277
- max: this.#max,
278
- maxRetries: this.#maxRetries,
279
- maxElapsed: this.#maxElapsed
280
- };
281
- }
282
- async execute(callback, { attemptTimeoutMs = 0 } = {}) {
283
- let elapsedMs = 0;
284
- let finalError = void 0;
285
- for await (const { delay, retry } of this) {
286
- const start = Date.now();
287
- if (retry > 0) {
288
- console.log(`Retrying in ${delay.milliseconds}ms`);
289
- await timeout(delay.milliseconds);
290
- }
291
- let attemptTimeout = void 0;
292
- try {
293
- const result = await new Promise(async (resolve, reject) => {
294
- if (attemptTimeoutMs > 0) {
295
- attemptTimeout = setTimeout(() => {
296
- reject(new AttemptTimeout());
297
- }, attemptTimeoutMs);
298
- }
299
- try {
300
- const callbackResult = await callback({ delay, retry, elapsedMs });
301
- resolve(callbackResult);
302
- } catch (error) {
303
- reject(error);
304
- }
305
- });
306
- return {
307
- success: true,
308
- result
309
- };
310
- } catch (error) {
311
- finalError = error;
312
- if (error instanceof StopRetrying) {
313
- return {
314
- success: false,
315
- cause: "StopRetrying",
316
- error: error.message
317
- };
318
- }
319
- if (error instanceof AttemptTimeout) {
320
- continue;
321
- }
322
- } finally {
323
- elapsedMs += Date.now() - start;
324
- clearTimeout(attemptTimeout);
325
- }
326
- }
327
- if (finalError instanceof AttemptTimeout) {
328
- return {
329
- success: false,
330
- cause: "Timeout"
331
- };
332
- } else {
333
- return {
334
- success: false,
335
- cause: "MaxRetries",
336
- error: finalError
337
- };
338
- }
339
- }
340
- static RetryLimitExceeded = RetryLimitExceeded;
341
- static StopRetrying = StopRetrying;
342
- };
343
-
344
- // src/workers/prod/backgroundWorker.ts
345
- import {
346
- ProdChildToWorkerMessages,
347
- ProdWorkerToChildMessages,
348
- SemanticInternalAttributes,
349
- TaskRunErrorCodes,
350
- correctErrorStackTrace
351
- } from "@trigger.dev/core/v3";
352
- import { ZodIpcConnection } from "@trigger.dev/core/v3/zodIpc";
353
- import { Evt } from "evt";
354
- import { fork } from "node:child_process";
355
-
356
- // src/workers/common/errors.ts
357
- var UncaughtExceptionError = class extends Error {
358
- constructor(originalError, origin) {
359
- super(`Uncaught exception: ${originalError.message}`);
360
- this.originalError = originalError;
361
- this.origin = origin;
362
- this.name = "UncaughtExceptionError";
363
- }
364
- };
365
- var TaskMetadataParseError = class extends Error {
366
- constructor(zodIssues, tasks) {
367
- super(`Failed to parse task metadata`);
368
- this.zodIssues = zodIssues;
369
- this.tasks = tasks;
370
- this.name = "TaskMetadataParseError";
371
- }
372
- };
373
- var UnexpectedExitError = class extends Error {
374
- constructor(code, signal, stderr) {
375
- super(`Unexpected exit with code ${code}`);
376
- this.code = code;
377
- this.signal = signal;
378
- this.stderr = stderr;
379
- this.name = "UnexpectedExitError";
380
- }
381
- };
382
- var CleanupProcessError = class extends Error {
383
- constructor() {
384
- super("Cancelled");
385
- this.name = "CleanupProcessError";
386
- }
387
- };
388
- var CancelledProcessError = class extends Error {
389
- constructor() {
390
- super("Cancelled");
391
- this.name = "CancelledProcessError";
392
- }
393
- };
394
- var SigKillTimeoutProcessError = class extends Error {
395
- constructor() {
396
- super("Process kill timeout");
397
- this.name = "SigKillTimeoutProcessError";
398
- }
399
- };
400
- var GracefulExitTimeoutError = class extends Error {
401
- constructor() {
402
- super("Graceful exit timeout");
403
- this.name = "GracefulExitTimeoutError";
404
- }
405
- };
406
- function getFriendlyErrorMessage(code, signal, stderr, dockerMode = true) {
407
- const message = (text) => {
408
- if (signal) {
409
- return `[${signal}] ${text}`;
410
- } else {
411
- return text;
412
- }
413
- };
414
- if (code === 137) {
415
- if (dockerMode) {
416
- return message(
417
- "Process ran out of memory! Try choosing a machine preset with more memory for this task."
418
- );
419
- } else {
420
- return message(
421
- "Process most likely ran out of memory, but we can't be certain. Try choosing a machine preset with more memory for this task."
422
- );
423
- }
424
- }
425
- if (stderr?.includes("OOMErrorHandler")) {
426
- return message(
427
- "Process ran out of memory! Try choosing a machine preset with more memory for this task."
428
- );
429
- }
430
- return message(`Process exited with code ${code}.`);
431
- }
432
-
433
- // src/workers/prod/backgroundWorker.ts
434
- var ProdBackgroundWorker = class {
435
- constructor(path, params) {
436
- this.path = path;
437
- this.params = params;
438
- }
439
- _initialized = false;
440
- /**
441
- * @deprecated use onTaskRunHeartbeat instead
442
- */
443
- onTaskHeartbeat = new Evt();
444
- onTaskRunHeartbeat = new Evt();
445
- onWaitForDuration = new Evt();
446
- onWaitForTask = new Evt();
447
- onWaitForBatch = new Evt();
448
- onCreateTaskRunAttempt = Evt.create();
449
- attemptCreatedNotification = Evt.create();
450
- _onClose = new Evt();
451
- tasks = [];
452
- stderr = [];
453
- _taskRunProcess;
454
- _taskRunProcessesBeingKilled = /* @__PURE__ */ new Map();
455
- _closed = false;
456
- async close(gracefulExitTimeoutElapsed = false) {
457
- console.log("Closing worker", { gracefulExitTimeoutElapsed, closed: this._closed });
458
- if (this._closed) {
459
- return;
460
- }
461
- this._closed = true;
462
- this.onTaskHeartbeat.detach();
463
- this.onTaskRunHeartbeat.detach();
464
- await this._taskRunProcess?.cleanup(true, gracefulExitTimeoutElapsed);
465
- }
466
- async #killTaskRunProcess(flush = true, initialSignal = "SIGTERM") {
467
- console.log("Killing task run process", { flush, initialSignal, closed: this._closed });
468
- if (this._closed || !this._taskRunProcess) {
469
- return;
470
- }
471
- if (flush) {
472
- await this.flushTelemetry();
473
- }
474
- const currentTaskRunProcess = this._taskRunProcess;
475
- this.#tryGracefulExit(currentTaskRunProcess, true, initialSignal).catch((error) => {
476
- console.error("Error while trying graceful exit", error);
477
- });
478
- console.log("Killed task run process, setting closed to true", {
479
- closed: this._closed,
480
- pid: currentTaskRunProcess.pid
481
- });
482
- this._closed = true;
483
- }
484
- async flushTelemetry() {
485
- console.log("Flushing telemetry");
486
- const start = performance.now();
487
- await this._taskRunProcess?.cleanup(false);
488
- console.log("Flushed telemetry", { duration: performance.now() - start });
489
- }
490
- async initialize(options) {
491
- if (this._initialized) {
492
- throw new Error("Worker already initialized");
493
- }
494
- let resolved = false;
495
- this.tasks = await new Promise((resolve, reject) => {
496
- const child = fork(this.path, {
497
- stdio: [
498
- /*stdin*/
499
- "ignore",
500
- /*stdout*/
501
- "pipe",
502
- /*stderr*/
503
- "pipe",
504
- "ipc"
505
- ],
506
- env: {
507
- ...this.params.env,
508
- ...options?.env
509
- }
510
- });
511
- const timeout3 = setTimeout(() => {
512
- if (resolved) {
513
- return;
514
- }
515
- resolved = true;
516
- child.kill();
517
- reject(new Error("Worker timed out"));
518
- }, 1e4);
519
- child.stdout?.on("data", (data) => {
520
- console.log(data.toString());
521
- });
522
- child.stderr?.on("data", (data) => {
523
- console.error(data.toString());
524
- this.stderr.push(data.toString());
525
- });
526
- child.on("exit", (code) => {
527
- if (!resolved) {
528
- clearTimeout(timeout3);
529
- resolved = true;
530
- reject(new Error(`Worker exited with code ${code}`));
531
- }
532
- });
533
- new ZodIpcConnection({
534
- listenSchema: ProdChildToWorkerMessages,
535
- emitSchema: ProdWorkerToChildMessages,
536
- process: child,
537
- handlers: {
538
- TASKS_READY: async (message) => {
539
- if (!resolved) {
540
- clearTimeout(timeout3);
541
- resolved = true;
542
- resolve(message.tasks);
543
- child.kill();
544
- }
545
- },
546
- UNCAUGHT_EXCEPTION: async (message) => {
547
- if (!resolved) {
548
- clearTimeout(timeout3);
549
- resolved = true;
550
- reject(new UncaughtExceptionError(message.error, message.origin));
551
- child.kill();
552
- }
553
- },
554
- TASKS_FAILED_TO_PARSE: async (message) => {
555
- if (!resolved) {
556
- clearTimeout(timeout3);
557
- resolved = true;
558
- reject(new TaskMetadataParseError(message.zodIssues, message.tasks));
559
- child.kill();
560
- }
561
- }
562
- }
563
- });
564
- });
565
- this._initialized = true;
566
- }
567
- getMetadata(workerId, version) {
568
- return {
569
- contentHash: this.params.contentHash,
570
- id: workerId,
571
- version
572
- };
573
- }
574
- // We need to notify all the task run processes that a task run has completed,
575
- // in case they are waiting for it through triggerAndWait
576
- async taskRunCompletedNotification(completion) {
577
- this._taskRunProcess?.taskRunCompletedNotification(completion);
578
- }
579
- async waitCompletedNotification() {
580
- this._taskRunProcess?.waitCompletedNotification();
581
- }
582
- async #getFreshTaskRunProcess(payload, messageId) {
583
- const metadata = this.getMetadata(
584
- payload.execution.worker.id,
585
- payload.execution.worker.version
586
- );
587
- console.log("Getting fresh task run process, setting closed to false", {
588
- closed: this._closed
589
- });
590
- this._closed = false;
591
- await this.#killCurrentTaskRunProcessBeforeAttempt();
592
- const taskRunProcess = new TaskRunProcess(
593
- payload.execution.run.id,
594
- payload.execution.run.isTest,
595
- this.path,
596
- {
597
- ...this.params.env,
598
- ...payload.environment ?? {}
599
- },
600
- metadata,
601
- this.params,
602
- messageId
603
- );
604
- taskRunProcess.onExit.attach(({ pid }) => {
605
- console.log("Task run process exited", { pid });
606
- if (this._taskRunProcess?.pid === pid) {
607
- this._taskRunProcess = void 0;
608
- }
609
- if (pid) {
610
- this._taskRunProcessesBeingKilled.delete(pid);
611
- }
612
- });
613
- taskRunProcess.onIsBeingKilled.attach((taskRunProcess2) => {
614
- if (taskRunProcess2?.pid) {
615
- this._taskRunProcessesBeingKilled.set(taskRunProcess2.pid, taskRunProcess2);
616
- }
617
- });
618
- taskRunProcess.onTaskHeartbeat.attach((id) => {
619
- this.onTaskHeartbeat.post(id);
620
- });
621
- taskRunProcess.onTaskRunHeartbeat.attach((id) => {
622
- this.onTaskRunHeartbeat.post(id);
623
- });
624
- taskRunProcess.onWaitForBatch.attach((message) => {
625
- this.onWaitForBatch.post(message);
626
- });
627
- taskRunProcess.onWaitForDuration.attach((message) => {
628
- this.onWaitForDuration.post(message);
629
- });
630
- taskRunProcess.onWaitForTask.attach((message) => {
631
- this.onWaitForTask.post(message);
632
- });
633
- await taskRunProcess.initialize();
634
- this._taskRunProcess = taskRunProcess;
635
- return this._taskRunProcess;
636
- }
637
- async forceKillOldTaskRunProcesses() {
638
- for (const taskRunProcess of this._taskRunProcessesBeingKilled.values()) {
639
- try {
640
- await taskRunProcess.kill("SIGKILL");
641
- } catch (error) {
642
- console.error("Error while force killing old task run processes", error);
643
- }
644
- }
645
- }
646
- async #killCurrentTaskRunProcessBeforeAttempt() {
647
- console.log("killCurrentTaskRunProcessBeforeAttempt()", {
648
- hasTaskRunProcess: !!this._taskRunProcess
649
- });
650
- if (!this._taskRunProcess) {
651
- return;
652
- }
653
- const currentTaskRunProcess = this._taskRunProcess;
654
- console.log("Killing current task run process", {
655
- isBeingKilled: currentTaskRunProcess?.isBeingKilled,
656
- totalBeingKilled: this._taskRunProcessesBeingKilled.size
657
- });
658
- if (currentTaskRunProcess.isBeingKilled) {
659
- if (this._taskRunProcessesBeingKilled.size > 1) {
660
- await this.#tryGracefulExit(currentTaskRunProcess);
661
- } else {
662
- }
663
- } else {
664
- if (this._taskRunProcessesBeingKilled.size > 0) {
665
- await this.#tryGracefulExit(currentTaskRunProcess);
666
- } else {
667
- currentTaskRunProcess.kill("SIGTERM", 5e3).catch(() => {
668
- });
669
- }
670
- }
671
- }
672
- async #tryGracefulExit(taskRunProcess, kill = false, initialSignal = "SIGTERM") {
673
- console.log("Trying graceful exit", { kill, initialSignal });
674
- try {
675
- const initialExit = taskRunProcess.onExit.waitFor(5e3);
676
- if (kill) {
677
- taskRunProcess.kill(initialSignal);
678
- }
679
- await initialExit;
680
- } catch (error) {
681
- console.error("TaskRunProcess graceful kill timeout exceeded", error);
682
- this.#tryForcefulExit(taskRunProcess);
683
- }
684
- }
685
- async #tryForcefulExit(taskRunProcess) {
686
- console.log("Trying forceful exit");
687
- try {
688
- const forcedKill = taskRunProcess.onExit.waitFor(5e3);
689
- taskRunProcess.kill("SIGKILL");
690
- await forcedKill;
691
- } catch (error) {
692
- console.error("TaskRunProcess forced kill timeout exceeded", error);
693
- throw new SigKillTimeoutProcessError();
694
- }
695
- }
696
- // We need to fork the process before we can execute any tasks, use a fresh process for each execution
697
- async executeTaskRun(payload, messageId) {
698
- try {
699
- const taskRunProcess = await this.#getFreshTaskRunProcess(payload, messageId);
700
- console.log("executing task run", {
701
- attempt: payload.execution.attempt.id,
702
- taskRunPid: taskRunProcess.pid
703
- });
704
- const result = await taskRunProcess.executeTaskRun(payload);
705
- if (result.ok) {
706
- return result;
707
- }
708
- const error = result.error;
709
- if (error.type === "BUILT_IN_ERROR") {
710
- const mappedError = await this.#correctError(error, payload.execution);
711
- return {
712
- ...result,
713
- error: mappedError
714
- };
715
- }
716
- return result;
717
- } catch (e) {
718
- if (e instanceof CancelledProcessError) {
719
- return {
720
- id: payload.execution.attempt.id,
721
- ok: false,
722
- retry: void 0,
723
- error: {
724
- type: "INTERNAL_ERROR",
725
- code: TaskRunErrorCodes.TASK_RUN_CANCELLED
726
- }
727
- };
728
- }
729
- if (e instanceof CleanupProcessError) {
730
- return {
731
- id: payload.execution.attempt.id,
732
- ok: false,
733
- retry: void 0,
734
- error: {
735
- type: "INTERNAL_ERROR",
736
- code: TaskRunErrorCodes.TASK_EXECUTION_ABORTED
737
- }
738
- };
739
- }
740
- if (e instanceof UnexpectedExitError) {
741
- return {
742
- id: payload.execution.attempt.id,
743
- ok: false,
744
- retry: void 0,
745
- error: {
746
- type: "INTERNAL_ERROR",
747
- code: TaskRunErrorCodes.TASK_PROCESS_EXITED_WITH_NON_ZERO_CODE,
748
- message: getFriendlyErrorMessage(e.code, e.signal, e.stderr),
749
- stackTrace: e.stderr
750
- }
751
- };
752
- }
753
- if (e instanceof SigKillTimeoutProcessError) {
754
- return {
755
- id: payload.execution.attempt.id,
756
- ok: false,
757
- retry: void 0,
758
- error: {
759
- type: "INTERNAL_ERROR",
760
- code: TaskRunErrorCodes.TASK_PROCESS_SIGKILL_TIMEOUT
761
- }
762
- };
763
- }
764
- if (e instanceof GracefulExitTimeoutError) {
765
- return {
766
- id: payload.execution.attempt.id,
767
- ok: false,
768
- retry: void 0,
769
- error: {
770
- type: "INTERNAL_ERROR",
771
- code: TaskRunErrorCodes.GRACEFUL_EXIT_TIMEOUT,
772
- message: "Worker process killed while attempt in progress."
773
- }
774
- };
775
- }
776
- return {
777
- id: payload.execution.attempt.id,
778
- ok: false,
779
- retry: void 0,
780
- error: {
781
- type: "INTERNAL_ERROR",
782
- code: TaskRunErrorCodes.TASK_EXECUTION_FAILED
783
- }
784
- };
785
- } finally {
786
- await this.#killTaskRunProcess();
787
- }
788
- }
789
- async cancelAttempt(attemptId) {
790
- if (!this._taskRunProcess) {
791
- console.error("No task run process to cancel attempt", { attemptId });
792
- return;
793
- }
794
- await this._taskRunProcess.cancel();
795
- }
796
- async executeTaskRunLazyAttempt(payload) {
797
- this.onCreateTaskRunAttempt.post({ runId: payload.runId });
798
- let execution;
799
- try {
800
- const start = performance.now();
801
- const attemptCreated = await this.attemptCreatedNotification.waitFor(12e4);
802
- if (!attemptCreated.success) {
803
- throw new Error(`${attemptCreated.reason ?? "Unknown error"}`);
804
- }
805
- console.log("Attempt created", {
806
- number: attemptCreated.execution.attempt.number,
807
- duration: performance.now() - start
808
- });
809
- execution = attemptCreated.execution;
810
- } catch (error) {
811
- console.error("Error while creating attempt", error);
812
- throw new Error(`Failed to create attempt: ${error}`);
813
- }
814
- const completion = await this.executeTaskRun(
815
- {
816
- execution,
817
- traceContext: payload.traceContext,
818
- environment: payload.environment
819
- },
820
- payload.messageId
821
- );
822
- return { execution, completion };
823
- }
824
- async #correctError(error, execution) {
825
- return {
826
- ...error,
827
- stackTrace: correctErrorStackTrace(error.stackTrace, this.params.projectConfig.projectDir)
828
- };
829
- }
830
- };
831
- var TaskRunProcess = class {
832
- constructor(runId, isTest, path, env, metadata, worker, messageId) {
833
- this.runId = runId;
834
- this.isTest = isTest;
835
- this.path = path;
836
- this.env = env;
837
- this.metadata = metadata;
838
- this.worker = worker;
839
- this.messageId = messageId;
840
- }
841
- _ipc;
842
- _child;
843
- _childPid;
844
- _attemptPromises = /* @__PURE__ */ new Map();
845
- _attemptStatuses = /* @__PURE__ */ new Map();
846
- _currentExecution;
847
- _isBeingKilled = false;
848
- _isBeingCancelled = false;
849
- _gracefulExitTimeoutElapsed = false;
850
- _stderr = [];
851
- /**
852
- * @deprecated use onTaskRunHeartbeat instead
853
- */
854
- onTaskHeartbeat = new Evt();
855
- onTaskRunHeartbeat = new Evt();
856
- onExit = new Evt();
857
- onIsBeingKilled = new Evt();
858
- onWaitForDuration = new Evt();
859
- onWaitForTask = new Evt();
860
- onWaitForBatch = new Evt();
861
- preCheckpointNotification = Evt.create();
862
- async initialize() {
863
- this._child = fork(this.path, {
864
- stdio: [
865
- /*stdin*/
866
- "ignore",
867
- /*stdout*/
868
- "pipe",
869
- /*stderr*/
870
- "pipe",
871
- "ipc"
872
- ],
873
- env: {
874
- ...this.isTest ? { TRIGGER_LOG_LEVEL: "debug" } : {},
875
- ...this.env,
876
- OTEL_RESOURCE_ATTRIBUTES: JSON.stringify({
877
- [SemanticInternalAttributes.PROJECT_DIR]: this.worker.projectConfig.projectDir
878
- }),
879
- ...this.worker.debugOtel ? { OTEL_LOG_LEVEL: "debug" } : {}
880
- }
881
- });
882
- this._childPid = this._child?.pid;
883
- this._ipc = new ZodIpcConnection({
884
- listenSchema: ProdChildToWorkerMessages,
885
- emitSchema: ProdWorkerToChildMessages,
886
- process: this._child,
887
- handlers: {
888
- TASK_RUN_COMPLETED: async (message) => {
889
- const { result, execution } = message;
890
- const promiseStatus = this._attemptStatuses.get(execution.attempt.id);
891
- if (promiseStatus !== "PENDING") {
892
- return;
893
- }
894
- this._attemptStatuses.set(execution.attempt.id, "RESOLVED");
895
- const attemptPromise = this._attemptPromises.get(execution.attempt.id);
896
- if (!attemptPromise) {
897
- return;
898
- }
899
- const { resolver } = attemptPromise;
900
- resolver(result);
901
- },
902
- READY_TO_DISPOSE: async (message) => {
903
- process.exit(0);
904
- },
905
- TASK_HEARTBEAT: async (message) => {
906
- if (this.messageId) {
907
- this.onTaskRunHeartbeat.post(this.messageId);
908
- } else {
909
- console.error(
910
- "No message id for task heartbeat, falling back to (deprecated) attempt heartbeat",
911
- { id: message.id }
912
- );
913
- this.onTaskHeartbeat.post(message.id);
914
- }
915
- },
916
- TASKS_READY: async (message) => {
917
- },
918
- WAIT_FOR_TASK: async (message) => {
919
- this.onWaitForTask.post(message);
920
- },
921
- WAIT_FOR_BATCH: async (message) => {
922
- this.onWaitForBatch.post(message);
923
- },
924
- WAIT_FOR_DURATION: async (message) => {
925
- this.onWaitForDuration.post(message);
926
- }
927
- }
928
- });
929
- this._child.on("exit", this.#handleExit.bind(this));
930
- this._child.stdout?.on("data", this.#handleLog.bind(this));
931
- this._child.stderr?.on("data", this.#handleStdErr.bind(this));
932
- }
933
- async cancel() {
934
- this._isBeingCancelled = true;
935
- await this.cleanup(true);
936
- }
937
- async cleanup(kill = false, gracefulExitTimeoutElapsed = false) {
938
- console.log("cleanup()", { kill, gracefulExitTimeoutElapsed });
939
- if (kill && this._isBeingKilled) {
940
- return;
941
- }
942
- if (kill) {
943
- this._isBeingKilled = true;
944
- this.onIsBeingKilled.post(this);
945
- }
946
- const killChildProcess = gracefulExitTimeoutElapsed && !!this._currentExecution;
947
- const killParentProcess = kill && !killChildProcess;
948
- console.log("Cleaning up task run process", {
949
- killChildProcess,
950
- killParentProcess,
951
- ipc: this._ipc,
952
- childPid: this._childPid,
953
- realChildPid: this._child?.pid
954
- });
955
- try {
956
- await this._ipc?.sendWithAck(
957
- "CLEANUP",
958
- {
959
- flush: true,
960
- kill: killParentProcess
961
- },
962
- 3e4
963
- );
964
- } catch (error) {
965
- console.error("Error while cleaning up task run process", error);
966
- if (killParentProcess) {
967
- process.exit(0);
968
- }
969
- }
970
- if (killChildProcess) {
971
- this._gracefulExitTimeoutElapsed = true;
972
- await this.kill("SIGKILL");
973
- }
974
- }
975
- async executeTaskRun(payload) {
976
- let resolver;
977
- let rejecter;
978
- const promise = new Promise((resolve, reject) => {
979
- resolver = resolve;
980
- rejecter = reject;
981
- });
982
- this._attemptStatuses.set(payload.execution.attempt.id, "PENDING");
983
- this._attemptPromises.set(payload.execution.attempt.id, { resolver, rejecter });
984
- const { execution, traceContext } = payload;
985
- this._currentExecution = execution;
986
- if (this._child?.connected && !this._isBeingKilled && !this._child.killed) {
987
- await this._ipc?.send("EXECUTE_TASK_RUN", {
988
- execution,
989
- traceContext,
990
- metadata: this.metadata
991
- });
992
- }
993
- const result = await promise;
994
- this._currentExecution = void 0;
995
- return result;
996
- }
997
- taskRunCompletedNotification(completion) {
998
- if (!completion.ok && typeof completion.retry !== "undefined") {
999
- console.error(
1000
- "Task run completed with error and wants to retry, won't send task run completed notification"
1001
- );
1002
- return;
1003
- }
1004
- if (!this._child?.connected || this._isBeingKilled || this._child.killed) {
1005
- console.error(
1006
- "Child process not connected or being killed, can't send task run completed notification"
1007
- );
1008
- return;
1009
- }
1010
- this._ipc?.send("TASK_RUN_COMPLETED_NOTIFICATION", {
1011
- version: "v2",
1012
- completion
1013
- });
1014
- }
1015
- waitCompletedNotification() {
1016
- if (!this._child?.connected || this._isBeingKilled || this._child.killed) {
1017
- console.error(
1018
- "Child process not connected or being killed, can't send wait completed notification"
1019
- );
1020
- return;
1021
- }
1022
- this._ipc?.send("WAIT_COMPLETED_NOTIFICATION", {});
1023
- }
1024
- async #handleExit(code, signal) {
1025
- console.log("handling child exit", { code, signal });
1026
- for (const [id, status] of this._attemptStatuses.entries()) {
1027
- if (status === "PENDING") {
1028
- console.log("found pending attempt", { id });
1029
- this._attemptStatuses.set(id, "REJECTED");
1030
- const attemptPromise = this._attemptPromises.get(id);
1031
- if (!attemptPromise) {
1032
- continue;
1033
- }
1034
- const { rejecter } = attemptPromise;
1035
- if (this._isBeingCancelled) {
1036
- rejecter(new CancelledProcessError());
1037
- } else if (this._gracefulExitTimeoutElapsed) {
1038
- rejecter(new GracefulExitTimeoutError());
1039
- } else if (this._isBeingKilled) {
1040
- rejecter(new CleanupProcessError());
1041
- } else {
1042
- rejecter(
1043
- new UnexpectedExitError(
1044
- code ?? -1,
1045
- signal,
1046
- this._stderr.length ? this._stderr.join("\n") : void 0
1047
- )
1048
- );
1049
- }
1050
- }
1051
- }
1052
- this.onExit.post({ code, signal, pid: this.pid });
1053
- }
1054
- #handleLog(data) {
1055
- console.log(data.toString());
1056
- }
1057
- #handleStdErr(data) {
1058
- const text = data.toString();
1059
- console.error(text);
1060
- if (this._stderr.length > 100) {
1061
- this._stderr.shift();
1062
- }
1063
- this._stderr.push(text);
1064
- }
1065
- async kill(signal, timeoutInMs) {
1066
- this._isBeingKilled = true;
1067
- const killTimeout = this.onExit.waitFor(timeoutInMs);
1068
- this.onIsBeingKilled.post(this);
1069
- this._child?.kill(signal);
1070
- if (timeoutInMs) {
1071
- await killTimeout;
1072
- }
1073
- }
1074
- get isBeingKilled() {
1075
- return this._isBeingKilled || this._child?.killed;
1076
- }
1077
- get pid() {
1078
- return this._childPid;
1079
- }
1080
- };
1081
-
1082
- // src/workers/prod/entry-point.ts
1083
- import { checkpointSafeTimeout, unboundedTimeout } from "@trigger.dev/core/v3/utils/timers";
1084
- import { randomUUID } from "node:crypto";
1085
- import { readFile } from "node:fs/promises";
1086
- import { createServer } from "node:http";
1087
- import { setTimeout as timeout2 } from "node:timers/promises";
1088
- import { Evt as Evt2 } from "evt";
1089
- var HTTP_SERVER_PORT = Number(process.env.HTTP_SERVER_PORT || getRandomPortNumber());
1090
- var COORDINATOR_HOST = process.env.COORDINATOR_HOST || "127.0.0.1";
1091
- var COORDINATOR_PORT = Number(process.env.COORDINATOR_PORT || 50080);
1092
- var MACHINE_NAME = process.env.MACHINE_NAME || "local";
1093
- var POD_NAME = process.env.POD_NAME || "some-pod";
1094
- var SHORT_HASH = process.env.TRIGGER_CONTENT_HASH.slice(0, 9);
1095
- var logger = new SimpleLogger(`[${MACHINE_NAME}][${SHORT_HASH}]`);
1096
- var defaultBackoff = new ExponentialBackoff("FullJitter", {
1097
- maxRetries: 5
1098
- });
1099
- var ProdWorker = class {
1100
- constructor(port, host = "0.0.0.0") {
1101
- this.host = host;
1102
- process.on("SIGTERM", this.#handleSignal.bind(this, "SIGTERM"));
1103
- this.#coordinatorSocket = this.#createCoordinatorSocket(COORDINATOR_HOST);
1104
- this.#backgroundWorker = this.#createBackgroundWorker();
1105
- this.#httpPort = port;
1106
- this.#httpServer = this.#createHttpServer();
1107
- }
1108
- apiUrl = process.env.TRIGGER_API_URL;
1109
- apiKey = process.env.TRIGGER_SECRET_KEY;
1110
- contentHash = process.env.TRIGGER_CONTENT_HASH;
1111
- projectRef = process.env.TRIGGER_PROJECT_REF;
1112
- envId = process.env.TRIGGER_ENV_ID;
1113
- runId = process.env.TRIGGER_RUN_ID || "index-only";
1114
- deploymentId = process.env.TRIGGER_DEPLOYMENT_ID;
1115
- deploymentVersion = process.env.TRIGGER_DEPLOYMENT_VERSION;
1116
- runningInKubernetes = !!process.env.KUBERNETES_PORT;
1117
- executing = false;
1118
- completed = /* @__PURE__ */ new Set();
1119
- paused = false;
1120
- attemptFriendlyId;
1121
- attemptNumber;
1122
- nextResumeAfter;
1123
- waitForPostStart = false;
1124
- connectionCount = 0;
1125
- restoreNotification = Evt2.create();
1126
- waitForTaskReplay;
1127
- waitForBatchReplay;
1128
- readyForLazyAttemptReplay;
1129
- durationResumeFallback;
1130
- #httpPort;
1131
- #backgroundWorker;
1132
- #httpServer;
1133
- #coordinatorSocket;
1134
- async #handleSignal(signal) {
1135
- logger.log("Received signal", { signal });
1136
- if (signal === "SIGTERM") {
1137
- let gracefulExitTimeoutElapsed = false;
1138
- if (this.executing) {
1139
- const terminationGracePeriodSeconds = 60 * 60;
1140
- logger.log("Waiting for attempt to complete before exiting", {
1141
- terminationGracePeriodSeconds
1142
- });
1143
- await timeout2(terminationGracePeriodSeconds * 1e3 - 5e3);
1144
- gracefulExitTimeoutElapsed = true;
1145
- logger.log("Termination timeout reached, exiting gracefully.");
1146
- } else {
1147
- logger.log("Not executing, exiting immediately.");
1148
- }
1149
- await this.#exitGracefully(gracefulExitTimeoutElapsed);
1150
- return;
1151
- }
1152
- logger.log("Unhandled signal", { signal });
1153
- }
1154
- async #exitGracefully(gracefulExitTimeoutElapsed = false, exitCode = 0) {
1155
- await this.#backgroundWorker.close(gracefulExitTimeoutElapsed);
1156
- if (!gracefulExitTimeoutElapsed) {
1157
- process.exit(exitCode);
1158
- }
1159
- }
1160
- async #reconnectAfterPostStart() {
1161
- this.waitForPostStart = false;
1162
- this.#coordinatorSocket.close();
1163
- this.connectionCount = 0;
1164
- let coordinatorHost = COORDINATOR_HOST;
1165
- try {
1166
- if (this.runningInKubernetes) {
1167
- coordinatorHost = (await readFile("/etc/taskinfo/coordinator-host", "utf-8")).replace(
1168
- "\n",
1169
- ""
1170
- );
1171
- logger.log("reconnecting", {
1172
- coordinatorHost: {
1173
- fromEnv: COORDINATOR_HOST,
1174
- fromVolume: coordinatorHost,
1175
- current: this.#coordinatorSocket.socket.io.opts.hostname
1176
- }
1177
- });
1178
- }
1179
- } catch (error) {
1180
- logger.error("taskinfo read error during reconnect", {
1181
- error: error instanceof Error ? error.message : error
1182
- });
1183
- } finally {
1184
- this.#coordinatorSocket = this.#createCoordinatorSocket(coordinatorHost);
1185
- }
1186
- }
1187
- // MARK: TASK WAIT
1188
- #waitForTaskHandlerFactory(workerId) {
1189
- return async (message, replayIdempotencyKey) => {
1190
- logger.log("onWaitForTask", { workerId, message });
1191
- if (this.nextResumeAfter) {
1192
- logger.error("Already waiting for resume, skipping wait for task", {
1193
- nextResumeAfter: this.nextResumeAfter
1194
- });
1195
- return;
1196
- }
1197
- const waitForTask = await defaultBackoff.execute(async ({ retry }) => {
1198
- logger.log("Wait for task with backoff", { retry });
1199
- if (!this.attemptFriendlyId) {
1200
- logger.error("Failed to send wait message, attempt friendly ID not set", { message });
1201
- throw new ExponentialBackoff.StopRetrying("No attempt ID");
1202
- }
1203
- return await this.#coordinatorSocket.socket.timeout(2e4).emitWithAck("WAIT_FOR_TASK", {
1204
- version: "v2",
1205
- friendlyId: message.friendlyId,
1206
- attemptFriendlyId: this.attemptFriendlyId
1207
- });
1208
- });
1209
- if (!waitForTask.success) {
1210
- logger.error("Failed to wait for task with backoff", {
1211
- cause: waitForTask.cause,
1212
- error: waitForTask.error
1213
- });
1214
- this.#emitUnrecoverableError(
1215
- "WaitForTaskFailed",
1216
- `${waitForTask.cause}: ${waitForTask.error}`
1217
- );
1218
- return;
1219
- }
1220
- const { willCheckpointAndRestore } = waitForTask.result;
1221
- await this.#prepareForWait("WAIT_FOR_TASK", willCheckpointAndRestore);
1222
- if (willCheckpointAndRestore) {
1223
- if (!this.waitForTaskReplay) {
1224
- this.waitForTaskReplay = {
1225
- message,
1226
- attempt: 1,
1227
- idempotencyKey: randomUUID()
1228
- };
1229
- } else {
1230
- if (replayIdempotencyKey && replayIdempotencyKey !== this.waitForTaskReplay.idempotencyKey) {
1231
- logger.error(
1232
- "wait for task handler called with mismatched idempotency key, won't overwrite replay request"
1233
- );
1234
- return;
1235
- }
1236
- this.waitForTaskReplay.attempt++;
1237
- }
1238
- }
1239
- };
1240
- }
1241
- // MARK: BATCH WAIT
1242
- #waitForBatchHandlerFactory(workerId) {
1243
- return async (message, replayIdempotencyKey) => {
1244
- logger.log("onWaitForBatch", { workerId, message });
1245
- if (this.nextResumeAfter) {
1246
- logger.error("Already waiting for resume, skipping wait for batch", {
1247
- nextResumeAfter: this.nextResumeAfter
1248
- });
1249
- return;
1250
- }
1251
- const waitForBatch = await defaultBackoff.execute(async ({ retry }) => {
1252
- logger.log("Wait for batch with backoff", { retry });
1253
- if (!this.attemptFriendlyId) {
1254
- logger.error("Failed to send wait message, attempt friendly ID not set", { message });
1255
- throw new ExponentialBackoff.StopRetrying("No attempt ID");
1256
- }
1257
- return await this.#coordinatorSocket.socket.timeout(2e4).emitWithAck("WAIT_FOR_BATCH", {
1258
- version: "v2",
1259
- batchFriendlyId: message.batchFriendlyId,
1260
- runFriendlyIds: message.runFriendlyIds,
1261
- attemptFriendlyId: this.attemptFriendlyId
1262
- });
1263
- });
1264
- if (!waitForBatch.success) {
1265
- logger.error("Failed to wait for batch with backoff", {
1266
- cause: waitForBatch.cause,
1267
- error: waitForBatch.error
1268
- });
1269
- this.#emitUnrecoverableError(
1270
- "WaitForBatchFailed",
1271
- `${waitForBatch.cause}: ${waitForBatch.error}`
1272
- );
1273
- return;
1274
- }
1275
- const { willCheckpointAndRestore } = waitForBatch.result;
1276
- await this.#prepareForWait("WAIT_FOR_BATCH", willCheckpointAndRestore);
1277
- if (willCheckpointAndRestore) {
1278
- if (!this.waitForBatchReplay) {
1279
- this.waitForBatchReplay = {
1280
- message,
1281
- attempt: 1,
1282
- idempotencyKey: randomUUID()
1283
- };
1284
- } else {
1285
- if (replayIdempotencyKey && replayIdempotencyKey !== this.waitForBatchReplay.idempotencyKey) {
1286
- logger.error(
1287
- "wait for task handler called with mismatched idempotency key, won't overwrite replay request"
1288
- );
1289
- return;
1290
- }
1291
- this.waitForBatchReplay.attempt++;
1292
- }
1293
- }
1294
- };
1295
- }
1296
- // MARK: WORKER CREATION
1297
- #createBackgroundWorker() {
1298
- const workerId = randomUUID();
1299
- logger.log("Creating background worker", { workerId });
1300
- const backgroundWorker = new ProdBackgroundWorker("worker.js", {
1301
- projectConfig: __PROJECT_CONFIG__,
1302
- env: {
1303
- ...gatherProcessEnv(),
1304
- TRIGGER_API_URL: this.apiUrl,
1305
- TRIGGER_SECRET_KEY: this.apiKey,
1306
- OTEL_EXPORTER_OTLP_ENDPOINT: process.env.OTEL_EXPORTER_OTLP_ENDPOINT ?? "http://0.0.0.0:4318"
1307
- },
1308
- contentHash: this.contentHash
1309
- });
1310
- backgroundWorker.onTaskHeartbeat.attach((attemptFriendlyId) => {
1311
- logger.log("onTaskHeartbeat", {
1312
- workerId,
1313
- attemptFriendlyId
1314
- });
1315
- this.#coordinatorSocket.socket.volatile.emit("TASK_HEARTBEAT", {
1316
- version: "v1",
1317
- attemptFriendlyId
1318
- });
1319
- });
1320
- backgroundWorker.onTaskRunHeartbeat.attach((runId) => {
1321
- logger.log("onTaskRunHeartbeat", {
1322
- workerId,
1323
- runId
1324
- });
1325
- this.#coordinatorSocket.socket.volatile.emit("TASK_RUN_HEARTBEAT", { version: "v1", runId });
1326
- });
1327
- backgroundWorker.onCreateTaskRunAttempt.attach(async (message) => {
1328
- logger.log("onCreateTaskRunAttempt()", {
1329
- workerId,
1330
- message
1331
- });
1332
- const createAttempt = await defaultBackoff.execute(async ({ retry }) => {
1333
- logger.log("Create task run attempt with backoff", { retry });
1334
- return await this.#coordinatorSocket.socket.timeout(15e3).emitWithAck("CREATE_TASK_RUN_ATTEMPT", {
1335
- version: "v1",
1336
- runId: message.runId
1337
- });
1338
- });
1339
- if (!createAttempt.success) {
1340
- backgroundWorker.attemptCreatedNotification.post({
1341
- success: false,
1342
- reason: `Failed to create attempt with backoff due to ${createAttempt.cause}. ${createAttempt.error}`
1343
- });
1344
- return;
1345
- }
1346
- if (!createAttempt.result.success) {
1347
- backgroundWorker.attemptCreatedNotification.post({
1348
- success: false,
1349
- reason: createAttempt.result.reason
1350
- });
1351
- return;
1352
- }
1353
- backgroundWorker.attemptCreatedNotification.post({
1354
- success: true,
1355
- execution: createAttempt.result.executionPayload.execution
1356
- });
1357
- });
1358
- backgroundWorker.attemptCreatedNotification.attach((message) => {
1359
- logger.log("attemptCreatedNotification", {
1360
- workerId,
1361
- success: message.success,
1362
- ...message.success ? {
1363
- attempt: message.execution.attempt,
1364
- queue: message.execution.queue,
1365
- worker: message.execution.worker,
1366
- machine: message.execution.machine
1367
- } : {
1368
- reason: message.reason
1369
- }
1370
- });
1371
- if (!message.success) {
1372
- return;
1373
- }
1374
- this.attemptFriendlyId = message.execution.attempt.id;
1375
- this.attemptNumber = message.execution.attempt.number;
1376
- });
1377
- backgroundWorker.onWaitForDuration.attach(async (message) => {
1378
- logger.log("onWaitForDuration", {
1379
- workerId,
1380
- ...message,
1381
- drift: Date.now() - message.now
1382
- });
1383
- if (this.nextResumeAfter) {
1384
- logger.error("Already waiting for resume, skipping wait for duration", {
1385
- nextResumeAfter: this.nextResumeAfter
1386
- });
1387
- return;
1388
- }
1389
- noResume: {
1390
- const { ms, waitThresholdInMs } = message;
1391
- const internalTimeout = unboundedTimeout(ms, "internal");
1392
- const checkpointSafeInternalTimeout = checkpointSafeTimeout(ms);
1393
- if (ms < waitThresholdInMs) {
1394
- await internalTimeout;
1395
- break noResume;
1396
- }
1397
- const waitForDuration = await defaultBackoff.execute(async ({ retry }) => {
1398
- logger.log("Wait for duration with backoff", { retry });
1399
- if (!this.attemptFriendlyId) {
1400
- logger.error("Failed to send wait message, attempt friendly ID not set", { message });
1401
- throw new ExponentialBackoff.StopRetrying("No attempt ID");
1402
- }
1403
- return await this.#coordinatorSocket.socket.timeout(2e4).emitWithAck("WAIT_FOR_DURATION", {
1404
- ...message,
1405
- attemptFriendlyId: this.attemptFriendlyId
1406
- });
1407
- });
1408
- if (!waitForDuration.success) {
1409
- logger.error("Failed to wait for duration with backoff", {
1410
- cause: waitForDuration.cause,
1411
- error: waitForDuration.error
1412
- });
1413
- this.#emitUnrecoverableError(
1414
- "WaitForDurationFailed",
1415
- `${waitForDuration.cause}: ${waitForDuration.error}`
1416
- );
1417
- return;
1418
- }
1419
- const { willCheckpointAndRestore } = waitForDuration.result;
1420
- if (!willCheckpointAndRestore) {
1421
- await internalTimeout;
1422
- break noResume;
1423
- }
1424
- await this.#prepareForWait("WAIT_FOR_DURATION", willCheckpointAndRestore);
1425
- await Promise.race([internalTimeout, checkpointSafeInternalTimeout]);
1426
- const idempotencyKey = randomUUID();
1427
- this.durationResumeFallback = { idempotencyKey };
1428
- try {
1429
- await this.restoreNotification.waitFor(5e3);
1430
- } catch (error) {
1431
- logger.error("Did not receive restore notification in time", {
1432
- error
1433
- });
1434
- }
1435
- try {
1436
- const { checkpointCanceled } = await this.#coordinatorSocket.socket.timeout(15e3).emitWithAck("CANCEL_CHECKPOINT", {
1437
- version: "v2",
1438
- reason: "WAIT_FOR_DURATION"
1439
- });
1440
- logger.log("onCancelCheckpoint coordinator response", { checkpointCanceled });
1441
- if (checkpointCanceled) {
1442
- break noResume;
1443
- }
1444
- logger.log("Waiting for external duration resume as we may have been restored");
1445
- setTimeout(() => {
1446
- if (!this.durationResumeFallback) {
1447
- logger.error("Already resumed after duration, skipping fallback");
1448
- return;
1449
- }
1450
- if (this.durationResumeFallback.idempotencyKey !== idempotencyKey) {
1451
- logger.error("Duration resume idempotency key mismatch, skipping fallback");
1452
- return;
1453
- }
1454
- logger.log("Resuming after duration with fallback");
1455
- this.#resumeAfterDuration();
1456
- }, 15e3);
1457
- } catch (error) {
1458
- logger.debug("Checkpoint cancellation timed out", {
1459
- workerId,
1460
- message,
1461
- error
1462
- });
1463
- }
1464
- return;
1465
- }
1466
- this.#resumeAfterDuration();
1467
- });
1468
- backgroundWorker.onWaitForTask.attach(this.#waitForTaskHandlerFactory(workerId).bind(this));
1469
- backgroundWorker.onWaitForBatch.attach(this.#waitForBatchHandlerFactory(workerId).bind(this));
1470
- return backgroundWorker;
1471
- }
1472
- async #prepareForWait(reason, willCheckpointAndRestore) {
1473
- logger.log(`prepare for ${reason}`, { willCheckpointAndRestore });
1474
- if (this.nextResumeAfter) {
1475
- logger.error("Already waiting for resume, skipping prepare for wait", {
1476
- nextResumeAfter: this.nextResumeAfter,
1477
- params: {
1478
- reason,
1479
- willCheckpointAndRestore
1480
- }
1481
- });
1482
- return;
1483
- }
1484
- if (!willCheckpointAndRestore) {
1485
- return;
1486
- }
1487
- this.paused = true;
1488
- this.nextResumeAfter = reason;
1489
- this.waitForPostStart = true;
1490
- await this.#prepareForCheckpoint();
1491
- }
1492
- // MARK: RETRY PREP
1493
- async #prepareForRetry(shouldExit, exitCode) {
1494
- logger.log("prepare for retry", { shouldExit, exitCode });
1495
- if (shouldExit) {
1496
- await this.#exitGracefully(false, exitCode);
1497
- return;
1498
- }
1499
- this.paused = false;
1500
- this.waitForPostStart = false;
1501
- this.executing = false;
1502
- this.attemptFriendlyId = void 0;
1503
- this.attemptNumber = void 0;
1504
- }
1505
- // MARK: CHECKPOINT PREP
1506
- async #prepareForCheckpoint(flush = true) {
1507
- if (flush) {
1508
- try {
1509
- await this.#backgroundWorker.flushTelemetry();
1510
- } catch (error) {
1511
- logger.error(
1512
- "Failed to flush telemetry while preparing for checkpoint, will proceed anyway",
1513
- { error }
1514
- );
1515
- }
1516
- }
1517
- try {
1518
- await this.#backgroundWorker.forceKillOldTaskRunProcesses();
1519
- } catch (error) {
1520
- logger.error(
1521
- "Failed to kill previous worker while preparing for checkpoint, will proceed anyway",
1522
- { error }
1523
- );
1524
- }
1525
- this.#readyForCheckpoint();
1526
- }
1527
- #resumeAfterDuration() {
1528
- this.paused = false;
1529
- this.nextResumeAfter = void 0;
1530
- this.waitForPostStart = false;
1531
- this.durationResumeFallback = void 0;
1532
- this.#backgroundWorker.waitCompletedNotification();
1533
- }
1534
- async #readyForLazyAttempt() {
1535
- const idempotencyKey = randomUUID();
1536
- this.readyForLazyAttemptReplay = {
1537
- idempotencyKey
1538
- };
1539
- for await (const { delay, retry } of defaultBackoff.min(10).maxRetries(3)) {
1540
- if (retry > 0) {
1541
- logger.log("retrying ready for lazy attempt", { retry });
1542
- }
1543
- this.#coordinatorSocket.socket.emit("READY_FOR_LAZY_ATTEMPT", {
1544
- version: "v1",
1545
- runId: this.runId,
1546
- totalCompletions: this.completed.size
1547
- });
1548
- await timeout2(delay.milliseconds);
1549
- if (!this.readyForLazyAttemptReplay) {
1550
- logger.error("replay ready for lazy attempt cancelled, discarding", {
1551
- idempotencyKey
1552
- });
1553
- return;
1554
- }
1555
- if (idempotencyKey !== this.readyForLazyAttemptReplay.idempotencyKey) {
1556
- logger.error("replay ready for lazy attempt idempotency key mismatch, discarding", {
1557
- idempotencyKey,
1558
- newIdempotencyKey: this.readyForLazyAttemptReplay.idempotencyKey
1559
- });
1560
- return;
1561
- }
1562
- }
1563
- this.#failRun(this.runId, "Failed to receive execute request in a reasonable time");
1564
- }
1565
- #readyForCheckpoint() {
1566
- this.#coordinatorSocket.socket.emit("READY_FOR_CHECKPOINT", { version: "v1" });
1567
- }
1568
- #failRun(anyRunId, error) {
1569
- logger.error("Failing run", { anyRunId, error });
1570
- const completion = {
1571
- ok: false,
1572
- id: anyRunId,
1573
- retry: void 0,
1574
- error: error instanceof Error ? {
1575
- type: "BUILT_IN_ERROR",
1576
- name: error.name,
1577
- message: error.message,
1578
- stackTrace: error.stack ?? ""
1579
- } : {
1580
- type: "BUILT_IN_ERROR",
1581
- name: "UnknownError",
1582
- message: String(error),
1583
- stackTrace: ""
1584
- }
1585
- };
1586
- this.#coordinatorSocket.socket.emit("TASK_RUN_FAILED_TO_RUN", {
1587
- version: "v1",
1588
- completion
1589
- });
1590
- }
1591
- // MARK: ATTEMPT COMPLETION
1592
- async #submitAttemptCompletion(execution, completion, replayIdempotencyKey) {
1593
- const taskRunCompleted = await defaultBackoff.execute(async ({ retry }) => {
1594
- logger.log("Submit attempt completion with backoff", { retry });
1595
- return await this.#coordinatorSocket.socket.timeout(2e4).emitWithAck("TASK_RUN_COMPLETED", {
1596
- version: "v2",
1597
- execution,
1598
- completion
1599
- });
1600
- });
1601
- if (!taskRunCompleted.success) {
1602
- logger.error("Failed to complete lazy attempt with backoff", {
1603
- cause: taskRunCompleted.cause,
1604
- error: taskRunCompleted.error
1605
- });
1606
- this.#failRun(execution.run.id, taskRunCompleted.error);
1607
- return;
1608
- }
1609
- const { willCheckpointAndRestore, shouldExit } = taskRunCompleted.result;
1610
- logger.log("completion acknowledged", { willCheckpointAndRestore, shouldExit });
1611
- const exitCode = !completion.ok && completion.error.type === "INTERNAL_ERROR" && completion.error.code === TaskRunErrorCodes2.TASK_PROCESS_EXITED_WITH_NON_ZERO_CODE ? EXIT_CODE_CHILD_NONZERO : 0;
1612
- await this.#prepareForRetry(shouldExit, exitCode);
1613
- if (willCheckpointAndRestore) {
1614
- logger.error("This worker should never be checkpointed between attempts. This is a bug.");
1615
- }
1616
- }
1617
- #returnValidatedExtraHeaders(headers) {
1618
- for (const [key, value] of Object.entries(headers)) {
1619
- if (value === void 0) {
1620
- throw new Error(`Extra header is undefined: ${key}`);
1621
- }
1622
- }
1623
- return headers;
1624
- }
1625
- // MARK: COORDINATOR SOCKET
1626
- #createCoordinatorSocket(host) {
1627
- const extraHeaders = this.#returnValidatedExtraHeaders({
1628
- "x-machine-name": MACHINE_NAME,
1629
- "x-pod-name": POD_NAME,
1630
- "x-trigger-content-hash": this.contentHash,
1631
- "x-trigger-project-ref": this.projectRef,
1632
- "x-trigger-env-id": this.envId,
1633
- "x-trigger-deployment-id": this.deploymentId,
1634
- "x-trigger-run-id": this.runId,
1635
- "x-trigger-deployment-version": this.deploymentVersion
1636
- });
1637
- if (this.attemptFriendlyId) {
1638
- extraHeaders["x-trigger-attempt-friendly-id"] = this.attemptFriendlyId;
1639
- }
1640
- if (this.attemptNumber !== void 0) {
1641
- extraHeaders["x-trigger-attempt-number"] = String(this.attemptNumber);
1642
- }
1643
- logger.log(`connecting to coordinator: ${host}:${COORDINATOR_PORT}`);
1644
- logger.debug(`connecting with extra headers`, { extraHeaders });
1645
- const coordinatorConnection = new ZodSocketConnection({
1646
- namespace: "prod-worker",
1647
- host,
1648
- port: COORDINATOR_PORT,
1649
- clientMessages: ProdWorkerToCoordinatorMessages,
1650
- serverMessages: CoordinatorToProdWorkerMessages,
1651
- extraHeaders,
1652
- ioOptions: {
1653
- reconnectionDelay: 1e3,
1654
- reconnectionDelayMax: 3e3
1655
- },
1656
- handlers: {
1657
- RESUME_AFTER_DEPENDENCY: async ({ completions }) => {
1658
- if (!this.paused) {
1659
- logger.error("Failed to resume after dependency: Worker not paused");
1660
- return;
1661
- }
1662
- if (completions.length === 0) {
1663
- logger.error("Failed to resume after dependency: No completions");
1664
- return;
1665
- }
1666
- if (this.nextResumeAfter !== "WAIT_FOR_TASK" && this.nextResumeAfter !== "WAIT_FOR_BATCH") {
1667
- logger.error("Failed to resume after dependency: Invalid next resume", {
1668
- nextResumeAfter: this.nextResumeAfter
1669
- });
1670
- return;
1671
- }
1672
- if (this.nextResumeAfter === "WAIT_FOR_TASK" && completions.length > 1) {
1673
- logger.error(
1674
- "Failed to resume after dependency: Waiting for single task but got multiple completions",
1675
- {
1676
- completions
1677
- }
1678
- );
1679
- return;
1680
- }
1681
- switch (this.nextResumeAfter) {
1682
- case "WAIT_FOR_TASK": {
1683
- this.waitForTaskReplay = void 0;
1684
- break;
1685
- }
1686
- case "WAIT_FOR_BATCH": {
1687
- this.waitForBatchReplay = void 0;
1688
- break;
1689
- }
1690
- }
1691
- this.paused = false;
1692
- this.nextResumeAfter = void 0;
1693
- this.waitForPostStart = false;
1694
- for (let i = 0; i < completions.length; i++) {
1695
- const completion = completions[i];
1696
- if (!completion)
1697
- continue;
1698
- this.#backgroundWorker.taskRunCompletedNotification(completion);
1699
- }
1700
- },
1701
- RESUME_AFTER_DURATION: async (message) => {
1702
- if (!this.paused) {
1703
- logger.error("worker not paused", {
1704
- attemptId: message.attemptId
1705
- });
1706
- return;
1707
- }
1708
- if (this.nextResumeAfter !== "WAIT_FOR_DURATION") {
1709
- logger.error("not waiting to resume after duration", {
1710
- nextResumeAfter: this.nextResumeAfter
1711
- });
1712
- return;
1713
- }
1714
- this.#resumeAfterDuration();
1715
- },
1716
- EXECUTE_TASK_RUN: async () => {
1717
- this.#failRun(
1718
- this.runId,
1719
- "Received deprecated EXECUTE_TASK_RUN message. Please contact us if you see this error."
1720
- );
1721
- },
1722
- EXECUTE_TASK_RUN_LAZY_ATTEMPT: async (message) => {
1723
- this.readyForLazyAttemptReplay = void 0;
1724
- if (this.executing) {
1725
- logger.error("dropping execute request, already executing");
1726
- return;
1727
- }
1728
- const attemptCount = message.lazyPayload.attemptCount ?? 0;
1729
- logger.log("execute attempt counts", { attemptCount, completed: this.completed.size });
1730
- if (this.completed.size > 0 && this.completed.size >= attemptCount + 1) {
1731
- logger.error("dropping execute request, already completed");
1732
- return;
1733
- }
1734
- this.executing = true;
1735
- try {
1736
- const { completion, execution } = await this.#backgroundWorker.executeTaskRunLazyAttempt(message.lazyPayload);
1737
- logger.log("completed", completion);
1738
- this.completed.add(execution.attempt.id);
1739
- await this.#submitAttemptCompletion(execution, completion);
1740
- } catch (error) {
1741
- logger.error("Failed to complete lazy attempt", {
1742
- error
1743
- });
1744
- this.#failRun(message.lazyPayload.runId, error);
1745
- }
1746
- },
1747
- REQUEST_ATTEMPT_CANCELLATION: async (message) => {
1748
- if (!this.executing) {
1749
- logger.log("dropping cancel request, not executing", { status: this.#status });
1750
- return;
1751
- }
1752
- logger.log("cancelling attempt", { attemptId: message.attemptId, status: this.#status });
1753
- await this.#backgroundWorker.cancelAttempt(message.attemptId);
1754
- },
1755
- REQUEST_EXIT: async (message) => {
1756
- if (message.version === "v2" && message.delayInMs) {
1757
- logger.log("exit requested with delay", { delayInMs: message.delayInMs });
1758
- await timeout2(message.delayInMs);
1759
- }
1760
- this.#coordinatorSocket.close();
1761
- process.exit(0);
1762
- },
1763
- READY_FOR_RETRY: async (message) => {
1764
- if (this.completed.size < 1) {
1765
- logger.error("Received READY_FOR_RETRY but no completions yet. This is a bug.");
1766
- return;
1767
- }
1768
- await this.#readyForLazyAttempt();
1769
- }
1770
- },
1771
- // MARK: ON CONNECTION
1772
- onConnection: async (socket, handler, sender, logger2) => {
1773
- logger2.log("connected to coordinator", {
1774
- status: this.#status,
1775
- connectionCount: ++this.connectionCount
1776
- });
1777
- socket.emit("SET_STATE", {
1778
- version: "v1",
1779
- attemptFriendlyId: this.attemptFriendlyId,
1780
- attemptNumber: this.attemptNumber ? String(this.attemptNumber) : void 0
1781
- });
1782
- try {
1783
- if (this.waitForPostStart) {
1784
- logger2.log("skip connection handler, waiting for post start hook");
1785
- return;
1786
- }
1787
- if (this.paused) {
1788
- if (!this.nextResumeAfter) {
1789
- logger2.error("Missing next resume reason", { status: this.#status });
1790
- this.#emitUnrecoverableError(
1791
- "NoNextResume",
1792
- "Next resume reason not set while resuming from paused state"
1793
- );
1794
- return;
1795
- }
1796
- if (!this.attemptFriendlyId) {
1797
- logger2.error("Missing attempt friendly ID", { status: this.#status });
1798
- this.#emitUnrecoverableError(
1799
- "NoAttemptId",
1800
- "Attempt ID not set while resuming from paused state"
1801
- );
1802
- return;
1803
- }
1804
- if (!this.attemptNumber) {
1805
- logger2.error("Missing attempt number", { status: this.#status });
1806
- this.#emitUnrecoverableError(
1807
- "NoAttemptNumber",
1808
- "Attempt number not set while resuming from paused state"
1809
- );
1810
- return;
1811
- }
1812
- socket.emit("READY_FOR_RESUME", {
1813
- version: "v2",
1814
- attemptFriendlyId: this.attemptFriendlyId,
1815
- attemptNumber: this.attemptNumber,
1816
- type: this.nextResumeAfter
1817
- });
1818
- return;
1819
- }
1820
- if (process.env.INDEX_TASKS === "true") {
1821
- const failIndex = (error) => {
1822
- socket.emit("INDEXING_FAILED", {
1823
- version: "v1",
1824
- deploymentId: this.deploymentId,
1825
- error
1826
- });
1827
- };
1828
- process.removeAllListeners("uncaughtException");
1829
- process.on("uncaughtException", (error) => {
1830
- console.error("Uncaught exception while indexing", error);
1831
- failIndex(error);
1832
- });
1833
- try {
1834
- const taskResources = await this.#initializeWorker();
1835
- const indexTasks = await defaultBackoff.maxRetries(3).execute(async () => {
1836
- return await socket.timeout(2e4).emitWithAck("INDEX_TASKS", {
1837
- version: "v2",
1838
- deploymentId: this.deploymentId,
1839
- ...taskResources,
1840
- supportsLazyAttempts: true
1841
- });
1842
- });
1843
- if (!indexTasks.success || !indexTasks.result.success) {
1844
- logger2.error("indexing failure, shutting down..", { indexTasks });
1845
- process.exit(1);
1846
- } else {
1847
- logger2.info("indexing done, shutting down..");
1848
- process.exit(0);
1849
- }
1850
- } catch (e) {
1851
- const stderr = this.#backgroundWorker.stderr.join("\n");
1852
- if (e instanceof TaskMetadataParseError) {
1853
- logger2.error("tasks metadata parse error", {
1854
- zodIssues: e.zodIssues,
1855
- tasks: e.tasks
1856
- });
1857
- failIndex({
1858
- name: "TaskMetadataParseError",
1859
- message: "There was an error parsing the task metadata",
1860
- stack: JSON.stringify({ zodIssues: e.zodIssues, tasks: e.tasks }),
1861
- stderr
1862
- });
1863
- } else if (e instanceof UncaughtExceptionError) {
1864
- const error = {
1865
- name: e.originalError.name,
1866
- message: e.originalError.message,
1867
- stack: e.originalError.stack,
1868
- stderr
1869
- };
1870
- logger2.error("uncaught exception", { originalError: error });
1871
- failIndex(error);
1872
- } else if (e instanceof Error) {
1873
- const error = {
1874
- name: e.name,
1875
- message: e.message,
1876
- stack: e.stack,
1877
- stderr
1878
- };
1879
- logger2.error("error", { error });
1880
- failIndex(error);
1881
- } else if (typeof e === "string") {
1882
- logger2.error("string error", { error: { message: e } });
1883
- failIndex({
1884
- name: "Error",
1885
- message: e,
1886
- stderr
1887
- });
1888
- } else {
1889
- logger2.error("unknown error", { error: e });
1890
- failIndex({
1891
- name: "Error",
1892
- message: "Unknown error",
1893
- stderr
1894
- });
1895
- }
1896
- await timeout2(1e3);
1897
- process.exit(EXIT_CODE_ALREADY_HANDLED);
1898
- }
1899
- }
1900
- if (this.executing) {
1901
- return;
1902
- }
1903
- process.removeAllListeners("uncaughtException");
1904
- process.on("uncaughtException", (error) => {
1905
- console.error("Uncaught exception during run", error);
1906
- this.#failRun(this.runId, error);
1907
- });
1908
- await this.#readyForLazyAttempt();
1909
- } catch (error) {
1910
- logger2.error("connection handler error", { error });
1911
- } finally {
1912
- if (this.connectionCount === 1) {
1913
- return;
1914
- }
1915
- this.#handleReplays();
1916
- }
1917
- },
1918
- onError: async (socket, err, logger2) => {
1919
- logger2.error("onError", {
1920
- error: {
1921
- name: err.name,
1922
- message: err.message
1923
- }
1924
- });
1925
- }
1926
- });
1927
- return coordinatorConnection;
1928
- }
1929
- // MARK: REPLAYS
1930
- async #handleReplays() {
1931
- const backoff = new ExponentialBackoff().type("FullJitter").maxRetries(3);
1932
- const replayCancellationDelay = 2e4;
1933
- if (this.waitForTaskReplay) {
1934
- logger.log("replaying wait for task", { ...this.waitForTaskReplay });
1935
- const { idempotencyKey, message, attempt } = this.waitForTaskReplay;
1936
- await timeout2(replayCancellationDelay);
1937
- if (!this.waitForTaskReplay) {
1938
- logger.error("wait for task replay cancelled, discarding", {
1939
- originalMessage: { idempotencyKey, message, attempt }
1940
- });
1941
- return;
1942
- }
1943
- if (idempotencyKey !== this.waitForTaskReplay.idempotencyKey) {
1944
- logger.error("wait for task replay idempotency key mismatch, discarding", {
1945
- originalMessage: { idempotencyKey, message, attempt },
1946
- newMessage: this.waitForTaskReplay
1947
- });
1948
- return;
1949
- }
1950
- try {
1951
- await backoff.wait(attempt + 1);
1952
- await this.#waitForTaskHandlerFactory("replay")(message, idempotencyKey);
1953
- } catch (error) {
1954
- if (error instanceof ExponentialBackoff.RetryLimitExceeded) {
1955
- logger.error("wait for task replay retry limit exceeded", { error });
1956
- } else {
1957
- logger.error("wait for task replay error", { error });
1958
- }
1959
- }
1960
- return;
1961
- }
1962
- if (this.waitForBatchReplay) {
1963
- logger.log("replaying wait for batch", {
1964
- ...this.waitForBatchReplay,
1965
- cancellationDelay: replayCancellationDelay
1966
- });
1967
- const { idempotencyKey, message, attempt } = this.waitForBatchReplay;
1968
- await timeout2(replayCancellationDelay);
1969
- if (!this.waitForBatchReplay) {
1970
- logger.error("wait for batch replay cancelled, discarding", {
1971
- originalMessage: { idempotencyKey, message, attempt }
1972
- });
1973
- return;
1974
- }
1975
- if (idempotencyKey !== this.waitForBatchReplay.idempotencyKey) {
1976
- logger.error("wait for batch replay idempotency key mismatch, discarding", {
1977
- originalMessage: { idempotencyKey, message, attempt },
1978
- newMessage: this.waitForBatchReplay
1979
- });
1980
- return;
1981
- }
1982
- try {
1983
- await backoff.wait(attempt + 1);
1984
- await this.#waitForBatchHandlerFactory("replay")(message, idempotencyKey);
1985
- } catch (error) {
1986
- if (error instanceof ExponentialBackoff.RetryLimitExceeded) {
1987
- logger.error("wait for batch replay retry limit exceeded", { error });
1988
- } else {
1989
- logger.error("wait for batch replay error", { error });
1990
- }
1991
- }
1992
- return;
1993
- }
1994
- }
1995
- // MARK: HTTP SERVER
1996
- #createHttpServer() {
1997
- const httpServer = createServer(async (req, res) => {
1998
- logger.log(`[${req.method}]`, req.url);
1999
- const reply = new HttpReply(res);
2000
- try {
2001
- const url = new URL(req.url ?? "", `http://${req.headers.host}`);
2002
- switch (url.pathname) {
2003
- case "/health": {
2004
- return reply.text("ok");
2005
- }
2006
- case "/status": {
2007
- return reply.json(this.#status);
2008
- }
2009
- case "/connect": {
2010
- this.#coordinatorSocket.connect();
2011
- return reply.text("Connected to coordinator");
2012
- }
2013
- case "/close": {
2014
- this.#coordinatorSocket.close();
2015
- this.connectionCount = 0;
2016
- return reply.text("Disconnected from coordinator");
2017
- }
2018
- case "/test": {
2019
- await this.#coordinatorSocket.socket.timeout(1e4).emitWithAck("TEST", {
2020
- version: "v1"
2021
- });
2022
- return reply.text("Received ACK from coordinator");
2023
- }
2024
- case "/preStop": {
2025
- const cause = PreStopCauses.safeParse(url.searchParams.get("cause"));
2026
- if (!cause.success) {
2027
- logger.error("Failed to parse cause", { cause });
2028
- return reply.text("Failed to parse cause", 400);
2029
- }
2030
- switch (cause.data) {
2031
- case "terminate": {
2032
- break;
2033
- }
2034
- default: {
2035
- logger.error("Unhandled cause", { cause: cause.data });
2036
- break;
2037
- }
2038
- }
2039
- return reply.text("preStop ok");
2040
- }
2041
- case "/postStart": {
2042
- const cause = PostStartCauses.safeParse(url.searchParams.get("cause"));
2043
- if (!cause.success) {
2044
- logger.error("Failed to parse cause", { cause });
2045
- return reply.text("Failed to parse cause", 400);
2046
- }
2047
- switch (cause.data) {
2048
- case "index": {
2049
- break;
2050
- }
2051
- case "create": {
2052
- break;
2053
- }
2054
- case "restore": {
2055
- await this.#reconnectAfterPostStart();
2056
- this.restoreNotification.post();
2057
- break;
2058
- }
2059
- default: {
2060
- logger.error("Unhandled cause", { cause: cause.data });
2061
- break;
2062
- }
2063
- }
2064
- return reply.text("postStart ok");
2065
- }
2066
- default: {
2067
- return reply.empty(404);
2068
- }
2069
- }
2070
- } catch (error) {
2071
- logger.error("HTTP server error", { error });
2072
- reply.empty(500);
2073
- }
2074
- });
2075
- httpServer.on("clientError", (err, socket) => {
2076
- socket.end("HTTP/1.1 400 Bad Request\r\n\r\n");
2077
- });
2078
- httpServer.on("listening", () => {
2079
- logger.log("http server listening on port", this.#httpPort);
2080
- });
2081
- httpServer.on("error", async (error) => {
2082
- if (error.code != "EADDRINUSE") {
2083
- return;
2084
- }
2085
- logger.error(`port ${this.#httpPort} already in use, retrying with random port..`);
2086
- this.#httpPort = getRandomPortNumber();
2087
- await timeout2(100);
2088
- this.start();
2089
- });
2090
- return httpServer;
2091
- }
2092
- async #initializeWorker() {
2093
- const envVars = await this.#fetchEnvironmentVariables();
2094
- await this.#backgroundWorker.initialize({ env: envVars });
2095
- let packageVersion;
2096
- const taskResources = [];
2097
- if (!this.#backgroundWorker.tasks || this.#backgroundWorker.tasks.length === 0) {
2098
- throw new Error(
2099
- `Background Worker started without tasks. Searched in: ${__PROJECT_CONFIG__.triggerDirectories?.join(
2100
- ", "
2101
- )}`
2102
- );
2103
- }
2104
- for (const task of this.#backgroundWorker.tasks) {
2105
- taskResources.push(task);
2106
- packageVersion = task.packageVersion;
2107
- }
2108
- if (!packageVersion) {
2109
- throw new Error(`Background Worker started without package version`);
2110
- }
2111
- return {
2112
- packageVersion,
2113
- tasks: taskResources
2114
- };
2115
- }
2116
- async #fetchEnvironmentVariables() {
2117
- const response = await fetch(`${this.apiUrl}/api/v1/projects/${this.projectRef}/envvars`, {
2118
- method: "GET",
2119
- headers: {
2120
- Authorization: `Bearer ${this.apiKey}`
2121
- }
2122
- });
2123
- if (!response.ok) {
2124
- return {};
2125
- }
2126
- const data = await response.json();
2127
- return data?.variables ?? {};
2128
- }
2129
- get #status() {
2130
- return {
2131
- executing: this.executing,
2132
- paused: this.paused,
2133
- completed: this.completed.size,
2134
- nextResumeAfter: this.nextResumeAfter,
2135
- waitForPostStart: this.waitForPostStart,
2136
- attemptFriendlyId: this.attemptFriendlyId,
2137
- attemptNumber: this.attemptNumber,
2138
- waitForTaskReplay: this.waitForTaskReplay,
2139
- waitForBatchReplay: this.waitForBatchReplay
2140
- };
2141
- }
2142
- #emitUnrecoverableError(name, message) {
2143
- this.#coordinatorSocket.socket.emit("UNRECOVERABLE_ERROR", {
2144
- version: "v1",
2145
- error: {
2146
- name,
2147
- message
2148
- }
2149
- });
2150
- }
2151
- start() {
2152
- this.#httpServer.listen(this.#httpPort, this.host);
2153
- }
2154
- };
2155
- var prodWorker = new ProdWorker(HTTP_SERVER_PORT);
2156
- prodWorker.start();
2157
- function gatherProcessEnv() {
2158
- const env = {
2159
- NODE_ENV: process.env.NODE_ENV ?? "production",
2160
- PATH: process.env.PATH,
2161
- USER: process.env.USER,
2162
- SHELL: process.env.SHELL,
2163
- LANG: process.env.LANG,
2164
- TERM: process.env.TERM,
2165
- NODE_PATH: process.env.NODE_PATH,
2166
- HOME: process.env.HOME,
2167
- NODE_EXTRA_CA_CERTS: process.env.NODE_EXTRA_CA_CERTS
2168
- };
2169
- return Object.fromEntries(Object.entries(env).filter(([key, value]) => value !== void 0));
2170
- }