From 99e9f880ee872684a5b8b802e8b7bace179a998f Mon Sep 17 00:00:00 2001 From: andrey Date: Mon, 20 Jul 2026 21:23:43 +0300 Subject: [PATCH] feat: config opencode path and timeout (opencode_path, opencode_timeout) --- pipeline-runner/pipeline-runner.conf | 4 +++- pipeline-runner/src/config.cpp | 18 ++++++++++++++++++ pipeline-runner/src/config.h | 4 ++++ pipeline-runner/src/main.cpp | 9 ++++++++- pipeline-runner/src/runner.cpp | 11 ++++++++--- pipeline-runner/src/runner.h | 4 +++- pipeline-runner/src/server.cpp | 11 +++++++++-- pipeline-runner/src/server.h | 9 ++++++++- pipeline-runner/tests/test_runner.cpp | 20 ++++++++++---------- 9 files changed, 71 insertions(+), 19 deletions(-) diff --git a/pipeline-runner/pipeline-runner.conf b/pipeline-runner/pipeline-runner.conf index aef59b9..3d8ecce 100644 --- a/pipeline-runner/pipeline-runner.conf +++ b/pipeline-runner/pipeline-runner.conf @@ -2,5 +2,7 @@ "port": 8000, "pipelines_dir": "./storage/pipelines/", "logs_dir": "./storage/logs/", - "web_root": "../../frontend/build/web/" + "web_root": "../../frontend/build/web/", + "opencode_path": "opencode", + "opencode_timeout": 300 } diff --git a/pipeline-runner/src/config.cpp b/pipeline-runner/src/config.cpp index 8374b20..713cdd7 100644 --- a/pipeline-runner/src/config.cpp +++ b/pipeline-runner/src/config.cpp @@ -7,12 +7,16 @@ static const int DefaultPort = 8000; static const PIString DefaultPipelinesDir = "./storage/pipelines/"; static const PIString DefaultLogsDir = "./storage/logs/"; +static const PIString DefaultOpencodePath = "opencode"; +static const int DefaultOpencodeTimeout = 300; ServerConfig::ServerConfig() : port_(DefaultPort) , pipelines_dir_(DefaultPipelinesDir) , logs_dir_(DefaultLogsDir) , web_root_() + , opencode_path_(DefaultOpencodePath) + , opencode_timeout_(DefaultOpencodeTimeout) , valid_(true) {} bool ServerConfig::loadFromFile(const PIString & path) { @@ -45,6 +49,12 @@ bool ServerConfig::loadFromFile(const PIString & path) { if (config.contains("web_root")) { web_root_ = config["web_root"].toString(); } + if (config.contains("opencode_path")) { + opencode_path_ = config["opencode_path"].toString(); + } + if (config.contains("opencode_timeout")) { + opencode_timeout_ = config["opencode_timeout"].toInt(); + } // Normalize directory paths pipelines_dir_ = normalizeDir(pipelines_dir_); @@ -88,6 +98,14 @@ PIString ServerConfig::webRoot() const { return web_root_; } +PIString ServerConfig::opencodePath() const { + return opencode_path_; +} + +int ServerConfig::opencodeTimeout() const { + return opencode_timeout_; +} + bool ServerConfig::isValid() const { return valid_; } diff --git a/pipeline-runner/src/config.h b/pipeline-runner/src/config.h index 1954088..c307f24 100644 --- a/pipeline-runner/src/config.h +++ b/pipeline-runner/src/config.h @@ -13,6 +13,8 @@ public: PIString pipelinesDir() const; PIString logsDir() const; PIString webRoot() const; + PIString opencodePath() const; + int opencodeTimeout() const; bool isValid() const; @@ -23,6 +25,8 @@ private: PIString pipelines_dir_; PIString logs_dir_; PIString web_root_; + PIString opencode_path_; + int opencode_timeout_; bool valid_; }; diff --git a/pipeline-runner/src/main.cpp b/pipeline-runner/src/main.cpp index 2999f3e..a5ba819 100644 --- a/pipeline-runner/src/main.cpp +++ b/pipeline-runner/src/main.cpp @@ -27,6 +27,8 @@ int main(int argc, char * argv[]) { piCout << "Pipelines dir: " << config.pipelinesDir(); piCout << "Logs dir: " << config.logsDir(); piCout << "Web root: " << (config.webRoot().isEmpty() ? "(not set)" : config.webRoot()); + piCout << "Opencode path: " << config.opencodePath(); + piCout << "Opencode timeout: " << config.opencodeTimeout() << "s"; // Handle signals PISignals::setSlot([](PISignals::Signal s) { @@ -37,7 +39,12 @@ int main(int argc, char * argv[]) { PISignals::grabSignals(PISignals::Interrupt); // Start server - Server server(static_cast(config.port()), config.pipelinesDir(), config.logsDir(), config.webRoot()); + Server server(static_cast(config.port()), + config.pipelinesDir(), + config.logsDir(), + config.webRoot(), + config.opencodePath(), + config.opencodeTimeout()); server.start(); // Wait for exit diff --git a/pipeline-runner/src/runner.cpp b/pipeline-runner/src/runner.cpp index 448c080..8ef1f58 100644 --- a/pipeline-runner/src/runner.cpp +++ b/pipeline-runner/src/runner.cpp @@ -86,9 +86,14 @@ RunState PipelineRunner::runStateFromJSON(const PIJSON & j) { return state; } -PipelineRunner::PipelineRunner(const PIString & pipelines_dir, const PIString & logs_dir) +PipelineRunner::PipelineRunner(const PIString & pipelines_dir, + const PIString & logs_dir, + const PIString & opencode_path, + int opencode_timeout) : pipelines_dir_(pipelines_dir) - , logs_dir_(logs_dir) {} + , logs_dir_(logs_dir) + , opencode_path_(opencode_path) + , opencode_timeout_(opencode_timeout) {} PipelineRunner::~PipelineRunner() { for (int i = 0; i < threads_.size(); ++i) { @@ -212,7 +217,7 @@ void PipelineRunner::executePipeline(const PIString & run_id, const Pipeline & p PIStringList args; args << "run" << prompt.text << "--title" << prompt.title; - ProcessResult result = ProcessExecutor::run("opencode", args, 300, pipeline.working_dir); + ProcessResult result = ProcessExecutor::run(opencode_path_, args, opencode_timeout_, pipeline.working_dir); int rc = result.exitCode; PIString output = result.output; PIString error = result.error; diff --git a/pipeline-runner/src/runner.h b/pipeline-runner/src/runner.h index 4a9acb1..7acdbeb 100644 --- a/pipeline-runner/src/runner.h +++ b/pipeline-runner/src/runner.h @@ -48,7 +48,7 @@ class PipelineRunner: public PIObject { PIOBJECT(PipelineRunner) public: - PipelineRunner(const PIString & pipelines_dir, const PIString & logs_dir); + PipelineRunner(const PIString & pipelines_dir, const PIString & logs_dir, const PIString & opencode_path, int opencode_timeout); ~PipelineRunner(); // Start a pipeline run, returns run_id or empty string on error @@ -76,6 +76,8 @@ private: const PIString pipelines_dir_; const PIString logs_dir_; + const PIString opencode_path_; + const int opencode_timeout_; PIMutex mutex_; PIMap runs_; diff --git a/pipeline-runner/src/server.cpp b/pipeline-runner/src/server.cpp index 6847fef..bed936f 100644 --- a/pipeline-runner/src/server.cpp +++ b/pipeline-runner/src/server.cpp @@ -31,12 +31,19 @@ const PIMap StaticContentTypes{ }; } // namespace -Server::Server(ushort port, const PIString & pipelines_dir, const PIString & logs_dir, const PIString & web_root) +Server::Server(ushort port, + const PIString & pipelines_dir, + const PIString & logs_dir, + const PIString & web_root, + const PIString & opencode_path, + int opencode_timeout) : port_(port) , pipelines_dir_(pipelines_dir) , logs_dir_(logs_dir) , web_root_(web_root) - , runner_(pipelines_dir, logs_dir) { + , opencode_path_(opencode_path) + , opencode_timeout_(opencode_timeout) + , runner_(pipelines_dir, logs_dir, opencode_path, opencode_timeout) { httpserver_ = new PIHTTPServer(); // CORS headers diff --git a/pipeline-runner/src/server.h b/pipeline-runner/src/server.h index 76ef48a..08fbfb9 100644 --- a/pipeline-runner/src/server.h +++ b/pipeline-runner/src/server.h @@ -13,7 +13,12 @@ class Server: public PIObject { PIOBJECT(Server) public: - Server(ushort port, const PIString & pipelines_dir, const PIString & logs_dir, const PIString & web_root = PIString()); + Server(ushort port, + const PIString & pipelines_dir, + const PIString & logs_dir, + const PIString & web_root, + const PIString & opencode_path, + int opencode_timeout); ~Server(); bool start(); @@ -34,6 +39,8 @@ private: PIString pipelines_dir_; PIString logs_dir_; PIString web_root_; + PIString opencode_path_; + int opencode_timeout_; PIHTTPServer * httpserver_; PipelineRunner runner_; }; diff --git a/pipeline-runner/tests/test_runner.cpp b/pipeline-runner/tests/test_runner.cpp index 10ba676..f281b7d 100644 --- a/pipeline-runner/tests/test_runner.cpp +++ b/pipeline-runner/tests/test_runner.cpp @@ -62,7 +62,7 @@ static Pipeline makeEchoPipeline() { TEST(RunnerTest, StartRunNotFound) { setupDirs(); - PipelineRunner runner(testPipelinesDir, testLogsDir); + PipelineRunner runner(testPipelinesDir, testLogsDir, "opencode", 300); PIString runId = runner.startRun("nonexistent"); EXPECT_TRUE(runId.isEmpty()); @@ -72,7 +72,7 @@ TEST(RunnerTest, StartRunNotFound) { TEST(RunnerTest, StartRunAndGetState) { setupDirs(); - PipelineRunner runner(testPipelinesDir, testLogsDir); + PipelineRunner runner(testPipelinesDir, testLogsDir, "opencode", 300); // Create a pipeline that runs 'echo hello' (uses shell command, not opencode) Pipeline pl; @@ -108,7 +108,7 @@ TEST(RunnerTest, StartRunAndGetState) { TEST(RunnerTest, GetStateNotFound) { setupDirs(); - PipelineRunner runner(testPipelinesDir, testLogsDir); + PipelineRunner runner(testPipelinesDir, testLogsDir, "opencode", 300); RunState state = runner.getRunState("nonexistent"); EXPECT_TRUE(state.run_id.isEmpty()); @@ -118,7 +118,7 @@ TEST(RunnerTest, GetStateNotFound) { TEST(RunnerTest, IsRunActive) { setupDirs(); - PipelineRunner runner(testPipelinesDir, testLogsDir); + PipelineRunner runner(testPipelinesDir, testLogsDir, "opencode", 300); Pipeline pl; pl.id = "active-test"; @@ -154,7 +154,7 @@ TEST(RunnerTest, RunStatusToString) { TEST(PersistenceTest, SaveLoadRoundTrip) { setupDirs(); - PipelineRunner runner(testPipelinesDir, testLogsDir); + PipelineRunner runner(testPipelinesDir, testLogsDir, "opencode", 300); // Test loadRunState on non-existent file RunState empty = runner.loadRunState("nonexistent"); @@ -243,7 +243,7 @@ TEST(PersistenceTest, RecoverRuns) { PIFile::writeAll(completedStatePath, j2.toJSON(PIJSON::Tree).toUTF8()); // New runner should recover only the running state - PipelineRunner runner(testPipelinesDir, testLogsDir); + PipelineRunner runner(testPipelinesDir, testLogsDir, "opencode", 300); runner.recoverRuns(); RunState recovered = runner.getRunState(runId); @@ -260,7 +260,7 @@ TEST(PersistenceTest, RecoverRuns) { TEST(PersistenceTest, CompletedRunStatePersisted) { setupDirs(); - PipelineRunner runner(testPipelinesDir, testLogsDir); + PipelineRunner runner(testPipelinesDir, testLogsDir, "opencode", 300); Pipeline pl; pl.id = "persist-test"; @@ -317,7 +317,7 @@ static Pipeline makeFakePipeline(const PIString & id, const PIString & name, int TEST(FakeRunnerTest, FullPipelineCompletes) { setupDirs(); - PipelineRunner runner(testPipelinesDir, testLogsDir); + PipelineRunner runner(testPipelinesDir, testLogsDir, "opencode", 300); Pipeline pl = makeFakePipeline("fake-full", "Fake Full Pipeline", 3); savePipeline(testPipelinesDir, pl); @@ -357,7 +357,7 @@ TEST(FakeRunnerTest, FullPipelineCompletes) { TEST(FakeRunnerTest, RunStatusTransitions) { setupDirs(); - PipelineRunner runner(testPipelinesDir, testLogsDir); + PipelineRunner runner(testPipelinesDir, testLogsDir, "opencode", 300); Pipeline pl = makeFakePipeline("fake-transition", "Fake Transition Pipeline", 2); savePipeline(testPipelinesDir, pl); @@ -397,7 +397,7 @@ TEST(FakeRunnerTest, RunStatusTransitions) { TEST(FakeRunnerTest, StepOutputContainsFakeMarker) { setupDirs(); - PipelineRunner runner(testPipelinesDir, testLogsDir); + PipelineRunner runner(testPipelinesDir, testLogsDir, "opencode", 300); Pipeline pl = makeFakePipeline("fake-output", "Fake Output Pipeline", 2); savePipeline(testPipelinesDir, pl);