@envpilot/cli 1.1.0 → 1.2.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/index.js +133 -3
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { Command as Command9 } from "commander";
4
+ import { Command as Command10 } from "commander";
5
5
 
6
6
  // src/commands/login.ts
7
7
  import { Command } from "commander";
@@ -95,6 +95,9 @@ function maskValue(value, showChars = 4) {
95
95
  }
96
96
  return value.slice(0, showChars) + "****" + value.slice(-showChars);
97
97
  }
98
+ function blank() {
99
+ console.log();
100
+ }
98
101
  function formatRole(role) {
99
102
  switch (role) {
100
103
  case "admin":
@@ -176,6 +179,9 @@ function setRefreshToken(token) {
176
179
  function setActiveProjectId(projectId) {
177
180
  config.set("activeProjectId", projectId);
178
181
  }
182
+ function getActiveOrganizationId() {
183
+ return config.get("activeOrganizationId");
184
+ }
179
185
  function setActiveOrganizationId(organizationId) {
180
186
  config.set("activeOrganizationId", organizationId);
181
187
  }
@@ -348,12 +354,19 @@ var APIClient = class {
348
354
  "UNAUTHORIZED"
349
355
  );
350
356
  }
357
+ if (response.status === 403 && code === "TIER_LIMIT_REACHED") {
358
+ throw new APIError(
359
+ message || "Tier limit reached. Run `envpilot usage` to see your plan limits.",
360
+ 403,
361
+ "TIER_LIMIT_REACHED"
362
+ );
363
+ }
351
364
  if (response.status === 403) {
352
365
  throw new APIError(message || "Access denied.", 403, code || "FORBIDDEN");
353
366
  }
354
367
  if (response.status === 402) {
355
368
  throw new APIError(
356
- message || "Payment is currently disabled for this pre-alpha build.",
369
+ message || "Tier limit reached. Run `envpilot usage` to see your plan limits.",
357
370
  402,
358
371
  "PAYMENT_REQUIRED"
359
372
  );
@@ -375,6 +388,12 @@ var APIClient = class {
375
388
  async getTierInfo(organizationId) {
376
389
  return this.get("/api/cli/tier", { organizationId });
377
390
  }
391
+ /**
392
+ * Get usage info for the active organization
393
+ */
394
+ async getUsage(organizationId) {
395
+ return this.get("/api/cli/usage", { organizationId });
396
+ }
378
397
  /**
379
398
  * List organizations the user has access to
380
399
  */
@@ -2041,8 +2060,118 @@ var logoutCommand = new Command8("logout").description("Log out from Envpilot").
2041
2060
  }
2042
2061
  });
2043
2062
 
2063
+ // src/commands/usage.ts
2064
+ import { Command as Command9 } from "commander";
2065
+ import chalk10 from "chalk";
2066
+ function formatUsage(current, limit) {
2067
+ const limitStr = limit === null ? "unlimited" : String(limit);
2068
+ const ratio = `${current}/${limitStr}`;
2069
+ if (limit === null) return chalk10.green(ratio);
2070
+ if (current >= limit) return chalk10.red(ratio);
2071
+ if (current >= limit * 0.8) return chalk10.yellow(ratio);
2072
+ return chalk10.green(ratio);
2073
+ }
2074
+ function featureStatus(enabled) {
2075
+ return enabled ? chalk10.green("Enabled") : chalk10.dim("Disabled (Pro)");
2076
+ }
2077
+ var usageCommand = new Command9("usage").description("Show plan usage and limits for the active organization").option("-o, --organization <id>", "Organization ID").option("--json", "Output as JSON").action(async (options) => {
2078
+ try {
2079
+ if (!isAuthenticated()) {
2080
+ throw notAuthenticated();
2081
+ }
2082
+ const api = createAPIClient();
2083
+ const projectConfig = readProjectConfig();
2084
+ let organizationId = options.organization || projectConfig?.organizationId || getActiveOrganizationId();
2085
+ if (!organizationId) {
2086
+ const orgs = await withSpinner(
2087
+ "Fetching organizations...",
2088
+ async () => {
2089
+ const response = await api.get("/api/cli/organizations");
2090
+ return response.data || [];
2091
+ }
2092
+ );
2093
+ if (orgs.length === 0) {
2094
+ error("No organizations found.");
2095
+ process.exit(1);
2096
+ }
2097
+ if (orgs.length === 1) {
2098
+ organizationId = orgs[0]._id;
2099
+ } else {
2100
+ error(
2101
+ "Multiple organizations found. Use --organization to specify one."
2102
+ );
2103
+ console.log();
2104
+ for (const org of orgs) {
2105
+ console.log(
2106
+ ` ${org.name} (${org.slug}): --organization ${org._id}`
2107
+ );
2108
+ }
2109
+ process.exit(1);
2110
+ }
2111
+ }
2112
+ const usage = await withSpinner(
2113
+ "Fetching usage...",
2114
+ () => api.getUsage(organizationId)
2115
+ );
2116
+ if (options.json) {
2117
+ console.log(JSON.stringify(usage, null, 2));
2118
+ return;
2119
+ }
2120
+ const tierLabel = usage.tier === "pro" ? chalk10.green("Pro") : chalk10.white("Free");
2121
+ header(`Plan: ${tierLabel}`);
2122
+ blank();
2123
+ if (!usage.enforcementEnabled) {
2124
+ info("Pre-alpha mode \u2014 all limits are bypassed. Billing coming soon.");
2125
+ blank();
2126
+ }
2127
+ header("Resource Usage");
2128
+ blank();
2129
+ keyValue([
2130
+ ["Projects", formatUsage(usage.usage.projects, usage.limits.projects)],
2131
+ [
2132
+ "Team Members",
2133
+ formatUsage(usage.usage.teamMembers, usage.limits.teamMembers)
2134
+ ],
2135
+ ["Pending Invitations", String(usage.usage.pendingInvitations)],
2136
+ ["Total Variables", String(usage.usage.totalVariables)]
2137
+ ]);
2138
+ blank();
2139
+ if (usage.usage.variablesPerProject.length > 0) {
2140
+ header("Variables per Project");
2141
+ blank();
2142
+ table(
2143
+ usage.usage.variablesPerProject.map((p) => ({
2144
+ project: p.projectName,
2145
+ variables: formatUsage(p.count, usage.limits.variablesPerProject)
2146
+ })),
2147
+ [
2148
+ { key: "project", header: "Project" },
2149
+ { key: "variables", header: "Variables" }
2150
+ ]
2151
+ );
2152
+ blank();
2153
+ }
2154
+ header("Features");
2155
+ blank();
2156
+ keyValue([
2157
+ ["Version History", featureStatus(usage.features.versionHistory)],
2158
+ ["Bulk Import", featureStatus(usage.features.bulkImport)],
2159
+ [
2160
+ "Granular Permissions",
2161
+ featureStatus(usage.features.granularPermissions)
2162
+ ],
2163
+ ["Extension Access", featureStatus(usage.features.extensionAccess)],
2164
+ ["Audit Log Retention", `${usage.features.auditLogRetentionDays} days`]
2165
+ ]);
2166
+ blank();
2167
+ } catch (err) {
2168
+ error(err instanceof Error ? err.message : "Failed to get usage");
2169
+ process.exit(1);
2170
+ }
2171
+ });
2172
+
2044
2173
  // src/index.ts
2045
- var program = new Command9();
2174
+ var program = new Command10();
2046
2175
  program.name("envpilot").description("Envpilot CLI - Sync, secure, and share environment variables").version("1.0.0");
2047
2176
  program.addCommand(loginCommand);
2048
2177
  program.addCommand(logoutCommand);
@@ -2052,4 +2181,5 @@ program.addCommand(pushCommand);
2052
2181
  program.addCommand(switchCommand);
2053
2182
  program.addCommand(listCommand);
2054
2183
  program.addCommand(configCommand);
2184
+ program.addCommand(usageCommand);
2055
2185
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@envpilot/cli",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Envpilot CLI — sync and manage environment variables from the terminal",
5
5
  "type": "module",
6
6
  "bin": {