@tonbo/cli 0.24.0 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/bin/tonbo.js +83 -2
  2. package/package.json +1 -1
package/dist/bin/tonbo.js CHANGED
@@ -1062,6 +1062,22 @@ var managementOperations = {
1062
1062
  "idempotencyKey": false,
1063
1063
  "cli": "tonbo volume delete"
1064
1064
  },
1065
+ "getVolumeNameMetrics": {
1066
+ "method": "GET",
1067
+ "path": "/volumes/{name}/metrics",
1068
+ "pathParams": [
1069
+ "name"
1070
+ ],
1071
+ "query": [
1072
+ "series",
1073
+ "window",
1074
+ "granularity",
1075
+ "from",
1076
+ "to"
1077
+ ],
1078
+ "idempotencyKey": false,
1079
+ "cli": "tonbo volume metrics"
1080
+ },
1065
1081
  "listStreams": {
1066
1082
  "method": "GET",
1067
1083
  "path": "/streams",
@@ -2459,6 +2475,10 @@ var ManagementApi = class {
2459
2475
  getVolumeMetrics(key, query = {}) {
2460
2476
  return this.call("getVolumeMetrics", key, { query });
2461
2477
  }
2478
+ /** One Volume's guest I/O and stored bytes per UTC hour or day. */
2479
+ getVolumeNameMetrics(key, name, query = {}) {
2480
+ return this.call("getVolumeNameMetrics", key, { params: { name }, query });
2481
+ }
2462
2482
  /** The account's writes of yesterday and today, hourly, per lane. */
2463
2483
  getUsageToday(key) {
2464
2484
  return this.call("getUsageToday", key);
@@ -5609,14 +5629,14 @@ async function metricsVolumesCommand(deps, options = {}) {
5609
5629
  ["DAY", "STORED", ""],
5610
5630
  points.map((point) => [
5611
5631
  point.start.slice(0, 10),
5612
- bytes(point.stored_bytes),
5632
+ bytes(point.stored_bytes ?? 0),
5613
5633
  point.partial ? "so far" : ""
5614
5634
  ])
5615
5635
  )
5616
5636
  ];
5617
5637
  const complete = points.filter((point) => !point.partial);
5618
5638
  if (complete.length > 1) {
5619
- const change = complete[complete.length - 1].stored_bytes - complete[0].stored_bytes;
5639
+ const change = (complete[complete.length - 1].stored_bytes ?? 0) - (complete[0].stored_bytes ?? 0);
5620
5640
  lines.push(
5621
5641
  `Change over its complete days: ${change < 0 ? "-" : "+"}${bytes(Math.abs(change))}`
5622
5642
  );
@@ -6009,6 +6029,44 @@ async function resolveVolumeBinding(deps, key, spec) {
6009
6029
  region: volume.region
6010
6030
  };
6011
6031
  }
6032
+ var VOLUME_COLUMNS = {
6033
+ stored_bytes: ["STORED", (value) => bytes(value)],
6034
+ read_bytes: ["READ", (value) => bytes(value)],
6035
+ write_bytes: ["WRITTEN", (value) => bytes(value)],
6036
+ read_ops: ["READ OPS", String],
6037
+ write_ops: ["WRITE OPS", String],
6038
+ trim_ops: ["TRIMS", String]
6039
+ };
6040
+ async function volumeMetricsCommand(deps, name, options = {}) {
6041
+ const metrics = await deps.api.getVolumeNameMetrics(
6042
+ await deps.auth.apiKey(),
6043
+ name,
6044
+ metricsQuery(options)
6045
+ );
6046
+ const series = Object.keys(VOLUME_COLUMNS).filter(
6047
+ (key) => metrics.points.some((point) => key in point)
6048
+ );
6049
+ const hourly = metrics.granularity === "hour";
6050
+ print(
6051
+ deps,
6052
+ options.json,
6053
+ metrics,
6054
+ [
6055
+ `Volume ${metrics.name} in ${metrics.region} from ${metrics.from} to ${metrics.to} (UTC)`,
6056
+ table(
6057
+ [hourly ? "HOUR (UTC)" : "DAY (UTC)", ...series.map((key) => VOLUME_COLUMNS[key][0]), ""],
6058
+ metrics.points.map((point) => [
6059
+ hourly ? point.start.slice(0, 16).replace("T", " ") : point.start.slice(0, 10),
6060
+ ...series.map((key) => {
6061
+ const value = point[key];
6062
+ return value === null || value === void 0 ? "-" : VOLUME_COLUMNS[key][1](value);
6063
+ }),
6064
+ point.partial ? "so far" : ""
6065
+ ])
6066
+ )
6067
+ ].join("\n")
6068
+ );
6069
+ }
6012
6070
 
6013
6071
  // src/machine.ts
6014
6072
  var MACHINE_ERROR_HINTS = {
@@ -7866,6 +7924,18 @@ var COMMAND_HELP = {
7866
7924
  examples: ["tonbo volume show data"],
7867
7925
  arguments: NAME("Volume")
7868
7926
  },
7927
+ "tonbo volume metrics": {
7928
+ about: [
7929
+ "Prints the Volume's metrics, one row per UTC hour or day: the bytes and requests its Machine read, wrote and trimmed, as the Volume served them, and what its files held at the step's last sample, compressed. The I/O updates about every 30 seconds while the Volume is attached; the current step is marked `so far`.",
7930
+ "Give `--window` (24h hourly; 7d, 30d or 90d daily, 7d also hourly with `--granularity hour`), or `--from` and `--to` with `--granularity` (at most 8 days hourly, 92 days daily). `--series` prints only the series it names. It carries no money: `tonbo usage show` says what the Volume cost."
7931
+ ],
7932
+ examples: [
7933
+ "tonbo volume metrics data --window 24h",
7934
+ "tonbo volume metrics data --series read_bytes,write_bytes --window 7d"
7935
+ ],
7936
+ arguments: NAME("Volume"),
7937
+ defaults: { "--window": "30d" }
7938
+ },
7869
7939
  "tonbo volume move": {
7870
7940
  about: [
7871
7941
  "Moves the Volume to the Machine `--to`, taking it from the Machine that has it now in one write: the Volume is unbound there (that Machine's ETag changes and it boots without it) and bound to `--to`, which may be awake and gets it at its next boot. The CLI reads both Machines' ETags first, so a Machine changed since is refused, never overwritten. Without `--path` the Volume keeps the path it has now, or stays a whole disk; a whole disk moves only to a Machine on the same image.",
@@ -9900,6 +9970,17 @@ function createProgram(makeDependencies = createDependencies) {
9900
9970
  ).action(
9901
9971
  async (name, options) => volumeShowCommand(dependencies(), name, options)
9902
9972
  );
9973
+ operationOptions(
9974
+ volume.command("metrics <name>").description(
9975
+ "show what a Volume's Machine read, wrote and trimmed, and what it held, over time"
9976
+ ).option("--window <window>", "24h hourly, or 7d, 30d or 90d daily; 30d when omitted").option(
9977
+ "--series <series>",
9978
+ "the series to print, comma-separated; every series when omitted"
9979
+ ).option("--granularity <step>", "hour or day: with --from and --to, or with --window 7d").option("--from <time>", "with --to, the window's start, on a UTC hour or day").option("--to <time>", "with --from, the window's end, exclusive"),
9980
+ "getVolumeNameMetrics"
9981
+ ).action(
9982
+ async (name, options) => volumeMetricsCommand(dependencies(), name, options)
9983
+ );
9903
9984
  operationOptions(
9904
9985
  volume.command("move <name>").description(
9905
9986
  "move a Volume to another Machine of its region, taking it from the asleep Machine that has it in the same write"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tonbo/cli",
3
- "version": "0.24.0",
3
+ "version": "0.25.0",
4
4
  "description": "The Tonbo CLI. Compute, disks, databases and streams for AI agents: your agent picks what it needs, sets it up, and knows what it costs, from the command line.",
5
5
  "homepage": "https://tonbo.dev",
6
6
  "bugs": {