@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.
- package/dist/bin/tonbo.js +83 -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.
|
|
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": {
|