@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.
Files changed (2) hide show
  1. package/dist/index.js +60 -0
  2. 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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agfs/cli",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "agfs": "./dist/index.js"