@shipfox/api-workflows-dto 10.0.0 → 12.1.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 +1 -1
- package/CHANGELOG.md +40 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/inter-module.d.ts +53 -1
- package/dist/inter-module.d.ts.map +1 -1
- package/dist/inter-module.js +30 -2
- package/dist/inter-module.js.map +1 -1
- package/dist/schemas/checkout-token.d.ts +9 -0
- package/dist/schemas/checkout-token.d.ts.map +1 -1
- package/dist/schemas/checkout-token.js +7 -0
- package/dist/schemas/checkout-token.js.map +1 -1
- package/dist/schemas/index.d.ts +3 -3
- package/dist/schemas/index.d.ts.map +1 -1
- package/dist/schemas/index.js +3 -3
- package/dist/schemas/index.js.map +1 -1
- package/dist/schemas/job-execution.d.ts +17 -0
- package/dist/schemas/job-execution.d.ts.map +1 -1
- package/dist/schemas/job-execution.js +7 -0
- package/dist/schemas/job-execution.js.map +1 -1
- package/dist/schemas/job-listening.d.ts +18 -0
- package/dist/schemas/job-listening.d.ts.map +1 -1
- package/dist/schemas/job-listening.js +6 -0
- package/dist/schemas/job-listening.js.map +1 -1
- package/dist/schemas/step.d.ts +6 -0
- package/dist/schemas/step.d.ts.map +1 -1
- package/dist/schemas/step.js +3 -1
- package/dist/schemas/step.js.map +1 -1
- package/dist/schemas/workflow-run-detail.d.ts +40 -0
- package/dist/schemas/workflow-run-detail.d.ts.map +1 -1
- package/dist/schemas/workflow-run.d.ts +155 -0
- package/dist/schemas/workflow-run.d.ts.map +1 -1
- package/dist/schemas/workflow-run.js +39 -1
- package/dist/schemas/workflow-run.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/dist/working-directory.d.ts +9 -0
- package/dist/working-directory.d.ts.map +1 -0
- package/dist/working-directory.js +23 -0
- package/dist/working-directory.js.map +1 -0
- package/package.json +3 -3
- package/src/index.ts +16 -0
- package/src/inter-module.test.ts +17 -0
- package/src/inter-module.ts +27 -1
- package/src/schemas/checkout-token.test.ts +44 -2
- package/src/schemas/checkout-token.ts +13 -0
- package/src/schemas/index.ts +15 -0
- package/src/schemas/job-execution.test.ts +15 -0
- package/src/schemas/job-execution.ts +13 -0
- package/src/schemas/job-listening.test.ts +4 -0
- package/src/schemas/job-listening.ts +4 -0
- package/src/schemas/step.test.ts +12 -0
- package/src/schemas/step.ts +3 -1
- package/src/schemas/workflow-run.test.ts +106 -1
- package/src/schemas/workflow-run.ts +56 -1
- package/src/working-directory.ts +28 -0
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare class InvalidWorkingDirectoryError extends Error {
|
|
2
|
+
constructor(value: unknown);
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* Checks the path shape shared by the server dispatch boundary and the runner.
|
|
6
|
+
* Filesystem containment is checked by the runner after resolving symlinks.
|
|
7
|
+
*/
|
|
8
|
+
export declare function assertWorkingDirectory(value: unknown): asserts value is string;
|
|
9
|
+
//# sourceMappingURL=working-directory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"working-directory.d.ts","sourceRoot":"","sources":["../src/working-directory.ts"],"names":[],"mappings":"AAGA,qBAAa,4BAA6B,SAAQ,KAAK;gBACzC,KAAK,EAAE,OAAO;CAM3B;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,MAAM,CAW9E"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const WINDOWS_ABSOLUTE_PATH = /^[A-Za-z]:[\\/]/;
|
|
2
|
+
const PATH_SEPARATOR = /[\\/]/;
|
|
3
|
+
export class InvalidWorkingDirectoryError extends Error {
|
|
4
|
+
constructor(value){
|
|
5
|
+
super(`Invalid working_directory ${JSON.stringify(value)}: it must be a relative path without '..' segments or absolute path syntax.`);
|
|
6
|
+
this.name = 'InvalidWorkingDirectoryError';
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Checks the path shape shared by the server dispatch boundary and the runner.
|
|
11
|
+
* Filesystem containment is checked by the runner after resolving symlinks.
|
|
12
|
+
*/ export function assertWorkingDirectory(value) {
|
|
13
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
14
|
+
throw new InvalidWorkingDirectoryError(value);
|
|
15
|
+
}
|
|
16
|
+
const isAbsolute = value.startsWith('/') || value.startsWith('\\') || WINDOWS_ABSOLUTE_PATH.test(value);
|
|
17
|
+
const hasParentSegment = value.split(PATH_SEPARATOR).some((segment)=>segment === '..');
|
|
18
|
+
if (isAbsolute || hasParentSegment) {
|
|
19
|
+
throw new InvalidWorkingDirectoryError(value);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
//# sourceMappingURL=working-directory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/working-directory.ts"],"sourcesContent":["const WINDOWS_ABSOLUTE_PATH = /^[A-Za-z]:[\\\\/]/;\nconst PATH_SEPARATOR = /[\\\\/]/;\n\nexport class InvalidWorkingDirectoryError extends Error {\n constructor(value: unknown) {\n super(\n `Invalid working_directory ${JSON.stringify(value)}: it must be a relative path without '..' segments or absolute path syntax.`,\n );\n this.name = 'InvalidWorkingDirectoryError';\n }\n}\n\n/**\n * Checks the path shape shared by the server dispatch boundary and the runner.\n * Filesystem containment is checked by the runner after resolving symlinks.\n */\nexport function assertWorkingDirectory(value: unknown): asserts value is string {\n if (typeof value !== 'string' || value.length === 0) {\n throw new InvalidWorkingDirectoryError(value);\n }\n\n const isAbsolute =\n value.startsWith('/') || value.startsWith('\\\\') || WINDOWS_ABSOLUTE_PATH.test(value);\n const hasParentSegment = value.split(PATH_SEPARATOR).some((segment) => segment === '..');\n if (isAbsolute || hasParentSegment) {\n throw new InvalidWorkingDirectoryError(value);\n }\n}\n"],"names":["WINDOWS_ABSOLUTE_PATH","PATH_SEPARATOR","InvalidWorkingDirectoryError","Error","value","JSON","stringify","name","assertWorkingDirectory","length","isAbsolute","startsWith","test","hasParentSegment","split","some","segment"],"mappings":"AAAA,MAAMA,wBAAwB;AAC9B,MAAMC,iBAAiB;AAEvB,OAAO,MAAMC,qCAAqCC;IAChD,YAAYC,KAAc,CAAE;QAC1B,KAAK,CACH,CAAC,0BAA0B,EAAEC,KAAKC,SAAS,CAACF,OAAO,2EAA2E,CAAC;QAEjI,IAAI,CAACG,IAAI,GAAG;IACd;AACF;AAEA;;;CAGC,GACD,OAAO,SAASC,uBAAuBJ,KAAc;IACnD,IAAI,OAAOA,UAAU,YAAYA,MAAMK,MAAM,KAAK,GAAG;QACnD,MAAM,IAAIP,6BAA6BE;IACzC;IAEA,MAAMM,aACJN,MAAMO,UAAU,CAAC,QAAQP,MAAMO,UAAU,CAAC,SAASX,sBAAsBY,IAAI,CAACR;IAChF,MAAMS,mBAAmBT,MAAMU,KAAK,CAACb,gBAAgBc,IAAI,CAAC,CAACC,UAAYA,YAAY;IACnF,IAAIN,cAAcG,kBAAkB;QAClC,MAAM,IAAIX,6BAA6BE;IACzC;AACF"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shipfox/api-workflows-dto",
|
|
3
3
|
"license": "MIT",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "12.1.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/ShipfoxHQ/shipfox.git",
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"zod": "^4.4.3",
|
|
26
|
-
"@shipfox/api-agent-dto": "
|
|
27
|
-
"@shipfox/inter-module": "0.2.
|
|
26
|
+
"@shipfox/api-agent-dto": "12.0.0",
|
|
27
|
+
"@shipfox/inter-module": "0.2.3"
|
|
28
28
|
},
|
|
29
29
|
"imports": {
|
|
30
30
|
"#*": "./dist/*"
|
package/src/index.ts
CHANGED
|
@@ -4,10 +4,16 @@ export {
|
|
|
4
4
|
agentConfigIssueSchema,
|
|
5
5
|
agentRuntimeConfigQuerySchema,
|
|
6
6
|
type CheckoutIntentDto,
|
|
7
|
+
type CheckoutResultDto,
|
|
7
8
|
type CheckoutTokenAuthDto,
|
|
9
|
+
type CheckoutTokenParamsDto,
|
|
10
|
+
type CheckoutTokenQueryDto,
|
|
8
11
|
type CheckoutTokenResponseDto,
|
|
9
12
|
checkoutIntentSchema,
|
|
13
|
+
checkoutResultSchema,
|
|
10
14
|
checkoutTokenAuthSchema,
|
|
15
|
+
checkoutTokenParamsSchema,
|
|
16
|
+
checkoutTokenQuerySchema,
|
|
11
17
|
checkoutTokenResponseSchema,
|
|
12
18
|
type JobDto,
|
|
13
19
|
type JobExecutionDto,
|
|
@@ -79,6 +85,9 @@ export {
|
|
|
79
85
|
workflowRunDtoSchema,
|
|
80
86
|
workflowRunJobDetailDtoSchema,
|
|
81
87
|
workflowRunJobExecutionDetailDtoSchema,
|
|
88
|
+
workflowRunJobStatusCountDtoSchema,
|
|
89
|
+
workflowRunJobSummaryDtoSchema,
|
|
90
|
+
workflowRunListItemSchema,
|
|
82
91
|
workflowRunListQuerySchema,
|
|
83
92
|
workflowRunListResponseSchema,
|
|
84
93
|
workflowRunRerunModeSchema,
|
|
@@ -88,7 +97,13 @@ export {
|
|
|
88
97
|
} from '#schemas/index.js';
|
|
89
98
|
export {type StepSourceLocationDto, stepSourceLocationSchema} from '#schemas/step.js';
|
|
90
99
|
export {
|
|
100
|
+
WORKFLOW_RUN_JOB_PREVIEW_LIMIT,
|
|
101
|
+
type WorkflowRunJobStatusCountDto,
|
|
102
|
+
type WorkflowRunJobSummaryDto,
|
|
103
|
+
type WorkflowRunListItemDto,
|
|
104
|
+
type WorkflowRunTriggerReferenceDto,
|
|
91
105
|
type WorkflowSourceSnapshotDto,
|
|
106
|
+
workflowRunTriggerReferenceSchema,
|
|
92
107
|
workflowSourceSnapshotSchema,
|
|
93
108
|
} from '#schemas/workflow-run.js';
|
|
94
109
|
export {
|
|
@@ -128,3 +143,4 @@ export {
|
|
|
128
143
|
workflowsWorkflowRunCancelledSchema,
|
|
129
144
|
workflowsWorkflowRunTerminatedSchema,
|
|
130
145
|
} from './events.js';
|
|
146
|
+
export {assertWorkingDirectory, InvalidWorkingDirectoryError} from './working-directory.js';
|
package/src/inter-module.test.ts
CHANGED
|
@@ -13,6 +13,7 @@ describe('workflowsInterModuleContract', () => {
|
|
|
13
13
|
deliveryId: 'delivery-1',
|
|
14
14
|
data: {ref: 'refs/heads/main'},
|
|
15
15
|
},
|
|
16
|
+
triggerConnectionId: '00000000-0000-4000-8000-000000000005',
|
|
16
17
|
idempotencyKey: 'subscription-1:event-1',
|
|
17
18
|
});
|
|
18
19
|
const delivery = workflowsInterModuleContract.methods.deliverEventToJobListener.input.parse({
|
|
@@ -23,12 +24,28 @@ describe('workflowsInterModuleContract', () => {
|
|
|
23
24
|
source: 'github',
|
|
24
25
|
event: 'push',
|
|
25
26
|
provider: 'github',
|
|
27
|
+
triggerConnectionId: '00000000-0000-4000-8000-000000000005',
|
|
26
28
|
payload: {ref: 'refs/heads/main'},
|
|
27
29
|
receivedAt: '2026-07-20T12:00:00.000Z',
|
|
28
30
|
});
|
|
31
|
+
const reference =
|
|
32
|
+
workflowsInterModuleContract.methods.resolveWorkflowRunTriggerReference.input.parse({
|
|
33
|
+
workspaceId: '00000000-0000-4000-8000-000000000001',
|
|
34
|
+
triggerConnectionId: '00000000-0000-4000-8000-000000000005',
|
|
35
|
+
triggerPayload: {
|
|
36
|
+
provider: 'github',
|
|
37
|
+
source: 'github',
|
|
38
|
+
event: 'push',
|
|
39
|
+
deliveryId: 'delivery-1',
|
|
40
|
+
data: {ref: 'refs/heads/main'},
|
|
41
|
+
},
|
|
42
|
+
});
|
|
29
43
|
|
|
30
44
|
expect(start.idempotencyKey).toBe('subscription-1:event-1');
|
|
45
|
+
expect(start.triggerConnectionId).toBe('00000000-0000-4000-8000-000000000005');
|
|
31
46
|
expect(delivery.disposition).toBe('fire');
|
|
47
|
+
expect(reference.triggerConnectionId).toBe('00000000-0000-4000-8000-000000000005');
|
|
48
|
+
expect(delivery.triggerConnectionId).toBe('00000000-0000-4000-8000-000000000005');
|
|
32
49
|
});
|
|
33
50
|
|
|
34
51
|
test('accepts the minimal Logs and agent-tools query payloads', () => {
|
package/src/inter-module.ts
CHANGED
|
@@ -3,6 +3,13 @@ import {defineInterModuleContract, type InterModuleClient} from '@shipfox/inter-
|
|
|
3
3
|
import {z} from 'zod';
|
|
4
4
|
|
|
5
5
|
const idSchema = z.string().uuid();
|
|
6
|
+
const workflowRunTriggerReferenceSchema = z.object({
|
|
7
|
+
project: z.object({id: idSchema}).nullable(),
|
|
8
|
+
repository: z.string().nullable(),
|
|
9
|
+
ref: z.string().nullable(),
|
|
10
|
+
commit: z.string().nullable(),
|
|
11
|
+
actor: z.string().nullable(),
|
|
12
|
+
});
|
|
6
13
|
const triggerPayloadSchema = z.union([
|
|
7
14
|
z.object({
|
|
8
15
|
provider: z.literal('manual').optional(),
|
|
@@ -32,11 +39,19 @@ const interpolationFieldSchema = z.enum([
|
|
|
32
39
|
'agent.prompt',
|
|
33
40
|
'agent.model',
|
|
34
41
|
'agent.provider',
|
|
42
|
+
'agent.thinking',
|
|
35
43
|
'job.runner',
|
|
36
44
|
'job.outputs',
|
|
37
|
-
'job.
|
|
45
|
+
'job.execution_name',
|
|
46
|
+
'workflow.run_name',
|
|
38
47
|
'step.name',
|
|
48
|
+
'step.working_directory',
|
|
39
49
|
'step.feedback',
|
|
50
|
+
'checkout.project',
|
|
51
|
+
'checkout.connection',
|
|
52
|
+
'checkout.repository',
|
|
53
|
+
'checkout.ref',
|
|
54
|
+
'checkout.path',
|
|
40
55
|
]);
|
|
41
56
|
|
|
42
57
|
/**
|
|
@@ -51,6 +66,7 @@ export const workflowsInterModuleContract = defineInterModuleContract({
|
|
|
51
66
|
workspaceId: idSchema,
|
|
52
67
|
projectId: idSchema,
|
|
53
68
|
definitionId: idSchema,
|
|
69
|
+
triggerConnectionId: idSchema.optional(),
|
|
54
70
|
triggerPayload: triggerPayloadSchema,
|
|
55
71
|
inputs: z.record(z.string(), z.unknown()).optional(),
|
|
56
72
|
idempotencyKey: z.string().min(1),
|
|
@@ -73,6 +89,14 @@ export const workflowsInterModuleContract = defineInterModuleContract({
|
|
|
73
89
|
'invalid-job-runner-labels': z.object({labels: z.array(z.string())}),
|
|
74
90
|
},
|
|
75
91
|
},
|
|
92
|
+
resolveWorkflowRunTriggerReference: {
|
|
93
|
+
input: z.object({
|
|
94
|
+
workspaceId: idSchema,
|
|
95
|
+
triggerConnectionId: idSchema,
|
|
96
|
+
triggerPayload: triggerPayloadSchema,
|
|
97
|
+
}),
|
|
98
|
+
output: workflowRunTriggerReferenceSchema.nullable(),
|
|
99
|
+
},
|
|
76
100
|
deliverEventToJobListener: {
|
|
77
101
|
input: z.object({
|
|
78
102
|
jobId: idSchema,
|
|
@@ -82,6 +106,8 @@ export const workflowsInterModuleContract = defineInterModuleContract({
|
|
|
82
106
|
source: z.string().min(1),
|
|
83
107
|
event: z.string().min(1),
|
|
84
108
|
provider: z.string().min(1),
|
|
109
|
+
triggerConnectionId: idSchema.optional(),
|
|
110
|
+
triggerReference: workflowRunTriggerReferenceSchema.nullable().optional(),
|
|
85
111
|
payload: z.unknown(),
|
|
86
112
|
receivedAt: z.string().datetime(),
|
|
87
113
|
}),
|
|
@@ -1,8 +1,33 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
checkoutTokenParamsSchema,
|
|
3
|
+
checkoutTokenQuerySchema,
|
|
4
|
+
checkoutTokenResponseSchema,
|
|
5
|
+
} from './checkout-token.js';
|
|
6
|
+
|
|
7
|
+
describe('checkout token request schemas', () => {
|
|
8
|
+
it('accepts a UUID step id', () => {
|
|
9
|
+
const stepId = crypto.randomUUID();
|
|
10
|
+
|
|
11
|
+
expect(checkoutTokenParamsSchema.parse({stepId})).toEqual({stepId});
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('rejects a non-UUID step id', () => {
|
|
15
|
+
expect(() => checkoutTokenParamsSchema.parse({stepId: 'step-1'})).toThrow();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('coerces a positive integer attempt', () => {
|
|
19
|
+
expect(checkoutTokenQuerySchema.parse({attempt: '2'})).toEqual({attempt: 2});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it.each([0, -1, 'not-a-number'])('rejects invalid attempt %p', (attempt) => {
|
|
23
|
+
expect(() => checkoutTokenQuerySchema.parse({attempt})).toThrow();
|
|
24
|
+
});
|
|
25
|
+
});
|
|
2
26
|
|
|
3
27
|
const bearerResponse = {
|
|
4
28
|
repository_url: 'https://github.com/acme/repo.git',
|
|
5
29
|
ref: 'main',
|
|
30
|
+
fetch_depth: 1,
|
|
6
31
|
auth: {
|
|
7
32
|
kind: 'bearer',
|
|
8
33
|
token: 'gh-token',
|
|
@@ -16,6 +41,7 @@ const bearerResponse = {
|
|
|
16
41
|
const basicResponse = {
|
|
17
42
|
repository_url: 'https://github.com/acme/repo.git',
|
|
18
43
|
ref: 'main',
|
|
44
|
+
fetch_depth: 1,
|
|
19
45
|
auth: {
|
|
20
46
|
kind: 'basic',
|
|
21
47
|
username: 'x-access-token',
|
|
@@ -55,7 +81,11 @@ describe('checkoutTokenResponseSchema', () => {
|
|
|
55
81
|
});
|
|
56
82
|
|
|
57
83
|
it('accepts a credential-free response with no auth (debug provider)', () => {
|
|
58
|
-
const input = {
|
|
84
|
+
const input = {
|
|
85
|
+
repository_url: 'https://github.com/acme/repo.git',
|
|
86
|
+
ref: 'main',
|
|
87
|
+
fetch_depth: 1,
|
|
88
|
+
};
|
|
59
89
|
|
|
60
90
|
const result = checkoutTokenResponseSchema.parse(input);
|
|
61
91
|
|
|
@@ -159,4 +189,16 @@ describe('checkoutTokenResponseSchema', () => {
|
|
|
159
189
|
|
|
160
190
|
expect(parse).toThrow();
|
|
161
191
|
});
|
|
192
|
+
|
|
193
|
+
it('accepts zero for a full-history fetch', () => {
|
|
194
|
+
const result = checkoutTokenResponseSchema.parse({...bearerResponse, fetch_depth: 0});
|
|
195
|
+
|
|
196
|
+
expect(result.fetch_depth).toBe(0);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('rejects a negative fetch depth', () => {
|
|
200
|
+
const parse = () => checkoutTokenResponseSchema.parse({...bearerResponse, fetch_depth: -1});
|
|
201
|
+
|
|
202
|
+
expect(parse).toThrow();
|
|
203
|
+
});
|
|
162
204
|
});
|
|
@@ -33,9 +33,22 @@ export const checkoutTokenAuthSchema = z.discriminatedUnion('kind', [
|
|
|
33
33
|
|
|
34
34
|
export type CheckoutTokenAuthDto = z.infer<typeof checkoutTokenAuthSchema>;
|
|
35
35
|
|
|
36
|
+
export const checkoutTokenParamsSchema = z.object({
|
|
37
|
+
stepId: z.string().uuid(),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
export type CheckoutTokenParamsDto = z.infer<typeof checkoutTokenParamsSchema>;
|
|
41
|
+
|
|
42
|
+
export const checkoutTokenQuerySchema = z.object({
|
|
43
|
+
attempt: z.coerce.number().int().positive(),
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
export type CheckoutTokenQueryDto = z.infer<typeof checkoutTokenQuerySchema>;
|
|
47
|
+
|
|
36
48
|
export const checkoutTokenResponseSchema = z.object({
|
|
37
49
|
repository_url: z.string().min(1),
|
|
38
50
|
ref: z.string().min(1),
|
|
51
|
+
fetch_depth: z.number().int().nonnegative(),
|
|
39
52
|
git_author: checkoutGitAuthorSchema.optional(),
|
|
40
53
|
// Optional: credential-free providers (e.g. the debug source control) return a
|
|
41
54
|
// public clone URL with no auth material, so the runner clones without a token.
|
package/src/schemas/index.ts
CHANGED
|
@@ -5,8 +5,12 @@ export {
|
|
|
5
5
|
export {type CheckoutIntentDto, checkoutIntentSchema} from './checkout.js';
|
|
6
6
|
export {
|
|
7
7
|
type CheckoutTokenAuthDto,
|
|
8
|
+
type CheckoutTokenParamsDto,
|
|
9
|
+
type CheckoutTokenQueryDto,
|
|
8
10
|
type CheckoutTokenResponseDto,
|
|
9
11
|
checkoutTokenAuthSchema,
|
|
12
|
+
checkoutTokenParamsSchema,
|
|
13
|
+
checkoutTokenQuerySchema,
|
|
10
14
|
checkoutTokenResponseSchema,
|
|
11
15
|
} from './checkout-token.js';
|
|
12
16
|
export {
|
|
@@ -18,6 +22,8 @@ export {
|
|
|
18
22
|
jobStatusSchema,
|
|
19
23
|
} from './job.js';
|
|
20
24
|
export {
|
|
25
|
+
type CheckoutResultDto,
|
|
26
|
+
checkoutResultSchema,
|
|
21
27
|
type NextStepResponseDto,
|
|
22
28
|
nextStepResponseSchema,
|
|
23
29
|
type ReportStepBodyDto,
|
|
@@ -66,27 +72,36 @@ export {
|
|
|
66
72
|
export {
|
|
67
73
|
type RerunWorkflowRunBodyDto,
|
|
68
74
|
rerunWorkflowRunBodySchema,
|
|
75
|
+
WORKFLOW_RUN_JOB_PREVIEW_LIMIT,
|
|
69
76
|
type WorkflowRunAggregatesQueryDto,
|
|
70
77
|
type WorkflowRunAggregatesResponseDto,
|
|
71
78
|
type WorkflowRunAttemptDto,
|
|
72
79
|
type WorkflowRunAttemptsResponseDto,
|
|
73
80
|
type WorkflowRunDto,
|
|
81
|
+
type WorkflowRunJobStatusCountDto,
|
|
82
|
+
type WorkflowRunJobSummaryDto,
|
|
83
|
+
type WorkflowRunListItemDto,
|
|
74
84
|
type WorkflowRunListQueryDto,
|
|
75
85
|
type WorkflowRunListResponseDto,
|
|
76
86
|
type WorkflowRunRerunModeDto,
|
|
77
87
|
type WorkflowRunResponseDto,
|
|
78
88
|
type WorkflowRunStatusDto,
|
|
89
|
+
type WorkflowRunTriggerReferenceDto,
|
|
79
90
|
type WorkflowSourceSnapshotDto,
|
|
80
91
|
workflowRunAggregatesQuerySchema,
|
|
81
92
|
workflowRunAggregatesResponseSchema,
|
|
82
93
|
workflowRunAttemptDtoSchema,
|
|
83
94
|
workflowRunAttemptsResponseSchema,
|
|
84
95
|
workflowRunDtoSchema,
|
|
96
|
+
workflowRunJobStatusCountDtoSchema,
|
|
97
|
+
workflowRunJobSummaryDtoSchema,
|
|
98
|
+
workflowRunListItemSchema,
|
|
85
99
|
workflowRunListQuerySchema,
|
|
86
100
|
workflowRunListResponseSchema,
|
|
87
101
|
workflowRunRerunModeSchema,
|
|
88
102
|
workflowRunResponseSchema,
|
|
89
103
|
workflowRunStatusSchema,
|
|
104
|
+
workflowRunTriggerReferenceSchema,
|
|
90
105
|
workflowSourceSnapshotSchema,
|
|
91
106
|
} from './workflow-run.js';
|
|
92
107
|
export {
|
|
@@ -24,4 +24,19 @@ describe('reportStepBodySchema', () => {
|
|
|
24
24
|
|
|
25
25
|
expect(result.success).toBe(false);
|
|
26
26
|
});
|
|
27
|
+
it('accepts resolved checkout details as a dedicated report field', () => {
|
|
28
|
+
const parsed = reportStepBodySchema.parse({
|
|
29
|
+
status: 'succeeded',
|
|
30
|
+
attempt: 1,
|
|
31
|
+
exit_code: 0,
|
|
32
|
+
log_outcome: 'drained',
|
|
33
|
+
checkout: {
|
|
34
|
+
repository: 'https://github.com/acme/api.git',
|
|
35
|
+
ref: 'refs/pull/412/head',
|
|
36
|
+
commit: '9f2c000000000000000000000000000000000000',
|
|
37
|
+
path: '/runner/workspace/job-1',
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
expect(parsed.checkout?.ref).toBe('refs/pull/412/head');
|
|
41
|
+
});
|
|
27
42
|
});
|
|
@@ -36,6 +36,16 @@ export const nextStepResponseSchema = z.discriminatedUnion('kind', [
|
|
|
36
36
|
|
|
37
37
|
export type NextStepResponseDto = z.infer<typeof nextStepResponseSchema>;
|
|
38
38
|
|
|
39
|
+
export const checkoutResultSchema = z
|
|
40
|
+
.object({
|
|
41
|
+
repository: z.string().min(1),
|
|
42
|
+
ref: z.string().min(1),
|
|
43
|
+
commit: z.string().min(1),
|
|
44
|
+
path: z.string().min(1),
|
|
45
|
+
})
|
|
46
|
+
.describe('Resolved repository checkout details reported by the runner.');
|
|
47
|
+
export type CheckoutResultDto = z.infer<typeof checkoutResultSchema>;
|
|
48
|
+
|
|
39
49
|
export const reportStepBodySchema = z
|
|
40
50
|
.object({
|
|
41
51
|
status: z.enum(['succeeded', 'failed']).describe('Final status reported for the step attempt.'),
|
|
@@ -65,6 +75,9 @@ export const reportStepBodySchema = z
|
|
|
65
75
|
.describe(
|
|
66
76
|
'Structured output captured for attempt history. Large textual logs are stored separately.',
|
|
67
77
|
),
|
|
78
|
+
checkout: checkoutResultSchema
|
|
79
|
+
.optional()
|
|
80
|
+
.describe('Resolved repository checkout details captured for attempt history.'),
|
|
68
81
|
response: z
|
|
69
82
|
.string()
|
|
70
83
|
.max(STEP_RESPONSE_MAX_LENGTH)
|
|
@@ -31,6 +31,10 @@ describe('execution context schemas', () => {
|
|
|
31
31
|
event: 'pull_request_review',
|
|
32
32
|
delivery_id: 'delivery-1',
|
|
33
33
|
received_at: '2026-06-25T00:00:00.000Z',
|
|
34
|
+
project: null,
|
|
35
|
+
repository: null,
|
|
36
|
+
ref: null,
|
|
37
|
+
commit: null,
|
|
34
38
|
data: {body: 'LGTM'},
|
|
35
39
|
};
|
|
36
40
|
|
|
@@ -33,6 +33,10 @@ export const workflowExecutionEventSchema = z.object({
|
|
|
33
33
|
event: z.string(),
|
|
34
34
|
delivery_id: z.string(),
|
|
35
35
|
received_at: z.string(),
|
|
36
|
+
project: z.object({id: z.string().uuid()}).nullable(),
|
|
37
|
+
repository: z.string().nullable(),
|
|
38
|
+
ref: z.string().nullable(),
|
|
39
|
+
commit: z.string().nullable(),
|
|
36
40
|
data: z.unknown(),
|
|
37
41
|
});
|
|
38
42
|
|
package/src/schemas/step.test.ts
CHANGED
|
@@ -61,6 +61,18 @@ describe('stepErrorDtoSchema', () => {
|
|
|
61
61
|
});
|
|
62
62
|
});
|
|
63
63
|
|
|
64
|
+
it.each([
|
|
65
|
+
'checkout_path_invalid',
|
|
66
|
+
'checkout_destination_occupied',
|
|
67
|
+
] as const)('accepts the runner checkout destination reason %s', (reason) => {
|
|
68
|
+
const result = stepErrorDtoSchema.parse({
|
|
69
|
+
message: 'Checkout destination policy rejected the step.',
|
|
70
|
+
reason,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
expect(result?.reason).toBe(reason);
|
|
74
|
+
});
|
|
75
|
+
|
|
64
76
|
it('rejects unknown agent config issues', () => {
|
|
65
77
|
const result = stepErrorDtoSchema.safeParse({
|
|
66
78
|
message: 'Model provider credentials are not configured',
|
package/src/schemas/step.ts
CHANGED
|
@@ -2,7 +2,7 @@ import {z} from 'zod';
|
|
|
2
2
|
|
|
3
3
|
// Machine-readable cause of a step failure, for DB troubleshooting. The runner
|
|
4
4
|
// reports it and the server stores it as-is. The `checkout_*`, `git_unavailable`,
|
|
5
|
-
// `workspace_prep_failed`, and `setup_aborted` values cover setup
|
|
5
|
+
// `workspace_prep_failed`, and `setup_aborted` values cover checkout/setup failures.
|
|
6
6
|
// For agent steps the cause is split three ways: `agent_config_invalid` is a user-fixable
|
|
7
7
|
// configuration error and carries an `agent_config_issue`; `agent_invocation_failed`
|
|
8
8
|
// covers a provider/API failure once the configuration is valid; and
|
|
@@ -13,6 +13,8 @@ export const stepErrorReasonSchema = z.enum([
|
|
|
13
13
|
'checkout_failed',
|
|
14
14
|
'checkout_auth_failed',
|
|
15
15
|
'checkout_unavailable',
|
|
16
|
+
'checkout_path_invalid',
|
|
17
|
+
'checkout_destination_occupied',
|
|
16
18
|
'git_unavailable',
|
|
17
19
|
'workspace_prep_failed',
|
|
18
20
|
'setup_aborted',
|
|
@@ -1,10 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
WORKFLOW_RUN_JOB_PREVIEW_LIMIT,
|
|
3
|
+
workflowRunDtoSchema,
|
|
4
|
+
workflowRunListItemSchema,
|
|
5
|
+
workflowSourceSnapshotSchema,
|
|
6
|
+
} from './workflow-run.js';
|
|
2
7
|
|
|
3
8
|
const baseRun = {
|
|
4
9
|
id: '11111111-1111-4111-8111-111111111111',
|
|
5
10
|
project_id: '22222222-2222-4222-8222-222222222222',
|
|
6
11
|
definition_id: '33333333-3333-4333-8333-333333333333',
|
|
12
|
+
number: 1,
|
|
7
13
|
name: 'Build',
|
|
14
|
+
workflow_name: 'Build',
|
|
8
15
|
status: 'pending',
|
|
9
16
|
source_run_id: null,
|
|
10
17
|
root_run_id: null,
|
|
@@ -16,6 +23,7 @@ const baseRun = {
|
|
|
16
23
|
trigger_source: 'manual',
|
|
17
24
|
trigger_event: 'fire',
|
|
18
25
|
trigger_payload: {source: 'manual', event: 'fire'},
|
|
26
|
+
trigger_reference: null,
|
|
19
27
|
inputs: null,
|
|
20
28
|
created_at: '2026-06-16T00:00:00.000Z',
|
|
21
29
|
updated_at: '2026-06-16T00:00:00.000Z',
|
|
@@ -46,6 +54,8 @@ describe('workflow source snapshot schemas', () => {
|
|
|
46
54
|
const result = workflowRunDtoSchema.parse({...baseRun, source_snapshot: null});
|
|
47
55
|
|
|
48
56
|
expect(result.source_snapshot).toBeNull();
|
|
57
|
+
expect(result.name).toBe('Build');
|
|
58
|
+
expect(result.workflow_name).toBe('Build');
|
|
49
59
|
});
|
|
50
60
|
|
|
51
61
|
test('accepts run DTOs with source snapshots', () => {
|
|
@@ -57,3 +67,98 @@ describe('workflow source snapshot schemas', () => {
|
|
|
57
67
|
expect(result.source_snapshot).toEqual({content: 'name: Build\njobs: {}\n', format: 'yaml'});
|
|
58
68
|
});
|
|
59
69
|
});
|
|
70
|
+
|
|
71
|
+
describe('workflow run trigger reference schema', () => {
|
|
72
|
+
test('accepts a partially resolved reference', () => {
|
|
73
|
+
const result = workflowRunDtoSchema.parse({
|
|
74
|
+
...baseRun,
|
|
75
|
+
source_snapshot: null,
|
|
76
|
+
trigger_reference: {
|
|
77
|
+
repository: 'acme/api',
|
|
78
|
+
ref: 'refs/heads/main',
|
|
79
|
+
commit: null,
|
|
80
|
+
actor: null,
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
expect(result.trigger_reference).toEqual({
|
|
85
|
+
repository: 'acme/api',
|
|
86
|
+
ref: 'refs/heads/main',
|
|
87
|
+
commit: null,
|
|
88
|
+
actor: null,
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('rejects a reference missing a field rather than defaulting it', () => {
|
|
93
|
+
const result = workflowRunDtoSchema.safeParse({
|
|
94
|
+
...baseRun,
|
|
95
|
+
source_snapshot: null,
|
|
96
|
+
trigger_reference: {repository: 'acme/api', ref: 'refs/heads/main', commit: null},
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
expect(result.success).toBe(false);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe('workflow run list item schema', () => {
|
|
104
|
+
function jobDto(position: number) {
|
|
105
|
+
return {
|
|
106
|
+
id: `44444444-4444-4444-8444-${String(position).padStart(12, '0')}`,
|
|
107
|
+
key: `job-${position}`,
|
|
108
|
+
name: null,
|
|
109
|
+
status: 'succeeded' as const,
|
|
110
|
+
position,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
test('carries job status glyphs alongside the run', () => {
|
|
115
|
+
const result = workflowRunListItemSchema.parse({
|
|
116
|
+
...baseRun,
|
|
117
|
+
source_snapshot: null,
|
|
118
|
+
jobs: [jobDto(0)],
|
|
119
|
+
job_status_counts: [{status: 'succeeded', count: 1}],
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
expect(result.jobs).toHaveLength(1);
|
|
123
|
+
expect(result.jobs[0]?.status).toBe('succeeded');
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// The preview is a bounded slice, so counts describe jobs the payload never carried.
|
|
127
|
+
test('accepts counts larger than the preview it ships', () => {
|
|
128
|
+
const result = workflowRunListItemSchema.parse({
|
|
129
|
+
...baseRun,
|
|
130
|
+
source_snapshot: null,
|
|
131
|
+
jobs: [jobDto(0)],
|
|
132
|
+
job_status_counts: [
|
|
133
|
+
{status: 'succeeded', count: 40},
|
|
134
|
+
{status: 'failed', count: 2},
|
|
135
|
+
],
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
expect(result.job_status_counts).toHaveLength(2);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test('rejects a preview longer than the documented bound', () => {
|
|
142
|
+
const result = workflowRunListItemSchema.safeParse({
|
|
143
|
+
...baseRun,
|
|
144
|
+
source_snapshot: null,
|
|
145
|
+
jobs: Array.from({length: WORKFLOW_RUN_JOB_PREVIEW_LIMIT + 1}, (_, index) => jobDto(index)),
|
|
146
|
+
job_status_counts: [],
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
expect(result.success).toBe(false);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test.each([
|
|
153
|
+
['a jobs array', {job_status_counts: []}],
|
|
154
|
+
['job status counts', {jobs: []}],
|
|
155
|
+
])('rejects a run list item without %s', (_missing, partial) => {
|
|
156
|
+
const result = workflowRunListItemSchema.safeParse({
|
|
157
|
+
...baseRun,
|
|
158
|
+
source_snapshot: null,
|
|
159
|
+
...partial,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
expect(result.success).toBe(false);
|
|
163
|
+
});
|
|
164
|
+
});
|