@tonbo/cli 0.19.0 → 0.21.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 +241 -4
- package/package.json +1 -1
package/dist/bin/tonbo.js
CHANGED
|
@@ -364,6 +364,20 @@ var managementOperations = {
|
|
|
364
364
|
"idempotencyKey": false,
|
|
365
365
|
"cli": "tonbo usage series"
|
|
366
366
|
},
|
|
367
|
+
"getVolumeMetrics": {
|
|
368
|
+
"method": "GET",
|
|
369
|
+
"path": "/metrics/volumes",
|
|
370
|
+
"pathParams": [],
|
|
371
|
+
"query": [
|
|
372
|
+
"series",
|
|
373
|
+
"window",
|
|
374
|
+
"granularity",
|
|
375
|
+
"from",
|
|
376
|
+
"to"
|
|
377
|
+
],
|
|
378
|
+
"idempotencyKey": false,
|
|
379
|
+
"cli": "tonbo metrics volumes"
|
|
380
|
+
},
|
|
367
381
|
"getUsageToday": {
|
|
368
382
|
"method": "GET",
|
|
369
383
|
"path": "/usage/today",
|
|
@@ -414,6 +428,7 @@ var managementOperations = {
|
|
|
414
428
|
"pathParams": [],
|
|
415
429
|
"query": [
|
|
416
430
|
"device_id",
|
|
431
|
+
"project",
|
|
417
432
|
"cursor",
|
|
418
433
|
"limit"
|
|
419
434
|
],
|
|
@@ -695,6 +710,36 @@ var managementOperations = {
|
|
|
695
710
|
"idempotencyKey": false,
|
|
696
711
|
"cli": "tonbo machine activations"
|
|
697
712
|
},
|
|
713
|
+
"getMachineMetrics": {
|
|
714
|
+
"method": "GET",
|
|
715
|
+
"path": "/machines/{name}/metrics",
|
|
716
|
+
"pathParams": [
|
|
717
|
+
"name"
|
|
718
|
+
],
|
|
719
|
+
"query": [
|
|
720
|
+
"series",
|
|
721
|
+
"window",
|
|
722
|
+
"granularity",
|
|
723
|
+
"from",
|
|
724
|
+
"to"
|
|
725
|
+
],
|
|
726
|
+
"idempotencyKey": false,
|
|
727
|
+
"cli": "tonbo machine metrics"
|
|
728
|
+
},
|
|
729
|
+
"getAccountMachineMetrics": {
|
|
730
|
+
"method": "GET",
|
|
731
|
+
"path": "/metrics/machines",
|
|
732
|
+
"pathParams": [],
|
|
733
|
+
"query": [
|
|
734
|
+
"series",
|
|
735
|
+
"window",
|
|
736
|
+
"granularity",
|
|
737
|
+
"from",
|
|
738
|
+
"to"
|
|
739
|
+
],
|
|
740
|
+
"idempotencyKey": false,
|
|
741
|
+
"cli": "tonbo metrics machines"
|
|
742
|
+
},
|
|
698
743
|
"listMachineShapes": {
|
|
699
744
|
"method": "GET",
|
|
700
745
|
"path": "/machine-shapes",
|
|
@@ -2316,6 +2361,10 @@ var ManagementApi = class {
|
|
|
2316
2361
|
getUsageSeries(key, query = {}) {
|
|
2317
2362
|
return this.call("getUsageSeries", key, { query });
|
|
2318
2363
|
}
|
|
2364
|
+
/** The account's Volume metrics over a window, such as what they stored each day. */
|
|
2365
|
+
getVolumeMetrics(key, query = {}) {
|
|
2366
|
+
return this.call("getVolumeMetrics", key, { query });
|
|
2367
|
+
}
|
|
2319
2368
|
/** The account's writes of yesterday and today, hourly, per lane. */
|
|
2320
2369
|
getUsageToday(key) {
|
|
2321
2370
|
return this.call("getUsageToday", key);
|
|
@@ -2480,6 +2529,14 @@ var ManagementApi = class {
|
|
|
2480
2529
|
query: { limit, cursor }
|
|
2481
2530
|
});
|
|
2482
2531
|
}
|
|
2532
|
+
/** A managed Machine's metrics per UTC hour or day. */
|
|
2533
|
+
getMachineMetrics(key, name, query = {}) {
|
|
2534
|
+
return this.call("getMachineMetrics", key, { params: { name }, query });
|
|
2535
|
+
}
|
|
2536
|
+
/** The account's Machines' metrics per UTC hour or day, each and summed. */
|
|
2537
|
+
getAccountMachineMetrics(key, query = {}) {
|
|
2538
|
+
return this.call("getAccountMachineMetrics", key, { query });
|
|
2539
|
+
}
|
|
2483
2540
|
/** The whole answer: the shapes and the default one. */
|
|
2484
2541
|
listMachineShapes(key) {
|
|
2485
2542
|
return this.request("listMachineShapes", key, {});
|
|
@@ -2819,7 +2876,8 @@ function accountLogins(login) {
|
|
|
2819
2876
|
...entry,
|
|
2820
2877
|
account_id,
|
|
2821
2878
|
kind: entry.kind ?? null,
|
|
2822
|
-
name: entry.name ?? null
|
|
2879
|
+
name: entry.name ?? null,
|
|
2880
|
+
role: entry.role ?? null
|
|
2823
2881
|
}));
|
|
2824
2882
|
if (!entries.some((entry) => entry.account_id === login.account_id))
|
|
2825
2883
|
entries.unshift({
|
|
@@ -2828,12 +2886,18 @@ function accountLogins(login) {
|
|
|
2828
2886
|
key: login.key,
|
|
2829
2887
|
expires_at: login.expires_at,
|
|
2830
2888
|
kind: null,
|
|
2831
|
-
name: null
|
|
2889
|
+
name: null,
|
|
2890
|
+
role: null
|
|
2832
2891
|
});
|
|
2833
2892
|
return entries;
|
|
2834
2893
|
}
|
|
2835
2894
|
function personalLogin(login) {
|
|
2836
|
-
|
|
2895
|
+
const logins = accountLogins(login);
|
|
2896
|
+
const personal = login.session?.personalAccountId;
|
|
2897
|
+
return (personal ? logins.find((entry) => entry.account_id === personal) : void 0) ?? logins.find((entry) => entry.kind === "user" && !isGuestLogin(entry)) ?? null;
|
|
2898
|
+
}
|
|
2899
|
+
function isGuestLogin(login) {
|
|
2900
|
+
return login.role === "collaborator";
|
|
2837
2901
|
}
|
|
2838
2902
|
async function preparePrivateDirectory(directory) {
|
|
2839
2903
|
const stat3 = await lstat(directory);
|
|
@@ -3387,7 +3451,8 @@ var LoginStore = class {
|
|
|
3387
3451
|
key: client.bearer(id),
|
|
3388
3452
|
expires_at: current.session.expires_at,
|
|
3389
3453
|
kind: current.accounts?.[id]?.kind ?? (id === current.personal_account_id ? "user" : null),
|
|
3390
|
-
name: current.accounts?.[id]?.name ?? null
|
|
3454
|
+
name: current.accounts?.[id]?.name ?? null,
|
|
3455
|
+
role: current.accounts?.[id]?.role ?? null
|
|
3391
3456
|
});
|
|
3392
3457
|
accounts[current.personal_account_id] = entry(current.personal_account_id);
|
|
3393
3458
|
for (const id of Object.keys(current.accounts ?? {}))
|
|
@@ -5272,6 +5337,109 @@ async function usageSeriesCommand(deps, options = {}) {
|
|
|
5272
5337
|
);
|
|
5273
5338
|
print(deps, options.json, series, lines.join("\n"));
|
|
5274
5339
|
}
|
|
5340
|
+
async function metricsVolumesCommand(deps, options = {}) {
|
|
5341
|
+
const metrics = await deps.api.getVolumeMetrics(await deps.auth.apiKey(), {
|
|
5342
|
+
series: "stored_bytes",
|
|
5343
|
+
window: options.window,
|
|
5344
|
+
granularity: options.from || options.to ? "day" : void 0,
|
|
5345
|
+
from: options.from,
|
|
5346
|
+
to: options.to
|
|
5347
|
+
});
|
|
5348
|
+
const points = metrics.total.points;
|
|
5349
|
+
const lines = [
|
|
5350
|
+
`What the Volumes stored from ${metrics.from.slice(0, 10)} to ${metrics.to.slice(0, 10)} (UTC, average per day)`,
|
|
5351
|
+
table(
|
|
5352
|
+
["DAY", "STORED", ""],
|
|
5353
|
+
points.map((point) => [
|
|
5354
|
+
point.start.slice(0, 10),
|
|
5355
|
+
bytes(point.stored_bytes),
|
|
5356
|
+
point.partial ? "so far" : ""
|
|
5357
|
+
])
|
|
5358
|
+
)
|
|
5359
|
+
];
|
|
5360
|
+
const complete = points.filter((point) => !point.partial);
|
|
5361
|
+
if (complete.length > 1) {
|
|
5362
|
+
const change = complete[complete.length - 1].stored_bytes - complete[0].stored_bytes;
|
|
5363
|
+
lines.push(
|
|
5364
|
+
`Change over its complete days: ${change < 0 ? "-" : "+"}${bytes(Math.abs(change))}`
|
|
5365
|
+
);
|
|
5366
|
+
}
|
|
5367
|
+
if (metrics.unavailable_regions.length > 0)
|
|
5368
|
+
lines.push(`Not read: ${metrics.unavailable_regions.join(", ")}`);
|
|
5369
|
+
print(deps, options.json, metrics, lines.join("\n"));
|
|
5370
|
+
}
|
|
5371
|
+
function metricsQuery(options) {
|
|
5372
|
+
return {
|
|
5373
|
+
series: options.series,
|
|
5374
|
+
window: options.window,
|
|
5375
|
+
granularity: options.granularity,
|
|
5376
|
+
from: options.from,
|
|
5377
|
+
to: options.to
|
|
5378
|
+
};
|
|
5379
|
+
}
|
|
5380
|
+
var MACHINE_COLUMNS = {
|
|
5381
|
+
awake_seconds: ["AWAKE", (value) => `${Math.round(value / 60)} min`],
|
|
5382
|
+
wakes: ["WAKES", String],
|
|
5383
|
+
refused_wakes: ["REFUSED", String],
|
|
5384
|
+
wake_ms_p50: ["WAKE P50", (value) => `${(value / 1e3).toFixed(1)} s`],
|
|
5385
|
+
wake_ms_max: ["WAKE MAX", (value) => `${(value / 1e3).toFixed(1)} s`],
|
|
5386
|
+
cpu_cores_avg: ["CPU", (value) => `${value} cores`],
|
|
5387
|
+
memory_mib_avg: ["MEMORY", (value) => bytes(value * 1024 * 1024)],
|
|
5388
|
+
network_rx_bytes: ["NET IN", (value) => bytes(value)],
|
|
5389
|
+
network_tx_bytes: ["NET OUT", (value) => bytes(value)],
|
|
5390
|
+
internet_rx_bytes: ["INTERNET IN", (value) => bytes(value)],
|
|
5391
|
+
internet_tx_bytes: ["INTERNET OUT", (value) => bytes(value)]
|
|
5392
|
+
};
|
|
5393
|
+
function machineMetricsTable(granularity, points) {
|
|
5394
|
+
const series = Object.keys(MACHINE_COLUMNS).filter(
|
|
5395
|
+
(name) => points.some((point) => name in point)
|
|
5396
|
+
);
|
|
5397
|
+
return table(
|
|
5398
|
+
[
|
|
5399
|
+
granularity === "hour" ? "HOUR (UTC)" : "DAY (UTC)",
|
|
5400
|
+
...series.map((name) => MACHINE_COLUMNS[name][0])
|
|
5401
|
+
],
|
|
5402
|
+
points.map((point) => [
|
|
5403
|
+
granularity === "hour" ? point.start.slice(0, 16).replace("T", " ") : point.start.slice(0, 10),
|
|
5404
|
+
...series.map((name) => {
|
|
5405
|
+
const value = point[name];
|
|
5406
|
+
if (value === null || value === void 0)
|
|
5407
|
+
return Date.parse(point.start) > Date.now() ? "" : "-";
|
|
5408
|
+
return MACHINE_COLUMNS[name][1](value);
|
|
5409
|
+
})
|
|
5410
|
+
])
|
|
5411
|
+
);
|
|
5412
|
+
}
|
|
5413
|
+
async function metricsMachinesCommand(deps, options = {}) {
|
|
5414
|
+
const metrics = await deps.api.getAccountMachineMetrics(
|
|
5415
|
+
await deps.auth.apiKey(),
|
|
5416
|
+
metricsQuery(options)
|
|
5417
|
+
);
|
|
5418
|
+
const lines = [
|
|
5419
|
+
`The account's Machines from ${metrics.from} to ${metrics.to} (UTC)`,
|
|
5420
|
+
machineMetricsTable(metrics.granularity, metrics.total.points)
|
|
5421
|
+
];
|
|
5422
|
+
if (metrics.resources.length > 0) {
|
|
5423
|
+
const sum = (points, name) => points.reduce((total, point) => total + (point[name] ?? 0), 0);
|
|
5424
|
+
const series = Object.keys(MACHINE_COLUMNS).filter(
|
|
5425
|
+
(name) => metrics.total.points.some((point) => name in point)
|
|
5426
|
+
);
|
|
5427
|
+
lines.push(
|
|
5428
|
+
"",
|
|
5429
|
+
table(
|
|
5430
|
+
["MACHINE", "REGION", ...series.map((name) => MACHINE_COLUMNS[name][0])],
|
|
5431
|
+
metrics.resources.map((resource) => [
|
|
5432
|
+
resource.name,
|
|
5433
|
+
resource.region,
|
|
5434
|
+
...series.map((name) => MACHINE_COLUMNS[name][1](sum(resource.points, name)))
|
|
5435
|
+
])
|
|
5436
|
+
)
|
|
5437
|
+
);
|
|
5438
|
+
}
|
|
5439
|
+
if (metrics.unavailable_regions.length > 0)
|
|
5440
|
+
lines.push(`Not read: ${metrics.unavailable_regions.join(", ")}`);
|
|
5441
|
+
print(deps, options.json, metrics, lines.join("\n"));
|
|
5442
|
+
}
|
|
5275
5443
|
async function streamActivityCommand(deps, name, options = {}) {
|
|
5276
5444
|
const activity = await deps.api.getStreamActivity(
|
|
5277
5445
|
await deps.auth.apiKey(),
|
|
@@ -6332,6 +6500,24 @@ async function machineActivationsCommand(deps, name, options = {}) {
|
|
|
6332
6500
|
);
|
|
6333
6501
|
});
|
|
6334
6502
|
}
|
|
6503
|
+
async function machineMetricsCommand(deps, name, options = {}) {
|
|
6504
|
+
await machineCommand(async () => {
|
|
6505
|
+
const metrics = await deps.api.getMachineMetrics(
|
|
6506
|
+
await deps.auth.apiKey(),
|
|
6507
|
+
name,
|
|
6508
|
+
metricsQuery(options)
|
|
6509
|
+
);
|
|
6510
|
+
print(
|
|
6511
|
+
deps,
|
|
6512
|
+
options.json,
|
|
6513
|
+
metrics,
|
|
6514
|
+
[
|
|
6515
|
+
`Machine ${metrics.name} in ${metrics.region} from ${metrics.from} to ${metrics.to} (UTC)`,
|
|
6516
|
+
machineMetricsTable(metrics.granularity, metrics.points)
|
|
6517
|
+
].join("\n")
|
|
6518
|
+
);
|
|
6519
|
+
});
|
|
6520
|
+
}
|
|
6335
6521
|
function appliesText(name, change) {
|
|
6336
6522
|
const lines = [];
|
|
6337
6523
|
if (change.ports !== void 0 || change.sleep_after_seconds !== void 0)
|
|
@@ -7189,6 +7375,18 @@ var COMMAND_HELP = {
|
|
|
7189
7375
|
arguments: NAME("Machine"),
|
|
7190
7376
|
defaults: { "--limit": LIMIT_DEFAULT }
|
|
7191
7377
|
},
|
|
7378
|
+
"tonbo machine metrics": {
|
|
7379
|
+
about: [
|
|
7380
|
+
"Prints the Machine's metrics, one row per UTC hour or day: how long it was awake, its wakes and refused wakes and how long they took (the median and the longest), its average CPU (in cores) and memory while awake, the bytes on its network interface, Tonbo's traffic included, and the bytes it exchanged with the internet. A dash is a step it was never awake in; the current step is still growing.",
|
|
7381
|
+
"Give `--window`, or `--from` and `--to` with `--granularity` (at most 32 days hourly, 92 days daily). `--series` prints only the series it names. It carries no money: `tonbo usage show` says what it cost."
|
|
7382
|
+
],
|
|
7383
|
+
examples: [
|
|
7384
|
+
"tonbo machine metrics web --window 7d",
|
|
7385
|
+
"tonbo machine metrics web --series cpu_cores_avg,memory_mib_avg"
|
|
7386
|
+
],
|
|
7387
|
+
arguments: NAME("Machine"),
|
|
7388
|
+
defaults: { "--window": "24h" }
|
|
7389
|
+
},
|
|
7192
7390
|
"tonbo machine shapes": {
|
|
7193
7391
|
about: [
|
|
7194
7392
|
"Lists the shapes a Machine may have, each with its vCPUs, memory, scratch disk, whether it is offered and can suspend, and its list price for an hour at full use, and marks the default."
|
|
@@ -7282,6 +7480,22 @@ var COMMAND_HELP = {
|
|
|
7282
7480
|
examples: ["tonbo usage series --days 7 --compare"],
|
|
7283
7481
|
defaults: { "--days": "30" }
|
|
7284
7482
|
},
|
|
7483
|
+
"tonbo metrics machines": {
|
|
7484
|
+
about: [
|
|
7485
|
+
"Prints the account's Machines per UTC hour or day, summed over every region: how long they were awake, their wakes and refused wakes, and the bytes they exchanged with the internet. Then each Machine's totals over the window, most first by the first series printed. `tonbo machine metrics` prints one Machine's CPU, memory and network too.",
|
|
7486
|
+
"Give `--window`, or `--from` and `--to` with `--granularity` (at most 32 days hourly, 92 days daily)."
|
|
7487
|
+
],
|
|
7488
|
+
examples: ["tonbo metrics machines --window 30d"],
|
|
7489
|
+
defaults: { "--window": "7d" }
|
|
7490
|
+
},
|
|
7491
|
+
"tonbo metrics volumes": {
|
|
7492
|
+
about: [
|
|
7493
|
+
"Prints what the account's Volumes stored, one line per UTC day of the window: the average bytes their files held that day, compressed, summed over every region, which is what they are billed for. Today, and a day not fully rated yet, is marked `so far`. Then the change between the window's first and last complete days.",
|
|
7494
|
+
"Give `--window`, or `--from` and `--to` (at most 92 days); the last 30 days when neither is given."
|
|
7495
|
+
],
|
|
7496
|
+
examples: ["tonbo metrics volumes --window 7d"],
|
|
7497
|
+
defaults: { "--window": "30d" }
|
|
7498
|
+
},
|
|
7285
7499
|
"tonbo usage today": {
|
|
7286
7500
|
about: [
|
|
7287
7501
|
"Prints the account's write units per UTC hour of yesterday and today, in three columns: databases, streams and other."
|
|
@@ -9187,6 +9401,15 @@ function createProgram(makeDependencies = createDependencies) {
|
|
|
9187
9401
|
).action(
|
|
9188
9402
|
async (name, options) => machineActivationsCommand(dependencies(), name, options)
|
|
9189
9403
|
);
|
|
9404
|
+
operationOptions(
|
|
9405
|
+
machine.command("metrics <name>").description("show a Machine's awake time, wakes, CPU, memory and network over time").option("--window <window>", "24h or 7d hourly, 30d or 90d daily; 24h when omitted").option(
|
|
9406
|
+
"--series <series>",
|
|
9407
|
+
"the series to print, comma-separated; every series when omitted"
|
|
9408
|
+
).option("--granularity <step>", "with --from and --to, hour or day").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"),
|
|
9409
|
+
"getMachineMetrics"
|
|
9410
|
+
).action(
|
|
9411
|
+
async (name, options) => machineMetricsCommand(dependencies(), name, options)
|
|
9412
|
+
);
|
|
9190
9413
|
operationOptions(
|
|
9191
9414
|
machine.command("shapes").description("list the shapes a Machine may have"),
|
|
9192
9415
|
"listMachineShapes"
|
|
@@ -9247,6 +9470,20 @@ function createProgram(makeDependencies = createDependencies) {
|
|
|
9247
9470
|
),
|
|
9248
9471
|
"getUsageToday"
|
|
9249
9472
|
).action(async (options) => usageTodayCommand(dependencies(), options));
|
|
9473
|
+
const metrics = program.command("metrics").description("show how the account's resources are used over time");
|
|
9474
|
+
operationOptions(
|
|
9475
|
+
metrics.command("volumes").description("show what the account's Volumes stored, day by day").option("--window <window>", "the last 7d, 30d or 90d, today included").option("--from <day>", "with --to, the first UTC day, as YYYY-MM-DD").option("--to <day>", "with --from, the UTC day the window ends before, as YYYY-MM-DD"),
|
|
9476
|
+
"getVolumeMetrics"
|
|
9477
|
+
).action(
|
|
9478
|
+
async (options) => metricsVolumesCommand(dependencies(), options)
|
|
9479
|
+
);
|
|
9480
|
+
operationOptions(
|
|
9481
|
+
metrics.command("machines").description("show the account's Machines' awake time, wakes and internet bytes over time").option("--window <window>", "24h or 7d hourly, 30d or 90d daily; 7d when omitted").option(
|
|
9482
|
+
"--series <series>",
|
|
9483
|
+
"the series to print, comma-separated; every series when omitted"
|
|
9484
|
+
).option("--granularity <step>", "with --from and --to, hour or day").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"),
|
|
9485
|
+
"getAccountMachineMetrics"
|
|
9486
|
+
).action(async (options) => metricsMachinesCommand(dependencies(), options));
|
|
9250
9487
|
const ssh = program.command("ssh").description("open a terminal on a Machine, or run a command there, over SSH").argument("<machine>", "the Machine's name").argument("[command...]", "a command to run instead of a shell, after --").action(async (name, command) => sshCommand(dependencies(), name, command));
|
|
9251
9488
|
ssh.command("config").description(
|
|
9252
9489
|
"print the ssh_config block that lets ssh, scp, sftp and Remote-SSH reach every Machine"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tonbo/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.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": {
|