syntax = "proto3";

package runme.runner.v1;

import "google/protobuf/wrappers.proto";

option go_package = "github.com/stateful/runme/internal/gen/proto/go/runme/runner/v1;runnerv1";

message Session {
  string id = 1;

  // envs keeps track of session environment variables.
  // They can be modified by executing programs which
  // alter them through "export" and "unset" commands.
  repeated string envs = 2;

  // metadata is a map of client specific metadata.
  map<string, string> metadata = 3;
}

message CreateSessionRequest {
  // metadata is a map of client specific metadata.
  map<string, string> metadata = 1;

  // envs field provides an initial set of environment variables
  // for a newly created session.
  repeated string envs = 2;

  // optional project from which to immediately load environment variables
  optional Project project = 3;
}

message CreateSessionResponse {
  Session session = 1;
}

message GetSessionRequest {
  string id = 1;
}

message GetSessionResponse {
  Session session = 1;
}

message ListSessionsRequest {}

message ListSessionsResponse {
  repeated Session sessions = 1;
}

message DeleteSessionRequest {
  string id = 1;
}

message DeleteSessionResponse {}

enum ExecuteStop {
  EXECUTE_STOP_UNSPECIFIED = 0;
  EXECUTE_STOP_INTERRUPT = 1;
  EXECUTE_STOP_KILL = 2;
}

enum CommandMode {
  COMMAND_MODE_UNSPECIFIED = 0;
  COMMAND_MODE_INLINE_SHELL = 1;
  COMMAND_MODE_TEMP_FILE = 2;
}

message Project {
  // project root folder
  string root = 1;

  // list of environment files to try and load
  // start with
  repeated string env_load_order = 2;
}

// strategy for selecting a session in an initial execute request
enum SessionStrategy {
  // uses session_id field to determine session
  // if none is present, a new session is created
  SESSION_STRATEGY_UNSPECIFIED = 0;
  // uses most recently used session on the grpc server
  // if there is none, a new one is created
  SESSION_STRATEGY_MOST_RECENT = 1;
}

message Winsize {
  // number of rows (in cells)
  uint32 rows = 1;
  // number of columns (in cells)
  uint32 cols = 2;
  // width in pixels
  uint32 x = 3;
  // height in pixels
  uint32 y = 4;
}

message ExecuteRequest {
  // program_name is a name of the program to execute.
  // If it's not a path (relative or absolute), the runner
  // will try to resolve the name.
  // For example: "sh", "/bin/bash".
  string program_name = 1;

  // arguments is a list of arguments passed to the program.
  repeated string arguments = 2;

  // directory to execute the program in.
  string directory = 3;

  // envs is a list of additional environment variables
  // that will be injected to the executed program.
  repeated string envs = 4;

  // commands are commands to be executed by the program.
  // The commands are joined and executed as a script.
  // For example: "echo 'Hello, World'", "ls -l /etc".
  // This is mutually exclusive with the script field.
  repeated string commands = 5;

  // script is code to be executed by the program.
  // Individual lines are joined with the new line character.
  // This is mutually exclusive with the commands field.
  string script = 6;

  // tty when true allocates a pseudo-TTY.
  bool tty = 7;

  // input_data is a byte array that will be send as input
  // to the program.
  bytes input_data = 8;

  // stop requests the running process to be stopped.
  // It is allowed only in the consecutive calls.
  ExecuteStop stop = 9;

  // sets pty winsize
  // has no effect in non-interactive mode
  optional Winsize winsize = 10;

  // background indicates a background process
  // this will send the process' PID as a first response
  bool background = 11;

  // session_id indicates in which Session the program should execute.
  // Executing in a Session might provide additional context like
  // environment variables.
  string session_id = 20;

  // strategy for selecting session
  SessionStrategy session_strategy = 21;

  // project for this runner
  // used to load environment variables from .env files
  optional Project project = 22;

  // whether to store the stdout of the last ran
  // block in the environment variable `__`
  bool store_last_output = 23;

  CommandMode command_mode = 24;

  // language id associated with script
  string language_id = 25;

  // file extension associated with script
  string file_extension = 26;
}

message ProcessPID {
  int64 pid = 1;
}

message ExecuteResponse {
  // exit_code is sent only in the final message.
  google.protobuf.UInt32Value exit_code = 1;

  // stdout_data contains bytes from stdout since the last response.
  bytes stdout_data = 2;

  // stderr_data contains bytes from stderr since the last response.
  bytes stderr_data = 3;

  // pid contains the process' PID
  // this is only sent once in an initial response for background processes.
  ProcessPID pid = 4;
}

service RunnerService {
  rpc CreateSession(CreateSessionRequest) returns (CreateSessionResponse) {}
  rpc GetSession(GetSessionRequest) returns (GetSessionResponse) {}
  rpc ListSessions(ListSessionsRequest) returns (ListSessionsResponse) {}
  rpc DeleteSession(DeleteSessionRequest) returns (DeleteSessionResponse) {}

  // Execute executes a program. Examine "ExecuteRequest" to explore
  // configuration options.
  //
  // It's a bidirectional stream RPC method. It expects the first
  // "ExecuteRequest" to contain details of a program to execute.
  // Subsequent "ExecuteRequest" should only contain "input_data" as
  // other fields will be ignored.
  rpc Execute(stream ExecuteRequest) returns (stream ExecuteResponse) {}
}
