@shipfox/api-runners-dto 12.2.0 → 13.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.
Files changed (48) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +13 -0
  3. package/dist/events.d.ts +0 -17
  4. package/dist/events.d.ts.map +1 -1
  5. package/dist/events.js +0 -9
  6. package/dist/events.js.map +1 -1
  7. package/dist/index.d.ts +2 -2
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +2 -2
  10. package/dist/index.js.map +1 -1
  11. package/dist/inter-module.d.ts +0 -27
  12. package/dist/inter-module.d.ts.map +1 -1
  13. package/dist/inter-module.js +0 -27
  14. package/dist/inter-module.js.map +1 -1
  15. package/dist/schemas/assign-runner-instances.d.ts +2 -0
  16. package/dist/schemas/assign-runner-instances.d.ts.map +1 -1
  17. package/dist/schemas/assign-runner-instances.js +2 -0
  18. package/dist/schemas/assign-runner-instances.js.map +1 -1
  19. package/dist/schemas/index.d.ts +2 -2
  20. package/dist/schemas/index.d.ts.map +1 -1
  21. package/dist/schemas/index.js +2 -2
  22. package/dist/schemas/index.js.map +1 -1
  23. package/dist/schemas/poll-demand.d.ts +1 -0
  24. package/dist/schemas/poll-demand.d.ts.map +1 -1
  25. package/dist/schemas/poll-demand.js +1 -0
  26. package/dist/schemas/poll-demand.js.map +1 -1
  27. package/dist/schemas/reconcile-runner-instances.d.ts +4 -0
  28. package/dist/schemas/reconcile-runner-instances.d.ts.map +1 -1
  29. package/dist/schemas/reconcile-runner-instances.js +3 -0
  30. package/dist/schemas/reconcile-runner-instances.js.map +1 -1
  31. package/dist/schemas/runner-enrollment.d.ts +2 -0
  32. package/dist/schemas/runner-enrollment.d.ts.map +1 -1
  33. package/dist/schemas/runner-enrollment.js +7 -2
  34. package/dist/schemas/runner-enrollment.js.map +1 -1
  35. package/dist/tsconfig.test.tsbuildinfo +1 -1
  36. package/package.json +1 -1
  37. package/src/events.ts +0 -12
  38. package/src/index.ts +4 -3
  39. package/src/inter-module.test.ts +0 -24
  40. package/src/inter-module.ts +0 -23
  41. package/src/schemas/assign-runner-instances.ts +3 -0
  42. package/src/schemas/index.ts +4 -0
  43. package/src/schemas/poll-demand.test.ts +1 -0
  44. package/src/schemas/poll-demand.ts +8 -0
  45. package/src/schemas/reconcile-runner-instances.test.ts +27 -3
  46. package/src/schemas/reconcile-runner-instances.ts +4 -0
  47. package/src/schemas/runner-enrollment.ts +9 -1
  48. package/tsconfig.build.tsbuildinfo +1 -1
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@shipfox/api-runners-dto",
3
3
  "license": "MIT",
4
- "version": "12.2.0",
4
+ "version": "13.0.0",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/ShipfoxHQ/shipfox.git",
package/src/events.ts CHANGED
@@ -4,7 +4,6 @@ const nonEmptyStringSchema = z.string().nonempty();
4
4
  const isoDateTimeSchema = z.string().datetime();
5
5
 
6
6
  export const RUNNER_JOB_LEASE_EXPIRED = 'runners.job.lease_expired' as const;
7
- export const RUNNER_JOB_QUEUED = 'runners.job.queued' as const;
8
7
  export const RUNNER_JOB_CLAIMED = 'runners.job.claimed' as const;
9
8
 
10
9
  export const runnerJobLeaseExpiredEventSchema = z.object({
@@ -15,15 +14,6 @@ export const runnerJobLeaseExpiredEventSchema = z.object({
15
14
  });
16
15
  export type RunnerJobLeaseExpiredEvent = z.infer<typeof runnerJobLeaseExpiredEventSchema>;
17
16
 
18
- export const runnerJobQueuedEventSchema = z.object({
19
- workflowRunId: nonEmptyStringSchema,
20
- workflowRunAttemptId: nonEmptyStringSchema,
21
- jobId: nonEmptyStringSchema,
22
- jobExecutionId: nonEmptyStringSchema,
23
- queuedAt: isoDateTimeSchema,
24
- });
25
- export type RunnerJobQueuedEvent = z.infer<typeof runnerJobQueuedEventSchema>;
26
-
27
17
  export const runnerJobClaimedEventSchema = z.object({
28
18
  workflowRunId: nonEmptyStringSchema,
29
19
  workflowRunAttemptId: nonEmptyStringSchema,
@@ -35,12 +25,10 @@ export type RunnerJobClaimedEvent = z.infer<typeof runnerJobClaimedEventSchema>;
35
25
 
36
26
  export interface RunnersEventMap {
37
27
  [RUNNER_JOB_LEASE_EXPIRED]: RunnerJobLeaseExpiredEvent;
38
- [RUNNER_JOB_QUEUED]: RunnerJobQueuedEvent;
39
28
  [RUNNER_JOB_CLAIMED]: RunnerJobClaimedEvent;
40
29
  }
41
30
 
42
31
  export const runnersEventSchemas = {
43
32
  [RUNNER_JOB_LEASE_EXPIRED]: runnerJobLeaseExpiredEventSchema,
44
- [RUNNER_JOB_QUEUED]: runnerJobQueuedEventSchema,
45
33
  [RUNNER_JOB_CLAIMED]: runnerJobClaimedEventSchema,
46
34
  } satisfies Record<keyof RunnersEventMap, z.ZodType>;
package/src/index.ts CHANGED
@@ -76,6 +76,9 @@ export {
76
76
  providerRunnerStateSchema,
77
77
  provisionerIdentityResponseSchema,
78
78
  provisionerTokenDtoSchema,
79
+ RECONCILE_RUNNER_INSTANCES_INTENDED_RESERVATION_HEADER,
80
+ RECONCILE_RUNNER_INSTANCES_INTENDED_RESERVATION_HEADER_VALUE,
81
+ RESERVATION_EXPIRED_ERROR_CODE,
79
82
  type ReconcileDesiredIntentDto,
80
83
  type ReconciledBoundJobDto,
81
84
  type ReconciledRunnerInstanceDto,
@@ -91,6 +94,7 @@ export {
91
94
  type RevokeManualRegistrationTokenResponseDto,
92
95
  type RevokeProvisionerTokenResponseDto,
93
96
  RUNNER_ASSIGNMENT_POLL_DEFAULT_WAIT_SECONDS,
97
+ RUNNER_INSTANCE_NOT_ASSIGNABLE_ERROR_CODE,
94
98
  RUNNER_SESSION_EXHAUSTED_CODE,
95
99
  type RunnerAdministratorInstanceDto,
96
100
  type RunnerAssignmentPollQueryDto,
@@ -134,13 +138,10 @@ export {
134
138
  export {
135
139
  RUNNER_JOB_CLAIMED,
136
140
  RUNNER_JOB_LEASE_EXPIRED,
137
- RUNNER_JOB_QUEUED,
138
141
  type RunnerJobClaimedEvent,
139
142
  type RunnerJobLeaseExpiredEvent,
140
- type RunnerJobQueuedEvent,
141
143
  type RunnersEventMap,
142
144
  runnerJobClaimedEventSchema,
143
145
  runnerJobLeaseExpiredEventSchema,
144
- runnerJobQueuedEventSchema,
145
146
  runnersEventSchemas,
146
147
  } from './events.js';
@@ -1,22 +1,6 @@
1
1
  import {runnersInterModuleContract} from './inter-module.js';
2
2
 
3
- const id = '00000000-0000-4000-8000-000000000001';
4
-
5
3
  describe('runnersInterModuleContract', () => {
6
- test('accepts stable job-execution identities for idempotent commands', () => {
7
- const input = runnersInterModuleContract.methods.enqueueJobExecution.input.parse({
8
- workspaceId: id,
9
- workflowRunId: id,
10
- workflowRunAttemptId: id,
11
- jobId: id,
12
- jobExecutionId: id,
13
- projectId: id,
14
- requiredLabels: ['linux'],
15
- });
16
-
17
- expect(input.jobExecutionId).toBe(id);
18
- });
19
-
20
4
  test('exposes bounded JSON capability results', () => {
21
5
  const result =
22
6
  runnersInterModuleContract.methods.getEffectiveRunnerToolCapabilities.output.parse({
@@ -29,12 +13,4 @@ describe('runnersInterModuleContract', () => {
29
13
  reportFresh: true,
30
14
  });
31
15
  });
32
-
33
- test('defines the scheduling validation failure', () => {
34
- const details = runnersInterModuleContract.methods.enqueueJobExecution.errors[
35
- 'empty-required-labels'
36
- ].parse({});
37
-
38
- expect(details).toEqual({});
39
- });
40
16
  });
@@ -7,29 +7,6 @@ const idSchema = z.string().uuid();
7
7
  export const runnersInterModuleContract = defineInterModuleContract({
8
8
  module: 'runners',
9
9
  methods: {
10
- enqueueJobExecution: {
11
- input: z.object({
12
- workspaceId: idSchema,
13
- workflowRunId: idSchema,
14
- workflowRunAttemptId: idSchema,
15
- jobId: idSchema,
16
- jobExecutionId: idSchema,
17
- projectId: idSchema,
18
- requiredLabels: z.array(z.string()),
19
- }),
20
- output: z.object({}),
21
- errors: {
22
- 'empty-required-labels': z.object({}),
23
- },
24
- },
25
- releaseJobExecution: {
26
- input: z.object({jobExecutionId: idSchema}),
27
- output: z.object({}),
28
- },
29
- cancelJobs: {
30
- input: z.object({jobIds: z.array(idSchema)}),
31
- output: z.object({}),
32
- },
33
10
  getLeaseState: {
34
11
  input: z.object({
35
12
  jobId: idSchema,
@@ -1,5 +1,8 @@
1
1
  import {z} from 'zod';
2
2
 
3
+ export const RESERVATION_EXPIRED_ERROR_CODE = 'reservation-expired';
4
+ export const RUNNER_INSTANCE_NOT_ASSIGNABLE_ERROR_CODE = 'runner-instance-not-assignable';
5
+
3
6
  export const assignRunnerInstancesBodySchema = z
4
7
  .object({
5
8
  reservation_id: z.string().uuid(),
@@ -33,6 +33,8 @@ export {
33
33
  type AssignRunnerInstancesResponseDto,
34
34
  assignRunnerInstancesBodySchema,
35
35
  assignRunnerInstancesResponseSchema,
36
+ RESERVATION_EXPIRED_ERROR_CODE,
37
+ RUNNER_INSTANCE_NOT_ASSIGNABLE_ERROR_CODE,
36
38
  } from './assign-runner-instances.js';
37
39
  export {
38
40
  type ClaimedJobResponseDto,
@@ -94,6 +96,8 @@ export {
94
96
  export {
95
97
  MAX_OBSERVED_PROVIDER_RUNNER_ID_LENGTH,
96
98
  MAX_RECONCILE_OBSERVED_RUNNERS,
99
+ RECONCILE_RUNNER_INSTANCES_INTENDED_RESERVATION_HEADER,
100
+ RECONCILE_RUNNER_INSTANCES_INTENDED_RESERVATION_HEADER_VALUE,
97
101
  type ReconcileDesiredIntentDto,
98
102
  type ReconciledBoundJobDto,
99
103
  type ReconciledRunnerInstanceDto,
@@ -67,6 +67,7 @@ describe('pollDemandResponseSchema', () => {
67
67
  expires_at: 'not-a-date',
68
68
  },
69
69
  ],
70
+ newly_reserved_count: 1,
70
71
  terminate_provider_runner_ids: [],
71
72
  });
72
73
 
@@ -42,6 +42,14 @@ export const reservationGrantSchema = z.object({
42
42
  export const pollDemandResponseSchema = z.object({
43
43
  stats: z.array(demandStatSchema),
44
44
  reservations: z.array(reservationGrantSchema),
45
+ newly_reserved_count: z
46
+ .number()
47
+ .int()
48
+ .min(0)
49
+ .optional()
50
+ .describe(
51
+ 'Reservation units created by this poll, including units satisfied by adopted runners.',
52
+ ),
45
53
  terminate_provider_runner_ids: z.array(z.string()),
46
54
  });
47
55
 
@@ -60,12 +60,33 @@ describe('reconcileRunnerInstancesBodySchema', () => {
60
60
  });
61
61
 
62
62
  describe('reconcileRunnerInstancesResponseSchema', () => {
63
- it('parses reconciled provisioned runner responses', () => {
64
- const result = reconcileRunnerInstancesResponseSchema.safeParse({
63
+ it('keeps the optional field absent for responses from older servers', () => {
64
+ const result = reconcileRunnerInstancesResponseSchema.parse({
65
65
  runners: [
66
66
  {
67
67
  provider_runner_id: 'provisioned-runner-1',
68
68
  state: 'running',
69
+ reservation_id: null,
70
+ runner_session_id: null,
71
+ bound_job: null,
72
+ desired_intent: 'keep',
73
+ },
74
+ ],
75
+ terminated_absent_provider_runner_ids: [],
76
+ });
77
+
78
+ const runner = result.runners[0];
79
+ if (!runner) throw new Error('Expected one parsed runner');
80
+ expect(Object.hasOwn(runner, 'intended_reservation_id')).toBe(false);
81
+ });
82
+
83
+ it('keeps an explicit null field present for current responses', () => {
84
+ const result = reconcileRunnerInstancesResponseSchema.parse({
85
+ runners: [
86
+ {
87
+ provider_runner_id: 'provisioned-runner-1',
88
+ state: 'running',
89
+ intended_reservation_id: null,
69
90
  reservation_id: crypto.randomUUID(),
70
91
  runner_session_id: crypto.randomUUID(),
71
92
  bound_job: {
@@ -80,6 +101,9 @@ describe('reconcileRunnerInstancesResponseSchema', () => {
80
101
  terminated_absent_provider_runner_ids: ['provisioned-runner-2'],
81
102
  });
82
103
 
83
- expect(result.success).toBe(true);
104
+ const runner = result.runners[0];
105
+ if (!runner) throw new Error('Expected one parsed runner');
106
+ expect(Object.hasOwn(runner, 'intended_reservation_id')).toBe(true);
107
+ expect(runner.intended_reservation_id).toBeNull();
84
108
  });
85
109
  });
@@ -3,6 +3,9 @@ import {providerRunnerStateSchema} from './report-runner-instances.js';
3
3
 
4
4
  export const MAX_RECONCILE_OBSERVED_RUNNERS = 5000;
5
5
  export const MAX_OBSERVED_PROVIDER_RUNNER_ID_LENGTH = 255;
6
+ export const RECONCILE_RUNNER_INSTANCES_INTENDED_RESERVATION_HEADER =
7
+ 'x-shipfox-reconcile-intended-reservation';
8
+ export const RECONCILE_RUNNER_INSTANCES_INTENDED_RESERVATION_HEADER_VALUE = '1';
6
9
 
7
10
  export const reconcileRunnerInstancesBodySchema = z
8
11
  .object({
@@ -35,6 +38,7 @@ export const reconciledRunnerInstanceSchema = z
35
38
  .object({
36
39
  provider_runner_id: z.string(),
37
40
  state: providerRunnerStateSchema.nullable(),
41
+ intended_reservation_id: z.string().uuid().nullable().optional(),
38
42
  reservation_id: z.string().uuid().nullable(),
39
43
  runner_session_id: z.string().uuid().nullable(),
40
44
  bound_job: reconciledBoundJobSchema.nullable(),
@@ -24,8 +24,16 @@ export const createRunnerInstancesBodySchema = z
24
24
  .strict();
25
25
  export const createRunnerInstancesResponseSchema = z.object({
26
26
  runner_instances: z.array(
27
- z.object({runner_instance_id: z.string().uuid(), bootstrap_token: z.string().min(1)}),
27
+ z.object({
28
+ runner_instance_id: z.string().uuid(),
29
+ bootstrap_token: z.string().min(1),
30
+ // Accepted instances retain their request position for short responses.
31
+ request_index: z.number().int().nonnegative().optional(),
32
+ }),
28
33
  ),
34
+ // A demand reservation can be consumed or stale between demand polling and launch.
35
+ // The field is omitted for the normal and warm-launch paths.
36
+ reservation_unavailable: z.literal(true).optional(),
29
37
  });
30
38
  export const runnerBootstrapExchangeResponseSchema = z.object({
31
39
  runner_instance_id: z.string().uuid(),