syntax = "proto3";

import "google/protobuf/timestamp.proto";

package api;
option go_package = "/api";

message Command {
  reserved 250, 252, 253, 254, 255;

  // channel and session act as namespaces with 0 being global.
  // both session and channel must match for a client to receive a command.
  int32 channel = 1;

  // < 0 : all user sessions except abs(id)
  //   0 : all user sessions
  //   1 : internal message to conman
  // > 1 : user session with given id
  int32 session = 2;

  oneof body {
    // global messages
    // these should always be on channel 0

    OpenChannel openChan = 3;
    OpenChannelRes openChanRes = 4;

    CloseChannel closeChan = 5;
    CloseChannelRes closeChanRes = 6;

    ContainerState containerState = 9;
    PortOpen portOpen = 10;
    Toast toast = 11;

    Redirect redirect = 12;

    // service specific messages

    RunMain runMain = 16;
    Clear clear = 17;

    string eval = 20;
    string result = 21;
    string input = 22;
    string output = 23;
    string error = 24;

    SaneTerm saneTerm = 26;
    ResizeTerm resizeTerm = 27;

    State state = 28;

    OK ok = 30;

    File persist = 31;
    File persistMirror = 41;
    File write = 32;
    File remove = 33;
    Move move = 34;
    File tryRemove = 36;
    File mkdir = 39;

    File read = 35;
    File readdir = 37;

    Files files = 38;
    File file = 40;

    CheckChanges checkChanges = 42;
    Files changedFiles = 43;

    LintResults lintResults = 44;

    ContainedTest runContainedTest = 70;
    TestResult testResult = 71;

    string debuggerStart = 90;
    RunMain debuggerStep = 91;
    DebugStatus debuggerStatus = 92;

    EnsurePackages ensurePackages = 100;

    Ping ping = 120;
    Pong pong = 121;
    Hello hello = 122;
    Goodbye goodbye = 123;

    Hint hint = 130;

    Connect connect = 150;
    Send send = 151;
    Recv recv = 152;
    Disconnect disconnect = 153;

    FileAuthReq fileAuthReq = 200;
    FileAuthRes fileAuthRes = 201;
    MultiFileAuthRes mutliFileAuthRes = 202;

    ListObjects listObjects = 205;
    ListObjectsResp listObjectsResp = 206;

    OTPacket ot = 220;
    OTStatus otstatus = 221;
    OTLinkFile otLinkFile = 222;
    OTCursor otNewCursor = 223;
    OTCursor otDeleteCursor = 224;
    OTFetchRequest otFetchRequest = 225;
    OTFetchResponse otFetchResponse = 226;
    Flush flush = 251;

    Debug debug = 230;

    StartVCR startVCR = 231;
    ReadVCR readVCR = 232;
    VCRLog VCRLog = 233;

    Auth auth = 235;

    ExecInfo execInfo = 240;

    SubscribeFile subscribeFile = 256;
    FileEvent fileEvent = 257;

    Roster roster = 260;
    User join = 261;
    User part = 262;

    Exec exec = 270;

    PackageSearch packageSearch = 280;
    PackageSearchResp packageSearchResp = 281;
    PackageInfo packageInfo = 282;
    PackageInfoResp packageInfoResp = 283;
    PackageAdd packageAdd = 284;
    PackageRemove packageRemove = 285;
    PackageInstall packageInstall = 286;
    PackageListSpecfile packageListSpecfile = 287;
    PackageListSpecfileResp packageListSpecfileResp = 288;

    PackageCacheSave packageCacheSave = 289;

    ChatMessage chatMessage = 310;
    ChatTyping chatTyping = 311;
    ChatScrollback chatScrollback = 312;

    FSSnapshot fsSnapshot = 330;
    FSLock fsTakeLock = 331;
    FSLock fsReleaseLock = 332;

    bool hasCap = 335;

    // used to configure pid1 at runtime
    Pid1Config pid1Config = 340;

    Metrics metrics = 350;

    BootStatus bootStatus = 351;

    // metadata store
    ReadMeta readMeta = 360;
    WriteMeta writeMeta = 361;
    AppendMeta appendMeta = 362;

    // audio
    Audio audio = 363;


    PprofRequest pprofRequest = 364;
    PprofResponse pprofResponse = 365;

    Audio2 audio2 = 366;

    // used to set configure pty settings 
    PTYConfig PTYConfig = 367;
  }

  string ref = 1000;
}

message Audio { repeated int32 data = 1; }

message Audio2 {
  repeated sint32 data = 1;
  int64 samples = 2;
}

message ReadMeta {
  string key = 1;
  bool exists = 2;
  bytes data = 3;
}

message WriteMeta {
  string key = 1;
  bytes data = 2;
}

message AppendMeta {
  string key = 1;
  bytes data = 2;
}

message BootStatus {
  enum Stage {
    HANDSHAKE = 0;
    ACQUIRING = 3;
    COMPLETE = 4;
    PROXY = 5;
    PULL_FILES = 6;
    LOAD_BLOCK = 7;
    RETRY = 8;
  };

  Stage stage = 1;

  // progress/total is context dependant. Most stages have no progress info,
  // this is sent as 0/0 (the default value).
  uint32 progress = 2;
  uint32 total = 3;
}

message Pid1Config {
  string cwd = 1;
  string language = 2;
  map<string, string> env = 3;
}

message FSLock { string name = 1; }

message FSSnapshot {}

message SubscribeFile { repeated File files = 1; }

message FileEvent {
  File file = 1;

  // when the Op is a Move dest is set to the destination of the move.
  File dest = 3;

  enum Op {
    Create = 0;
    Move = 1;
    Remove = 2;
    Modify = 3;
  }
  Op op = 2;
}

message Flush {}

message OTLinkFile {
  File file = 1;
  bool highConsistency = 2;
  bool useModTime = 3;
}

message Auth {
  string token = 1;
  string containerID = 2;
}

message VCREntry {
  uint64 timestamp = 1;

  enum Direction {
    IN = 0;
    OUT = 1;
  }
  Direction direction = 2;

  Command command = 3;

  string uid = 4;
}

message StartVCR {}

message ReadVCR {}

message VCRLog {
  repeated VCREntry log = 1;
  File logfile = 2;
}

message ExecInfo {
  repeated string command = 1;
  string reason = 2;
}

message Debug { string text = 1; }

enum FileAuthMethod {
  GET = 0;
  HEAD = 1;
  PUT = 2;
  DELETE = 3;
}

message FileAuthReq {
  File file = 1;
  FileAuthMethod method = 2;
}

message MultiFileAuthRes {
  FileAuthRes put = 1;
  FileAuthRes del = 2;
  FileAuthRes get = 3;
}

message FileAuthRes {
  File file = 1;

  string url = 2;
  FileAuthMethod method = 3;

  int64 expire = 4;

  string error = 5;
}

message ListObjects { string prefix = 1; }

message ListObjectsResp { repeated string objects = 1; }

message Disconnect { string error = 1; }

message Send { bytes buff = 1; }

message Recv { bytes buff = 1; }

message Connect {
  string proto = 1;
  string addr = 2;
}

message Hint { string text = 1; }

message Ping {}
message Pong {}

message Hello {
  uint32 userid = 1;
  string username = 2;
  string token = 3;
}

message Goodbye {}

enum State {
  Stopped = 0;
  Running = 1;
}

message CheckChanges {}

message EnsurePackages {
  bool install = 1;
  File file = 2;
}

message Start {}

message DebugStatus {
  bool done = 1;
  repeated StackFrame stack = 2;
}

message StackFrame {
  string function = 1;
  uint32 line = 2;
}

message ContainedTest {
  File suite = 1;
  repeated File project = 2;
}

message TestResult {
  bool passed = 1;
  string stderr = 2;
  repeated TestFailure fails = 3;
}

message TestFailure {
  string name = 1;
  string trace = 2;
}

message ResizeTerm {
  uint32 rows = 1;
  uint32 cols = 2;
}

message SaneTerm {}

message LintResults { repeated LintResult results = 1; }

message LintResult {
  string text = 1;
  int32 row = 2;
  int32 column = 3;
  string type = 4;
}

message OK {}

message Move {
  string oldPath = 1;
  string newPath = 2;
}

message Files { repeated File files = 1; }

message File {
  string path = 1;
  enum Type {
    REGULAR = 0;
    DIRECTORY = 1;
  }
  Type type = 2;
  bytes content = 3;
}

message Clear {}

message Toast { string text = 1; }

// Redirect indicates that a client should attempt to connect through another
// URL. This is needed since most browser WebSockets implementations do not
// support following standard HTTP redirects.
message Redirect {
  // The URL to try again. If empty, the negotiation to figure out the URL for
  // a repl needs to start from scratch.
  string url = 1;
}

message RunMain {}

message OpenChannel {
  string service = 1;
  string name = 2;

  enum Action {
    CREATE = 0;
    ATTACH = 1;
    ATTACH_OR_CREATE = 2;
  }
  Action action = 3;

  int32 id = 4;
}

message OpenChannelRes {
  int32 id = 1;

  enum State {
    CREATED = 0;
    ATTACHED = 1;
    ERROR = 2;
  }
  State state = 2;

  string error = 3;
}

message CloseChannel {
  int32 id = 1;

  enum Action {
    DISCONNECT = 0;
    TRY_CLOSE = 1;
    CLOSE = 2;
  }
  Action action = 2;
}

message CloseChannelRes {
  int32 id = 1;

  enum Status {
    DISCONNECT = 0;
    CLOSE = 1;
    NOTHING = 2;
  }
  Status status = 2;
}

message ContainerState {
  enum State {
    SLEEP = 0;
    READY = 1;
  }
  State state = 1;
}

message PortOpen {
  bool forwarded = 1;
  uint32 port = 2;
  string address = 3;
}

message OTFetchRequest {
  uint32 versionFrom = 1;
  uint32 versionTo = 2;
}

message OTFetchResponse {
  repeated OTPacket packets = 1;
}

message OTPacket {
  // so here's the deal. Once uppon a time `spookyVersion` was `version` and things
  // worked okay. Then one day someone came along and decided our handling of
  // version 0 was all kinds of messed up. Sending a version that already existed
  // never transformed the packet by that particular version. It was as if
  // `version` was treated as `version + 1`. This could not stand and thus the
  // great rift of versions was created. `version` does the right thing and
  // `spookyVersion` maintains backwards compatibility.
  uint32 spookyVersion = 1;
  uint32 version = 5;

  repeated OTRuneTransformOp ops = 2;
  uint32 crc32 = 3;
  google.protobuf.Timestamp committed = 4;

  uint32 nonce = 6;
}

message OTRuneTransformOp {
  oneof op {
    uint32 skip = 1;
    uint32 delete = 2;
    string insert = 3;
  }
}
message OTStatus {
  string contents = 1;
  uint32 version = 2;
  File linkedFile = 3;
  repeated OTCursor cursors = 4;
}

message OTCursor {
  uint32 position = 1;
  uint32 selectionStart = 2;
  uint32 selectionEnd = 3;
  User user = 4;
  string id = 5;
}

message ChatMessage {
  string username = 1;
  string text = 2;
}

message ChatTyping {
  string username = 1;
  bool typing = 2;
}

message User {
  uint32 id = 1;
  string name = 2;
  repeated string roles = 3;
  int32 session = 4;
}

message Roster { repeated User user = 1; }

message Exec {
  repeated string args = 1;
  map<string, string> env = 2;
  bool blocking = 3;
}

message Package {
  // Used always.
  string name = 1;
  // Used only for add and remove.
  string spec = 2;
  // Used only for search and info.
  string description = 10;
  string version = 11;
  string homepageURL = 12;
  string documentationURL = 13;
  string sourceCodeURL = 14;
  string bugTrackerURL = 15;
  string author = 16;
  string license = 17;
  repeated Package dependencies = 18;
}

message PackageSearch { string query = 1; }

message PackageSearchResp { repeated Package results = 1; }

message PackageInfo { Package pkg = 1; }

message PackageInfoResp { Package pkg = 1; }

message PackageAdd { repeated Package pkgs = 1; }

message PackageRemove { repeated Package pkgs = 1; }

message PackageInstall {}

message PackageListSpecfile {}

message PackageListSpecfileResp { repeated Package pkgs = 1; }

message PackageCacheSave {}

message ChatScrollback { repeated ChatMessage scrollback = 1; }

message Metrics { repeated bytes prometheusMetricFamilies = 1; }

message PprofRequest {
  string id = 1;
  oneof body {
    PprofCpuProfileRequest pprofCpuProfileRequest = 2;
    PprofHeapProfileRequest pprofHeapProfileRequest = 3;
    PprofAllocsProfileRequest pprofAllocsProfileRequest = 4;
    PprofBlockProfileRequest pprofBlockProfileRequest = 5;
    PprofMutexProfileRequest pprofMutexProfileRequest = 6;
  }
}

message PprofAllocsProfileRequest {
  bool debug = 1;
}

message PprofBlockProfileRequest {
  bool debug = 1;
}

message PprofCpuProfileRequest {
  int64 seconds = 1;
}

message PprofHeapProfileRequest {
  bool gc = 1;
  bool debug = 2;
}

message PprofMutexProfileRequest {
  bool debug = 1;
}

message PprofResponse {
  string id = 1;
  bytes profile = 2;
}

// Message used to configure PTY
message PTYConfig {
  // Whether "PipeMode" should be enabled or not
  // PipeMode is the equivelent of stty raw icrnl isig -echo. 
  // This is designed for comparing and output - the output 
  // should be as close as possible to its original state.  
  bool pipeMode = 1;
}
