@shipfox/api-workflows 5.0.0 → 6.0.0
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.
- package/.turbo/turbo-build.log +4 -4
- package/CHANGELOG.md +72 -0
- package/dist/core/agent-defaults.d.ts +17 -0
- package/dist/core/agent-defaults.d.ts.map +1 -0
- package/dist/core/agent-defaults.js +21 -0
- package/dist/core/agent-defaults.js.map +1 -0
- package/dist/core/agent-tool-capability-warning.d.ts +4 -0
- package/dist/core/agent-tool-capability-warning.d.ts.map +1 -1
- package/dist/core/agent-tool-capability-warning.js +15 -17
- package/dist/core/agent-tool-capability-warning.js.map +1 -1
- package/dist/core/agent-tools.d.ts +11 -9
- package/dist/core/agent-tools.d.ts.map +1 -1
- package/dist/core/agent-tools.js +23 -18
- package/dist/core/agent-tools.js.map +1 -1
- package/dist/core/checkout.d.ts +21 -7
- package/dist/core/checkout.d.ts.map +1 -1
- package/dist/core/checkout.js +23 -11
- package/dist/core/checkout.js.map +1 -1
- package/dist/core/entities/workflow-run-attempt.d.ts +1 -1
- package/dist/core/entities/workflow-run-attempt.d.ts.map +1 -1
- package/dist/core/entities/workflow-run-attempt.js.map +1 -1
- package/dist/core/job-execution.d.ts +3 -1
- package/dist/core/job-execution.d.ts.map +1 -1
- package/dist/core/job-execution.js +14 -12
- package/dist/core/job-execution.js.map +1 -1
- package/dist/core/job-transition/derive-job-execution-outputs.d.ts +1 -1
- package/dist/core/job-transition/derive-job-execution-outputs.d.ts.map +1 -1
- package/dist/core/job-transition/derive-job-execution-outputs.js.map +1 -1
- package/dist/core/job-transition/derive-job-success.js +1 -1
- package/dist/core/job-transition/derive-job-success.js.map +1 -1
- package/dist/core/listener-execution-materialization.d.ts +3 -3
- package/dist/core/listener-execution-materialization.d.ts.map +1 -1
- package/dist/core/listener-execution-materialization.js +3 -4
- package/dist/core/listener-execution-materialization.js.map +1 -1
- package/dist/core/run-workflow.d.ts +12 -1
- package/dist/core/run-workflow.d.ts.map +1 -1
- package/dist/core/run-workflow.js +13 -8
- package/dist/core/run-workflow.js.map +1 -1
- package/dist/core/step-config/agent.d.ts +8 -8
- package/dist/core/step-config/agent.d.ts.map +1 -1
- package/dist/core/step-config/agent.js +12 -10
- package/dist/core/step-config/agent.js.map +1 -1
- package/dist/core/step-config/complete-step-dispatch-config.d.ts +4 -4
- package/dist/core/step-config/complete-step-dispatch-config.d.ts.map +1 -1
- package/dist/core/step-config/complete-step-dispatch-config.js +2 -2
- package/dist/core/step-config/complete-step-dispatch-config.js.map +1 -1
- package/dist/core/step-config/materialize-job-execution-steps.d.ts +3 -3
- package/dist/core/step-config/materialize-job-execution-steps.d.ts.map +1 -1
- package/dist/core/step-config/materialize-job-execution-steps.js +5 -6
- package/dist/core/step-config/materialize-job-execution-steps.js.map +1 -1
- package/dist/core/step-config/materialize-workflow-model.d.ts +3 -3
- package/dist/core/step-config/materialize-workflow-model.d.ts.map +1 -1
- package/dist/core/step-config/materialize-workflow-model.js +4 -4
- package/dist/core/step-config/materialize-workflow-model.js.map +1 -1
- package/dist/core/step-config/resolve-job-execution-name.d.ts +1 -1
- package/dist/core/step-config/resolve-job-execution-name.d.ts.map +1 -1
- package/dist/core/step-config/resolve-job-execution-name.js.map +1 -1
- package/dist/core/step-config/resolve-step-config.d.ts +4 -4
- package/dist/core/step-config/resolve-step-config.d.ts.map +1 -1
- package/dist/core/step-config/resolve-step-config.js +6 -6
- package/dist/core/step-config/resolve-step-config.js.map +1 -1
- package/dist/core/step-config/run.d.ts +1 -1
- package/dist/core/step-config/run.d.ts.map +1 -1
- package/dist/core/step-config/run.js.map +1 -1
- package/dist/core/workflow-run-creation.d.ts +3 -3
- package/dist/core/workflow-run-creation.d.ts.map +1 -1
- package/dist/core/workflow-run-creation.js +3 -4
- package/dist/core/workflow-run-creation.js.map +1 -1
- package/dist/db/db.d.ts +16 -4
- package/dist/db/db.d.ts.map +1 -1
- package/dist/db/job-listeners.d.ts +7 -1
- package/dist/db/job-listeners.d.ts.map +1 -1
- package/dist/db/job-listeners.js +10 -5
- package/dist/db/job-listeners.js.map +1 -1
- package/dist/db/runner-lease-table.d.ts +5 -5
- package/dist/db/runner-lease-table.d.ts.map +1 -1
- package/dist/db/runner-lease-table.js +3 -2
- package/dist/db/runner-lease-table.js.map +1 -1
- package/dist/db/schema/workflow-run-attempts.d.ts +9 -3
- package/dist/db/schema/workflow-run-attempts.d.ts.map +1 -1
- package/dist/db/schema/workflow-run-attempts.js +2 -1
- package/dist/db/schema/workflow-run-attempts.js.map +1 -1
- package/dist/db/schema/workflow-runs.js +1 -1
- package/dist/db/schema/workflow-runs.js.map +1 -1
- package/dist/db/workflow-runs/job-executions.d.ts +5 -0
- package/dist/db/workflow-runs/job-executions.d.ts.map +1 -1
- package/dist/db/workflow-runs/job-executions.js +14 -7
- package/dist/db/workflow-runs/job-executions.js.map +1 -1
- package/dist/db/workflow-runs/jobs.d.ts.map +1 -1
- package/dist/db/workflow-runs/jobs.js +3 -1
- package/dist/db/workflow-runs/jobs.js.map +1 -1
- package/dist/db/workflow-runs/run-create.d.ts +9 -2
- package/dist/db/workflow-runs/run-create.d.ts.map +1 -1
- package/dist/db/workflow-runs/run-create.js +12 -9
- package/dist/db/workflow-runs/run-create.js.map +1 -1
- package/dist/db/workflow-runs/run-rerun.d.ts.map +1 -1
- package/dist/db/workflow-runs/run-rerun.js +3 -1
- package/dist/db/workflow-runs/run-rerun.js.map +1 -1
- package/dist/index.d.ts +18 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +61 -37
- package/dist/index.js.map +1 -1
- package/dist/presentation/dto/checkout-token.d.ts +14 -1
- package/dist/presentation/dto/checkout-token.d.ts.map +1 -1
- package/dist/presentation/dto/checkout-token.js.map +1 -1
- package/dist/presentation/index.d.ts +1 -1
- package/dist/presentation/index.d.ts.map +1 -1
- package/dist/presentation/index.js +1 -1
- package/dist/presentation/index.js.map +1 -1
- package/dist/presentation/inter-module.d.ts +18 -0
- package/dist/presentation/inter-module.d.ts.map +1 -0
- package/dist/presentation/inter-module.js +113 -0
- package/dist/presentation/inter-module.js.map +1 -0
- package/dist/presentation/routes/agent-runtime-config.d.ts +6 -1
- package/dist/presentation/routes/agent-runtime-config.d.ts.map +1 -1
- package/dist/presentation/routes/agent-runtime-config.js +59 -57
- package/dist/presentation/routes/agent-runtime-config.js.map +1 -1
- package/dist/presentation/routes/cancel-run.d.ts +2 -1
- package/dist/presentation/routes/cancel-run.d.ts.map +1 -1
- package/dist/presentation/routes/cancel-run.js +37 -34
- package/dist/presentation/routes/cancel-run.js.map +1 -1
- package/dist/presentation/routes/checkout-token.d.ts +8 -1
- package/dist/presentation/routes/checkout-token.d.ts.map +1 -1
- package/dist/presentation/routes/checkout-token.js +65 -47
- package/dist/presentation/routes/checkout-token.js.map +1 -1
- package/dist/presentation/routes/get-run-aggregates.d.ts +2 -1
- package/dist/presentation/routes/get-run-aggregates.d.ts.map +1 -1
- package/dist/presentation/routes/get-run-aggregates.js +44 -53
- package/dist/presentation/routes/get-run-aggregates.js.map +1 -1
- package/dist/presentation/routes/get-run.d.ts +2 -1
- package/dist/presentation/routes/get-run.d.ts.map +1 -1
- package/dist/presentation/routes/get-run.js +53 -50
- package/dist/presentation/routes/get-run.js.map +1 -1
- package/dist/presentation/routes/get-step-secrets.d.ts +3 -1
- package/dist/presentation/routes/get-step-secrets.d.ts.map +1 -1
- package/dist/presentation/routes/get-step-secrets.js +72 -68
- package/dist/presentation/routes/get-step-secrets.js.map +1 -1
- package/dist/presentation/routes/index.d.ts +19 -2
- package/dist/presentation/routes/index.d.ts.map +1 -1
- package/dist/presentation/routes/index.js +38 -30
- package/dist/presentation/routes/index.js.map +1 -1
- package/dist/presentation/routes/leased-step.d.ts +2 -0
- package/dist/presentation/routes/leased-step.d.ts.map +1 -1
- package/dist/presentation/routes/leased-step.js +1 -2
- package/dist/presentation/routes/leased-step.js.map +1 -1
- package/dist/presentation/routes/list-run-attempts.d.ts +2 -1
- package/dist/presentation/routes/list-run-attempts.d.ts.map +1 -1
- package/dist/presentation/routes/list-run-attempts.js +29 -26
- package/dist/presentation/routes/list-run-attempts.js.map +1 -1
- package/dist/presentation/routes/list-runs.d.ts +2 -1
- package/dist/presentation/routes/list-runs.d.ts.map +1 -1
- package/dist/presentation/routes/list-runs.js +51 -60
- package/dist/presentation/routes/list-runs.js.map +1 -1
- package/dist/presentation/routes/next-step.d.ts +10 -1
- package/dist/presentation/routes/next-step.d.ts.map +1 -1
- package/dist/presentation/routes/next-step.js +64 -50
- package/dist/presentation/routes/next-step.js.map +1 -1
- package/dist/presentation/routes/project-access.d.ts +10 -0
- package/dist/presentation/routes/project-access.d.ts.map +1 -0
- package/dist/presentation/routes/project-access.js +17 -0
- package/dist/presentation/routes/project-access.js.map +1 -0
- package/dist/presentation/routes/report-step.d.ts +2 -1
- package/dist/presentation/routes/report-step.d.ts.map +1 -1
- package/dist/presentation/routes/report-step.js +62 -61
- package/dist/presentation/routes/report-step.js.map +1 -1
- package/dist/presentation/routes/require-accessible-run.d.ts +3 -1
- package/dist/presentation/routes/require-accessible-run.d.ts.map +1 -1
- package/dist/presentation/routes/require-accessible-run.js +3 -6
- package/dist/presentation/routes/require-accessible-run.js.map +1 -1
- package/dist/presentation/routes/rerun-run.d.ts +2 -1
- package/dist/presentation/routes/rerun-run.d.ts.map +1 -1
- package/dist/presentation/routes/rerun-run.js +46 -43
- package/dist/presentation/routes/rerun-run.js.map +1 -1
- package/dist/temporal/activities/index.d.ts +44 -9
- package/dist/temporal/activities/index.d.ts.map +1 -1
- package/dist/temporal/activities/index.js +13 -9
- package/dist/temporal/activities/index.js.map +1 -1
- package/dist/temporal/activities/orchestration-activities.d.ts +20 -11
- package/dist/temporal/activities/orchestration-activities.d.ts.map +1 -1
- package/dist/temporal/activities/orchestration-activities.js +48 -33
- package/dist/temporal/activities/orchestration-activities.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +28 -21
- package/src/core/agent-defaults.test.ts +28 -0
- package/src/core/agent-defaults.ts +36 -0
- package/src/core/agent-tool-capability-warning.test.ts +36 -11
- package/src/core/agent-tool-capability-warning.ts +27 -25
- package/src/core/agent-tools.ts +33 -36
- package/src/core/checkout.test.ts +38 -25
- package/src/core/checkout.ts +41 -14
- package/src/core/entities/workflow-run-attempt.ts +1 -1
- package/src/core/job-execution.ts +19 -9
- package/src/core/job-transition/derive-job-execution-outputs.ts +1 -1
- package/src/core/job-transition/derive-job-success.ts +1 -1
- package/src/core/listener-execution-materialization.test.ts +4 -2
- package/src/core/listener-execution-materialization.ts +6 -9
- package/src/core/run-workflow.test.ts +43 -38
- package/src/core/run-workflow.ts +26 -9
- package/src/core/step-config/agent.ts +24 -26
- package/src/core/step-config/complete-step-dispatch-config.test.ts +22 -23
- package/src/core/step-config/complete-step-dispatch-config.ts +6 -6
- package/src/core/step-config/materialize-job-execution-steps.test.ts +34 -23
- package/src/core/step-config/materialize-job-execution-steps.ts +38 -39
- package/src/core/step-config/materialize-workflow-model.test.ts +94 -82
- package/src/core/step-config/materialize-workflow-model.ts +31 -29
- package/src/core/step-config/resolve-job-execution-name.ts +1 -1
- package/src/core/step-config/resolve-step-config.ts +11 -9
- package/src/core/step-config/run.ts +1 -1
- package/src/core/workflow-run-creation.test.ts +14 -10
- package/src/core/workflow-run-creation.ts +6 -9
- package/src/db/job-listeners.ts +20 -5
- package/src/db/runner-lease-table.ts +2 -2
- package/src/db/schema/workflow-run-attempts.ts +7 -3
- package/src/db/schema/workflow-runs.ts +1 -1
- package/src/db/workflow-runs/job-executions.ts +15 -1
- package/src/db/workflow-runs/jobs.ts +4 -1
- package/src/db/workflow-runs/run-create.test.ts +7 -1
- package/src/db/workflow-runs/run-create.ts +25 -15
- package/src/db/workflow-runs/run-rerun.test.ts +1 -1
- package/src/db/workflow-runs/run-rerun.ts +6 -3
- package/src/index.ts +75 -33
- package/src/presentation/dto/checkout-token.ts +7 -1
- package/src/presentation/index.ts +1 -1
- package/src/presentation/inter-module.test.ts +149 -0
- package/src/presentation/inter-module.ts +132 -0
- package/src/presentation/routes/agent-runtime-config.test.ts +53 -10
- package/src/presentation/routes/agent-runtime-config.ts +89 -64
- package/src/presentation/routes/cancel-run.test.ts +10 -20
- package/src/presentation/routes/cancel-run.ts +30 -27
- package/src/presentation/routes/checkout-token.test.ts +52 -33
- package/src/presentation/routes/checkout-token.ts +109 -49
- package/src/presentation/routes/get-run-aggregates.test.ts +13 -7
- package/src/presentation/routes/get-run-aggregates.ts +54 -57
- package/src/presentation/routes/get-run.test.ts +10 -18
- package/src/presentation/routes/get-run.ts +51 -48
- package/src/presentation/routes/get-step-secrets.test.ts +29 -7
- package/src/presentation/routes/get-step-secrets.ts +72 -61
- package/src/presentation/routes/index.test.ts +16 -1
- package/src/presentation/routes/index.ts +54 -29
- package/src/presentation/routes/leased-step.ts +3 -2
- package/src/presentation/routes/list-run-attempts.test.ts +9 -19
- package/src/presentation/routes/list-run-attempts.ts +21 -18
- package/src/presentation/routes/list-runs.test.ts +13 -7
- package/src/presentation/routes/list-runs.ts +64 -67
- package/src/presentation/routes/next-step.test.ts +30 -11
- package/src/presentation/routes/next-step.ts +68 -48
- package/src/presentation/routes/project-access.ts +16 -0
- package/src/presentation/routes/report-step.test.ts +18 -2
- package/src/presentation/routes/report-step.ts +51 -49
- package/src/presentation/routes/require-accessible-run.ts +5 -2
- package/src/presentation/routes/rerun-run.test.ts +9 -19
- package/src/presentation/routes/rerun-run.ts +39 -36
- package/src/temporal/activities/index.ts +32 -12
- package/src/temporal/activities/orchestration-activities.test.ts +10 -8
- package/src/temporal/activities/orchestration-activities.ts +74 -50
- package/src/temporal/workflows/job-execution-orchestration.test.ts +47 -2
- package/test/env.ts +1 -3
- package/test/factories/project.ts +10 -5
- package/test/factories/workflow-model.test.ts +5 -1
- package/test/factories/workflow-model.ts +1 -1
- package/test/factories/workflow-run.ts +1 -1
- package/test/fixtures/agent-inter-module.ts +27 -0
- package/test/fixtures/annotations-inter-module.ts +5 -0
- package/test/fixtures/auth-inter-module.ts +22 -0
- package/test/fixtures/lease-token.ts +2 -1
- package/test/fixtures/projects-inter-module.ts +8 -0
- package/test/fixtures/runners-inter-module.ts +52 -0
- package/test/fixtures/secrets-inter-module.ts +61 -0
- package/test/globalSetup.ts +11 -11
- package/tsconfig.build.tsbuildinfo +1 -1
- package/dist/core/source-control.d.ts +0 -5
- package/dist/core/source-control.d.ts.map +0 -1
- package/dist/core/source-control.js +0 -19
- package/dist/core/source-control.js.map +0 -1
- package/src/core/source-control.test.ts +0 -17
- package/src/core/source-control.ts +0 -22
- package/src/temporal/workflows/job-execution-orchestration-timeout.test.ts +0 -165
|
@@ -1,81 +1,106 @@
|
|
|
1
|
-
import {ModelProviderConfigNotFoundError} from '@shipfox/api-agent/core/errors';
|
|
2
|
-
import {resolveRuntimeCredentials} from '@shipfox/api-agent/core/resolve-runtime-credentials';
|
|
3
1
|
import {
|
|
4
2
|
agentRuntimeCredentialsResponseSchema,
|
|
5
3
|
type MaterializedAgentStepConfigDto,
|
|
6
4
|
materializedAgentStepConfigSchema,
|
|
7
5
|
} from '@shipfox/api-agent-dto';
|
|
8
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
type AgentInterModuleClient,
|
|
8
|
+
agentInterModuleContract,
|
|
9
|
+
} from '@shipfox/api-agent-dto/inter-module';
|
|
10
|
+
import type {RunnersInterModuleClient} from '@shipfox/api-runners-dto/inter-module';
|
|
9
11
|
import {agentRuntimeConfigQuerySchema} from '@shipfox/api-workflows-dto';
|
|
12
|
+
import {isInterModuleKnownError} from '@shipfox/inter-module';
|
|
10
13
|
import {captureException} from '@shipfox/node-error-monitoring';
|
|
11
14
|
import {ClientError, defineRoute} from '@shipfox/node-fastify';
|
|
12
15
|
import {ZodError} from 'zod';
|
|
13
16
|
import {loadRunningLeasedStep} from './leased-step.js';
|
|
14
17
|
|
|
15
|
-
export
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
export function createAgentRuntimeConfigRoute(params: {
|
|
19
|
+
agent: AgentInterModuleClient;
|
|
20
|
+
runners: RunnersInterModuleClient;
|
|
21
|
+
}) {
|
|
22
|
+
return defineRoute({
|
|
23
|
+
method: 'GET',
|
|
24
|
+
path: '/agent-runtime-config',
|
|
25
|
+
description:
|
|
26
|
+
"Returns the resolved harness, provider, model, thinking effort, and decrypted provider credential bundle for the runner's currently leased running agent step. The job is identified by the lease token and the step is bound to that job before credentials are returned.",
|
|
27
|
+
schema: {
|
|
28
|
+
querystring: agentRuntimeConfigQuerySchema,
|
|
29
|
+
response: {
|
|
30
|
+
200: agentRuntimeCredentialsResponseSchema,
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
errorHandler: (error) => {
|
|
34
|
+
if (
|
|
35
|
+
isInterModuleKnownError(
|
|
36
|
+
agentInterModuleContract.methods.resolveRuntimeCredentials,
|
|
37
|
+
error,
|
|
38
|
+
) &&
|
|
39
|
+
error.code === 'model-provider-credentials-invalid'
|
|
40
|
+
) {
|
|
41
|
+
captureException(error);
|
|
42
|
+
throw new ClientError(
|
|
43
|
+
'Model provider credentials could not be decrypted',
|
|
44
|
+
'model-provider-credentials-invalid',
|
|
45
|
+
{
|
|
46
|
+
status: 409,
|
|
47
|
+
cause: error,
|
|
48
|
+
},
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
if (
|
|
52
|
+
isInterModuleKnownError(
|
|
53
|
+
agentInterModuleContract.methods.resolveRuntimeCredentials,
|
|
54
|
+
error,
|
|
55
|
+
) &&
|
|
56
|
+
error.code === 'model-provider-not-configured'
|
|
57
|
+
) {
|
|
58
|
+
throw new ClientError(
|
|
59
|
+
'Model provider credentials are not configured',
|
|
60
|
+
'model-provider-not-configured',
|
|
61
|
+
{
|
|
62
|
+
status: 409,
|
|
63
|
+
},
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
throw error;
|
|
24
67
|
},
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
status: 409,
|
|
34
|
-
cause: error,
|
|
35
|
-
},
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
if (error instanceof ModelProviderConfigNotFoundError) {
|
|
39
|
-
throw new ClientError(
|
|
40
|
-
'Model provider credentials are not configured',
|
|
41
|
-
'model-provider-not-configured',
|
|
42
|
-
{
|
|
43
|
-
status: 409,
|
|
44
|
-
},
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
throw error;
|
|
48
|
-
},
|
|
49
|
-
handler: async (request, reply) => {
|
|
50
|
-
const {step_id: stepId, attempt} = request.query;
|
|
51
|
-
const {step, workspaceId} = await loadRunningLeasedStep({request, stepId, attempt});
|
|
68
|
+
handler: async (request, reply) => {
|
|
69
|
+
const {step_id: stepId, attempt} = request.query;
|
|
70
|
+
const {step, workspaceId} = await loadRunningLeasedStep({
|
|
71
|
+
runners: params.runners,
|
|
72
|
+
request,
|
|
73
|
+
stepId,
|
|
74
|
+
attempt,
|
|
75
|
+
});
|
|
52
76
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
77
|
+
if (step.type !== 'agent') {
|
|
78
|
+
throw new ClientError('Step is not an agent step', 'step-not-agent', {status: 409});
|
|
79
|
+
}
|
|
56
80
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
81
|
+
let agentConfig: MaterializedAgentStepConfigDto;
|
|
82
|
+
try {
|
|
83
|
+
agentConfig = materializedAgentStepConfigSchema.parse(step.config);
|
|
84
|
+
} catch (error) {
|
|
85
|
+
if (error instanceof ZodError) {
|
|
86
|
+
throw new ClientError('Agent step config is invalid', 'agent-step-config-invalid', {
|
|
87
|
+
status: 409,
|
|
88
|
+
cause: error,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
throw error;
|
|
66
92
|
}
|
|
67
|
-
throw error;
|
|
68
|
-
}
|
|
69
93
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
94
|
+
const runtimeConfig = await params.agent.resolveRuntimeCredentials({
|
|
95
|
+
workspaceId,
|
|
96
|
+
harness: agentConfig.harness,
|
|
97
|
+
provider: agentConfig.provider,
|
|
98
|
+
model: agentConfig.model,
|
|
99
|
+
thinking: agentConfig.thinking,
|
|
100
|
+
});
|
|
77
101
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
});
|
|
102
|
+
reply.header('cache-control', 'no-store');
|
|
103
|
+
return runtimeConfig;
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {buildUserContext, setUserContext} from '@shipfox/api-auth-context';
|
|
2
|
-
import {
|
|
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';
|
|
@@ -12,16 +12,11 @@ import {cancelRunRoute} from './cancel-run.js';
|
|
|
12
12
|
|
|
13
13
|
const projectAccessState = vi.hoisted(() => ({workspaceId: ''}));
|
|
14
14
|
|
|
15
|
-
vi.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}),
|
|
21
|
-
),
|
|
22
|
-
}));
|
|
23
|
-
|
|
24
|
-
const mockRequireProjectAccess = vi.mocked(requireProjectAccess);
|
|
15
|
+
const getProjectById = vi.fn();
|
|
16
|
+
const projects = {
|
|
17
|
+
getProjectById,
|
|
18
|
+
requireProjectForWorkspace: vi.fn(),
|
|
19
|
+
} as unknown as ProjectsModuleClient;
|
|
25
20
|
|
|
26
21
|
describe('POST /api/workflows/runs/:id/cancel', () => {
|
|
27
22
|
let app: FastifyInstance;
|
|
@@ -44,7 +39,7 @@ describe('POST /api/workflows/runs/:id/cancel', () => {
|
|
|
44
39
|
);
|
|
45
40
|
done();
|
|
46
41
|
});
|
|
47
|
-
app.post('/api/workflows/runs/:id/cancel', cancelRunRoute);
|
|
42
|
+
app.post('/api/workflows/runs/:id/cancel', cancelRunRoute(projects));
|
|
48
43
|
await app.ready();
|
|
49
44
|
});
|
|
50
45
|
|
|
@@ -53,7 +48,7 @@ describe('POST /api/workflows/runs/:id/cancel', () => {
|
|
|
53
48
|
projectId = crypto.randomUUID();
|
|
54
49
|
definitionId = crypto.randomUUID();
|
|
55
50
|
projectAccessState.workspaceId = workspaceId;
|
|
56
|
-
|
|
51
|
+
getProjectById.mockImplementation(({projectId: requestedProjectId}) =>
|
|
57
52
|
Promise.resolve({
|
|
58
53
|
project: {
|
|
59
54
|
id: requestedProjectId,
|
|
@@ -61,10 +56,7 @@ describe('POST /api/workflows/runs/:id/cancel', () => {
|
|
|
61
56
|
sourceConnectionId: crypto.randomUUID(),
|
|
62
57
|
sourceExternalRepositoryId: `repo:${crypto.randomUUID()}`,
|
|
63
58
|
name: 'Project',
|
|
64
|
-
createdAt: new Date(),
|
|
65
|
-
updatedAt: new Date(),
|
|
66
59
|
},
|
|
67
|
-
workspaceId,
|
|
68
60
|
}),
|
|
69
61
|
);
|
|
70
62
|
});
|
|
@@ -97,7 +89,7 @@ describe('POST /api/workflows/runs/:id/cancel', () => {
|
|
|
97
89
|
|
|
98
90
|
test('maps a run disappearing during cancellation to 404', () => {
|
|
99
91
|
let mapped: unknown;
|
|
100
|
-
const errorHandler = cancelRunRoute.errorHandler as (error: Error) => void;
|
|
92
|
+
const errorHandler = cancelRunRoute(projects).errorHandler as (error: Error) => void;
|
|
101
93
|
|
|
102
94
|
try {
|
|
103
95
|
errorHandler(new WorkflowRunNotFoundError(crypto.randomUUID()));
|
|
@@ -111,9 +103,7 @@ describe('POST /api/workflows/runs/:id/cancel', () => {
|
|
|
111
103
|
|
|
112
104
|
test('returns 404 when project access is denied', async () => {
|
|
113
105
|
const run = await createRun();
|
|
114
|
-
|
|
115
|
-
new ClientError('Forbidden', 'forbidden', {status: 403}),
|
|
116
|
-
);
|
|
106
|
+
getProjectById.mockRejectedValueOnce(new ClientError('Forbidden', 'forbidden', {status: 403}));
|
|
117
107
|
|
|
118
108
|
const res = await app.inject({
|
|
119
109
|
method: 'POST',
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type {ProjectsModuleClient} from '@shipfox/api-projects-dto';
|
|
1
2
|
import {workflowRunDtoSchema} from '@shipfox/api-workflows-dto';
|
|
2
3
|
import {ClientError, defineRoute} from '@shipfox/node-fastify';
|
|
3
4
|
import {z} from 'zod';
|
|
@@ -6,32 +7,34 @@ import {cancelWorkflowRun} from '#db/index.js';
|
|
|
6
7
|
import {toRunDto} from '#presentation/dto/index.js';
|
|
7
8
|
import {requireAccessibleRun} from './require-accessible-run.js';
|
|
8
9
|
|
|
9
|
-
export
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
10
|
+
export function cancelRunRoute(projects: ProjectsModuleClient) {
|
|
11
|
+
return defineRoute({
|
|
12
|
+
method: 'POST',
|
|
13
|
+
path: '/:id/cancel',
|
|
14
|
+
description: 'Cancel an in-progress workflow run',
|
|
15
|
+
schema: {
|
|
16
|
+
params: z.object({
|
|
17
|
+
id: z.string().uuid(),
|
|
18
|
+
}),
|
|
19
|
+
response: {
|
|
20
|
+
200: workflowRunDtoSchema,
|
|
21
|
+
},
|
|
19
22
|
},
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const run = await requireAccessibleRun({request, id});
|
|
23
|
+
errorHandler: (error) => {
|
|
24
|
+
if (error instanceof WorkflowRunNotCancellableError) {
|
|
25
|
+
throw new ClientError(error.message, 'run-already-finished', {status: 409});
|
|
26
|
+
}
|
|
27
|
+
if (error instanceof WorkflowRunNotFoundError) {
|
|
28
|
+
throw new ClientError('Run not found', 'not-found', {status: 404});
|
|
29
|
+
}
|
|
30
|
+
throw error;
|
|
31
|
+
},
|
|
32
|
+
handler: async (request) => {
|
|
33
|
+
const {id} = request.params;
|
|
34
|
+
const run = await requireAccessibleRun({request, id, projects});
|
|
33
35
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
});
|
|
36
|
+
const cancelled = await cancelWorkflowRun({workflowRunId: run.id});
|
|
37
|
+
return toRunDto(cancelled);
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
}
|
|
@@ -1,27 +1,22 @@
|
|
|
1
1
|
import {createLeaseTokenAuthMethod} from '@shipfox/api-auth';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
IntegrationConnectionInactiveError,
|
|
6
|
-
IntegrationProviderError,
|
|
7
|
-
type IntegrationSourceControlService,
|
|
8
|
-
} from '@shipfox/api-integration-core';
|
|
9
|
-
import {getProjectById} from '@shipfox/api-projects';
|
|
2
|
+
import {integrationsInterModuleContract} from '@shipfox/api-integration-core-dto';
|
|
3
|
+
import type {ProjectsModuleClient} from '@shipfox/api-projects-dto';
|
|
4
|
+
import {createInterModuleKnownError} from '@shipfox/inter-module';
|
|
10
5
|
import {closeApp, createApp, type FastifyInstance} from '@shipfox/node-fastify';
|
|
11
6
|
import {createCapturingLogger} from '@shipfox/node-log/test';
|
|
12
|
-
import {clearSourceControl, setSourceControl} from '#core/source-control.js';
|
|
13
7
|
import {jobFactory} from '#test/factories/job.js';
|
|
14
8
|
import {projectFactory} from '#test/factories/project.js';
|
|
15
9
|
import {mintActiveLeaseToken} from '#test/fixtures/active-lease-token.js';
|
|
16
10
|
import {mintLeaseToken} from '#test/fixtures/lease-token.js';
|
|
17
|
-
import {
|
|
11
|
+
import {runnersTestClient} from '#test/fixtures/runners-inter-module.js';
|
|
12
|
+
import {createLeaseTokenRouteGroup} from './index.js';
|
|
18
13
|
|
|
19
|
-
|
|
20
|
-
const
|
|
14
|
+
const getProjectById = vi.fn();
|
|
15
|
+
const projects = {getProjectById} as Pick<ProjectsModuleClient, 'getProjectById'>;
|
|
21
16
|
|
|
22
17
|
const URL = '/runs/jobs/current/checkout-token';
|
|
23
18
|
|
|
24
|
-
const githubSpec = (token: string)
|
|
19
|
+
const githubSpec = (token: string) => ({
|
|
25
20
|
repositoryUrl: 'https://github.com/acme/repo.git',
|
|
26
21
|
ref: 'main',
|
|
27
22
|
credentials: {username: 'x-access-token', token, expiresAt: new Date('2026-06-10T12:00:00.000Z')},
|
|
@@ -33,10 +28,19 @@ describe('POST /runs/jobs/current/checkout-token', () => {
|
|
|
33
28
|
const {logger, lines: logLines, clear: clearLogLines} = createCapturingLogger();
|
|
34
29
|
|
|
35
30
|
beforeAll(async () => {
|
|
36
|
-
setSourceControl({createCheckoutSpec} as unknown as IntegrationSourceControlService);
|
|
37
31
|
app = await createApp({
|
|
38
32
|
auth: [createLeaseTokenAuthMethod()],
|
|
39
|
-
routes: [
|
|
33
|
+
routes: [
|
|
34
|
+
createLeaseTokenRouteGroup({
|
|
35
|
+
agent: {} as never,
|
|
36
|
+
annotations: {} as never,
|
|
37
|
+
auth: {} as never,
|
|
38
|
+
integrations: {createCheckoutSpec} as never,
|
|
39
|
+
projects: projects as never,
|
|
40
|
+
runners: runnersTestClient,
|
|
41
|
+
secrets: {} as never,
|
|
42
|
+
}),
|
|
43
|
+
],
|
|
40
44
|
swagger: false,
|
|
41
45
|
fastifyOptions: {loggerInstance: logger},
|
|
42
46
|
});
|
|
@@ -45,13 +49,12 @@ describe('POST /runs/jobs/current/checkout-token', () => {
|
|
|
45
49
|
|
|
46
50
|
beforeEach(() => {
|
|
47
51
|
createCheckoutSpec.mockReset();
|
|
48
|
-
|
|
52
|
+
getProjectById.mockReset();
|
|
49
53
|
clearLogLines();
|
|
50
54
|
});
|
|
51
55
|
|
|
52
56
|
afterAll(async () => {
|
|
53
57
|
await closeApp();
|
|
54
|
-
clearSourceControl();
|
|
55
58
|
});
|
|
56
59
|
|
|
57
60
|
describe('lease-token auth', () => {
|
|
@@ -97,7 +100,7 @@ describe('POST /runs/jobs/current/checkout-token', () => {
|
|
|
97
100
|
|
|
98
101
|
test('returns basic auth for a GitHub-style spec with credentials', async () => {
|
|
99
102
|
const project = projectFactory.build();
|
|
100
|
-
|
|
103
|
+
getProjectById.mockResolvedValue({project});
|
|
101
104
|
const job = await jobFactory.create({}, {transient: {projectId: project.id}});
|
|
102
105
|
createCheckoutSpec.mockResolvedValue(githubSpec('ghs-secret-token'));
|
|
103
106
|
const token = await mintActiveLeaseToken({jobId: job.id});
|
|
@@ -127,12 +130,12 @@ describe('POST /runs/jobs/current/checkout-token', () => {
|
|
|
127
130
|
|
|
128
131
|
test('omits auth for a credential-free (debug) spec', async () => {
|
|
129
132
|
const project = projectFactory.build();
|
|
130
|
-
|
|
133
|
+
getProjectById.mockResolvedValue({project});
|
|
131
134
|
const job = await jobFactory.create({}, {transient: {projectId: project.id}});
|
|
132
135
|
createCheckoutSpec.mockResolvedValue({
|
|
133
136
|
repositoryUrl: 'https://example.com/acme/repo.git',
|
|
134
137
|
ref: 'trunk',
|
|
135
|
-
}
|
|
138
|
+
});
|
|
136
139
|
const token = await mintActiveLeaseToken({jobId: job.id});
|
|
137
140
|
|
|
138
141
|
const res = await app.inject({
|
|
@@ -172,7 +175,7 @@ describe('POST /runs/jobs/current/checkout-token', () => {
|
|
|
172
175
|
'skipped',
|
|
173
176
|
] as const)('returns 404 and mints nothing without an active lease for %s job', async (status) => {
|
|
174
177
|
const project = projectFactory.build();
|
|
175
|
-
|
|
178
|
+
getProjectById.mockResolvedValue({project});
|
|
176
179
|
const job = await jobFactory.create({}, {transient: {projectId: project.id, status}});
|
|
177
180
|
const token = await mintLeaseToken({jobId: job.id});
|
|
178
181
|
|
|
@@ -189,7 +192,7 @@ describe('POST /runs/jobs/current/checkout-token', () => {
|
|
|
189
192
|
|
|
190
193
|
test('returns checkout credentials while the parent job projection is still pending', async () => {
|
|
191
194
|
const project = projectFactory.build();
|
|
192
|
-
|
|
195
|
+
getProjectById.mockResolvedValue({project});
|
|
193
196
|
const job = await jobFactory.create(
|
|
194
197
|
{},
|
|
195
198
|
{transient: {projectId: project.id, status: 'pending'}},
|
|
@@ -216,7 +219,9 @@ describe('POST /runs/jobs/current/checkout-token', () => {
|
|
|
216
219
|
[projectA.id, projectA],
|
|
217
220
|
[projectB.id, projectB],
|
|
218
221
|
]);
|
|
219
|
-
|
|
222
|
+
getProjectById.mockImplementation((input: {projectId: string}) =>
|
|
223
|
+
Promise.resolve({project: projectsById.get(input.projectId) ?? null}),
|
|
224
|
+
);
|
|
220
225
|
const jobA = await jobFactory.create({}, {transient: {projectId: projectA.id}});
|
|
221
226
|
const jobB = await jobFactory.create({}, {transient: {projectId: projectB.id}});
|
|
222
227
|
createCheckoutSpec.mockResolvedValue(githubSpec('token'));
|
|
@@ -242,7 +247,7 @@ describe('POST /runs/jobs/current/checkout-token', () => {
|
|
|
242
247
|
|
|
243
248
|
test('passes the project workspace, never the lease workspace claim', async () => {
|
|
244
249
|
const project = projectFactory.build();
|
|
245
|
-
|
|
250
|
+
getProjectById.mockResolvedValue({project});
|
|
246
251
|
const job = await jobFactory.create({}, {transient: {projectId: project.id}});
|
|
247
252
|
createCheckoutSpec.mockResolvedValue(githubSpec('token'));
|
|
248
253
|
const token = await mintActiveLeaseToken({
|
|
@@ -258,7 +263,7 @@ describe('POST /runs/jobs/current/checkout-token', () => {
|
|
|
258
263
|
});
|
|
259
264
|
|
|
260
265
|
test('returns 404 when the run has no project linkage', async () => {
|
|
261
|
-
|
|
266
|
+
getProjectById.mockResolvedValue({project: null});
|
|
262
267
|
const project = {id: crypto.randomUUID(), workspaceId: crypto.randomUUID()};
|
|
263
268
|
const job = await jobFactory.create({}, {transient: {projectId: project.id}});
|
|
264
269
|
const token = await mintActiveLeaseToken({jobId: job.id});
|
|
@@ -275,10 +280,14 @@ describe('POST /runs/jobs/current/checkout-token', () => {
|
|
|
275
280
|
|
|
276
281
|
test('maps a rate-limited provider error to 429 with retry_after_seconds', async () => {
|
|
277
282
|
const project = projectFactory.build();
|
|
278
|
-
|
|
283
|
+
getProjectById.mockResolvedValue({project});
|
|
279
284
|
const job = await jobFactory.create({}, {transient: {projectId: project.id}});
|
|
280
285
|
createCheckoutSpec.mockRejectedValue(
|
|
281
|
-
|
|
286
|
+
createInterModuleKnownError(
|
|
287
|
+
integrationsInterModuleContract.methods.createCheckoutSpec,
|
|
288
|
+
'provider-failure',
|
|
289
|
+
{reason: 'rate-limited', retryAfterSeconds: 60},
|
|
290
|
+
),
|
|
282
291
|
);
|
|
283
292
|
const token = await mintActiveLeaseToken({jobId: job.id});
|
|
284
293
|
|
|
@@ -295,10 +304,14 @@ describe('POST /runs/jobs/current/checkout-token', () => {
|
|
|
295
304
|
|
|
296
305
|
test('maps an inactive connection error to 422', async () => {
|
|
297
306
|
const project = projectFactory.build();
|
|
298
|
-
|
|
307
|
+
getProjectById.mockResolvedValue({project});
|
|
299
308
|
const job = await jobFactory.create({}, {transient: {projectId: project.id}});
|
|
300
309
|
createCheckoutSpec.mockRejectedValue(
|
|
301
|
-
|
|
310
|
+
createInterModuleKnownError(
|
|
311
|
+
integrationsInterModuleContract.methods.createCheckoutSpec,
|
|
312
|
+
'connection-inactive',
|
|
313
|
+
{connectionId: project.sourceConnectionId},
|
|
314
|
+
),
|
|
302
315
|
);
|
|
303
316
|
const token = await mintActiveLeaseToken({jobId: job.id});
|
|
304
317
|
|
|
@@ -313,9 +326,15 @@ describe('POST /runs/jobs/current/checkout-token', () => {
|
|
|
313
326
|
|
|
314
327
|
test('maps an unsupported-checkout provider error to 422', async () => {
|
|
315
328
|
const project = projectFactory.build();
|
|
316
|
-
|
|
329
|
+
getProjectById.mockResolvedValue({project});
|
|
317
330
|
const job = await jobFactory.create({}, {transient: {projectId: project.id}});
|
|
318
|
-
createCheckoutSpec.mockRejectedValue(
|
|
331
|
+
createCheckoutSpec.mockRejectedValue(
|
|
332
|
+
createInterModuleKnownError(
|
|
333
|
+
integrationsInterModuleContract.methods.createCheckoutSpec,
|
|
334
|
+
'checkout-unsupported',
|
|
335
|
+
{provider: 'github'},
|
|
336
|
+
),
|
|
337
|
+
);
|
|
319
338
|
const token = await mintActiveLeaseToken({jobId: job.id});
|
|
320
339
|
|
|
321
340
|
const res = await app.inject({
|
|
@@ -330,7 +349,7 @@ describe('POST /runs/jobs/current/checkout-token', () => {
|
|
|
330
349
|
|
|
331
350
|
test('surfaces an unexpected provider error as a 500 server-error', async () => {
|
|
332
351
|
const project = projectFactory.build();
|
|
333
|
-
|
|
352
|
+
getProjectById.mockResolvedValue({project});
|
|
334
353
|
const job = await jobFactory.create({}, {transient: {projectId: project.id}});
|
|
335
354
|
createCheckoutSpec.mockRejectedValue(new Error('unexpected provider failure'));
|
|
336
355
|
const token = await mintActiveLeaseToken({jobId: job.id});
|
|
@@ -347,7 +366,7 @@ describe('POST /runs/jobs/current/checkout-token', () => {
|
|
|
347
366
|
|
|
348
367
|
test('never writes the minted token to a log line', async () => {
|
|
349
368
|
const project = projectFactory.build();
|
|
350
|
-
|
|
369
|
+
getProjectById.mockResolvedValue({project});
|
|
351
370
|
const job = await jobFactory.create({}, {transient: {projectId: project.id}});
|
|
352
371
|
const secret = 'ghs-super-secret-token-value';
|
|
353
372
|
createCheckoutSpec.mockResolvedValue(githubSpec(secret));
|