feat: config opencode path and timeout (opencode_path, opencode_timeout)
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
|
||||
|
||||
@@ -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<ushort>(config.port()), config.pipelinesDir(), config.logsDir(), config.webRoot());
|
||||
Server server(static_cast<ushort>(config.port()),
|
||||
config.pipelinesDir(),
|
||||
config.logsDir(),
|
||||
config.webRoot(),
|
||||
config.opencodePath(),
|
||||
config.opencodeTimeout());
|
||||
server.start();
|
||||
|
||||
// Wait for exit
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<PIString, RunState> runs_;
|
||||
|
||||
@@ -31,12 +31,19 @@ const PIMap<PIString, PIString> 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
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user