107 lines
3.2 KiB
TypeScript
Executable File
107 lines
3.2 KiB
TypeScript
Executable File
#!/usr/bin/env bun
|
|
/**
|
|
* Direct pipeline runner — executes a swarm pipeline outside of the TUI.
|
|
*
|
|
* Usage: bun cli.ts <path-to-yaml>
|
|
*/
|
|
|
|
import * as fs from "node:fs/promises";
|
|
import * as path from "node:path";
|
|
import { discoverAuthStorage } from "@oh-my-pi/pi-coding-agent";
|
|
import { ModelRegistry } from "@oh-my-pi/pi-coding-agent/config/model-registry";
|
|
import { Settings } from "@oh-my-pi/pi-coding-agent/config/settings";
|
|
import { buildDependencyGraph, buildExecutionWaves, detectCycles } from "./swarm/dag";
|
|
import { PipelineController } from "./swarm/pipeline";
|
|
import { renderSwarmProgress } from "./swarm/render";
|
|
import { parseSwarmYaml, validateSwarmDefinition } from "./swarm/schema";
|
|
import { StateTracker } from "./swarm/state";
|
|
|
|
const yamlPath = process.argv[2];
|
|
if (!yamlPath) {
|
|
console.error("Usage: omp-swarm <path-to-yaml>");
|
|
process.exit(1);
|
|
}
|
|
|
|
const resolvedPath = path.resolve(yamlPath);
|
|
console.log(`Reading: ${resolvedPath}`);
|
|
|
|
const content = await Bun.file(resolvedPath).text();
|
|
const def = parseSwarmYaml(content);
|
|
|
|
console.log(`Swarm: ${def.name}`);
|
|
console.log(`Mode: ${def.mode}`);
|
|
console.log(`Target count: ${def.targetCount}`);
|
|
console.log(`Agents: ${[...def.agents.keys()].join(", ")}`);
|
|
|
|
// Validate
|
|
const errors = validateSwarmDefinition(def);
|
|
if (errors.length > 0) {
|
|
console.error("Validation errors:", errors);
|
|
process.exit(1);
|
|
}
|
|
|
|
// Build DAG
|
|
const deps = buildDependencyGraph(def);
|
|
const cycles = detectCycles(deps);
|
|
if (cycles) {
|
|
console.error("Cycle detected:", cycles);
|
|
process.exit(1);
|
|
}
|
|
const waves = buildExecutionWaves(deps);
|
|
console.log(`Waves: ${waves.map((w, i) => `W${i + 1}:[${w.join(",")}]`).join(" -> ")}`);
|
|
|
|
// Resolve workspace
|
|
const workspace = path.isAbsolute(def.workspace)
|
|
? def.workspace
|
|
: path.resolve(path.dirname(resolvedPath), def.workspace);
|
|
|
|
await fs.mkdir(workspace, { recursive: true });
|
|
console.log(`Workspace: ${workspace}`);
|
|
|
|
// Initialize
|
|
const stateTracker = new StateTracker(workspace, def.name);
|
|
await stateTracker.init([...def.agents.keys()], def.targetCount, def.mode);
|
|
|
|
// Auth + settings
|
|
const authStorage = await discoverAuthStorage();
|
|
const modelRegistry = new ModelRegistry(authStorage);
|
|
const settings = Settings.isolated();
|
|
|
|
// Progress display
|
|
let lastProgressDump = 0;
|
|
const PROGRESS_INTERVAL_MS = 5000;
|
|
|
|
// Run
|
|
console.log("\n--- Pipeline starting ---\n");
|
|
|
|
const controller = new PipelineController(def, waves, stateTracker);
|
|
const result = await controller.run({
|
|
workspace,
|
|
onProgress: () => {
|
|
const now = Date.now();
|
|
if (now - lastProgressDump > PROGRESS_INTERVAL_MS) {
|
|
lastProgressDump = now;
|
|
const lines = renderSwarmProgress(stateTracker.state);
|
|
console.log(lines.join("\n"));
|
|
console.log();
|
|
}
|
|
},
|
|
modelRegistry,
|
|
settings,
|
|
});
|
|
|
|
console.log("\n--- Pipeline finished ---\n");
|
|
console.log(`Status: ${result.status}`);
|
|
console.log(`Iterations completed: ${result.iterations}/${def.targetCount}`);
|
|
if (result.errors.length > 0) {
|
|
console.log(`Errors (${result.errors.length}):`);
|
|
for (const err of result.errors) {
|
|
console.log(` - ${err}`);
|
|
}
|
|
}
|
|
console.log(`\nState saved to: ${stateTracker.swarmDir}`);
|
|
|
|
// Final state dump
|
|
const lines = renderSwarmProgress(stateTracker.state);
|
|
console.log(lines.join("\n"));
|