@shipfox/api-workflows 5.0.0 → 7.0.1

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 (277) hide show
  1. package/.turbo/turbo-build.log +4 -4
  2. package/CHANGELOG.md +89 -0
  3. package/dist/core/agent-defaults.d.ts +17 -0
  4. package/dist/core/agent-defaults.d.ts.map +1 -0
  5. package/dist/core/agent-defaults.js +21 -0
  6. package/dist/core/agent-defaults.js.map +1 -0
  7. package/dist/core/agent-tool-capability-warning.d.ts +4 -0
  8. package/dist/core/agent-tool-capability-warning.d.ts.map +1 -1
  9. package/dist/core/agent-tool-capability-warning.js +15 -17
  10. package/dist/core/agent-tool-capability-warning.js.map +1 -1
  11. package/dist/core/agent-tools.d.ts +11 -9
  12. package/dist/core/agent-tools.d.ts.map +1 -1
  13. package/dist/core/agent-tools.js +23 -18
  14. package/dist/core/agent-tools.js.map +1 -1
  15. package/dist/core/checkout.d.ts +21 -7
  16. package/dist/core/checkout.d.ts.map +1 -1
  17. package/dist/core/checkout.js +23 -11
  18. package/dist/core/checkout.js.map +1 -1
  19. package/dist/core/entities/workflow-run-attempt.d.ts +1 -1
  20. package/dist/core/entities/workflow-run-attempt.d.ts.map +1 -1
  21. package/dist/core/entities/workflow-run-attempt.js.map +1 -1
  22. package/dist/core/job-execution.d.ts +3 -1
  23. package/dist/core/job-execution.d.ts.map +1 -1
  24. package/dist/core/job-execution.js +14 -12
  25. package/dist/core/job-execution.js.map +1 -1
  26. package/dist/core/job-transition/derive-job-execution-outputs.d.ts +1 -1
  27. package/dist/core/job-transition/derive-job-execution-outputs.d.ts.map +1 -1
  28. package/dist/core/job-transition/derive-job-execution-outputs.js.map +1 -1
  29. package/dist/core/job-transition/derive-job-success.js +1 -1
  30. package/dist/core/job-transition/derive-job-success.js.map +1 -1
  31. package/dist/core/listener-execution-materialization.d.ts +3 -3
  32. package/dist/core/listener-execution-materialization.d.ts.map +1 -1
  33. package/dist/core/listener-execution-materialization.js +3 -4
  34. package/dist/core/listener-execution-materialization.js.map +1 -1
  35. package/dist/core/run-workflow.d.ts +12 -1
  36. package/dist/core/run-workflow.d.ts.map +1 -1
  37. package/dist/core/run-workflow.js +13 -8
  38. package/dist/core/run-workflow.js.map +1 -1
  39. package/dist/core/step-config/agent.d.ts +8 -8
  40. package/dist/core/step-config/agent.d.ts.map +1 -1
  41. package/dist/core/step-config/agent.js +12 -10
  42. package/dist/core/step-config/agent.js.map +1 -1
  43. package/dist/core/step-config/complete-step-dispatch-config.d.ts +4 -4
  44. package/dist/core/step-config/complete-step-dispatch-config.d.ts.map +1 -1
  45. package/dist/core/step-config/complete-step-dispatch-config.js +2 -2
  46. package/dist/core/step-config/complete-step-dispatch-config.js.map +1 -1
  47. package/dist/core/step-config/materialize-job-execution-steps.d.ts +3 -3
  48. package/dist/core/step-config/materialize-job-execution-steps.d.ts.map +1 -1
  49. package/dist/core/step-config/materialize-job-execution-steps.js +5 -6
  50. package/dist/core/step-config/materialize-job-execution-steps.js.map +1 -1
  51. package/dist/core/step-config/materialize-workflow-model.d.ts +3 -3
  52. package/dist/core/step-config/materialize-workflow-model.d.ts.map +1 -1
  53. package/dist/core/step-config/materialize-workflow-model.js +4 -4
  54. package/dist/core/step-config/materialize-workflow-model.js.map +1 -1
  55. package/dist/core/step-config/resolve-job-execution-name.d.ts +1 -1
  56. package/dist/core/step-config/resolve-job-execution-name.d.ts.map +1 -1
  57. package/dist/core/step-config/resolve-job-execution-name.js.map +1 -1
  58. package/dist/core/step-config/resolve-step-config.d.ts +4 -4
  59. package/dist/core/step-config/resolve-step-config.d.ts.map +1 -1
  60. package/dist/core/step-config/resolve-step-config.js +6 -6
  61. package/dist/core/step-config/resolve-step-config.js.map +1 -1
  62. package/dist/core/step-config/run.d.ts +1 -1
  63. package/dist/core/step-config/run.d.ts.map +1 -1
  64. package/dist/core/step-config/run.js.map +1 -1
  65. package/dist/core/workflow-run-creation.d.ts +3 -3
  66. package/dist/core/workflow-run-creation.d.ts.map +1 -1
  67. package/dist/core/workflow-run-creation.js +3 -4
  68. package/dist/core/workflow-run-creation.js.map +1 -1
  69. package/dist/db/db.d.ts +16 -4
  70. package/dist/db/db.d.ts.map +1 -1
  71. package/dist/db/job-listeners.d.ts +7 -1
  72. package/dist/db/job-listeners.d.ts.map +1 -1
  73. package/dist/db/job-listeners.js +10 -5
  74. package/dist/db/job-listeners.js.map +1 -1
  75. package/dist/db/runner-lease-table.d.ts +5 -5
  76. package/dist/db/runner-lease-table.d.ts.map +1 -1
  77. package/dist/db/runner-lease-table.js +3 -2
  78. package/dist/db/runner-lease-table.js.map +1 -1
  79. package/dist/db/schema/workflow-run-attempts.d.ts +9 -3
  80. package/dist/db/schema/workflow-run-attempts.d.ts.map +1 -1
  81. package/dist/db/schema/workflow-run-attempts.js +2 -1
  82. package/dist/db/schema/workflow-run-attempts.js.map +1 -1
  83. package/dist/db/schema/workflow-runs.js +1 -1
  84. package/dist/db/schema/workflow-runs.js.map +1 -1
  85. package/dist/db/workflow-runs/job-executions.d.ts +5 -0
  86. package/dist/db/workflow-runs/job-executions.d.ts.map +1 -1
  87. package/dist/db/workflow-runs/job-executions.js +14 -7
  88. package/dist/db/workflow-runs/job-executions.js.map +1 -1
  89. package/dist/db/workflow-runs/jobs.d.ts.map +1 -1
  90. package/dist/db/workflow-runs/jobs.js +3 -1
  91. package/dist/db/workflow-runs/jobs.js.map +1 -1
  92. package/dist/db/workflow-runs/run-create.d.ts +9 -2
  93. package/dist/db/workflow-runs/run-create.d.ts.map +1 -1
  94. package/dist/db/workflow-runs/run-create.js +12 -9
  95. package/dist/db/workflow-runs/run-create.js.map +1 -1
  96. package/dist/db/workflow-runs/run-rerun.d.ts.map +1 -1
  97. package/dist/db/workflow-runs/run-rerun.js +3 -1
  98. package/dist/db/workflow-runs/run-rerun.js.map +1 -1
  99. package/dist/index.d.ts +18 -4
  100. package/dist/index.d.ts.map +1 -1
  101. package/dist/index.js +61 -37
  102. package/dist/index.js.map +1 -1
  103. package/dist/presentation/dto/checkout-token.d.ts +14 -1
  104. package/dist/presentation/dto/checkout-token.d.ts.map +1 -1
  105. package/dist/presentation/dto/checkout-token.js.map +1 -1
  106. package/dist/presentation/index.d.ts +1 -1
  107. package/dist/presentation/index.d.ts.map +1 -1
  108. package/dist/presentation/index.js +1 -1
  109. package/dist/presentation/index.js.map +1 -1
  110. package/dist/presentation/inter-module.d.ts +18 -0
  111. package/dist/presentation/inter-module.d.ts.map +1 -0
  112. package/dist/presentation/inter-module.js +113 -0
  113. package/dist/presentation/inter-module.js.map +1 -0
  114. package/dist/presentation/routes/agent-runtime-config.d.ts +6 -1
  115. package/dist/presentation/routes/agent-runtime-config.d.ts.map +1 -1
  116. package/dist/presentation/routes/agent-runtime-config.js +59 -57
  117. package/dist/presentation/routes/agent-runtime-config.js.map +1 -1
  118. package/dist/presentation/routes/cancel-run.d.ts +2 -1
  119. package/dist/presentation/routes/cancel-run.d.ts.map +1 -1
  120. package/dist/presentation/routes/cancel-run.js +37 -34
  121. package/dist/presentation/routes/cancel-run.js.map +1 -1
  122. package/dist/presentation/routes/checkout-token.d.ts +8 -1
  123. package/dist/presentation/routes/checkout-token.d.ts.map +1 -1
  124. package/dist/presentation/routes/checkout-token.js +65 -47
  125. package/dist/presentation/routes/checkout-token.js.map +1 -1
  126. package/dist/presentation/routes/get-run-aggregates.d.ts +2 -1
  127. package/dist/presentation/routes/get-run-aggregates.d.ts.map +1 -1
  128. package/dist/presentation/routes/get-run-aggregates.js +44 -53
  129. package/dist/presentation/routes/get-run-aggregates.js.map +1 -1
  130. package/dist/presentation/routes/get-run.d.ts +2 -1
  131. package/dist/presentation/routes/get-run.d.ts.map +1 -1
  132. package/dist/presentation/routes/get-run.js +53 -50
  133. package/dist/presentation/routes/get-run.js.map +1 -1
  134. package/dist/presentation/routes/get-step-secrets.d.ts +3 -1
  135. package/dist/presentation/routes/get-step-secrets.d.ts.map +1 -1
  136. package/dist/presentation/routes/get-step-secrets.js +72 -68
  137. package/dist/presentation/routes/get-step-secrets.js.map +1 -1
  138. package/dist/presentation/routes/index.d.ts +19 -2
  139. package/dist/presentation/routes/index.d.ts.map +1 -1
  140. package/dist/presentation/routes/index.js +38 -30
  141. package/dist/presentation/routes/index.js.map +1 -1
  142. package/dist/presentation/routes/leased-step.d.ts +2 -0
  143. package/dist/presentation/routes/leased-step.d.ts.map +1 -1
  144. package/dist/presentation/routes/leased-step.js +1 -2
  145. package/dist/presentation/routes/leased-step.js.map +1 -1
  146. package/dist/presentation/routes/list-run-attempts.d.ts +2 -1
  147. package/dist/presentation/routes/list-run-attempts.d.ts.map +1 -1
  148. package/dist/presentation/routes/list-run-attempts.js +29 -26
  149. package/dist/presentation/routes/list-run-attempts.js.map +1 -1
  150. package/dist/presentation/routes/list-runs.d.ts +2 -1
  151. package/dist/presentation/routes/list-runs.d.ts.map +1 -1
  152. package/dist/presentation/routes/list-runs.js +51 -60
  153. package/dist/presentation/routes/list-runs.js.map +1 -1
  154. package/dist/presentation/routes/next-step.d.ts +10 -1
  155. package/dist/presentation/routes/next-step.d.ts.map +1 -1
  156. package/dist/presentation/routes/next-step.js +64 -50
  157. package/dist/presentation/routes/next-step.js.map +1 -1
  158. package/dist/presentation/routes/project-access.d.ts +10 -0
  159. package/dist/presentation/routes/project-access.d.ts.map +1 -0
  160. package/dist/presentation/routes/project-access.js +17 -0
  161. package/dist/presentation/routes/project-access.js.map +1 -0
  162. package/dist/presentation/routes/report-step.d.ts +2 -1
  163. package/dist/presentation/routes/report-step.d.ts.map +1 -1
  164. package/dist/presentation/routes/report-step.js +62 -61
  165. package/dist/presentation/routes/report-step.js.map +1 -1
  166. package/dist/presentation/routes/require-accessible-run.d.ts +3 -1
  167. package/dist/presentation/routes/require-accessible-run.d.ts.map +1 -1
  168. package/dist/presentation/routes/require-accessible-run.js +3 -6
  169. package/dist/presentation/routes/require-accessible-run.js.map +1 -1
  170. package/dist/presentation/routes/rerun-run.d.ts +2 -1
  171. package/dist/presentation/routes/rerun-run.d.ts.map +1 -1
  172. package/dist/presentation/routes/rerun-run.js +46 -43
  173. package/dist/presentation/routes/rerun-run.js.map +1 -1
  174. package/dist/temporal/activities/index.d.ts +44 -9
  175. package/dist/temporal/activities/index.d.ts.map +1 -1
  176. package/dist/temporal/activities/index.js +13 -9
  177. package/dist/temporal/activities/index.js.map +1 -1
  178. package/dist/temporal/activities/orchestration-activities.d.ts +20 -11
  179. package/dist/temporal/activities/orchestration-activities.d.ts.map +1 -1
  180. package/dist/temporal/activities/orchestration-activities.js +48 -33
  181. package/dist/temporal/activities/orchestration-activities.js.map +1 -1
  182. package/dist/tsconfig.test.tsbuildinfo +1 -1
  183. package/package.json +28 -21
  184. package/src/core/agent-defaults.test.ts +28 -0
  185. package/src/core/agent-defaults.ts +36 -0
  186. package/src/core/agent-tool-capability-warning.test.ts +36 -11
  187. package/src/core/agent-tool-capability-warning.ts +27 -25
  188. package/src/core/agent-tools.ts +33 -36
  189. package/src/core/checkout.test.ts +38 -25
  190. package/src/core/checkout.ts +41 -14
  191. package/src/core/entities/workflow-run-attempt.ts +1 -1
  192. package/src/core/job-execution.ts +19 -9
  193. package/src/core/job-transition/derive-job-execution-outputs.ts +1 -1
  194. package/src/core/job-transition/derive-job-success.ts +1 -1
  195. package/src/core/listener-execution-materialization.test.ts +4 -2
  196. package/src/core/listener-execution-materialization.ts +6 -9
  197. package/src/core/run-workflow.test.ts +43 -38
  198. package/src/core/run-workflow.ts +26 -9
  199. package/src/core/step-config/agent.ts +24 -26
  200. package/src/core/step-config/complete-step-dispatch-config.test.ts +22 -23
  201. package/src/core/step-config/complete-step-dispatch-config.ts +6 -6
  202. package/src/core/step-config/materialize-job-execution-steps.test.ts +34 -23
  203. package/src/core/step-config/materialize-job-execution-steps.ts +38 -39
  204. package/src/core/step-config/materialize-workflow-model.test.ts +94 -82
  205. package/src/core/step-config/materialize-workflow-model.ts +31 -29
  206. package/src/core/step-config/resolve-job-execution-name.ts +1 -1
  207. package/src/core/step-config/resolve-step-config.ts +11 -9
  208. package/src/core/step-config/run.ts +1 -1
  209. package/src/core/workflow-run-creation.test.ts +14 -10
  210. package/src/core/workflow-run-creation.ts +6 -9
  211. package/src/db/job-listeners.ts +20 -5
  212. package/src/db/runner-lease-table.ts +2 -2
  213. package/src/db/schema/workflow-run-attempts.ts +7 -3
  214. package/src/db/schema/workflow-runs.ts +1 -1
  215. package/src/db/workflow-runs/job-executions.ts +15 -1
  216. package/src/db/workflow-runs/jobs.ts +4 -1
  217. package/src/db/workflow-runs/run-create.test.ts +7 -1
  218. package/src/db/workflow-runs/run-create.ts +25 -15
  219. package/src/db/workflow-runs/run-rerun.test.ts +1 -1
  220. package/src/db/workflow-runs/run-rerun.ts +6 -3
  221. package/src/index.ts +75 -33
  222. package/src/presentation/dto/checkout-token.ts +7 -1
  223. package/src/presentation/index.ts +1 -1
  224. package/src/presentation/inter-module.test.ts +149 -0
  225. package/src/presentation/inter-module.ts +132 -0
  226. package/src/presentation/routes/agent-runtime-config.test.ts +53 -10
  227. package/src/presentation/routes/agent-runtime-config.ts +89 -64
  228. package/src/presentation/routes/cancel-run.test.ts +10 -20
  229. package/src/presentation/routes/cancel-run.ts +30 -27
  230. package/src/presentation/routes/checkout-token.test.ts +52 -33
  231. package/src/presentation/routes/checkout-token.ts +109 -49
  232. package/src/presentation/routes/get-run-aggregates.test.ts +13 -7
  233. package/src/presentation/routes/get-run-aggregates.ts +54 -57
  234. package/src/presentation/routes/get-run.test.ts +10 -18
  235. package/src/presentation/routes/get-run.ts +51 -48
  236. package/src/presentation/routes/get-step-secrets.test.ts +29 -7
  237. package/src/presentation/routes/get-step-secrets.ts +72 -61
  238. package/src/presentation/routes/index.test.ts +16 -1
  239. package/src/presentation/routes/index.ts +54 -29
  240. package/src/presentation/routes/leased-step.ts +3 -2
  241. package/src/presentation/routes/list-run-attempts.test.ts +9 -19
  242. package/src/presentation/routes/list-run-attempts.ts +21 -18
  243. package/src/presentation/routes/list-runs.test.ts +13 -7
  244. package/src/presentation/routes/list-runs.ts +64 -67
  245. package/src/presentation/routes/next-step.test.ts +30 -11
  246. package/src/presentation/routes/next-step.ts +68 -48
  247. package/src/presentation/routes/project-access.ts +16 -0
  248. package/src/presentation/routes/report-step.test.ts +18 -2
  249. package/src/presentation/routes/report-step.ts +51 -49
  250. package/src/presentation/routes/require-accessible-run.ts +5 -2
  251. package/src/presentation/routes/rerun-run.test.ts +9 -19
  252. package/src/presentation/routes/rerun-run.ts +39 -36
  253. package/src/temporal/activities/index.ts +32 -12
  254. package/src/temporal/activities/orchestration-activities.test.ts +10 -8
  255. package/src/temporal/activities/orchestration-activities.ts +74 -50
  256. package/src/temporal/workflows/job-execution-orchestration.test.ts +47 -2
  257. package/test/env.ts +1 -3
  258. package/test/factories/project.ts +10 -5
  259. package/test/factories/workflow-model.test.ts +5 -1
  260. package/test/factories/workflow-model.ts +1 -1
  261. package/test/factories/workflow-run.ts +1 -1
  262. package/test/fixtures/agent-inter-module.ts +27 -0
  263. package/test/fixtures/annotations-inter-module.ts +5 -0
  264. package/test/fixtures/auth-inter-module.ts +22 -0
  265. package/test/fixtures/lease-token.ts +2 -1
  266. package/test/fixtures/projects-inter-module.ts +8 -0
  267. package/test/fixtures/runners-inter-module.ts +52 -0
  268. package/test/fixtures/secrets-inter-module.ts +61 -0
  269. package/test/globalSetup.ts +11 -11
  270. package/tsconfig.build.tsbuildinfo +1 -1
  271. package/dist/core/source-control.d.ts +0 -5
  272. package/dist/core/source-control.d.ts.map +0 -1
  273. package/dist/core/source-control.js +0 -19
  274. package/dist/core/source-control.js.map +0 -1
  275. package/src/core/source-control.test.ts +0 -17
  276. package/src/core/source-control.ts +0 -22
  277. package/src/temporal/workflows/job-execution-orchestration-timeout.test.ts +0 -165
@@ -1,4 +1,4 @@
1
- import {getSecret, SecretDecryptionError} from '@shipfox/api-secrets';
1
+ import type {RunnersInterModuleClient} from '@shipfox/api-runners-dto/inter-module';
2
2
  import {
3
3
  materializedSecretBindingSchema,
4
4
  type StepSecretDto,
@@ -6,6 +6,11 @@ import {
6
6
  stepSecretsQuerySchema,
7
7
  stepSecretsResponseSchema,
8
8
  } from '@shipfox/api-secrets-dto';
9
+ import {
10
+ type SecretsInterModuleClient,
11
+ secretsInterModuleContract,
12
+ } from '@shipfox/api-secrets-dto/inter-module';
13
+ import {isInterModuleKnownError} from '@shipfox/inter-module';
9
14
  import {captureException} from '@shipfox/node-error-monitoring';
10
15
  import {ClientError, defineRoute} from '@shipfox/node-fastify';
11
16
  import {logger} from '@shipfox/node-opentelemetry';
@@ -14,72 +19,78 @@ import {loadRunningLeasedStep} from './leased-step.js';
14
19
 
15
20
  const secretBindingsSchema = z.array(materializedSecretBindingSchema);
16
21
 
17
- export const getStepSecretsRoute = defineRoute({
18
- method: 'GET',
19
- path: '/steps/:stepId/secrets',
20
- description:
21
- "Returns decrypted secret values referenced by the runner's currently leased running run step. The job scope and secret bindings are re-derived from server state; the runner supplies only the step id and current attempt.",
22
- schema: {
23
- params: stepSecretsParamsSchema,
24
- querystring: stepSecretsQuerySchema,
25
- response: {
26
- 200: stepSecretsResponseSchema,
22
+ export function createGetStepSecretsRoute(
23
+ runners: RunnersInterModuleClient,
24
+ secretsClient: SecretsInterModuleClient,
25
+ ) {
26
+ return defineRoute({
27
+ method: 'GET',
28
+ path: '/steps/:stepId/secrets',
29
+ description:
30
+ "Returns decrypted secret values referenced by the runner's currently leased running run step. The job scope and secret bindings are re-derived from server state; the runner supplies only the step id and current attempt.",
31
+ schema: {
32
+ params: stepSecretsParamsSchema,
33
+ querystring: stepSecretsQuerySchema,
34
+ response: {
35
+ 200: stepSecretsResponseSchema,
36
+ },
27
37
  },
28
- },
29
- errorHandler: (error) => {
30
- if (error instanceof SecretDecryptionError) {
31
- captureException(error);
32
- throw new ClientError('Step secret could not be decrypted', 'secret-value-invalid', {
33
- status: 409,
34
- cause: error,
38
+ errorHandler: (error) => {
39
+ if (isInterModuleKnownError(secretsInterModuleContract.methods.getSecret, error)) {
40
+ captureException(error);
41
+ throw new ClientError('Step secret could not be decrypted', 'secret-value-invalid', {
42
+ status: 409,
43
+ cause: error,
44
+ });
45
+ }
46
+ throw error;
47
+ },
48
+ handler: async (request, reply) => {
49
+ const {stepId} = request.params;
50
+ const {attempt} = request.query;
51
+ const {leasedJob, step, workspaceId, projectId} = await loadRunningLeasedStep({
52
+ runners,
53
+ request,
54
+ stepId,
55
+ attempt,
35
56
  });
36
- }
37
- throw error;
38
- },
39
- handler: async (request, reply) => {
40
- const {stepId} = request.params;
41
- const {attempt} = request.query;
42
- const {leasedJob, step, workspaceId, projectId} = await loadRunningLeasedStep({
43
- request,
44
- stepId,
45
- attempt,
46
- });
47
57
 
48
- if (step.type !== 'run') {
49
- throw new ClientError('Step is not a run step', 'step-not-run', {status: 409});
50
- }
58
+ if (step.type !== 'run') {
59
+ throw new ClientError('Step is not a run step', 'step-not-run', {status: 409});
60
+ }
51
61
 
52
- const secretBindings = parseSecretBindings(step.config.secret_bindings);
53
- const references = distinctSecretReferences(secretBindings);
54
- const secrets = await Promise.all(
55
- references.map(async (reference): Promise<StepSecretDto> => {
56
- const value = await getSecret({
57
- workspaceId,
58
- projectId,
59
- namespace: '',
60
- key: reference.key,
61
- store: reference.store,
62
- });
63
- if (value === null) {
64
- throw new ClientError('Secret not found', 'secret-not-found', {status: 422});
65
- }
66
- return {...reference, value};
67
- }),
68
- );
62
+ const secretBindings = parseSecretBindings(step.config.secret_bindings);
63
+ const references = distinctSecretReferences(secretBindings);
64
+ const secrets = await Promise.all(
65
+ references.map(async (reference): Promise<StepSecretDto> => {
66
+ const {value} = await secretsClient.getSecret({
67
+ workspaceId,
68
+ projectId,
69
+ namespace: '',
70
+ key: reference.key,
71
+ store: reference.store,
72
+ });
73
+ if (value === null) {
74
+ throw new ClientError('Secret not found', 'secret-not-found', {status: 422});
75
+ }
76
+ return {...reference, value};
77
+ }),
78
+ );
69
79
 
70
- logger().info(
71
- {jobId: leasedJob.jobId, workspaceId, stepId, keyCount: references.length},
72
- 'Resolved step secrets',
73
- );
74
- logger().debug(
75
- {jobId: leasedJob.jobId, workspaceId, stepId, keys: references.map((ref) => ref.key)},
76
- 'Resolved step secret keys',
77
- );
80
+ logger().info(
81
+ {jobId: leasedJob.jobId, workspaceId, stepId, keyCount: references.length},
82
+ 'Resolved step secrets',
83
+ );
84
+ logger().debug(
85
+ {jobId: leasedJob.jobId, workspaceId, stepId, keys: references.map((ref) => ref.key)},
86
+ 'Resolved step secret keys',
87
+ );
78
88
 
79
- reply.header('cache-control', 'no-store');
80
- return {secrets};
81
- },
82
- });
89
+ reply.header('cache-control', 'no-store');
90
+ return {secrets};
91
+ },
92
+ });
93
+ }
83
94
 
84
95
  function parseSecretBindings(value: unknown): z.infer<typeof secretBindingsSchema> {
85
96
  try {
@@ -7,7 +7,13 @@ import {
7
7
  } from '@shipfox/api-auth-context';
8
8
  import {type AuthMethod, ClientError, closeApp, createApp} from '@shipfox/node-fastify';
9
9
  import type {FastifyRequest} from 'fastify';
10
- import {workflowRoutes} from './index.js';
10
+ import {agentTestClient} from '#test/fixtures/agent-inter-module.js';
11
+ import {annotationsTestClient} from '#test/fixtures/annotations-inter-module.js';
12
+ import {workflowsTestAuthClient} from '#test/fixtures/auth-inter-module.js';
13
+ import {projectsTestClient} from '#test/fixtures/projects-inter-module.js';
14
+ import {runnersTestClient} from '#test/fixtures/runners-inter-module.js';
15
+ import {createTestSecretsClient} from '#test/fixtures/secrets-inter-module.js';
16
+ import {createWorkflowRoutes} from './index.js';
11
17
 
12
18
  const fakeUserAuth: AuthMethod = {
13
19
  name: AUTH_USER,
@@ -29,6 +35,15 @@ afterEach(async () => {
29
35
  });
30
36
 
31
37
  describe('workflow route auth', () => {
38
+ const workflowRoutes = createWorkflowRoutes({
39
+ agent: agentTestClient,
40
+ annotations: annotationsTestClient,
41
+ auth: workflowsTestAuthClient,
42
+ integrations: {} as never,
43
+ projects: projectsTestClient,
44
+ runners: runnersTestClient,
45
+ secrets: createTestSecretsClient(),
46
+ });
32
47
  test('uses user auth', () => {
33
48
  expect(workflowRoutes[0]?.auth).toBe(AUTH_USER);
34
49
  });
@@ -1,42 +1,67 @@
1
+ import type {AnnotationsInterModuleClient} from '@shipfox/annotations-dto/inter-module';
2
+ import type {AgentInterModuleClient} from '@shipfox/api-agent-dto/inter-module';
1
3
  import {AUTH_LEASED_JOB, AUTH_USER} from '@shipfox/api-auth-context';
4
+ import type {AuthInterModuleClient} from '@shipfox/api-auth-dto/inter-module';
5
+ import type {IntegrationsModuleClient} from '@shipfox/api-integration-core-dto';
6
+ import type {ProjectsModuleClient} from '@shipfox/api-projects-dto';
7
+ import type {RunnersInterModuleClient} from '@shipfox/api-runners-dto/inter-module';
8
+ import type {SecretsInterModuleClient} from '@shipfox/api-secrets-dto/inter-module';
2
9
  import type {RouteGroup} from '@shipfox/node-fastify';
3
- import {agentRuntimeConfigRoute} from './agent-runtime-config.js';
10
+ import {createAgentRuntimeConfigRoute} from './agent-runtime-config.js';
4
11
  import {cancelRunRoute} from './cancel-run.js';
5
- import {checkoutTokenRoute} from './checkout-token.js';
12
+ import {createCheckoutTokenRoute} from './checkout-token.js';
6
13
  import {getRunRoute} from './get-run.js';
7
14
  import {getRunAggregatesRoute} from './get-run-aggregates.js';
8
- import {getStepSecretsRoute} from './get-step-secrets.js';
15
+ import {createGetStepSecretsRoute} from './get-step-secrets.js';
9
16
  import {listRunAttemptsRoute} from './list-run-attempts.js';
10
17
  import {listRunsRoute} from './list-runs.js';
11
- import {nextStepRoute} from './next-step.js';
12
- import {reportStepRoute} from './report-step.js';
18
+ import {createNextStepRoute} from './next-step.js';
19
+ import {createReportStepRoute} from './report-step.js';
13
20
  import {rerunRunRoute} from './rerun-run.js';
14
21
 
15
- export const leaseTokenRouteGroup: RouteGroup = {
16
- // The lease token names the job, so the path carries no job id ("current").
17
- prefix: '/runs/jobs/current',
18
- auth: AUTH_LEASED_JOB,
19
- routes: [
20
- nextStepRoute,
21
- reportStepRoute,
22
- checkoutTokenRoute,
23
- agentRuntimeConfigRoute,
24
- getStepSecretsRoute,
25
- ],
22
+ type WorkflowRouteClients = {
23
+ agent: AgentInterModuleClient;
24
+ annotations: AnnotationsInterModuleClient;
25
+ auth: AuthInterModuleClient;
26
+ integrations: IntegrationsModuleClient;
27
+ projects: ProjectsModuleClient;
28
+ runners: RunnersInterModuleClient;
29
+ secrets: SecretsInterModuleClient;
26
30
  };
27
31
 
28
- export const workflowRoutes: RouteGroup[] = [
29
- {
30
- prefix: '/workflows/runs',
31
- auth: AUTH_USER,
32
+ export function createLeaseTokenRouteGroup(params: WorkflowRouteClients): RouteGroup {
33
+ return {
34
+ // The lease token names the job, so the path carries no job id ("current").
35
+ prefix: '/runs/jobs/current',
36
+ auth: AUTH_LEASED_JOB,
32
37
  routes: [
33
- listRunsRoute,
34
- getRunAggregatesRoute,
35
- listRunAttemptsRoute,
36
- getRunRoute,
37
- cancelRunRoute,
38
- rerunRunRoute,
38
+ createNextStepRoute(params),
39
+ createReportStepRoute(params.runners),
40
+ createCheckoutTokenRoute({
41
+ integrations: params.integrations,
42
+ projects: params.projects,
43
+ runners: params.runners,
44
+ }),
45
+ createAgentRuntimeConfigRoute(params),
46
+ createGetStepSecretsRoute(params.runners, params.secrets),
39
47
  ],
40
- },
41
- leaseTokenRouteGroup,
42
- ];
48
+ };
49
+ }
50
+
51
+ export function createWorkflowRoutes(params: WorkflowRouteClients): RouteGroup[] {
52
+ return [
53
+ {
54
+ prefix: '/workflows/runs',
55
+ auth: AUTH_USER,
56
+ routes: [
57
+ listRunsRoute(params.projects),
58
+ getRunAggregatesRoute(params.projects),
59
+ listRunAttemptsRoute(params.projects),
60
+ getRunRoute(params.projects),
61
+ cancelRunRoute(params.projects),
62
+ rerunRunRoute(params.projects),
63
+ ],
64
+ },
65
+ createLeaseTokenRouteGroup(params),
66
+ ];
67
+ }
@@ -1,5 +1,5 @@
1
1
  import {type LeasedJobContext, requireLeasedJobContext} from '@shipfox/api-auth-context';
2
- import {isJobLeaseActive} from '@shipfox/api-runners';
2
+ import type {RunnersInterModuleClient} from '@shipfox/api-runners-dto/inter-module';
3
3
  import {ClientError} from '@shipfox/node-fastify';
4
4
  import type {Step} from '#core/entities/step.js';
5
5
  import {getJobScope, getStepByIdForJobExecution} from '#db/index.js';
@@ -12,13 +12,14 @@ export interface LoadedRunningLeasedStep {
12
12
  }
13
13
 
14
14
  export async function loadRunningLeasedStep(params: {
15
+ runners: RunnersInterModuleClient;
15
16
  request: object;
16
17
  stepId: string;
17
18
  attempt: number;
18
19
  }): Promise<LoadedRunningLeasedStep> {
19
20
  const leasedJob = requireLeasedJobContext(params.request);
20
21
 
21
- const leaseIsActive = await isJobLeaseActive({
22
+ const {active: leaseIsActive} = await params.runners.getLeaseState({
22
23
  jobId: leasedJob.jobId,
23
24
  jobExecutionId: leasedJob.jobExecutionId,
24
25
  runnerSessionId: leasedJob.runnerSessionId,
@@ -1,5 +1,5 @@
1
1
  import {buildUserContext, setUserContext} from '@shipfox/api-auth-context';
2
- import {requireProjectAccess} from '@shipfox/api-projects';
2
+ import type {ProjectsModuleClient} from '@shipfox/api-projects-dto';
3
3
  import {ClientError} from '@shipfox/node-fastify';
4
4
  import type {FastifyInstance} from 'fastify';
5
5
  import Fastify from 'fastify';
@@ -14,16 +14,11 @@ import {listRunAttemptsRoute} from './list-run-attempts.js';
14
14
 
15
15
  const projectAccessState = vi.hoisted(() => ({workspaceId: ''}));
16
16
 
17
- vi.mock('@shipfox/api-projects', () => ({
18
- requireProjectAccess: vi.fn(({projectId}) =>
19
- Promise.resolve({
20
- project: {id: projectId, workspaceId: projectAccessState.workspaceId},
21
- workspaceId: projectAccessState.workspaceId,
22
- }),
23
- ),
24
- }));
25
-
26
- const mockRequireProjectAccess = vi.mocked(requireProjectAccess);
17
+ const getProjectById = vi.fn();
18
+ const projects = {
19
+ getProjectById,
20
+ requireProjectForWorkspace: vi.fn(),
21
+ } as unknown as ProjectsModuleClient;
27
22
 
28
23
  describe('GET /api/workflows/runs/:id/attempts', () => {
29
24
  let app: FastifyInstance;
@@ -46,7 +41,7 @@ describe('GET /api/workflows/runs/:id/attempts', () => {
46
41
  );
47
42
  done();
48
43
  });
49
- app.get('/api/workflows/runs/:id/attempts', listRunAttemptsRoute);
44
+ app.get('/api/workflows/runs/:id/attempts', listRunAttemptsRoute(projects));
50
45
  await app.ready();
51
46
  });
52
47
 
@@ -55,7 +50,7 @@ describe('GET /api/workflows/runs/:id/attempts', () => {
55
50
  projectId = crypto.randomUUID();
56
51
  definitionId = crypto.randomUUID();
57
52
  projectAccessState.workspaceId = workspaceId;
58
- mockRequireProjectAccess.mockImplementation(({projectId: requestedProjectId}) =>
53
+ getProjectById.mockImplementation(({projectId: requestedProjectId}) =>
59
54
  Promise.resolve({
60
55
  project: {
61
56
  id: requestedProjectId,
@@ -63,10 +58,7 @@ describe('GET /api/workflows/runs/:id/attempts', () => {
63
58
  sourceConnectionId: crypto.randomUUID(),
64
59
  sourceExternalRepositoryId: `repo:${crypto.randomUUID()}`,
65
60
  name: 'Project',
66
- createdAt: new Date(),
67
- updatedAt: new Date(),
68
61
  },
69
- workspaceId,
70
62
  }),
71
63
  );
72
64
  });
@@ -121,9 +113,7 @@ describe('GET /api/workflows/runs/:id/attempts', () => {
121
113
  url: `/api/workflows/runs/${crypto.randomUUID()}/attempts`,
122
114
  });
123
115
  const run = await createRun();
124
- mockRequireProjectAccess.mockRejectedValueOnce(
125
- new ClientError('Forbidden', 'forbidden', {status: 403}),
126
- );
116
+ getProjectById.mockRejectedValueOnce(new ClientError('Forbidden', 'forbidden', {status: 403}));
127
117
 
128
118
  const inaccessible = await app.inject({
129
119
  method: 'GET',
@@ -1,3 +1,4 @@
1
+ import type {ProjectsModuleClient} from '@shipfox/api-projects-dto';
1
2
  import {workflowRunAttemptsResponseSchema} from '@shipfox/api-workflows-dto';
2
3
  import {defineRoute} from '@shipfox/node-fastify';
3
4
  import {z} from 'zod';
@@ -5,23 +6,25 @@ import {listRunAttempts} from '#db/index.js';
5
6
  import {toRunAttemptDto} from '#presentation/dto/index.js';
6
7
  import {requireAccessibleRun} from './require-accessible-run.js';
7
8
 
8
- export const listRunAttemptsRoute = defineRoute({
9
- method: 'GET',
10
- path: '/:id/attempts',
11
- description: 'List attempts in a workflow run lineage',
12
- schema: {
13
- params: z.object({
14
- id: z.string().uuid(),
15
- }),
16
- response: {
17
- 200: workflowRunAttemptsResponseSchema,
9
+ export function listRunAttemptsRoute(projects: ProjectsModuleClient) {
10
+ return defineRoute({
11
+ method: 'GET',
12
+ path: '/:id/attempts',
13
+ description: 'List attempts in a workflow run lineage',
14
+ schema: {
15
+ params: z.object({
16
+ id: z.string().uuid(),
17
+ }),
18
+ response: {
19
+ 200: workflowRunAttemptsResponseSchema,
20
+ },
18
21
  },
19
- },
20
- handler: async (request) => {
21
- const {id} = request.params;
22
- const run = await requireAccessibleRun({request, id});
23
- const attempts = await listRunAttempts({workflowRunId: run.id, projectId: run.projectId});
22
+ handler: async (request) => {
23
+ const {id} = request.params;
24
+ const run = await requireAccessibleRun({request, id, projects});
25
+ const attempts = await listRunAttempts({workflowRunId: run.id, projectId: run.projectId});
24
26
 
25
- return {attempts: attempts.map(toRunAttemptDto)};
26
- },
27
- });
27
+ return {attempts: attempts.map(toRunAttemptDto)};
28
+ },
29
+ });
30
+ }
@@ -1,4 +1,5 @@
1
1
  import {buildUserContext, setUserContext} from '@shipfox/api-auth-context';
2
+ import type {ProjectsModuleClient} from '@shipfox/api-projects-dto';
2
3
  import {encodeTimestampIdCursor} from '@shipfox/node-drizzle';
3
4
  import {eq} from 'drizzle-orm';
4
5
  import type {FastifyInstance} from 'fastify';
@@ -12,15 +13,20 @@ import {listRunsRoute} from './list-runs.js';
12
13
 
13
14
  const projectAccessState = vi.hoisted(() => ({workspaceId: ''}));
14
15
 
15
- vi.mock('@shipfox/api-projects', () => ({
16
- ProjectNotFoundError: class ProjectNotFoundError extends Error {},
17
- requireProjectAccess: vi.fn(({projectId}) =>
16
+ const projects = {
17
+ getProjectById: vi.fn(({projectId}) =>
18
18
  Promise.resolve({
19
- project: {id: projectId, workspaceId: projectAccessState.workspaceId},
20
- workspaceId: projectAccessState.workspaceId,
19
+ project: {
20
+ id: projectId,
21
+ workspaceId: projectAccessState.workspaceId,
22
+ sourceConnectionId: crypto.randomUUID(),
23
+ sourceExternalRepositoryId: 'repo',
24
+ name: 'Project',
25
+ },
21
26
  }),
22
27
  ),
23
- }));
28
+ requireProjectForWorkspace: vi.fn(),
29
+ } as unknown as ProjectsModuleClient;
24
30
 
25
31
  describe('GET /api/workflows/runs', () => {
26
32
  let app: FastifyInstance;
@@ -42,7 +48,7 @@ describe('GET /api/workflows/runs', () => {
42
48
  );
43
49
  done();
44
50
  });
45
- app.get('/api/workflows/runs', listRunsRoute);
51
+ app.get('/api/workflows/runs', listRunsRoute(projects));
46
52
  await app.ready();
47
53
  });
48
54
 
@@ -1,4 +1,4 @@
1
- import {ProjectNotFoundError, requireProjectAccess} from '@shipfox/api-projects';
1
+ import type {ProjectsModuleClient} from '@shipfox/api-projects-dto';
2
2
  import {
3
3
  workflowRunListQuerySchema,
4
4
  workflowRunListResponseSchema,
@@ -8,76 +8,73 @@ import {ClientError, defineRoute} from '@shipfox/node-fastify';
8
8
  import {logger} from '@shipfox/node-opentelemetry';
9
9
  import {listWorkflowRuns} from '#db/index.js';
10
10
  import {toRunDto} from '#presentation/dto/index.js';
11
+ import {requireProjectAccess} from './project-access.js';
11
12
 
12
- export const listRunsRoute = defineRoute({
13
- method: 'GET',
14
- path: '/',
15
- description: 'List workflow runs for a project',
16
- schema: {
17
- querystring: workflowRunListQuerySchema,
18
- response: {
19
- 200: workflowRunListResponseSchema,
13
+ export function listRunsRoute(projects: ProjectsModuleClient) {
14
+ return defineRoute({
15
+ method: 'GET',
16
+ path: '/',
17
+ description: 'List workflow runs for a project',
18
+ schema: {
19
+ querystring: workflowRunListQuerySchema,
20
+ response: {
21
+ 200: workflowRunListResponseSchema,
22
+ },
20
23
  },
21
- },
22
- errorHandler: (error) => {
23
- if (error instanceof ProjectNotFoundError) {
24
- throw new ClientError(error.message, 'project-not-found', {status: 404});
25
- }
26
- throw error;
27
- },
28
- handler: async (request) => {
29
- const startedAt = performance.now();
30
- const {
31
- project_id: projectId,
32
- limit,
33
- cursor,
34
- status,
35
- definition_id: definitionId,
36
- trigger_source: triggerSource,
37
- created_from: createdFrom,
38
- created_to: createdTo,
39
- } = request.query;
40
- const decodedCursor = decodeTimestampIdCursor(cursor);
41
- if (cursor && !decodedCursor) {
42
- throw new ClientError('Invalid cursor', 'invalid-cursor', {status: 400});
43
- }
44
-
45
- const {project} = await requireProjectAccess({request, projectId});
24
+ handler: async (request) => {
25
+ const startedAt = performance.now();
26
+ const {
27
+ project_id: projectId,
28
+ limit,
29
+ cursor,
30
+ status,
31
+ definition_id: definitionId,
32
+ trigger_source: triggerSource,
33
+ created_from: createdFrom,
34
+ created_to: createdTo,
35
+ } = request.query;
36
+ const decodedCursor = decodeTimestampIdCursor(cursor);
37
+ if (cursor && !decodedCursor) {
38
+ throw new ClientError('Invalid cursor', 'invalid-cursor', {status: 400});
39
+ }
46
40
 
47
- const filters = {
48
- status,
49
- definitionId,
50
- triggerSource,
51
- createdFrom: createdFrom ? new Date(createdFrom) : undefined,
52
- createdTo: createdTo ? new Date(createdTo) : undefined,
53
- };
54
- const result = await listWorkflowRuns({
55
- projectId: project.id,
56
- limit,
57
- cursor: decodedCursor,
58
- filters,
59
- includeTotal: !decodedCursor,
60
- });
41
+ const project = await requireProjectAccess(request, projectId, projects);
61
42
 
62
- logger().info(
63
- {
43
+ const filters = {
44
+ status,
45
+ definitionId,
46
+ triggerSource,
47
+ createdFrom: createdFrom ? new Date(createdFrom) : undefined,
48
+ createdTo: createdTo ? new Date(createdTo) : undefined,
49
+ };
50
+ const result = await listWorkflowRuns({
64
51
  projectId: project.id,
65
- filterKeys: Object.entries(filters)
66
- .filter(([, value]) => value !== undefined)
67
- .map(([key]) => key),
68
52
  limit,
69
- cursorPresent: Boolean(cursor),
70
- resultCount: result.runs.length,
71
- nextCursorPresent: Boolean(result.nextCursor),
72
- durationMs: Math.round(performance.now() - startedAt),
73
- },
74
- 'Listed workflow runs',
75
- );
53
+ cursor: decodedCursor,
54
+ filters,
55
+ includeTotal: !decodedCursor,
56
+ });
57
+
58
+ logger().info(
59
+ {
60
+ projectId: project.id,
61
+ filterKeys: Object.entries(filters)
62
+ .filter(([, value]) => value !== undefined)
63
+ .map(([key]) => key),
64
+ limit,
65
+ cursorPresent: Boolean(cursor),
66
+ resultCount: result.runs.length,
67
+ nextCursorPresent: Boolean(result.nextCursor),
68
+ durationMs: Math.round(performance.now() - startedAt),
69
+ },
70
+ 'Listed workflow runs',
71
+ );
76
72
 
77
- return {
78
- runs: result.runs.map((run) => toRunDto(run)),
79
- next_cursor: result.nextCursor ? encodeTimestampIdCursor(result.nextCursor) : null,
80
- filtered_total_count: result.filteredTotalCount,
81
- };
82
- },
83
- });
73
+ return {
74
+ runs: result.runs.map((run) => toRunDto(run)),
75
+ next_cursor: result.nextCursor ? encodeTimestampIdCursor(result.nextCursor) : null,
76
+ filtered_total_count: result.filteredTotalCount,
77
+ };
78
+ },
79
+ });
80
+ }