@shipfox/api-workflows 7.1.0 → 9.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.
- package/.turbo/turbo-build.log +4 -4
- package/CHANGELOG.md +80 -0
- package/dist/core/agent-defaults.d.ts +2 -1
- package/dist/core/agent-defaults.d.ts.map +1 -1
- package/dist/core/agent-defaults.js.map +1 -1
- package/dist/core/agent-tools.d.ts +5 -2
- package/dist/core/agent-tools.d.ts.map +1 -1
- package/dist/core/agent-tools.js.map +1 -1
- package/dist/core/checkout.d.ts +2 -2
- package/dist/core/checkout.d.ts.map +1 -1
- package/dist/core/checkout.js.map +1 -1
- package/dist/core/entities/step.d.ts +2 -1
- package/dist/core/entities/step.d.ts.map +1 -1
- package/dist/core/entities/step.js.map +1 -1
- package/dist/core/errors.d.ts +0 -4
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +0 -6
- package/dist/core/errors.js.map +1 -1
- package/dist/core/job-execution.d.ts +0 -2
- package/dist/core/job-execution.d.ts.map +1 -1
- package/dist/core/job-execution.js +3 -7
- package/dist/core/job-execution.js.map +1 -1
- package/dist/core/run-workflow.d.ts +2 -2
- package/dist/core/run-workflow.d.ts.map +1 -1
- package/dist/core/run-workflow.js.map +1 -1
- package/dist/core/step-config/agent.d.ts +1 -1
- package/dist/core/step-config/agent.d.ts.map +1 -1
- package/dist/core/step-config/agent.js.map +1 -1
- package/dist/db/job-listeners.d.ts +2 -2
- package/dist/db/job-listeners.d.ts.map +1 -1
- package/dist/db/job-listeners.js.map +1 -1
- package/dist/db/workflow-runs/job-executions.d.ts +0 -5
- package/dist/db/workflow-runs/job-executions.d.ts.map +1 -1
- package/dist/db/workflow-runs/job-executions.js +0 -7
- package/dist/db/workflow-runs/job-executions.js.map +1 -1
- package/dist/db/workflow-runs/run-create.d.ts +2 -2
- package/dist/db/workflow-runs/run-create.d.ts.map +1 -1
- package/dist/db/workflow-runs/run-create.js.map +1 -1
- package/dist/db/workflow-runs.d.ts +1 -1
- package/dist/db/workflow-runs.d.ts.map +1 -1
- package/dist/db/workflow-runs.js +1 -1
- package/dist/db/workflow-runs.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/presentation/inter-module.d.ts +2 -2
- package/dist/presentation/inter-module.d.ts.map +1 -1
- package/dist/presentation/inter-module.js +2 -1
- package/dist/presentation/inter-module.js.map +1 -1
- package/dist/presentation/routes/cancel-run.d.ts +1 -1
- package/dist/presentation/routes/cancel-run.d.ts.map +1 -1
- package/dist/presentation/routes/cancel-run.js.map +1 -1
- package/dist/presentation/routes/checkout-token.d.ts +2 -2
- package/dist/presentation/routes/checkout-token.d.ts.map +1 -1
- package/dist/presentation/routes/checkout-token.js +1 -1
- package/dist/presentation/routes/checkout-token.js.map +1 -1
- package/dist/presentation/routes/get-run-aggregates.d.ts +1 -1
- package/dist/presentation/routes/get-run-aggregates.d.ts.map +1 -1
- package/dist/presentation/routes/get-run-aggregates.js.map +1 -1
- package/dist/presentation/routes/get-run.d.ts +1 -1
- package/dist/presentation/routes/get-run.d.ts.map +1 -1
- package/dist/presentation/routes/get-run.js.map +1 -1
- package/dist/presentation/routes/index.d.ts +2 -2
- package/dist/presentation/routes/index.d.ts.map +1 -1
- package/dist/presentation/routes/index.js.map +1 -1
- package/dist/presentation/routes/list-run-attempts.d.ts +1 -1
- package/dist/presentation/routes/list-run-attempts.d.ts.map +1 -1
- package/dist/presentation/routes/list-run-attempts.js.map +1 -1
- package/dist/presentation/routes/list-runs.d.ts +1 -1
- package/dist/presentation/routes/list-runs.d.ts.map +1 -1
- package/dist/presentation/routes/list-runs.js.map +1 -1
- package/dist/presentation/routes/next-step.d.ts.map +1 -1
- package/dist/presentation/routes/next-step.js +11 -8
- package/dist/presentation/routes/next-step.js.map +1 -1
- package/dist/presentation/routes/project-access.d.ts +1 -1
- package/dist/presentation/routes/project-access.d.ts.map +1 -1
- package/dist/presentation/routes/project-access.js.map +1 -1
- package/dist/presentation/routes/require-accessible-run.d.ts +1 -1
- package/dist/presentation/routes/require-accessible-run.d.ts.map +1 -1
- package/dist/presentation/routes/require-accessible-run.js.map +1 -1
- package/dist/presentation/routes/rerun-run.d.ts +1 -1
- package/dist/presentation/routes/rerun-run.d.ts.map +1 -1
- package/dist/presentation/routes/rerun-run.js.map +1 -1
- package/dist/temporal/activities/index.d.ts +2 -2
- package/dist/temporal/activities/index.d.ts.map +1 -1
- package/dist/temporal/activities/index.js.map +1 -1
- package/dist/temporal/activities/orchestration-activities.d.ts +2 -2
- package/dist/temporal/activities/orchestration-activities.d.ts.map +1 -1
- package/dist/temporal/activities/orchestration-activities.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +26 -28
- package/src/core/agent-defaults.ts +2 -1
- package/src/core/agent-tool-capability-warning.test.ts +26 -56
- package/src/core/agent-tools.ts +9 -7
- package/src/core/checkout.test.ts +2 -2
- package/src/core/checkout.ts +2 -2
- package/src/core/entities/step.ts +1 -2
- package/src/core/errors.ts +0 -7
- package/src/core/job-execution.ts +3 -10
- package/src/core/run-workflow.test.ts +22 -55
- package/src/core/run-workflow.ts +2 -2
- package/src/core/step-config/agent.ts +1 -1
- package/src/core/step-config/materialize-job-execution-steps.test.ts +1 -2
- package/src/core/step-config/materialize-workflow-model.test.ts +10 -13
- package/src/db/job-listeners.ts +2 -2
- package/src/db/workflow-runs/job-executions.ts +0 -21
- package/src/db/workflow-runs/run-create.test.ts +30 -23
- package/src/db/workflow-runs/run-create.ts +2 -2
- package/src/db/workflow-runs.ts +0 -1
- package/src/index.ts +3 -3
- package/src/presentation/dto/checkout-token.test.ts +7 -1
- package/src/presentation/inter-module.ts +4 -7
- package/src/presentation/routes/agent-runtime-config.test.ts +40 -132
- package/src/presentation/routes/cancel-run.test.ts +1 -1
- package/src/presentation/routes/cancel-run.ts +1 -1
- package/src/presentation/routes/checkout-token.test.ts +4 -37
- package/src/presentation/routes/checkout-token.ts +2 -2
- package/src/presentation/routes/get-run-aggregates.test.ts +1 -1
- package/src/presentation/routes/get-run-aggregates.ts +1 -1
- package/src/presentation/routes/get-run.test.ts +1 -1
- package/src/presentation/routes/get-run.ts +1 -1
- package/src/presentation/routes/get-step-secrets.test.ts +2 -2
- package/src/presentation/routes/index.test.ts +3 -3
- package/src/presentation/routes/index.ts +2 -2
- package/src/presentation/routes/list-run-attempts.test.ts +1 -1
- package/src/presentation/routes/list-run-attempts.ts +1 -1
- package/src/presentation/routes/list-runs.test.ts +1 -1
- package/src/presentation/routes/list-runs.ts +1 -1
- package/src/presentation/routes/next-step.test.ts +27 -69
- package/src/presentation/routes/next-step.ts +9 -5
- package/src/presentation/routes/project-access.ts +1 -1
- package/src/presentation/routes/report-step.test.ts +2 -3
- package/src/presentation/routes/require-accessible-run.ts +1 -1
- package/src/presentation/routes/rerun-run.test.ts +1 -1
- package/src/presentation/routes/rerun-run.ts +1 -1
- package/src/temporal/activities/index.ts +2 -2
- package/src/temporal/activities/orchestration-activities.ts +2 -2
- package/test/factories/workflow-model.ts +3 -1
- package/test/fixtures/active-lease-token.ts +5 -32
- package/test/fixtures/agent-inter-module.ts +32 -1
- package/test/fixtures/auth-inter-module.ts +2 -13
- package/test/fixtures/lease-token.ts +43 -29
- package/test/fixtures/projects-inter-module.ts +1 -1
- package/test/fixtures/runners-inter-module.ts +40 -48
- package/test/globalSetup.ts +0 -38
- package/test/setup.ts +2 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/dist/db/runner-lease-table.d.ts +0 -469
- package/dist/db/runner-lease-table.d.ts.map +0 -1
- package/dist/db/runner-lease-table.js +0 -43
- package/dist/db/runner-lease-table.js.map +0 -1
- package/src/db/runner-lease-table.ts +0 -39
|
@@ -17,7 +17,6 @@ import {
|
|
|
17
17
|
} from '#metrics/instance.js';
|
|
18
18
|
import {db, type Tx} from '../db.js';
|
|
19
19
|
import {writeWorkflowsOutboxEvent} from '../outbox-writes.js';
|
|
20
|
-
import {runningJobExecutions} from '../runner-lease-table.js';
|
|
21
20
|
import {jobExecutions, toJobExecution} from '../schema/job-executions.js';
|
|
22
21
|
import {jobs, toJob} from '../schema/jobs.js';
|
|
23
22
|
import {stepAttempts, toStepAttempt} from '../schema/step-attempts.js';
|
|
@@ -33,26 +32,6 @@ import {
|
|
|
33
32
|
} from './shared.js';
|
|
34
33
|
import {bulkUpdateStepStatuses, getStepsByJobExecutionIdForUpdate} from './steps.js';
|
|
35
34
|
|
|
36
|
-
export async function lockActiveJobExecutionLeaseForUpdate(
|
|
37
|
-
params: {jobId: string; jobExecutionId: string; runnerSessionId: string},
|
|
38
|
-
tx: Tx,
|
|
39
|
-
): Promise<boolean> {
|
|
40
|
-
const rows = await tx
|
|
41
|
-
.select({id: runningJobExecutions.id})
|
|
42
|
-
.from(runningJobExecutions)
|
|
43
|
-
.where(
|
|
44
|
-
and(
|
|
45
|
-
eq(runningJobExecutions.jobId, params.jobId),
|
|
46
|
-
eq(runningJobExecutions.jobExecutionId, params.jobExecutionId),
|
|
47
|
-
eq(runningJobExecutions.runnerSessionId, params.runnerSessionId),
|
|
48
|
-
),
|
|
49
|
-
)
|
|
50
|
-
.limit(1)
|
|
51
|
-
.for('update');
|
|
52
|
-
|
|
53
|
-
return rows.length > 0;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
35
|
export async function getJobExecutionById(id: string, tx?: Tx): Promise<JobExecution | undefined> {
|
|
57
36
|
const rows = await (tx ?? db())
|
|
58
37
|
.select()
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import {normalizeWorkflowDocument} from '@shipfox/api-definitions';
|
|
2
1
|
import {WORKFLOWS_WORKFLOW_RUN_ATTEMPT_CREATED} from '@shipfox/api-workflows-dto';
|
|
3
2
|
import {and, eq, sql} from 'drizzle-orm';
|
|
4
3
|
import type {AgentDefaultsResolver} from '#core/agent-defaults.js';
|
|
@@ -7,6 +6,7 @@ import {nextStepForJob, recordStepResult} from '#core/job-execution.js';
|
|
|
7
6
|
import {resolveTestAgentDefaults} from '#test/fixtures/agent-inter-module.js';
|
|
8
7
|
import {createTestSecretsClient} from '#test/fixtures/secrets-inter-module.js';
|
|
9
8
|
import {buildModel, expression, shellRef, template} from '#test/helpers/workflow-runs.js';
|
|
9
|
+
import {workflowModel} from '#test/index.js';
|
|
10
10
|
import {db} from '../db.js';
|
|
11
11
|
import {workflowsOutbox} from '../schema/outbox.js';
|
|
12
12
|
import {workflowRuns} from '../schema/workflow-runs.js';
|
|
@@ -330,7 +330,7 @@ describe('workflow run queries', () => {
|
|
|
330
330
|
const displayNameSource = ['Review batch $', '{{ execution.index }}'].join('');
|
|
331
331
|
const stepNameSource = ['Review $', '{{ execution.index }}'].join('');
|
|
332
332
|
const promptSource = ['Review $', '{{ execution.events[0].data.body }}'].join('');
|
|
333
|
-
const model =
|
|
333
|
+
const model = workflowModel({
|
|
334
334
|
name: 'Listening workflow',
|
|
335
335
|
runner: 'ubuntu-latest',
|
|
336
336
|
jobs: {
|
|
@@ -339,10 +339,10 @@ describe('workflow run queries', () => {
|
|
|
339
339
|
listening: {
|
|
340
340
|
on: [{source: 'github', event: 'pull_request_review'}],
|
|
341
341
|
until: [{source: 'github', event: 'pull_request'}],
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
batch: {
|
|
345
|
-
|
|
342
|
+
timeoutMs: 30 * 24 * 60 * 60 * 1000,
|
|
343
|
+
maxExecutions: 3,
|
|
344
|
+
batch: {debounceMs: 5000, maxSize: 10, maxWaitMs: 60 * 60 * 1000},
|
|
345
|
+
onResolve: 'cancel',
|
|
346
346
|
},
|
|
347
347
|
steps: [{name: stepNameSource, prompt: promptSource}],
|
|
348
348
|
},
|
|
@@ -400,7 +400,7 @@ describe('workflow run queries', () => {
|
|
|
400
400
|
test('persists the listening workflow model on the run attempt', async () => {
|
|
401
401
|
const displayNameSource = ['Review batch $', '{{ execution.index }}'].join('');
|
|
402
402
|
const promptSource = ['Review $', '{{ execution.events[0].data.body }}'].join('');
|
|
403
|
-
const model =
|
|
403
|
+
const model = workflowModel({
|
|
404
404
|
name: 'Listening workflow',
|
|
405
405
|
runner: 'ubuntu-latest',
|
|
406
406
|
jobs: {
|
|
@@ -409,10 +409,10 @@ describe('workflow run queries', () => {
|
|
|
409
409
|
listening: {
|
|
410
410
|
on: [{source: 'github', event: 'pull_request_review'}],
|
|
411
411
|
until: [{source: 'github', event: 'pull_request'}],
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
batch: {
|
|
415
|
-
|
|
412
|
+
timeoutMs: 30 * 24 * 60 * 60 * 1000,
|
|
413
|
+
maxExecutions: 3,
|
|
414
|
+
batch: {debounceMs: 5000, maxSize: 10, maxWaitMs: 60 * 60 * 1000},
|
|
415
|
+
onResolve: 'cancel',
|
|
416
416
|
},
|
|
417
417
|
steps: [{prompt: promptSource}],
|
|
418
418
|
},
|
|
@@ -441,7 +441,7 @@ describe('workflow run queries', () => {
|
|
|
441
441
|
});
|
|
442
442
|
|
|
443
443
|
test('does not load variables referenced only by listening job executions at run creation', async () => {
|
|
444
|
-
const model =
|
|
444
|
+
const model = workflowModel({
|
|
445
445
|
name: 'Listening vars workflow',
|
|
446
446
|
runner: 'ubuntu-latest',
|
|
447
447
|
jobs: {
|
|
@@ -449,6 +449,7 @@ describe('workflow run queries', () => {
|
|
|
449
449
|
listening: {
|
|
450
450
|
on: [{source: 'github', event: 'pull_request_review'}],
|
|
451
451
|
until: [{source: 'github', event: 'pull_request'}],
|
|
452
|
+
onResolve: 'finish',
|
|
452
453
|
},
|
|
453
454
|
steps: [
|
|
454
455
|
{
|
|
@@ -483,7 +484,7 @@ describe('workflow run queries', () => {
|
|
|
483
484
|
{
|
|
484
485
|
field: 'run',
|
|
485
486
|
model: () =>
|
|
486
|
-
|
|
487
|
+
workflowModel({
|
|
487
488
|
name: 'Missing run var',
|
|
488
489
|
runner: 'ubuntu-latest',
|
|
489
490
|
jobs: {build: {steps: [{run: `echo ${template('vars.REQUIRED')}`}]}},
|
|
@@ -493,7 +494,7 @@ describe('workflow run queries', () => {
|
|
|
493
494
|
{
|
|
494
495
|
field: 'env',
|
|
495
496
|
model: () =>
|
|
496
|
-
|
|
497
|
+
workflowModel({
|
|
497
498
|
name: 'Missing env var',
|
|
498
499
|
runner: 'ubuntu-latest',
|
|
499
500
|
jobs: {
|
|
@@ -507,7 +508,7 @@ describe('workflow run queries', () => {
|
|
|
507
508
|
{
|
|
508
509
|
field: 'agent.prompt',
|
|
509
510
|
model: () =>
|
|
510
|
-
|
|
511
|
+
workflowModel({
|
|
511
512
|
name: 'Missing prompt var',
|
|
512
513
|
runner: 'ubuntu-latest',
|
|
513
514
|
jobs: {fix: {steps: [{prompt: template('vars.REQUIRED')}]}},
|
|
@@ -517,7 +518,7 @@ describe('workflow run queries', () => {
|
|
|
517
518
|
{
|
|
518
519
|
field: 'agent.model',
|
|
519
520
|
model: () =>
|
|
520
|
-
|
|
521
|
+
workflowModel({
|
|
521
522
|
name: 'Missing model var',
|
|
522
523
|
runner: 'ubuntu-latest',
|
|
523
524
|
jobs: {fix: {steps: [{prompt: 'Fix it', model: template('vars.REQUIRED')}]}},
|
|
@@ -527,7 +528,7 @@ describe('workflow run queries', () => {
|
|
|
527
528
|
{
|
|
528
529
|
field: 'agent.provider',
|
|
529
530
|
model: () =>
|
|
530
|
-
|
|
531
|
+
workflowModel({
|
|
531
532
|
name: 'Missing provider var',
|
|
532
533
|
runner: 'ubuntu-latest',
|
|
533
534
|
jobs: {fix: {steps: [{prompt: 'Fix it', provider: template('vars.REQUIRED')}]}},
|
|
@@ -537,17 +538,23 @@ describe('workflow run queries', () => {
|
|
|
537
538
|
{
|
|
538
539
|
field: 'job.runner',
|
|
539
540
|
model: () =>
|
|
540
|
-
|
|
541
|
+
workflowModel({
|
|
541
542
|
name: 'Missing runner var',
|
|
542
543
|
runner: 'ubuntu-latest',
|
|
543
|
-
jobs: {
|
|
544
|
+
jobs: {
|
|
545
|
+
build: {
|
|
546
|
+
runner: [],
|
|
547
|
+
runnerTemplates: [template('vars.REQUIRED')],
|
|
548
|
+
steps: [{run: 'echo ok'}],
|
|
549
|
+
},
|
|
550
|
+
},
|
|
544
551
|
}),
|
|
545
552
|
expected: {field: 'job.runner', source: 'vars.REQUIRED'},
|
|
546
553
|
},
|
|
547
554
|
{
|
|
548
555
|
field: 'step.name',
|
|
549
556
|
model: () =>
|
|
550
|
-
|
|
557
|
+
workflowModel({
|
|
551
558
|
name: 'Missing step name var',
|
|
552
559
|
runner: 'ubuntu-latest',
|
|
553
560
|
jobs: {build: {steps: [{name: template('vars.REQUIRED'), run: 'echo ok'}]}},
|
|
@@ -614,7 +621,7 @@ describe('workflow run queries', () => {
|
|
|
614
621
|
});
|
|
615
622
|
|
|
616
623
|
test('persists resolved step config and authored step config separately', async () => {
|
|
617
|
-
const model =
|
|
624
|
+
const model = workflowModel({
|
|
618
625
|
name: 'Interpolated workflow',
|
|
619
626
|
runner: 'ubuntu-latest',
|
|
620
627
|
env: {RUN_ID: template('run.id'), REF: template('event.ref')},
|
|
@@ -665,7 +672,7 @@ describe('workflow run queries', () => {
|
|
|
665
672
|
});
|
|
666
673
|
|
|
667
674
|
test('resolves webhook trigger payload body and headers into step config', async () => {
|
|
668
|
-
const model =
|
|
675
|
+
const model = workflowModel({
|
|
669
676
|
name: 'Webhook workflow',
|
|
670
677
|
runner: 'ubuntu-latest',
|
|
671
678
|
env: {
|
|
@@ -711,7 +718,7 @@ describe('workflow run queries', () => {
|
|
|
711
718
|
});
|
|
712
719
|
|
|
713
720
|
test('fails for missing available untrusted interpolation paths', async () => {
|
|
714
|
-
const model =
|
|
721
|
+
const model = workflowModel({
|
|
715
722
|
name: 'Diagnostic workflow',
|
|
716
723
|
runner: 'ubuntu-latest',
|
|
717
724
|
env: {REF: template('event.ref')},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {createWorkflowModelSnapshot, type WorkflowModel} from '@shipfox/api-definitions-dto';
|
|
2
|
-
import type {IntegrationsModuleClient} from '@shipfox/api-integration-core-dto';
|
|
3
|
-
import type {ProjectsModuleClient} from '@shipfox/api-projects-dto';
|
|
2
|
+
import type {IntegrationsModuleClient} from '@shipfox/api-integration-core-dto/inter-module';
|
|
3
|
+
import type {ProjectsModuleClient} from '@shipfox/api-projects-dto/inter-module';
|
|
4
4
|
import type {SecretsInterModuleClient} from '@shipfox/api-secrets-dto/inter-module';
|
|
5
5
|
import {analyzeContextKeyAccess, type ResolvedFieldSegment} from '@shipfox/expression';
|
|
6
6
|
import {logger} from '@shipfox/node-opentelemetry';
|
package/src/db/workflow-runs.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -4,8 +4,8 @@ import type {AnnotationsInterModuleClient} from '@shipfox/annotations-dto/inter-
|
|
|
4
4
|
import type {AgentInterModuleClient} from '@shipfox/api-agent-dto/inter-module';
|
|
5
5
|
import type {AuthInterModuleClient} from '@shipfox/api-auth-dto/inter-module';
|
|
6
6
|
import type {DefinitionsInterModuleClient} from '@shipfox/api-definitions-dto/inter-module';
|
|
7
|
-
import type {IntegrationsModuleClient} from '@shipfox/api-integration-core-dto';
|
|
8
|
-
import type {ProjectsModuleClient} from '@shipfox/api-projects-dto';
|
|
7
|
+
import type {IntegrationsModuleClient} from '@shipfox/api-integration-core-dto/inter-module';
|
|
8
|
+
import type {ProjectsModuleClient} from '@shipfox/api-projects-dto/inter-module';
|
|
9
9
|
import {
|
|
10
10
|
RUNNER_JOB_CLAIMED,
|
|
11
11
|
RUNNER_JOB_LEASE_EXPIRED,
|
|
@@ -95,7 +95,7 @@ export function createWorkflowsModule({
|
|
|
95
95
|
}): ShipfoxModule {
|
|
96
96
|
return {
|
|
97
97
|
name: 'workflows',
|
|
98
|
-
database: {db, migrationsPath},
|
|
98
|
+
database: {db, migrationsPath, databaseNamespace: 'workflows'},
|
|
99
99
|
routes: createWorkflowRoutes({
|
|
100
100
|
agent,
|
|
101
101
|
annotations,
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import type {CheckoutSpec} from '@shipfox/api-integration-core';
|
|
2
1
|
import {toCheckoutTokenDto} from './checkout-token.js';
|
|
3
2
|
|
|
3
|
+
type CheckoutSpec = {
|
|
4
|
+
repositoryUrl: string;
|
|
5
|
+
ref: string;
|
|
6
|
+
credentials?: {username: string; token: string; expiresAt: Date};
|
|
7
|
+
gitAuthor?: {name: string; email: string};
|
|
8
|
+
};
|
|
9
|
+
|
|
4
10
|
describe('toCheckoutTokenDto', () => {
|
|
5
11
|
it('maps credentials to basic auth with an ISO expiry', () => {
|
|
6
12
|
const spec: CheckoutSpec = {
|
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DEFAULT_HARNESS,
|
|
3
|
-
harnessSchema,
|
|
4
|
-
materializedAgentStepConfigSchema,
|
|
5
|
-
} from '@shipfox/api-agent-dto';
|
|
1
|
+
import {materializedAgentStepConfigSchema} from '@shipfox/api-agent-dto';
|
|
6
2
|
import type {AgentInterModuleClient} from '@shipfox/api-agent-dto/inter-module';
|
|
7
3
|
import type {DefinitionsInterModuleClient} from '@shipfox/api-definitions-dto/inter-module';
|
|
8
|
-
import type {IntegrationsModuleClient} from '@shipfox/api-integration-core-dto';
|
|
9
|
-
import type {ProjectsModuleClient} from '@shipfox/api-projects-dto';
|
|
4
|
+
import type {IntegrationsModuleClient} from '@shipfox/api-integration-core-dto/inter-module';
|
|
5
|
+
import type {ProjectsModuleClient} from '@shipfox/api-projects-dto/inter-module';
|
|
10
6
|
import type {RunnersInterModuleClient} from '@shipfox/api-runners-dto/inter-module';
|
|
11
7
|
import type {SecretsInterModuleClient} from '@shipfox/api-secrets-dto/inter-module';
|
|
12
8
|
import {workflowsInterModuleContract} from '@shipfox/api-workflows-dto/inter-module';
|
|
@@ -15,6 +11,7 @@ import {
|
|
|
15
11
|
defineInterModulePresentation,
|
|
16
12
|
type InterModulePresentation,
|
|
17
13
|
} from '@shipfox/inter-module';
|
|
14
|
+
import {DEFAULT_HARNESS, harnessSchema} from '@shipfox/workflow-document';
|
|
18
15
|
import {InvalidJobRunnerLabelsError} from '#core/errors.js';
|
|
19
16
|
import {
|
|
20
17
|
AgentConfigUnresolvableError,
|
|
@@ -1,20 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
} from '@shipfox/api-agent';
|
|
6
|
-
import {resolveRuntimeCredentials} from '@shipfox/api-agent/core/resolve-runtime-credentials';
|
|
7
|
-
import {agentInterModuleContract} from '@shipfox/api-agent-dto/inter-module';
|
|
8
|
-
import {createLeaseTokenAuthMethod} from '@shipfox/api-auth';
|
|
2
|
+
type AgentInterModuleClient,
|
|
3
|
+
agentInterModuleContract,
|
|
4
|
+
} from '@shipfox/api-agent-dto/inter-module';
|
|
9
5
|
import type {WorkflowModel} from '@shipfox/api-definitions-dto';
|
|
10
|
-
import {secretsInterModuleContract} from '@shipfox/api-secrets-dto/inter-module';
|
|
11
6
|
import {createInterModuleKnownError} from '@shipfox/inter-module';
|
|
12
7
|
import {closeApp, createApp, type FastifyInstance} from '@shipfox/node-fastify';
|
|
13
8
|
import {createCapturingLogger} from '@shipfox/node-log/test';
|
|
14
|
-
import {
|
|
15
|
-
createInMemoryInterModuleTransport,
|
|
16
|
-
registerInterModulePresentations,
|
|
17
|
-
} from '@shipfox/node-module/inter-module';
|
|
18
9
|
import {eq} from 'drizzle-orm';
|
|
19
10
|
import type {StepStatus} from '#core/entities/step.js';
|
|
20
11
|
import {db} from '#db/db.js';
|
|
@@ -26,29 +17,39 @@ import {insertRunningJobLease, mintActiveLeaseToken} from '#test/fixtures/active
|
|
|
26
17
|
import {resolveTestAgentDefaults} from '#test/fixtures/agent-inter-module.js';
|
|
27
18
|
import {annotationsTestClient} from '#test/fixtures/annotations-inter-module.js';
|
|
28
19
|
import {workflowsTestAuthClient} from '#test/fixtures/auth-inter-module.js';
|
|
29
|
-
import {mintLeaseToken} from '#test/fixtures/lease-token.js';
|
|
20
|
+
import {fakeLeaseTokenAuthMethod, mintLeaseToken} from '#test/fixtures/lease-token.js';
|
|
30
21
|
import {projectsTestClient} from '#test/fixtures/projects-inter-module.js';
|
|
31
22
|
import {runnersTestClient} from '#test/fixtures/runners-inter-module.js';
|
|
32
23
|
import {createTestSecretsClient} from '#test/fixtures/secrets-inter-module.js';
|
|
33
24
|
import {createLeaseTokenRouteGroup} from './index.js';
|
|
34
25
|
|
|
35
26
|
const {captureExceptionMock} = vi.hoisted(() => ({captureExceptionMock: vi.fn()}));
|
|
36
|
-
vi.mock('@shipfox/api-agent/core/resolve-runtime-credentials', async (importOriginal) => {
|
|
37
|
-
const actual =
|
|
38
|
-
await importOriginal<typeof import('@shipfox/api-agent/core/resolve-runtime-credentials')>();
|
|
39
|
-
return {...actual, resolveRuntimeCredentials: vi.fn(actual.resolveRuntimeCredentials)};
|
|
40
|
-
});
|
|
41
27
|
vi.mock('@shipfox/node-error-monitoring', () => ({captureException: captureExceptionMock}));
|
|
42
28
|
|
|
43
29
|
const URL = '/runs/jobs/current/agent-runtime-config';
|
|
44
30
|
const secrets = createTestSecretsClient();
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
})
|
|
51
|
-
|
|
31
|
+
const runtimeConfigs = new Map<
|
|
32
|
+
string,
|
|
33
|
+
Awaited<ReturnType<AgentInterModuleClient['resolveRuntimeCredentials']>>
|
|
34
|
+
>();
|
|
35
|
+
const resolveRuntimeCredentials = vi.fn<AgentInterModuleClient['resolveRuntimeCredentials']>(
|
|
36
|
+
({workspaceId}) => {
|
|
37
|
+
const runtimeConfig = runtimeConfigs.get(workspaceId);
|
|
38
|
+
if (runtimeConfig) return Promise.resolve(runtimeConfig);
|
|
39
|
+
return Promise.reject(
|
|
40
|
+
createInterModuleKnownError(
|
|
41
|
+
agentInterModuleContract.methods.resolveRuntimeCredentials,
|
|
42
|
+
'model-provider-not-configured',
|
|
43
|
+
{},
|
|
44
|
+
),
|
|
45
|
+
);
|
|
46
|
+
},
|
|
47
|
+
);
|
|
48
|
+
const agentTestClient: AgentInterModuleClient = {
|
|
49
|
+
getValidationCatalog: vi.fn(),
|
|
50
|
+
resolveAgentConfig: vi.fn(),
|
|
51
|
+
resolveRuntimeCredentials,
|
|
52
|
+
};
|
|
52
53
|
|
|
53
54
|
describe('GET /runs/jobs/current/agent-runtime-config', () => {
|
|
54
55
|
let app: FastifyInstance;
|
|
@@ -56,7 +57,7 @@ describe('GET /runs/jobs/current/agent-runtime-config', () => {
|
|
|
56
57
|
|
|
57
58
|
beforeAll(async () => {
|
|
58
59
|
app = await createApp({
|
|
59
|
-
auth: [
|
|
60
|
+
auth: [fakeLeaseTokenAuthMethod],
|
|
60
61
|
routes: [
|
|
61
62
|
createLeaseTokenRouteGroup({
|
|
62
63
|
agent: agentTestClient,
|
|
@@ -77,7 +78,8 @@ describe('GET /runs/jobs/current/agent-runtime-config', () => {
|
|
|
77
78
|
beforeEach(() => {
|
|
78
79
|
clearLogLines();
|
|
79
80
|
captureExceptionMock.mockReset();
|
|
80
|
-
|
|
81
|
+
runtimeConfigs.clear();
|
|
82
|
+
resolveRuntimeCredentials.mockClear();
|
|
81
83
|
});
|
|
82
84
|
|
|
83
85
|
afterAll(async () => {
|
|
@@ -91,38 +93,6 @@ describe('GET /runs/jobs/current/agent-runtime-config', () => {
|
|
|
91
93
|
expect(res.statusCode).toBe(401);
|
|
92
94
|
expect(res.json().code).toBe('unauthorized');
|
|
93
95
|
});
|
|
94
|
-
|
|
95
|
-
test('rejects an expired token', async () => {
|
|
96
|
-
const token = await mintLeaseToken({
|
|
97
|
-
jobId: crypto.randomUUID(),
|
|
98
|
-
jobExecutionId: crypto.randomUUID(),
|
|
99
|
-
expiresIn: '-1s',
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
const res = await app.inject({
|
|
103
|
-
method: 'GET',
|
|
104
|
-
url: runtimeConfigUrl(crypto.randomUUID(), 1),
|
|
105
|
-
headers: {authorization: `Bearer ${token}`},
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
expect(res.statusCode).toBe(401);
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
test('rejects a token with the wrong audience', async () => {
|
|
112
|
-
const token = await mintLeaseToken({
|
|
113
|
-
jobId: crypto.randomUUID(),
|
|
114
|
-
jobExecutionId: crypto.randomUUID(),
|
|
115
|
-
audience: 'user-session',
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
const res = await app.inject({
|
|
119
|
-
method: 'GET',
|
|
120
|
-
url: runtimeConfigUrl(crypto.randomUUID(), 1),
|
|
121
|
-
headers: {authorization: `Bearer ${token}`},
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
expect(res.statusCode).toBe(401);
|
|
125
|
-
});
|
|
126
96
|
});
|
|
127
97
|
|
|
128
98
|
test('returns decrypted runtime credentials for a running leased agent step', async () => {
|
|
@@ -193,67 +163,6 @@ describe('GET /runs/jobs/current/agent-runtime-config', () => {
|
|
|
193
163
|
expect(res.json().credentials.api_key).toBe('sk-gated-workspace-secret');
|
|
194
164
|
});
|
|
195
165
|
|
|
196
|
-
test('returns custom provider descriptors for a running leased custom agent step', async () => {
|
|
197
|
-
const workspaceId = crypto.randomUUID();
|
|
198
|
-
await createCustomModelProviderConfig(
|
|
199
|
-
{
|
|
200
|
-
workspaceId,
|
|
201
|
-
body: {
|
|
202
|
-
slug: 'local-vllm',
|
|
203
|
-
display_name: 'Local vLLM',
|
|
204
|
-
api: 'openai-responses',
|
|
205
|
-
base_url: 'http://127.0.0.1:11434/v1',
|
|
206
|
-
api_key: 'sk-local-secret',
|
|
207
|
-
headers: [{name: 'authorization', value: 'Bearer local', secret: true}],
|
|
208
|
-
models: [{id: 'llama-3.1', label: 'Llama 3.1'}],
|
|
209
|
-
},
|
|
210
|
-
},
|
|
211
|
-
{probe: async () => undefined, secrets},
|
|
212
|
-
);
|
|
213
|
-
const {job, step} = await createRunningAgentStep({
|
|
214
|
-
workspaceId,
|
|
215
|
-
resolveAgentDefaults: () => ({
|
|
216
|
-
harness: 'pi',
|
|
217
|
-
provider: 'local-vllm',
|
|
218
|
-
model: 'llama-3.1',
|
|
219
|
-
thinking: 'high',
|
|
220
|
-
}),
|
|
221
|
-
steps: [
|
|
222
|
-
{
|
|
223
|
-
provider: 'local-vllm',
|
|
224
|
-
model: 'llama-3.1',
|
|
225
|
-
thinking: 'high',
|
|
226
|
-
prompt: 'Fix the tests.',
|
|
227
|
-
},
|
|
228
|
-
],
|
|
229
|
-
});
|
|
230
|
-
const token = await mintActiveLeaseToken({jobId: job.id});
|
|
231
|
-
|
|
232
|
-
const res = await app.inject({
|
|
233
|
-
method: 'GET',
|
|
234
|
-
url: runtimeConfigUrl(step.id, step.currentAttempt),
|
|
235
|
-
headers: {authorization: `Bearer ${token}`},
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
expect(res.statusCode).toBe(200);
|
|
239
|
-
expect(res.json()).toMatchObject({
|
|
240
|
-
harness: 'pi',
|
|
241
|
-
provider_id: 'local-vllm',
|
|
242
|
-
model: 'llama-3.1',
|
|
243
|
-
credentials: {
|
|
244
|
-
api_key: 'sk-local-secret',
|
|
245
|
-
'header:authorization': 'Bearer local',
|
|
246
|
-
},
|
|
247
|
-
custom_provider: {
|
|
248
|
-
api: 'openai-responses',
|
|
249
|
-
base_url: 'http://127.0.0.1:11434/v1',
|
|
250
|
-
secret_header_names: ['authorization'],
|
|
251
|
-
models: [{id: 'llama-3.1', label: 'Llama 3.1'}],
|
|
252
|
-
requires_api_key: true,
|
|
253
|
-
},
|
|
254
|
-
});
|
|
255
|
-
});
|
|
256
|
-
|
|
257
166
|
test('returns 404 when the lease token is no longer the active job lease', async () => {
|
|
258
167
|
const {run, job, step} = await createRunningAgentStep();
|
|
259
168
|
await insertRunningJobLease({
|
|
@@ -381,10 +290,10 @@ describe('GET /runs/jobs/current/agent-runtime-config', () => {
|
|
|
381
290
|
|
|
382
291
|
test('returns 409 and reports when workspace credentials cannot be decrypted', async () => {
|
|
383
292
|
const {job, step} = await createRunningAgentStep();
|
|
384
|
-
|
|
293
|
+
resolveRuntimeCredentials.mockRejectedValueOnce(
|
|
385
294
|
createInterModuleKnownError(
|
|
386
|
-
|
|
387
|
-
'
|
|
295
|
+
agentInterModuleContract.methods.resolveRuntimeCredentials,
|
|
296
|
+
'model-provider-credentials-invalid',
|
|
388
297
|
{},
|
|
389
298
|
),
|
|
390
299
|
);
|
|
@@ -430,15 +339,14 @@ function runtimeConfigUrl(stepId: string, attempt: number): string {
|
|
|
430
339
|
return `${URL}?${search.toString()}`;
|
|
431
340
|
}
|
|
432
341
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
},
|
|
440
|
-
|
|
441
|
-
);
|
|
342
|
+
function saveWorkspaceCredential(workspaceId: string, apiKey: string): void {
|
|
343
|
+
runtimeConfigs.set(workspaceId, {
|
|
344
|
+
harness: 'pi',
|
|
345
|
+
provider_id: 'anthropic',
|
|
346
|
+
model: 'claude-opus-4-8',
|
|
347
|
+
thinking: 'xhigh',
|
|
348
|
+
credentials: {api_key: apiKey},
|
|
349
|
+
});
|
|
442
350
|
}
|
|
443
351
|
|
|
444
352
|
type TestStepInput =
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {buildUserContext, setUserContext} from '@shipfox/api-auth-context';
|
|
2
|
-
import type {ProjectsModuleClient} from '@shipfox/api-projects-dto';
|
|
2
|
+
import type {ProjectsModuleClient} from '@shipfox/api-projects-dto/inter-module';
|
|
3
3
|
import {ClientError} from '@shipfox/node-fastify';
|
|
4
4
|
import type {FastifyInstance} from 'fastify';
|
|
5
5
|
import Fastify from 'fastify';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {ProjectsModuleClient} from '@shipfox/api-projects-dto';
|
|
1
|
+
import type {ProjectsModuleClient} from '@shipfox/api-projects-dto/inter-module';
|
|
2
2
|
import {workflowRunDtoSchema} from '@shipfox/api-workflows-dto';
|
|
3
3
|
import {ClientError, defineRoute} from '@shipfox/node-fastify';
|
|
4
4
|
import {z} from 'zod';
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import type {ProjectsModuleClient} from '@shipfox/api-projects-dto';
|
|
1
|
+
import {integrationsInterModuleContract} from '@shipfox/api-integration-core-dto/inter-module';
|
|
2
|
+
import type {ProjectsModuleClient} from '@shipfox/api-projects-dto/inter-module';
|
|
4
3
|
import {createInterModuleKnownError} from '@shipfox/inter-module';
|
|
5
4
|
import {closeApp, createApp, type FastifyInstance} from '@shipfox/node-fastify';
|
|
6
5
|
import {createCapturingLogger} from '@shipfox/node-log/test';
|
|
7
6
|
import {jobFactory} from '#test/factories/job.js';
|
|
8
7
|
import {projectFactory} from '#test/factories/project.js';
|
|
9
8
|
import {mintActiveLeaseToken} from '#test/fixtures/active-lease-token.js';
|
|
10
|
-
import {mintLeaseToken} from '#test/fixtures/lease-token.js';
|
|
9
|
+
import {fakeLeaseTokenAuthMethod, mintLeaseToken} from '#test/fixtures/lease-token.js';
|
|
11
10
|
import {runnersTestClient} from '#test/fixtures/runners-inter-module.js';
|
|
12
11
|
import {createLeaseTokenRouteGroup} from './index.js';
|
|
13
12
|
|
|
@@ -29,7 +28,7 @@ describe('POST /runs/jobs/current/checkout-token', () => {
|
|
|
29
28
|
|
|
30
29
|
beforeAll(async () => {
|
|
31
30
|
app = await createApp({
|
|
32
|
-
auth: [
|
|
31
|
+
auth: [fakeLeaseTokenAuthMethod],
|
|
33
32
|
routes: [
|
|
34
33
|
createLeaseTokenRouteGroup({
|
|
35
34
|
agent: {} as never,
|
|
@@ -64,38 +63,6 @@ describe('POST /runs/jobs/current/checkout-token', () => {
|
|
|
64
63
|
expect(res.statusCode).toBe(401);
|
|
65
64
|
expect(res.json().code).toBe('unauthorized');
|
|
66
65
|
});
|
|
67
|
-
|
|
68
|
-
test('rejects an expired token', async () => {
|
|
69
|
-
const token = await mintLeaseToken({
|
|
70
|
-
jobId: crypto.randomUUID(),
|
|
71
|
-
jobExecutionId: crypto.randomUUID(),
|
|
72
|
-
expiresIn: '-1s',
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
const res = await app.inject({
|
|
76
|
-
method: 'POST',
|
|
77
|
-
url: URL,
|
|
78
|
-
headers: {authorization: `Bearer ${token}`},
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
expect(res.statusCode).toBe(401);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
test('rejects a token with the wrong audience', async () => {
|
|
85
|
-
const token = await mintLeaseToken({
|
|
86
|
-
jobId: crypto.randomUUID(),
|
|
87
|
-
jobExecutionId: crypto.randomUUID(),
|
|
88
|
-
audience: 'user-session',
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
const res = await app.inject({
|
|
92
|
-
method: 'POST',
|
|
93
|
-
url: URL,
|
|
94
|
-
headers: {authorization: `Bearer ${token}`},
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
expect(res.statusCode).toBe(401);
|
|
98
|
-
});
|
|
99
66
|
});
|
|
100
67
|
|
|
101
68
|
test('returns basic auth for a GitHub-style spec with credentials', async () => {
|
|
@@ -2,8 +2,8 @@ import {requireLeasedJobContext} from '@shipfox/api-auth-context';
|
|
|
2
2
|
import {
|
|
3
3
|
type IntegrationsModuleClient,
|
|
4
4
|
integrationsInterModuleContract,
|
|
5
|
-
} from '@shipfox/api-integration-core-dto';
|
|
6
|
-
import type {ProjectsModuleClient} from '@shipfox/api-projects-dto';
|
|
5
|
+
} from '@shipfox/api-integration-core-dto/inter-module';
|
|
6
|
+
import type {ProjectsModuleClient} from '@shipfox/api-projects-dto/inter-module';
|
|
7
7
|
import type {RunnersInterModuleClient} from '@shipfox/api-runners-dto/inter-module';
|
|
8
8
|
import {checkoutTokenResponseSchema} from '@shipfox/api-workflows-dto';
|
|
9
9
|
import {isInterModuleKnownError} from '@shipfox/inter-module';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {buildUserContext, setUserContext} from '@shipfox/api-auth-context';
|
|
2
|
-
import type {ProjectsModuleClient} from '@shipfox/api-projects-dto';
|
|
2
|
+
import type {ProjectsModuleClient} from '@shipfox/api-projects-dto/inter-module';
|
|
3
3
|
import type {FastifyInstance} from 'fastify';
|
|
4
4
|
import Fastify from 'fastify';
|
|
5
5
|
import {serializerCompiler, validatorCompiler} from 'fastify-type-provider-zod';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {buildUserContext, setUserContext} from '@shipfox/api-auth-context';
|
|
2
|
-
import type {ProjectsModuleClient} from '@shipfox/api-projects-dto';
|
|
2
|
+
import type {ProjectsModuleClient} from '@shipfox/api-projects-dto/inter-module';
|
|
3
3
|
import {ClientError} from '@shipfox/node-fastify';
|
|
4
4
|
import {eq} from 'drizzle-orm';
|
|
5
5
|
import type {FastifyInstance} from 'fastify';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {ProjectsModuleClient} from '@shipfox/api-projects-dto';
|
|
1
|
+
import type {ProjectsModuleClient} from '@shipfox/api-projects-dto/inter-module';
|
|
2
2
|
import {workflowRunDetailResponseSchema} from '@shipfox/api-workflows-dto';
|
|
3
3
|
import {ClientError, defineRoute} from '@shipfox/node-fastify';
|
|
4
4
|
import {z} from 'zod';
|