@agfs/cli 0.1.1 → 0.1.2
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/index.js +60 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -15,11 +15,13 @@ import { z } from "zod";
|
|
|
15
15
|
var ENTRY_KIND_VALUES = ["file", "folder"];
|
|
16
16
|
var TOKEN_SOURCE_VALUES = ["session", "api-token"];
|
|
17
17
|
var DEVICE_STATUS_VALUES = ["pending", "approved", "consumed", "expired"];
|
|
18
|
+
var STORAGE_PLAN_ID_VALUES = ["free", "paid"];
|
|
18
19
|
var pathSchema = z.string().min(1).max(4096).startsWith("/");
|
|
19
20
|
var ttlSchema = z.string().regex(/^\d+\s*(m|h|d)$/i, "TTL must use m, h, or d units");
|
|
20
21
|
var entryKindSchema = z.enum(ENTRY_KIND_VALUES);
|
|
21
22
|
var tokenSourceSchema = z.enum(TOKEN_SOURCE_VALUES);
|
|
22
23
|
var deviceStatusSchema = z.enum(DEVICE_STATUS_VALUES);
|
|
24
|
+
var storagePlanIdSchema = z.enum(STORAGE_PLAN_ID_VALUES);
|
|
23
25
|
var sessionUserSchema = z.object({
|
|
24
26
|
id: z.string(),
|
|
25
27
|
name: z.string().nullable(),
|
|
@@ -78,6 +80,15 @@ var whoAmIResponseSchema = z.object({
|
|
|
78
80
|
user: sessionUserSchema,
|
|
79
81
|
authSource: tokenSourceSchema
|
|
80
82
|
});
|
|
83
|
+
var accountSummarySchema = z.object({
|
|
84
|
+
planId: storagePlanIdSchema,
|
|
85
|
+
planName: z.string(),
|
|
86
|
+
storageUsedBytes: z.number().int().nonnegative(),
|
|
87
|
+
storageLimitBytes: z.number().int().nonnegative(),
|
|
88
|
+
storageRemainingBytes: z.number().int().nonnegative(),
|
|
89
|
+
isOverLimit: z.boolean(),
|
|
90
|
+
paidPlanComingSoon: z.boolean()
|
|
91
|
+
});
|
|
81
92
|
var deviceStartRequestSchema = z.object({
|
|
82
93
|
clientName: z.string().min(1).max(100).default("agfs cli")
|
|
83
94
|
});
|
|
@@ -372,6 +383,10 @@ var AgfsClient = class _AgfsClient {
|
|
|
372
383
|
const response = await this.request("/api/v1/whoami");
|
|
373
384
|
return whoAmIResponseSchema.parse(await response.json());
|
|
374
385
|
}
|
|
386
|
+
async account() {
|
|
387
|
+
const response = await this.request("/api/v1/account");
|
|
388
|
+
return accountSummarySchema.parse(await response.json());
|
|
389
|
+
}
|
|
375
390
|
async startDeviceLogin(clientName = "agfs cli") {
|
|
376
391
|
const response = await this.request("/api/v1/device/start", {
|
|
377
392
|
method: "POST",
|
|
@@ -639,6 +654,46 @@ function registerAuthCommands(program2) {
|
|
|
639
654
|
}
|
|
640
655
|
|
|
641
656
|
// src/lib/format.ts
|
|
657
|
+
function formatStorageBytes(bytes) {
|
|
658
|
+
if (!Number.isFinite(bytes) || bytes < 1024) {
|
|
659
|
+
return `${bytes} B`;
|
|
660
|
+
}
|
|
661
|
+
if (bytes < 1024 * 1024) {
|
|
662
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
663
|
+
}
|
|
664
|
+
if (bytes < 1024 * 1024 * 1024) {
|
|
665
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
666
|
+
}
|
|
667
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
|
|
668
|
+
}
|
|
669
|
+
function renderMeter(currentBytes, limitBytes, width = 24) {
|
|
670
|
+
if (limitBytes <= 0) {
|
|
671
|
+
return `[${"-".repeat(width)}]`;
|
|
672
|
+
}
|
|
673
|
+
const ratio = Math.min(currentBytes / limitBytes, 1);
|
|
674
|
+
const filled = Math.round(ratio * width);
|
|
675
|
+
return `[${"#".repeat(filled).padEnd(width, "-")}]`;
|
|
676
|
+
}
|
|
677
|
+
function wrapInBox(title, lines) {
|
|
678
|
+
const width = Math.max(title.length, ...lines.map((line) => line.length));
|
|
679
|
+
const top = `\u256D${"\u2500".repeat(width + 2)}\u256E`;
|
|
680
|
+
const bottom = `\u2570${"\u2500".repeat(width + 2)}\u256F`;
|
|
681
|
+
const content = [`\u2502 ${title.padEnd(width)} \u2502`, ...lines.map((line) => `\u2502 ${line.padEnd(width)} \u2502`)];
|
|
682
|
+
return [top, ...content, bottom].join("\n");
|
|
683
|
+
}
|
|
684
|
+
function renderAccountSummary(summary) {
|
|
685
|
+
const percentUsed = summary.storageLimitBytes > 0 ? Math.round(summary.storageUsedBytes / summary.storageLimitBytes * 100) : 0;
|
|
686
|
+
const status = summary.isOverLimit ? `Over by ${formatStorageBytes(summary.storageUsedBytes - summary.storageLimitBytes)}` : `${percentUsed}% used`;
|
|
687
|
+
const lines = [
|
|
688
|
+
`Plan ${summary.planName}`,
|
|
689
|
+
`Used ${formatStorageBytes(summary.storageUsedBytes)} / ${formatStorageBytes(summary.storageLimitBytes)}`,
|
|
690
|
+
`Left ${formatStorageBytes(summary.storageRemainingBytes)}`,
|
|
691
|
+
`Status ${status}`,
|
|
692
|
+
`Meter ${renderMeter(summary.storageUsedBytes, summary.storageLimitBytes)}`,
|
|
693
|
+
...summary.paidPlanComingSoon && summary.planId === "free" ? ["Upgrade Paid self-serve is coming soon"] : []
|
|
694
|
+
];
|
|
695
|
+
return wrapInBox("AGFS storage", lines);
|
|
696
|
+
}
|
|
642
697
|
function renderEntries(entries) {
|
|
643
698
|
if (entries.length === 0) {
|
|
644
699
|
return "(empty)";
|
|
@@ -677,6 +732,11 @@ function registerFsCommands(program2) {
|
|
|
677
732
|
const result = await client.tree(pathname);
|
|
678
733
|
console.log(renderTree(result.tree));
|
|
679
734
|
});
|
|
735
|
+
program2.command("usage").description("Show the current plan, storage usage, and remaining quota").action(async () => {
|
|
736
|
+
const client = await AgfsClient.fromConfig();
|
|
737
|
+
const result = await client.account();
|
|
738
|
+
console.log(renderAccountSummary(result));
|
|
739
|
+
});
|
|
680
740
|
program2.command("mkdir").description("Create a folder path").argument("<path>", "Remote path").action(async (pathname) => {
|
|
681
741
|
const client = await AgfsClient.fromConfig();
|
|
682
742
|
await client.mkdir(pathname);
|