///|
/// Wire protocol version this build speaks. Bumped on every breaking
/// change to Register/Connect or the sys-channel handshake. Both
/// fields default to 0 so peers built before versioning was introduced
/// decode as version 0 — fully backwards compatible.
pub const ProtoVersion : Int = 0
///|
/// Lowest `proto_version` the relay accepts on Register/Connect. Bump
/// to match `ProtoVersion` when dropping support for a prior version.
pub const ProtoMinVersion : Int = 0
///|
pub(all) enum ErrorCode {
TaskNotFound
TaskOffline
InternalError
/// Relay refused the Connect because the client's `proto_version` is
/// below `proto_min_version`. `min_version` tells the client which
/// version to upgrade to before retrying.
ProtoVersionTooOld(min_version~ : Int)
} derive(Debug, Eq)
///|
pub fn ErrorCode::to_wire(self : ErrorCode) -> String {
match self {
TaskNotFound => "server_not_found"
TaskOffline => "server_offline"
InternalError => "internal_error"
ProtoVersionTooOld(_) => "proto_version_too_old"
}
}
///|
pub(all) enum RegisterRejectReason {
UuidAlreadyClaimedThisSession
InvalidUuid
/// Relay refused the Register because the server's `proto_version`
/// is below `proto_min_version`. `min_version` tells the server
/// operator which version to upgrade to before retrying.
ProtoVersionTooOld(min_version~ : Int)
} derive(Eq, Debug)
///|
pub fn RegisterRejectReason::to_wire(self : RegisterRejectReason) -> String {
match self {
UuidAlreadyClaimedThisSession => "uuid_already_claimed_this_session"
InvalidUuid => "invalid_uuid"
ProtoVersionTooOld(_) => "proto_version_too_old"
}
}
///|
pub(all) enum SysMessage {
Register(
task_uuid~ : @uuid.UUID,
hostname~ : String,
cwd~ : String,
proto_version~ : Int
)
Registered(task_uuid~ : @uuid.UUID)
RegisterRejected(reason~ : RegisterRejectReason)
TaskExited(exit_code~ : Int)
Connect(task_uuid~ : @uuid.UUID, last_seen_id~ : Int64?, proto_version~ : Int)
Connected
TaskOnline
NewClient(client_id~ : String, last_seen_id~ : Int64?)
ClientDisconnected(client_id~ : String)
Error(ErrorCode)
AuthError(message~ : String)
}
///|
/// Manual `Debug` impl because `@uuid.UUID` does not implement `Debug` and
/// the orphan rule prevents us from adding it from this package. We render
/// UUID fields as their canonical hex-string form via `Repr::string`, which
/// matches what `derive(Debug)` would produce if `UUID : Debug` were bridged
/// through its `Show` impl.
pub impl @debug.Debug for SysMessage with to_repr(self) {
match self {
Register(task_uuid~, hostname~, cwd~, proto_version~) =>
@debug.Repr::ctor("Register", [
(Some("task_uuid"), @debug.Repr::string(task_uuid.to_string())),
(Some("hostname"), @debug.to_repr(hostname)),
(Some("cwd"), @debug.to_repr(cwd)),
(Some("proto_version"), @debug.to_repr(proto_version)),
])
Registered(task_uuid~) =>
@debug.Repr::ctor("Registered", [
(Some("task_uuid"), @debug.Repr::string(task_uuid.to_string())),
])
RegisterRejected(reason~) =>
@debug.Repr::ctor("RegisterRejected", [
(Some("reason"), @debug.to_repr(reason)),
])
TaskExited(exit_code~) =>
@debug.Repr::ctor("TaskExited", [
(Some("exit_code"), @debug.to_repr(exit_code)),
])
Connect(task_uuid~, last_seen_id~, proto_version~) =>
@debug.Repr::ctor("Connect", [
(Some("task_uuid"), @debug.Repr::string(task_uuid.to_string())),
(Some("last_seen_id"), @debug.to_repr(last_seen_id)),
(Some("proto_version"), @debug.to_repr(proto_version)),
])
Connected => @debug.Repr::ctor("Connected", [])
TaskOnline => @debug.Repr::ctor("TaskOnline", [])
NewClient(client_id~, last_seen_id~) =>
@debug.Repr::ctor("NewClient", [
(Some("client_id"), @debug.to_repr(client_id)),
(Some("last_seen_id"), @debug.to_repr(last_seen_id)),
])
ClientDisconnected(client_id~) =>
@debug.Repr::ctor("ClientDisconnected", [
(Some("client_id"), @debug.to_repr(client_id)),
])
Error(code) => @debug.Repr::ctor("Error", [(None, @debug.to_repr(code))])
AuthError(message~) =>
@debug.Repr::ctor("AuthError", [
(Some("message"), @debug.to_repr(message)),
])
}
}
///|
pub(all) enum TermMessage {
// `id` is monotonic per task, dense from 1, assigned by the wrapper. A
// client records the highest id it has written to its log; on reconnect
// it sends that id as Connect.last_seen_id and the wrapper replays the
// tail as ordinary Data frames — there is no separate scrollback variant.
// `created` is ms since epoch, stamped by the wrapper on PTY output.
// `None` means the sender did not stamp this frame (older peer, or a
// client→server keystroke that is not the stamped direction).
Data(client_id~ : String, id~ : Int64, created~ : Int64?, data~ : String)
// `id` is the next id after the last Data emitted, so a client can tell
// whether its log is caught up at stream end.
Exited(client_id~ : String, id~ : Int64)
} derive(Debug)
///|
pub(all) enum Message {
Sys(SysMessage)
Fs(FsMessage)
Term(TermMessage)
Codex(client_id~ : String, data~ : String)
} derive(Debug)
///|
pub(all) struct FileStat {
file_type : Int
size : Int64
mtime : Int64
ctime : Int64
} derive(Debug)
///|
pub(all) enum FsMessage {
Stat(req_id~ : String, client_id~ : String, path~ : String)
StatResult(req_id~ : String, client_id~ : String, stat~ : FileStat)
Readdir(req_id~ : String, client_id~ : String, path~ : String)
ReaddirResult(
req_id~ : String,
client_id~ : String,
entries~ : Array[(String, Int)]
)
ReadFile(req_id~ : String, client_id~ : String, path~ : String)
ReadFileResult(req_id~ : String, client_id~ : String, data~ : String)
WriteFile(
req_id~ : String,
client_id~ : String,
path~ : String,
data~ : String,
create~ : Bool,
overwrite~ : Bool
)
Mkdir(req_id~ : String, client_id~ : String, path~ : String)
FsDelete(
req_id~ : String,
client_id~ : String,
path~ : String,
recursive~ : Bool
)
FsRename(
req_id~ : String,
client_id~ : String,
from~ : String,
to~ : String,
overwrite~ : Bool
)
FsOk(req_id~ : String, client_id~ : String)
FsError(
req_id~ : String,
client_id~ : String,
code~ : String,
message~ : String
)
} derive(Debug)