@envpilot/cli 0.1.1 → 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 (3) hide show
  1. package/README.md +13 -13
  2. package/dist/index.js +152 -9
  3. package/package.json +7 -4
package/README.md CHANGED
@@ -40,19 +40,19 @@ envpilot push
40
40
 
41
41
  ## Commands
42
42
 
43
- | Command | Description |
44
- |---------|-------------|
45
- | `envpilot login` | Authenticate with your Envpilot account |
46
- | `envpilot logout` | Log out and clear stored credentials |
47
- | `envpilot init` | Link the current directory to an Envpilot project |
48
- | `envpilot pull` | Pull environment variables into a local `.env` file |
49
- | `envpilot push` | Push local `.env` changes to Envpilot |
50
- | `envpilot list orgs` | List your organizations |
51
- | `envpilot list projects` | List projects in the active organization |
52
- | `envpilot list variables` | List variables in the active project |
53
- | `envpilot switch` | Switch the active project |
54
- | `envpilot config` | View or update CLI configuration |
55
- | `envpilot whoami` | Show the currently authenticated user |
43
+ | Command | Description |
44
+ | ------------------------- | --------------------------------------------------- |
45
+ | `envpilot login` | Authenticate with your Envpilot account |
46
+ | `envpilot logout` | Log out and clear stored credentials |
47
+ | `envpilot init` | Link the current directory to an Envpilot project |
48
+ | `envpilot pull` | Pull environment variables into a local `.env` file |
49
+ | `envpilot push` | Push local `.env` changes to Envpilot |
50
+ | `envpilot list orgs` | List your organizations |
51
+ | `envpilot list projects` | List projects in the active organization |
52
+ | `envpilot list variables` | List variables in the active project |
53
+ | `envpilot switch` | Switch the active project |
54
+ | `envpilot config` | View or update CLI configuration |
55
+ | `envpilot whoami` | Show the currently authenticated user |
56
56
 
57
57
  ## Role-Based Access
58
58
 
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
  */
@@ -403,11 +422,14 @@ var APIClient = class {
403
422
  /**
404
423
  * List variables in a project
405
424
  */
406
- async listVariables(projectId, environment) {
425
+ async listVariables(projectId, environment, organizationId) {
407
426
  const params = { projectId };
408
427
  if (environment) {
409
428
  params.environment = environment;
410
429
  }
430
+ if (organizationId) {
431
+ params.organizationId = organizationId;
432
+ }
411
433
  const response = await this.get(
412
434
  "/api/cli/variables",
413
435
  params
@@ -1067,7 +1089,10 @@ var pullCommand = new Command3("pull").description("Download environment variabl
1067
1089
  async () => {
1068
1090
  const response = await api.get("/api/cli/variables", {
1069
1091
  projectId: projectConfig.projectId,
1070
- environment
1092
+ environment,
1093
+ ...projectConfig.organizationId && {
1094
+ organizationId: projectConfig.organizationId
1095
+ }
1071
1096
  });
1072
1097
  metaProjectRole = response.meta?.projectRole;
1073
1098
  return response.data || [];
@@ -1284,10 +1309,14 @@ var pushCommand = new Command4("push").description("Upload local .env file to cl
1284
1309
  const remoteVariables = await withSpinner(
1285
1310
  "Fetching current variables...",
1286
1311
  async () => {
1287
- const response = await api.get("/api/cli/variables", {
1312
+ const params = {
1288
1313
  projectId: projectConfig.projectId,
1289
1314
  environment
1290
- });
1315
+ };
1316
+ if (projectConfig.organizationId) {
1317
+ params.organizationId = projectConfig.organizationId;
1318
+ }
1319
+ const response = await api.get("/api/cli/variables", params);
1291
1320
  return response.data || [];
1292
1321
  }
1293
1322
  );
@@ -1355,7 +1384,10 @@ var pushCommand = new Command4("push").description("Upload local .env file to cl
1355
1384
  key,
1356
1385
  value
1357
1386
  })),
1358
- mode
1387
+ mode,
1388
+ ...projectConfig.organizationId && {
1389
+ organizationId: projectConfig.organizationId
1390
+ }
1359
1391
  });
1360
1392
  return response.data;
1361
1393
  }
@@ -2028,9 +2060,119 @@ var logoutCommand = new Command8("logout").description("Log out from Envpilot").
2028
2060
  }
2029
2061
  });
2030
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
+
2031
2173
  // src/index.ts
2032
- var program = new Command9();
2033
- program.name("envpilot").description("Envpilot CLI - Sync, secure, and share environment variables").version("0.1.0");
2174
+ var program = new Command10();
2175
+ program.name("envpilot").description("Envpilot CLI - Sync, secure, and share environment variables").version("1.0.0");
2034
2176
  program.addCommand(loginCommand);
2035
2177
  program.addCommand(logoutCommand);
2036
2178
  program.addCommand(initCommand);
@@ -2039,4 +2181,5 @@ program.addCommand(pushCommand);
2039
2181
  program.addCommand(switchCommand);
2040
2182
  program.addCommand(listCommand);
2041
2183
  program.addCommand(configCommand);
2184
+ program.addCommand(usageCommand);
2042
2185
  program.parse();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@envpilot/cli",
3
- "version": "0.1.1",
4
- "description": "CLI tool for Envpilot environment variable management",
3
+ "version": "1.2.0",
4
+ "description": "Envpilot CLI sync and manage environment variables from the terminal",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "envpilot": "./dist/index.js"
@@ -51,11 +51,14 @@
51
51
  "dotenv",
52
52
  "secrets"
53
53
  ],
54
- "author": "",
54
+ "author": "Envpilot <hello@envpilot.dev> (https://www.envpilot.dev)",
55
55
  "license": "UNLICENSED",
56
56
  "repository": {
57
57
  "type": "git",
58
58
  "url": "https://github.com/rafay99-epic/envpilot.dev"
59
59
  },
60
- "homepage": "https://www.envpilot.dev"
60
+ "homepage": "https://www.envpilot.dev",
61
+ "bugs": {
62
+ "url": "https://github.com/rafay99-epic/envpilot.dev/issues"
63
+ }
61
64
  }