Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 20 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,30 +222,32 @@ bt eval foo.eval.ts -- --description "Prod" --shard=1/4
- Detail view: `t` span/thread, `Left/Right` switch panes, `Backspace`/`Esc` back
- Global: `q` quit

## `bt util xact`
## `bt util version`

Local transaction-id conversion helpers:
Local version and pagination-key conversion helpers:

- Convert transaction id to pretty version id:
- `bt util xact to-pretty 1000192656880881099`
- `bt util version to-pretty 1000192656880881099`
- Convert pretty version id to transaction id:
- `bt util xact from-pretty 81cd05ee665fdfb3`
- `bt util version from-pretty 81cd05ee665fdfb3`
- Convert transaction id, pretty version id, or pagination key to timestamp (local timezone by default):
- `bt util xact to-time 1000192656880881099`
- `bt util xact to-time 81cd05ee665fdfb3`
- `bt util xact to-time p07639577379371417602`
- `bt util xact to-time p07639577379371417602 --utc`
- `bt util xact to-time 1000192656880881099 --format unix`
- `bt util version to-time 1000192656880881099`
- `bt util version to-time 81cd05ee665fdfb3`
- `bt util version to-time p07639577379371417602`
- `bt util version to-time p07639577379371417602 --utc`
- `bt util version to-time 1000192656880881099 --format unix`
- Convert timestamp to transaction id:
- `bt util xact from-time` (defaults to current time)
- `bt util xact from-time 2025-01-01` (date-only ISO at UTC midnight)
- `bt util xact from-time 2024-03-14T18:00:00Z`
- `bt util xact from-time 1710439200 --input unix --counter 42`
- Inspect any xact value:
- `bt util xact inspect 1000192656880881099`
- `bt util xact inspect 81cd05ee665fdfb3`
- `bt util xact inspect p07639577379371417602`
- `bt util xact inspect p07639577379371417602 --utc`
- `bt util version from-time` (defaults to current time)
- `bt util version from-time 2025-01-01` (date-only ISO at UTC midnight)
- `bt util version from-time 2024-03-14T18:00:00Z`
- `bt util version from-time 1710439200 --input unix --counter 42`
- Convert timestamp to pagination key:
- `bt util version from-time 2026-05-14T08:00:09-07:00 --pagination-key`
- Inspect any version-like value:
- `bt util version inspect 1000192656880881099`
- `bt util version inspect 81cd05ee665fdfb3`
- `bt util version inspect p07639577379371417602`
- `bt util version inspect p07639577379371417602 --utc`

## `bt auth`

Expand Down
88 changes: 61 additions & 27 deletions src/util_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,26 @@ pub struct UtilArgs {

#[derive(Debug, Clone, Subcommand)]
enum UtilCommands {
/// Transaction-id conversion utilities
Xact(XactArgs),
/// Version and pagination-key conversion utilities
#[command(name = "version")]
Version(VersionArgs),
}

#[derive(Debug, Clone, Args)]
struct XactArgs {
struct VersionArgs {
#[command(subcommand)]
command: XactCommands,
command: VersionCommands,
}

#[derive(Debug, Clone, Subcommand)]
enum XactCommands {
enum VersionCommands {
/// Convert a transaction id to a pretty version id
ToPretty(ToPrettyArgs),
/// Convert a pretty version id to a transaction id
FromPretty(FromPrettyArgs),
/// Convert a transaction id to a timestamp
ToTime(ToTimeArgs),
/// Convert a timestamp to a transaction id
/// Convert a timestamp to a transaction id or pagination key
FromTime(FromTimeArgs),
/// Decode and display transaction id details
Inspect(InspectArgs),
Expand Down Expand Up @@ -91,6 +92,10 @@ struct FromTimeArgs {
/// Low 16-bit transaction counter value
#[arg(long, default_value_t = 0)]
counter: u16,

/// Output a pagination key instead of a transaction id
#[arg(long)]
pagination_key: bool,
}

#[derive(Debug, Clone, Copy, ValueEnum, Eq, PartialEq)]
Expand Down Expand Up @@ -135,17 +140,17 @@ struct XactInfo {

pub async fn run(base: BaseArgs, args: UtilArgs) -> Result<()> {
match args.command {
UtilCommands::Xact(xact) => run_xact(base, xact),
UtilCommands::Version(version) => run_version(base, version),
}
}

fn run_xact(base: BaseArgs, args: XactArgs) -> Result<()> {
fn run_version(base: BaseArgs, args: VersionArgs) -> Result<()> {
match args.command {
XactCommands::ToPretty(args) => run_to_pretty(base.json, args),
XactCommands::FromPretty(args) => run_from_pretty(base.json, args),
XactCommands::ToTime(args) => run_to_time(base.json, args),
XactCommands::FromTime(args) => run_from_time(base.json, args),
XactCommands::Inspect(args) => run_inspect(base.json, args),
VersionCommands::ToPretty(args) => run_to_pretty(base.json, args),
VersionCommands::FromPretty(args) => run_from_pretty(base.json, args),
VersionCommands::ToTime(args) => run_to_time(base.json, args),
VersionCommands::FromTime(args) => run_from_time(base.json, args),
VersionCommands::Inspect(args) => run_inspect(base.json, args),
}
}

Expand Down Expand Up @@ -212,21 +217,32 @@ fn run_from_time(json: bool, args: FromTimeArgs) -> Result<()> {
let unix_seconds = parse_timestamp_or_now(args.timestamp.as_deref(), args.input)?;
let xact = build_xact_id(unix_seconds, args.counter);
let pretty = prettify_xact(xact);
let pagination_key = build_pagination_key(unix_seconds, args.counter, 0);
let output_kind = if args.pagination_key {
"pagination_key"
} else {
"xact_id"
};
if json {
println!(
"{}",
serde_json::to_string(&serde_json::json!({
"input_timestamp": args.timestamp,
"input_format": match args.input {
TimeInputFormat::Iso => "iso",
TimeInputFormat::Unix => "unix",
},
"unix_seconds": unix_seconds,
"counter": args.counter,
"xact_id": xact.to_string(),
"pretty_version": pretty,
}))?
);
let mut payload = serde_json::json!({
"output_kind": output_kind,
"input_timestamp": args.timestamp,
"input_format": match args.input {
TimeInputFormat::Iso => "iso",
TimeInputFormat::Unix => "unix",
},
"unix_seconds": unix_seconds,
"counter": args.counter,
"xact_id": xact.to_string(),
"pretty_version": pretty,
});
if args.pagination_key {
payload["pagination_key"] = serde_json::json!(format_pagination_key(pagination_key));
payload["pagination_row_num"] = serde_json::json!(0);
}
println!("{}", serde_json::to_string(&payload)?);
} else if args.pagination_key {
println!("{}", format_pagination_key(pagination_key));
} else {
println!("{xact}");
}
Expand Down Expand Up @@ -401,6 +417,10 @@ fn build_xact_id(unix_seconds: u64, counter: u16) -> u64 {
TOP_BITS | ((unix_seconds & 0xffff_ffff_ffff) << 16) | u64::from(counter)
}

fn build_pagination_key(unix_seconds: u64, counter: u16, row_num: u16) -> u64 {
((unix_seconds & 0xffff_ffff) << 32) | (u64::from(counter) << 16) | u64::from(row_num)
}

fn format_pagination_key(pagination_key: u64) -> String {
format!("p{pagination_key:020}")
}
Expand Down Expand Up @@ -507,6 +527,20 @@ mod tests {
assert_eq!(xact_counter(xact), counter);
}

#[test]
fn from_time_to_pagination_key_uses_xact_counter() {
let unix_seconds = 1_778_727_718u64;
let counter = 31_627u16;
let pagination_key = build_pagination_key(unix_seconds, counter, 0);
assert_eq!(
format_pagination_key(pagination_key),
"p07639577379371417600"
);
assert_eq!(pagination_key_to_unix_seconds(pagination_key), unix_seconds);
assert_eq!(pagination_key_xact_counter(pagination_key), counter);
assert_eq!(pagination_key_row_num(pagination_key), 0);
}

#[test]
fn load_pretty_passthrough_for_non_pretty_input() {
assert_eq!(load_pretty_xact("123").unwrap(), "123");
Expand Down
25 changes: 23 additions & 2 deletions tests/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,14 +122,35 @@ fn setup_instrument_accepts_deprecated_agents_alias() {
}

#[test]
fn util_xact_to_time_accepts_pagination_key_with_utc() {
fn util_version_to_time_accepts_pagination_key_with_utc() {
bt_command()
.args(["util", "xact", "to-time", "p07639577379371417602", "--utc"])
.args([
"util",
"version",
"to-time",
"p07639577379371417602",
"--utc",
])
.assert()
.success()
.stdout(predicate::str::contains("2026-05-14T03:01:58Z"));
}

#[test]
fn util_version_from_time_can_output_pagination_key() {
bt_command()
.args([
"util",
"version",
"from-time",
"2026-05-14T08:00:09-07:00",
"--pagination-key",
])
.assert()
.success()
.stdout(predicate::str::contains("p07639762451734462464"));
}

#[test]
fn setup_uses_codex_detected_on_path_without_explicit_agent() {
let repo = make_git_repo();
Expand Down
Loading