Cli::Run is the whole client but its transport, so tests run it in process against a real Session; main will hand it the QUIC connection. - Commands: ls, status, cmds, help, call, get, set, pause, resume, step, wait, trace, log (with --follow), screenshot, quit, debugger, guide and version, with -i, --json and --timeout. - call types key=value from the command's argument schema (slot=1 is a number, name=1 a string, at.x=3 nests), so agents need no JSON for simple calls; a value that cannot fit its field is a usage error before anything is sent. - The instance comes from discovery; one running instance needs no -i. The record's protocol is checked before any request: a mismatch exits 4, says which side is newer and prints the app's own client. Other sources only warn. - A request with no reply is diagnosed: the app has exited, stopped in a debugger (with the attach command), or no longer calls Pump (with what to change). Exit codes: 2 usage, 3 no instance, 4 version, 5 timeout, 6 the app's error. - Output is short by default, arrays cut at 20 with a note; --json prints everything. `guide` is the full reference for agents. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
72 lines
2.8 KiB
C++
72 lines
2.8 KiB
C++
// SPDX-License-Identifier: LGPL-3.0-only
|
|
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
|
|
|
// crafter-debug, the client agents drive an app with. Run is the whole
|
|
// program but for the transport: main hands it a Connector that reaches an
|
|
// instance over QUIC, and tests hand it one that reaches a Session in the
|
|
// same process.
|
|
//
|
|
// Exit codes:
|
|
// 0 done
|
|
// 2 a usage error, or no debug build yet (the launcher's stub)
|
|
// 3 no instance to talk to
|
|
// 4 the app and this client speak different protocols
|
|
// 5 timed out
|
|
// 6 the app answered with an error
|
|
// 1 is left to the launcher and `flatpak run` failing.
|
|
export module Crafter.Debug.Cli:Client;
|
|
import std;
|
|
import Crafter.Debug.Core;
|
|
|
|
export namespace Crafter::Debug::Cli {
|
|
inline constexpr std::int32_t exitOk = 0;
|
|
inline constexpr std::int32_t exitUsage = 2;
|
|
inline constexpr std::int32_t exitNoInstance = 3;
|
|
inline constexpr std::int32_t exitVersion = 4;
|
|
inline constexpr std::int32_t exitTimeout = 5;
|
|
inline constexpr std::int32_t exitError = 6;
|
|
|
|
struct CallError {
|
|
enum class Kind : std::uint8_t {
|
|
// No connection: the app is gone, or its port does not answer.
|
|
Unreachable,
|
|
// Connected, but no reply within the timeout.
|
|
TimedOut,
|
|
// The reply was not what the protocol says.
|
|
Broken,
|
|
};
|
|
Kind kind = Kind::Unreachable;
|
|
std::string message;
|
|
};
|
|
|
|
// One request to an instance. The reply is the last object it sends;
|
|
// objects before it are stream lines (`log --follow`), handed to
|
|
// `onLine`, which returns false to stop.
|
|
class Connection {
|
|
public:
|
|
virtual ~Connection() = default;
|
|
virtual std::expected<Value, CallError> Call(const Value& request, std::chrono::milliseconds timeout,
|
|
const std::function<bool(const Value& line)>& onLine) = 0;
|
|
};
|
|
|
|
using Connector = std::function<std::unique_ptr<Connection>(const InstanceRecord& instance)>;
|
|
|
|
struct Environment {
|
|
Connector connect;
|
|
std::function<std::vector<Instance>()> list = ListInstances;
|
|
std::ostream* out = &std::cout;
|
|
std::ostream* err = &std::cerr;
|
|
// Where a screenshot without -o goes, relative paths included.
|
|
std::filesystem::path cwd = std::filesystem::current_path();
|
|
// Whether `lldb` exists here, for the attach command `debugger` prints.
|
|
std::function<bool()> haveLldb;
|
|
};
|
|
|
|
std::int32_t Run(std::span<const std::string_view> args, Environment& environment);
|
|
|
|
// The full reference an agent reads with `crafter-debug guide`.
|
|
std::string_view Guide();
|
|
|
|
// "30s", "500ms", "2m", "1h"; a bare number is seconds.
|
|
std::optional<std::chrono::milliseconds> ParseDuration(std::string_view text);
|
|
}
|