@studio-foundation/engine 0.3.0-beta.1 → 0.3.0-beta.6
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/dist/__tests__/engine.conditions-group.test.d.ts +2 -0
- package/dist/__tests__/engine.conditions-group.test.d.ts.map +1 -0
- package/dist/__tests__/engine.conditions-group.test.js +222 -0
- package/dist/__tests__/engine.conditions-group.test.js.map +1 -0
- package/dist/__tests__/engine.conditions.test.d.ts +2 -0
- package/dist/__tests__/engine.conditions.test.d.ts.map +1 -0
- package/dist/__tests__/engine.conditions.test.js +164 -0
- package/dist/__tests__/engine.conditions.test.js.map +1 -0
- package/dist/__tests__/engine.resume.test.d.ts +2 -0
- package/dist/__tests__/engine.resume.test.d.ts.map +1 -0
- package/dist/__tests__/engine.resume.test.js +93 -0
- package/dist/__tests__/engine.resume.test.js.map +1 -0
- package/dist/__tests__/engine.script-stage.test.d.ts +2 -0
- package/dist/__tests__/engine.script-stage.test.d.ts.map +1 -0
- package/dist/__tests__/engine.script-stage.test.js +108 -0
- package/dist/__tests__/engine.script-stage.test.js.map +1 -0
- package/dist/db/client.d.ts +3 -0
- package/dist/db/client.d.ts.map +1 -0
- package/{src/db/client.ts → dist/db/client.js} +1 -2
- package/dist/db/client.js.map +1 -0
- package/dist/engine.context-event.test.d.ts +2 -0
- package/dist/engine.context-event.test.d.ts.map +1 -0
- package/dist/engine.context-event.test.js +111 -0
- package/dist/engine.context-event.test.js.map +1 -0
- package/dist/engine.d.ts +52 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +364 -0
- package/dist/engine.js.map +1 -0
- package/dist/events.d.ts +163 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +12 -0
- package/dist/events.js.map +1 -0
- package/{src/index.ts → dist/index.d.ts} +3 -45
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/pipeline/agent-loader.d.ts +4 -0
- package/dist/pipeline/agent-loader.d.ts.map +1 -0
- package/dist/pipeline/agent-loader.js +28 -0
- package/dist/pipeline/agent-loader.js.map +1 -0
- package/dist/pipeline/agent-loader.test.d.ts +2 -0
- package/dist/pipeline/agent-loader.test.d.ts.map +1 -0
- package/dist/pipeline/agent-loader.test.js +134 -0
- package/dist/pipeline/agent-loader.test.js.map +1 -0
- package/dist/pipeline/condition-evaluator.d.ts +6 -0
- package/dist/pipeline/condition-evaluator.d.ts.map +1 -0
- package/dist/pipeline/condition-evaluator.js +105 -0
- package/dist/pipeline/condition-evaluator.js.map +1 -0
- package/dist/pipeline/condition-evaluator.test.d.ts +2 -0
- package/dist/pipeline/condition-evaluator.test.d.ts.map +1 -0
- package/dist/pipeline/condition-evaluator.test.js +103 -0
- package/dist/pipeline/condition-evaluator.test.js.map +1 -0
- package/dist/pipeline/context-pack-loader.d.ts +3 -0
- package/dist/pipeline/context-pack-loader.d.ts.map +1 -0
- package/dist/pipeline/context-pack-loader.js +51 -0
- package/dist/pipeline/context-pack-loader.js.map +1 -0
- package/dist/pipeline/context-propagation.d.ts +28 -0
- package/dist/pipeline/context-propagation.d.ts.map +1 -0
- package/dist/pipeline/context-propagation.js +161 -0
- package/dist/pipeline/context-propagation.js.map +1 -0
- package/dist/pipeline/context-propagation.test.d.ts +2 -0
- package/dist/pipeline/context-propagation.test.d.ts.map +1 -0
- package/dist/pipeline/context-propagation.test.js +194 -0
- package/dist/pipeline/context-propagation.test.js.map +1 -0
- package/dist/pipeline/contract-loader.d.ts +4 -0
- package/dist/pipeline/contract-loader.d.ts.map +1 -0
- package/dist/pipeline/contract-loader.js +30 -0
- package/dist/pipeline/contract-loader.js.map +1 -0
- package/dist/pipeline/group-orchestrator.d.ts +19 -0
- package/dist/pipeline/group-orchestrator.d.ts.map +1 -0
- package/dist/pipeline/group-orchestrator.js +376 -0
- package/dist/pipeline/group-orchestrator.js.map +1 -0
- package/dist/pipeline/hook-executor.d.ts +28 -0
- package/dist/pipeline/hook-executor.d.ts.map +1 -0
- package/dist/pipeline/hook-executor.js +63 -0
- package/dist/pipeline/hook-executor.js.map +1 -0
- package/dist/pipeline/hook-executor.test.d.ts +2 -0
- package/dist/pipeline/hook-executor.test.d.ts.map +1 -0
- package/dist/pipeline/hook-executor.test.js +71 -0
- package/dist/pipeline/hook-executor.test.js.map +1 -0
- package/dist/pipeline/invariants-loader.d.ts +6 -0
- package/dist/pipeline/invariants-loader.d.ts.map +1 -0
- package/dist/pipeline/invariants-loader.js +15 -0
- package/dist/pipeline/invariants-loader.js.map +1 -0
- package/dist/pipeline/invariants-loader.test.d.ts +2 -0
- package/dist/pipeline/invariants-loader.test.d.ts.map +1 -0
- package/dist/pipeline/invariants-loader.test.js +39 -0
- package/dist/pipeline/invariants-loader.test.js.map +1 -0
- package/dist/pipeline/loader.d.ts +5 -0
- package/dist/pipeline/loader.d.ts.map +1 -0
- package/dist/pipeline/loader.js +132 -0
- package/dist/pipeline/loader.js.map +1 -0
- package/dist/pipeline/loader.test.d.ts +2 -0
- package/dist/pipeline/loader.test.d.ts.map +1 -0
- package/dist/pipeline/loader.test.js +120 -0
- package/dist/pipeline/loader.test.js.map +1 -0
- package/dist/pipeline/output-validator.d.ts +10 -0
- package/dist/pipeline/output-validator.d.ts.map +1 -0
- package/dist/pipeline/output-validator.js +20 -0
- package/dist/pipeline/output-validator.js.map +1 -0
- package/dist/pipeline/output-validator.test.d.ts +2 -0
- package/dist/pipeline/output-validator.test.d.ts.map +1 -0
- package/dist/pipeline/output-validator.test.js +77 -0
- package/dist/pipeline/output-validator.test.js.map +1 -0
- package/dist/pipeline/post-validator.d.ts +8 -0
- package/dist/pipeline/post-validator.d.ts.map +1 -0
- package/dist/pipeline/post-validator.js +103 -0
- package/dist/pipeline/post-validator.js.map +1 -0
- package/dist/pipeline/skill-loader.d.ts +10 -0
- package/dist/pipeline/skill-loader.d.ts.map +1 -0
- package/dist/pipeline/skill-loader.js +26 -0
- package/dist/pipeline/skill-loader.js.map +1 -0
- package/dist/pipeline/skill-loader.test.d.ts +2 -0
- package/dist/pipeline/skill-loader.test.d.ts.map +1 -0
- package/dist/pipeline/skill-loader.test.js +38 -0
- package/dist/pipeline/skill-loader.test.js.map +1 -0
- package/dist/pipeline/stage-executor.d.ts +26 -0
- package/dist/pipeline/stage-executor.d.ts.map +1 -0
- package/dist/pipeline/stage-executor.js +540 -0
- package/dist/pipeline/stage-executor.js.map +1 -0
- package/dist/pipeline/stage-resolver.d.ts +3 -0
- package/dist/pipeline/stage-resolver.d.ts.map +1 -0
- package/dist/pipeline/stage-resolver.js +6 -0
- package/dist/pipeline/stage-resolver.js.map +1 -0
- package/dist/pipeline/startup-executor.d.ts +3 -0
- package/dist/pipeline/startup-executor.d.ts.map +1 -0
- package/dist/pipeline/startup-executor.js +23 -0
- package/dist/pipeline/startup-executor.js.map +1 -0
- package/dist/pipeline/startup-executor.test.d.ts +2 -0
- package/dist/pipeline/startup-executor.test.d.ts.map +1 -0
- package/dist/pipeline/startup-executor.test.js +34 -0
- package/dist/pipeline/startup-executor.test.js.map +1 -0
- package/dist/pipeline/types.d.ts +28 -0
- package/dist/pipeline/types.d.ts.map +1 -0
- package/dist/pipeline/types.js +12 -0
- package/dist/pipeline/types.js.map +1 -0
- package/dist/repo-resolver.d.ts +10 -0
- package/dist/repo-resolver.d.ts.map +1 -0
- package/dist/repo-resolver.js +39 -0
- package/dist/repo-resolver.js.map +1 -0
- package/dist/spawners/direct-engine-spawner.d.ts +8 -0
- package/dist/spawners/direct-engine-spawner.d.ts.map +1 -0
- package/dist/spawners/direct-engine-spawner.js +24 -0
- package/dist/spawners/direct-engine-spawner.js.map +1 -0
- package/dist/state/run-store.d.ts +74 -0
- package/dist/state/run-store.d.ts.map +1 -0
- package/dist/state/run-store.js +269 -0
- package/dist/state/run-store.js.map +1 -0
- package/dist/state/run-store.test.d.ts +2 -0
- package/dist/state/run-store.test.d.ts.map +1 -0
- package/dist/state/run-store.test.js +130 -0
- package/dist/state/run-store.test.js.map +1 -0
- package/dist/state/state-machine.d.ts +6 -0
- package/dist/state/state-machine.d.ts.map +1 -0
- package/dist/state/state-machine.js +27 -0
- package/dist/state/state-machine.js.map +1 -0
- package/dist/state/status-derivation.d.ts +12 -0
- package/dist/state/status-derivation.d.ts.map +1 -0
- package/dist/state/status-derivation.js +30 -0
- package/dist/state/status-derivation.js.map +1 -0
- package/package.json +7 -4
- package/ARCHITECTURE.md +0 -57
- package/src/__tests__/__fixtures__/script-stage/contracts/book-context.contract.yaml +0 -4
- package/src/__tests__/engine.conditions-group.test.ts +0 -244
- package/src/__tests__/engine.conditions.test.ts +0 -186
- package/src/__tests__/engine.resume.test.ts +0 -108
- package/src/__tests__/engine.script-stage.test.ts +0 -125
- package/src/engine.context-event.test.ts +0 -175
- package/src/engine.ts +0 -491
- package/src/events.ts +0 -167
- package/src/pipeline/agent-loader.test.ts +0 -151
- package/src/pipeline/agent-loader.ts +0 -39
- package/src/pipeline/condition-evaluator.test.ts +0 -129
- package/src/pipeline/condition-evaluator.ts +0 -121
- package/src/pipeline/context-pack-loader.ts +0 -63
- package/src/pipeline/context-propagation.test.ts +0 -237
- package/src/pipeline/context-propagation.ts +0 -235
- package/src/pipeline/contract-loader.ts +0 -41
- package/src/pipeline/group-orchestrator.ts +0 -483
- package/src/pipeline/hook-executor.test.ts +0 -121
- package/src/pipeline/hook-executor.ts +0 -87
- package/src/pipeline/invariants-loader.test.ts +0 -51
- package/src/pipeline/invariants-loader.ts +0 -14
- package/src/pipeline/loader.test.ts +0 -128
- package/src/pipeline/loader.ts +0 -149
- package/src/pipeline/output-validator.test.ts +0 -100
- package/src/pipeline/output-validator.ts +0 -40
- package/src/pipeline/post-validator.ts +0 -124
- package/src/pipeline/skill-loader.test.ts +0 -44
- package/src/pipeline/skill-loader.ts +0 -32
- package/src/pipeline/stage-executor.ts +0 -654
- package/src/pipeline/stage-resolver.ts +0 -8
- package/src/pipeline/startup-executor.test.ts +0 -37
- package/src/pipeline/startup-executor.ts +0 -32
- package/src/pipeline/types.ts +0 -42
- package/src/repo-resolver.ts +0 -61
- package/src/spawners/direct-engine-spawner.ts +0 -26
- package/src/state/run-store.test.ts +0 -157
- package/src/state/run-store.ts +0 -362
- package/src/state/state-machine.ts +0 -35
- package/src/state/status-derivation.ts +0 -36
- package/tests/context-pack-loader.test.ts +0 -113
- package/tests/context-propagation.test.ts +0 -267
- package/tests/direct-engine-spawner.test.ts +0 -102
- package/tests/e2e/feature-v5.test.ts +0 -57
- package/tests/events.test.ts +0 -56
- package/tests/fixtures/agents/test-agent.agent.yaml +0 -5
- package/tests/fixtures/contracts/code-gen.contract.yaml +0 -6
- package/tests/fixtures/contracts/qa-gate.contract.yaml +0 -15
- package/tests/fixtures/contracts/test-contract.contract.yaml +0 -6
- package/tests/fixtures/pipelines/group-test.pipeline.yaml +0 -40
- package/tests/fixtures/pipelines/simple.pipeline.yaml +0 -15
- package/tests/fixtures/pipelines/two-stage.pipeline.yaml +0 -24
- package/tests/fixtures/software/pipelines/feature-builder.pipeline.yaml +0 -75
- package/tests/fixtures/test-project/agents/test-agent.agent.yaml +0 -5
- package/tests/fixtures/test-project/contracts/basic-result.contract.yaml +0 -6
- package/tests/fixtures/test-project/contracts/code-gen.contract.yaml +0 -6
- package/tests/fixtures/test-project/contracts/maximum-only.contract.yaml +0 -8
- package/tests/fixtures/test-project/contracts/qa-gate.contract.yaml +0 -15
- package/tests/fixtures/test-project/contracts/strict-result.contract.yaml +0 -7
- package/tests/fixtures/test-project/contracts/test-contract.contract.yaml +0 -6
- package/tests/fixtures/test-project/pipelines/group-simple.pipeline.yaml +0 -26
- package/tests/fixtures/test-project/pipelines/group-test.pipeline.yaml +0 -40
- package/tests/fixtures/test-project/pipelines/hook-output-template.pipeline.yaml +0 -19
- package/tests/fixtures/test-project/pipelines/hook-reject-on-failure.pipeline.yaml +0 -19
- package/tests/fixtures/test-project/pipelines/maximum-only.pipeline.yaml +0 -15
- package/tests/fixtures/test-project/pipelines/parallel-collect-all-test.pipeline.yaml +0 -39
- package/tests/fixtures/test-project/pipelines/parallel-context-isolation-test.pipeline.yaml +0 -38
- package/tests/fixtures/test-project/pipelines/parallel-fail-test.pipeline.yaml +0 -39
- package/tests/fixtures/test-project/pipelines/parallel-test.pipeline.yaml +0 -39
- package/tests/fixtures/test-project/pipelines/parallel-then-sequential-test.pipeline.yaml +0 -38
- package/tests/fixtures/test-project/pipelines/simple.pipeline.yaml +0 -15
- package/tests/fixtures/test-project/pipelines/two-stage.pipeline.yaml +0 -24
- package/tests/fixtures/test-project/pipelines/with-startup.pipeline.yaml +0 -19
- package/tests/loader.test.ts +0 -385
- package/tests/post-validator.test.ts +0 -297
- package/tests/repo-resolver.test.ts +0 -102
- package/tests/run-store.test.ts +0 -143
- package/tests/state-machine.test.ts +0 -110
- package/tests/unit/context-propagation.test.ts +0 -45
- package/tests/unit/engine.test.ts +0 -770
- package/tests/unit/group-loop.test.ts +0 -389
- package/tests/unit/group-parallel.test.ts +0 -423
- package/tests/unit/state/status-derivation.test.ts +0 -88
- package/tsconfig.json +0 -24
- package/vitest.config.ts +0 -14
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
-
import { executeStartupCommands } from './startup-executor.js';
|
|
3
|
-
|
|
4
|
-
describe('executeStartupCommands', () => {
|
|
5
|
-
it('returns stdout keyed by inject_as', async () => {
|
|
6
|
-
const result = await executeStartupCommands([
|
|
7
|
-
{ command: 'echo hello', inject_as: 'greeting' },
|
|
8
|
-
{ command: 'echo world', inject_as: 'place' },
|
|
9
|
-
]);
|
|
10
|
-
expect(result.greeting).toBe('hello');
|
|
11
|
-
expect(result.place).toBe('world');
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it('skips and warns when a command fails', async () => {
|
|
15
|
-
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
16
|
-
const result = await executeStartupCommands([
|
|
17
|
-
{ command: 'exit 1', inject_as: 'bad' },
|
|
18
|
-
{ command: 'echo ok', inject_as: 'good' },
|
|
19
|
-
]);
|
|
20
|
-
expect(result.bad).toBeUndefined();
|
|
21
|
-
expect(result.good).toBe('ok');
|
|
22
|
-
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('[on_pipeline_start]'));
|
|
23
|
-
warnSpy.mockRestore();
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('returns empty object when no commands given', async () => {
|
|
27
|
-
const result = await executeStartupCommands([]);
|
|
28
|
-
expect(result).toEqual({});
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it('trims trailing whitespace from stdout', async () => {
|
|
32
|
-
const result = await executeStartupCommands([
|
|
33
|
-
{ command: 'printf "trimmed"', inject_as: 'val' },
|
|
34
|
-
]);
|
|
35
|
-
expect(result.val).toBe('trimmed');
|
|
36
|
-
});
|
|
37
|
-
});
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
// Execute on_pipeline_start commands and collect their stdout
|
|
2
|
-
|
|
3
|
-
import { exec } from 'node:child_process';
|
|
4
|
-
import { promisify } from 'node:util';
|
|
5
|
-
import type { StartupCommand } from '@studio-foundation/contracts';
|
|
6
|
-
|
|
7
|
-
const execAsync = promisify(exec);
|
|
8
|
-
const COMMAND_TIMEOUT_MS = 10_000;
|
|
9
|
-
|
|
10
|
-
export async function executeStartupCommands(
|
|
11
|
-
commands: StartupCommand[],
|
|
12
|
-
cwd?: string
|
|
13
|
-
): Promise<Record<string, string>> {
|
|
14
|
-
const result: Record<string, string> = {};
|
|
15
|
-
|
|
16
|
-
for (const cmd of commands) {
|
|
17
|
-
try {
|
|
18
|
-
const { stdout } = await execAsync(cmd.command, {
|
|
19
|
-
cwd,
|
|
20
|
-
timeout: COMMAND_TIMEOUT_MS,
|
|
21
|
-
maxBuffer: 1024 * 1024 * 10,
|
|
22
|
-
});
|
|
23
|
-
result[cmd.inject_as] = stdout.trim();
|
|
24
|
-
} catch (err) {
|
|
25
|
-
console.warn(
|
|
26
|
-
`[on_pipeline_start] command failed: "${cmd.command}" — ${(err as Error).message}`
|
|
27
|
-
);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return result;
|
|
32
|
-
}
|
package/src/pipeline/types.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
// engine/src/pipeline/types.ts
|
|
2
|
-
// Shared local types for pipeline execution — used by engine, StageExecutor, GroupOrchestrator
|
|
3
|
-
|
|
4
|
-
import { join } from 'node:path';
|
|
5
|
-
import type { StageRun, StageStatus, ToolCall } from '@studio-foundation/contracts';
|
|
6
|
-
import type { PostValidationResult } from './post-validator.js';
|
|
7
|
-
import type { PipelineContext } from './context-propagation.js';
|
|
8
|
-
|
|
9
|
-
export interface ProjectPaths {
|
|
10
|
-
projectDir: string;
|
|
11
|
-
pipelinesDir: string;
|
|
12
|
-
agentsDir: string;
|
|
13
|
-
contractsDir: string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function resolveProjectPaths(configsDir: string): ProjectPaths {
|
|
17
|
-
return {
|
|
18
|
-
projectDir: configsDir,
|
|
19
|
-
pipelinesDir: join(configsDir, 'pipelines'),
|
|
20
|
-
agentsDir: join(configsDir, 'agents'),
|
|
21
|
-
contractsDir: join(configsDir, 'contracts'),
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface StageResult {
|
|
26
|
-
stageRun: StageRun;
|
|
27
|
-
status: StageStatus;
|
|
28
|
-
postValidation?: PostValidationResult;
|
|
29
|
-
lastAgentOutput?: unknown;
|
|
30
|
-
toolCalls?: ToolCall[];
|
|
31
|
-
tokensDelta?: number; // tokens consumed by this stage
|
|
32
|
-
toolCallsDelta?: number; // tool calls made by this stage
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export interface GroupResult {
|
|
36
|
-
status: StageStatus;
|
|
37
|
-
stageRuns: StageRun[];
|
|
38
|
-
stagesExecuted: number;
|
|
39
|
-
context: PipelineContext;
|
|
40
|
-
totalTokensDelta: number;
|
|
41
|
-
totalToolCallsDelta: number;
|
|
42
|
-
}
|
package/src/repo-resolver.ts
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { execSync } from 'node:child_process';
|
|
2
|
-
import { mkdir } from 'node:fs/promises';
|
|
3
|
-
import { resolve, join } from 'node:path';
|
|
4
|
-
import { homedir } from 'node:os';
|
|
5
|
-
|
|
6
|
-
export interface RepoResolveOptions {
|
|
7
|
-
repoPathOverride?: string;
|
|
8
|
-
repoUrl?: string;
|
|
9
|
-
rawProjectsDir?: string;
|
|
10
|
-
pipelineName: string;
|
|
11
|
-
branch?: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export async function cloneRepo(
|
|
15
|
-
repoUrl: string,
|
|
16
|
-
projectsDir: string,
|
|
17
|
-
pipelineName: string,
|
|
18
|
-
branch?: string
|
|
19
|
-
): Promise<string> {
|
|
20
|
-
await mkdir(projectsDir, { recursive: true });
|
|
21
|
-
|
|
22
|
-
const timestamp = new Date().toISOString()
|
|
23
|
-
.replace(/:/g, 'h')
|
|
24
|
-
.replace(/\..+$/, '')
|
|
25
|
-
.replace('T', 'T');
|
|
26
|
-
const dirName = `${pipelineName}-${timestamp}`;
|
|
27
|
-
const clonePath = join(projectsDir, dirName);
|
|
28
|
-
|
|
29
|
-
const branchArg = branch ? `--branch ${branch}` : '';
|
|
30
|
-
const cmd = `git clone --depth 1 ${branchArg} ${repoUrl} ${clonePath}`;
|
|
31
|
-
|
|
32
|
-
try {
|
|
33
|
-
execSync(cmd, { stdio: 'pipe' });
|
|
34
|
-
} catch (error) {
|
|
35
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
36
|
-
throw new Error(`Failed to clone ${repoUrl}: ${msg}`);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return clonePath;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export async function resolveRepoPath(options: RepoResolveOptions): Promise<string> {
|
|
43
|
-
const { repoPathOverride, repoUrl, rawProjectsDir, pipelineName, branch } = options;
|
|
44
|
-
|
|
45
|
-
if (repoPathOverride) {
|
|
46
|
-
return resolve(repoPathOverride);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (repoUrl) {
|
|
50
|
-
const rawDir = rawProjectsDir || process.env['STUDIO_PROJECTS_DIR'];
|
|
51
|
-
const projectsDir = rawDir?.replace(/^~/, homedir());
|
|
52
|
-
if (!projectsDir) {
|
|
53
|
-
throw new Error(
|
|
54
|
-
'STUDIO_PROJECTS_DIR is not set. Set it in config.yaml paths.projects_dir or as an environment variable.'
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
return cloneRepo(repoUrl, projectsDir, pipelineName, branch);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return '.';
|
|
61
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import type { RunSpawner, SpawnConfig, SpawnResult, PipelineRun } from '@studio-foundation/contracts';
|
|
2
|
-
import { PipelineEngine, type EngineConfig } from '../engine.js';
|
|
3
|
-
|
|
4
|
-
export class DirectEngineSpawner implements RunSpawner {
|
|
5
|
-
constructor(private engineConfig: EngineConfig) {}
|
|
6
|
-
|
|
7
|
-
async spawnAndWait(config: SpawnConfig): Promise<SpawnResult> {
|
|
8
|
-
const child = new PipelineEngine(this.engineConfig);
|
|
9
|
-
const result: PipelineRun = await child.run({
|
|
10
|
-
pipeline: config.pipeline,
|
|
11
|
-
input: config.input,
|
|
12
|
-
parentRunId: config.parentRunId,
|
|
13
|
-
depth: config.depth,
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
if (result.status === 'failed' || result.status === 'rejected' || result.status === 'cancelled') {
|
|
17
|
-
throw new Error(`Child run ${result.id} ${result.status}`);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// Extract output from the last successful stage
|
|
21
|
-
const lastStage = [...result.stages].reverse().find(s => s.status === 'success');
|
|
22
|
-
const output = (lastStage as { output?: unknown } | undefined)?.output ?? null;
|
|
23
|
-
|
|
24
|
-
return { run_id: result.id, status: result.status, output };
|
|
25
|
-
}
|
|
26
|
-
}
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { SQLiteRunStore, PgRunStore } from './run-store.js';
|
|
3
|
-
import type { PipelineRun } from '@studio-foundation/contracts';
|
|
4
|
-
|
|
5
|
-
function makeRun(overrides?: Partial<PipelineRun>): PipelineRun {
|
|
6
|
-
return {
|
|
7
|
-
id: 'run-1',
|
|
8
|
-
pipeline_name: 'test-pipeline',
|
|
9
|
-
status: 'running',
|
|
10
|
-
started_at: '2026-01-01T00:00:00.000Z',
|
|
11
|
-
stages: [],
|
|
12
|
-
...overrides,
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
describe('SQLiteRunStore', () => {
|
|
17
|
-
describe('saveLogPath / getLogPath', () => {
|
|
18
|
-
it('returns null when no log path has been set', () => {
|
|
19
|
-
const store = new SQLiteRunStore(':memory:');
|
|
20
|
-
store.savePipelineRun(makeRun());
|
|
21
|
-
expect(store.getLogPath('run-1')).toBeNull();
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it('returns the log path after saveLogPath', () => {
|
|
25
|
-
const store = new SQLiteRunStore(':memory:');
|
|
26
|
-
store.savePipelineRun(makeRun());
|
|
27
|
-
store.saveLogPath('run-1', '/tmp/run-1.jsonl');
|
|
28
|
-
expect(store.getLogPath('run-1')).toBe('/tmp/run-1.jsonl');
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it('preserves log_path when savePipelineRun is called again after saveLogPath', () => {
|
|
32
|
-
// Regression for STU-150: savePipelineRun used INSERT OR REPLACE which clobbers log_path
|
|
33
|
-
const store = new SQLiteRunStore(':memory:');
|
|
34
|
-
store.savePipelineRun(makeRun());
|
|
35
|
-
store.saveLogPath('run-1', '/tmp/run-1.jsonl');
|
|
36
|
-
|
|
37
|
-
// Simulate engine completing the run (this triggered the bug)
|
|
38
|
-
store.savePipelineRun(makeRun({ status: 'success', completed_at: '2026-01-01T00:01:00.000Z' }));
|
|
39
|
-
|
|
40
|
-
expect(store.getLogPath('run-1')).toBe('/tmp/run-1.jsonl');
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('returns null for unknown run id', () => {
|
|
44
|
-
const store = new SQLiteRunStore(':memory:');
|
|
45
|
-
expect(store.getLogPath('does-not-exist')).toBeNull();
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
describe('parent_run_id', () => {
|
|
50
|
-
it('persists and retrieves parent_run_id', () => {
|
|
51
|
-
const run: PipelineRun = {
|
|
52
|
-
id: 'child-123',
|
|
53
|
-
pipeline_name: 'child-pipe',
|
|
54
|
-
status: 'success',
|
|
55
|
-
started_at: new Date().toISOString(),
|
|
56
|
-
stages: [],
|
|
57
|
-
parent_run_id: 'parent-456',
|
|
58
|
-
};
|
|
59
|
-
const store = new SQLiteRunStore(':memory:');
|
|
60
|
-
store.savePipelineRun(run);
|
|
61
|
-
const retrieved = store.getPipelineRun('child-123');
|
|
62
|
-
expect(retrieved?.parent_run_id).toBe('parent-456');
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
const TEST_PG_URL = process.env.TEST_DATABASE_URL;
|
|
68
|
-
|
|
69
|
-
describe.skipIf(!TEST_PG_URL)('PgRunStore', () => {
|
|
70
|
-
let store: PgRunStore;
|
|
71
|
-
|
|
72
|
-
beforeEach(async () => {
|
|
73
|
-
store = new PgRunStore(TEST_PG_URL!);
|
|
74
|
-
await store.dangerouslyTruncateForTests();
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
afterEach(async () => {
|
|
78
|
-
await store.close();
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it('saves and retrieves a pipeline run', async () => {
|
|
82
|
-
const run = makePgRun('pg-run-1');
|
|
83
|
-
await store.savePipelineRun(run);
|
|
84
|
-
const found = await store.getPipelineRun('pg-run-1');
|
|
85
|
-
expect(found).toMatchObject({ id: 'pg-run-1', status: 'success' });
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it('returns null for unknown id', async () => {
|
|
89
|
-
const found = await store.getPipelineRun('doesnt-exist');
|
|
90
|
-
expect(found).toBeNull();
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it('updates an existing run (upsert)', async () => {
|
|
94
|
-
const run = makePgRun('pg-run-2');
|
|
95
|
-
await store.savePipelineRun(run);
|
|
96
|
-
await store.savePipelineRun({ ...run, status: 'failed' });
|
|
97
|
-
const found = await store.getPipelineRun('pg-run-2');
|
|
98
|
-
expect(found?.status).toBe('failed');
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it('lists runs with status filter', async () => {
|
|
102
|
-
await store.savePipelineRun(makePgRun('pg-1', 'success'));
|
|
103
|
-
await store.savePipelineRun(makePgRun('pg-2', 'failed'));
|
|
104
|
-
const successes = await store.listPipelineRuns({ status: 'success' });
|
|
105
|
-
expect(successes).toHaveLength(1);
|
|
106
|
-
expect(successes[0].id).toBe('pg-1');
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
it('lists runs with limit', async () => {
|
|
110
|
-
for (let i = 0; i < 5; i++) {
|
|
111
|
-
await store.savePipelineRun(makePgRun(`pg-limit-${i}`));
|
|
112
|
-
}
|
|
113
|
-
const runs = await store.listPipelineRuns({ limit: 3 });
|
|
114
|
-
expect(runs).toHaveLength(3);
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
it('getLatestRun returns most recent', async () => {
|
|
118
|
-
await store.savePipelineRun(makePgRun('pg-old', 'success', '2024-01-01T00:00:00Z'));
|
|
119
|
-
await store.savePipelineRun(makePgRun('pg-new', 'success', '2024-06-01T00:00:00Z'));
|
|
120
|
-
const latest = await store.getLatestRun();
|
|
121
|
-
expect(latest?.id).toBe('pg-new');
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
it('getLatestRun filters by pipeline name', async () => {
|
|
125
|
-
await store.savePipelineRun(makePgRun('pg-a', 'success', undefined, 'pipeline-a'));
|
|
126
|
-
await store.savePipelineRun(makePgRun('pg-b', 'success', undefined, 'pipeline-b'));
|
|
127
|
-
const latest = await store.getLatestRun('pipeline-a');
|
|
128
|
-
expect(latest?.id).toBe('pg-a');
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
it('saves and retrieves log path', async () => {
|
|
132
|
-
await store.savePipelineRun(makePgRun('pg-log-1'));
|
|
133
|
-
await store.saveLogPath('pg-log-1', '/tmp/test.jsonl');
|
|
134
|
-
const path = await store.getLogPath('pg-log-1');
|
|
135
|
-
expect(path).toBe('/tmp/test.jsonl');
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
it('returns null log path for unknown run', async () => {
|
|
139
|
-
const path = await store.getLogPath('no-such-run');
|
|
140
|
-
expect(path).toBeNull();
|
|
141
|
-
});
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
function makePgRun(
|
|
145
|
-
id: string,
|
|
146
|
-
status: string = 'success',
|
|
147
|
-
startedAt: string = new Date().toISOString(),
|
|
148
|
-
pipelineName: string = 'test-pipeline'
|
|
149
|
-
) {
|
|
150
|
-
return {
|
|
151
|
-
id,
|
|
152
|
-
pipeline_name: pipelineName,
|
|
153
|
-
status,
|
|
154
|
-
started_at: startedAt,
|
|
155
|
-
stages: [],
|
|
156
|
-
} as PipelineRun;
|
|
157
|
-
}
|