@rowlabs/ev 0.4.5 → 0.5.1

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 +380 -12
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -6735,7 +6735,7 @@ var init_auth = __esm({
6735
6735
 
6736
6736
  // src/index.ts
6737
6737
  init_dist();
6738
- import { Command as Command20 } from "commander";
6738
+ import { Command as Command22 } from "commander";
6739
6739
 
6740
6740
  // src/commands/login.ts
6741
6741
  init_dist();
@@ -7573,7 +7573,7 @@ envCommand.command("create <name>").description("Create a new environment").acti
7573
7573
  async ({ context, spinner }) => {
7574
7574
  const client = await createApiClient();
7575
7575
  const project = await client.getProject(context.project);
7576
- const app = context.app ? project.apps.find((a2) => a2.name.toLowerCase() === context.app.toLowerCase()) : project.apps.find((a2) => a2.name === "default") ?? project.apps[0];
7576
+ const app = context.app ? project.apps.find((a2) => a2.name.toLowerCase() === context.app?.toLowerCase()) : project.apps.find((a2) => a2.name === "default") ?? project.apps[0];
7577
7577
  if (!app) {
7578
7578
  spinner.fail(chalk10.red("App not found"));
7579
7579
  process.exit(1);
@@ -7605,7 +7605,7 @@ envCommand.command("delete <name>").description("Delete an environment").action(
7605
7605
  async ({ context, spinner }) => {
7606
7606
  const client = await createApiClient();
7607
7607
  const project = await client.getProject(context.project);
7608
- const app = context.app ? project.apps.find((a2) => a2.name.toLowerCase() === context.app.toLowerCase()) : project.apps.find((a2) => a2.name === "default") ?? project.apps[0];
7608
+ const app = context.app ? project.apps.find((a2) => a2.name.toLowerCase() === context.app?.toLowerCase()) : project.apps.find((a2) => a2.name === "default") ?? project.apps[0];
7609
7609
  if (!app) {
7610
7610
  spinner.fail(chalk10.red("App not found"));
7611
7611
  process.exit(1);
@@ -8642,12 +8642,16 @@ var completionsCommand = new Command16("completions").description("Generate shel
8642
8642
  });
8643
8643
 
8644
8644
  // src/commands/scan.ts
8645
- init_dist();
8646
8645
  init_api_client();
8647
8646
  init_config();
8648
8647
  import { Command as Command17 } from "commander";
8649
8648
  import chalk19 from "chalk";
8650
8649
  import ora11 from "ora";
8650
+ import { stat as stat2 } from "fs/promises";
8651
+ import { join as join9 } from "path";
8652
+
8653
+ // src/lib/scanner.ts
8654
+ init_dist();
8651
8655
  import { readdir, readFile as readFile6, stat } from "fs/promises";
8652
8656
  import { existsSync as existsSync6 } from "fs";
8653
8657
  import { join as join8, extname } from "path";
@@ -8729,7 +8733,7 @@ async function scanDir(dir) {
8729
8733
  const varName = match[1];
8730
8734
  if (IGNORE_VARS.has(varName)) continue;
8731
8735
  if (!results.has(varName)) results.set(varName, /* @__PURE__ */ new Set());
8732
- results.get(varName).add(fullPath);
8736
+ results.get(varName)?.add(fullPath);
8733
8737
  }
8734
8738
  }
8735
8739
  }
@@ -8752,6 +8756,8 @@ async function getLocalEnvKeys(dir, configEnvFiles) {
8752
8756
  }
8753
8757
  return keys;
8754
8758
  }
8759
+
8760
+ // src/commands/scan.ts
8755
8761
  function printTable(label, refs, envData, repoRoot, showFiles, localKeys) {
8756
8762
  const varNames = [...refs.keys()].sort();
8757
8763
  if (varNames.length === 0) return 0;
@@ -8787,7 +8793,7 @@ function printTable(label, refs, envData, repoRoot, showFiles, localKeys) {
8787
8793
  if (!allPresent) missingCount++;
8788
8794
  console.log(row);
8789
8795
  if (showFiles) {
8790
- const files = refs.get(varName);
8796
+ const files = refs.get(varName) ?? [];
8791
8797
  for (const file of files) {
8792
8798
  const relative = file.replace(repoRoot + "/", "");
8793
8799
  console.log(chalk19.dim(` \u2192 ${relative}`));
@@ -8831,9 +8837,9 @@ var scanCommand = new Command17("scan").description("Scan codebase for env var r
8831
8837
  let totalMissing = 0;
8832
8838
  let totalVars = 0;
8833
8839
  for (const [appName, appConfig] of Object.entries(config.apps)) {
8834
- const appDir = join8(repoRoot, appConfig.path);
8840
+ const appDir = join9(repoRoot, appConfig.path);
8835
8841
  try {
8836
- await stat(appDir);
8842
+ await stat2(appDir);
8837
8843
  } catch {
8838
8844
  continue;
8839
8845
  }
@@ -8885,7 +8891,7 @@ var scanCommand = new Command17("scan").description("Scan codebase for env var r
8885
8891
  }
8886
8892
  console.log();
8887
8893
  } else {
8888
- const scanRoot = context.app && config.apps?.[context.app] ? join8(repoRoot, config.apps[context.app].path) : cwd;
8894
+ const scanRoot = context.app && config.apps?.[context.app] ? join9(repoRoot, config.apps[context.app].path) : cwd;
8889
8895
  const refs = await scanDir(scanRoot);
8890
8896
  if (refs.size === 0) {
8891
8897
  spinner.succeed(
@@ -8934,7 +8940,7 @@ var updateCommand = new Command18("update").description("Update ev to the latest
8934
8940
  const spinner = ora12("Checking for updates...").start();
8935
8941
  try {
8936
8942
  const latest = execSync2("npm view @rowlabs/ev version", { encoding: "utf-8" }).trim();
8937
- const current = "0.4.5";
8943
+ const current = "0.5.1";
8938
8944
  if (current === latest) {
8939
8945
  spinner.succeed(chalk20.green(`Already on the latest version (${current})`));
8940
8946
  return;
@@ -9029,9 +9035,369 @@ function prompt2(question) {
9029
9035
  });
9030
9036
  }
9031
9037
 
9038
+ // src/commands/audit.ts
9039
+ init_api_client();
9040
+ init_config();
9041
+ import { Command as Command20 } from "commander";
9042
+ import chalk22 from "chalk";
9043
+ import ora14 from "ora";
9044
+ import { stat as stat3 } from "fs/promises";
9045
+ import { join as join10 } from "path";
9046
+ var isTTY = process.stdout.isTTY && !process.env.NO_COLOR;
9047
+ function stripAnsi(str) {
9048
+ const ESC = String.fromCharCode(27);
9049
+ return str.replace(new RegExp(`${ESC}\\[[0-9;]*m`, "g"), "");
9050
+ }
9051
+ function log(msg) {
9052
+ console.log(isTTY ? msg : stripAnsi(msg));
9053
+ }
9054
+ async function auditApp(appName, envName, scanPath, projectId) {
9055
+ const client = await createApiClient();
9056
+ const project = await client.getProject(projectId);
9057
+ const serverApp = project.apps.find((a2) => a2.name.toLowerCase() === appName.toLowerCase());
9058
+ if (!serverApp) {
9059
+ log(isTTY ? chalk22.red(` App "${appName}" not found on server`) : `ERROR: App "${appName}" not found on server`);
9060
+ process.exit(1);
9061
+ }
9062
+ const envs = await client.listEnvironments(serverApp.id);
9063
+ const serverEnv = envs.find((e) => e.name.toLowerCase() === envName.toLowerCase());
9064
+ if (!serverEnv) {
9065
+ log(isTTY ? chalk22.red(` Environment "${envName}" not found for app "${appName}"`) : `ERROR: Environment "${envName}" not found for app "${appName}"`);
9066
+ process.exit(1);
9067
+ }
9068
+ const response = await client.pullSecrets(serverEnv.id);
9069
+ const remoteKeys = new Set(response.secrets.map((s2) => s2.key));
9070
+ const refs = await scanDir(scanPath);
9071
+ const referencedVars = [...refs.keys()];
9072
+ const missingVars = referencedVars.filter((v2) => !remoteKeys.has(v2));
9073
+ return {
9074
+ label: `${appName}:${envName}`,
9075
+ total: referencedVars.length,
9076
+ missing: missingVars
9077
+ };
9078
+ }
9079
+ function printResult(result) {
9080
+ if (result.missing.length === 0) {
9081
+ const msg = ` ${result.label} \u2014 ${result.total} variable${result.total === 1 ? "" : "s"}, all present \u2714`;
9082
+ log(isTTY ? chalk22.green(msg) : msg);
9083
+ } else {
9084
+ const header = ` ${result.label} \u2014 ${result.missing.length} missing`;
9085
+ log(isTTY ? chalk22.yellow(header) : header);
9086
+ log("");
9087
+ for (const v2 of result.missing) {
9088
+ log(isTTY ? ` ${chalk22.red(v2)}` : ` ${v2}`);
9089
+ }
9090
+ }
9091
+ }
9092
+ var auditCommand = new Command20("audit").description("Check that all env vars referenced in code exist in a remote environment").argument("[target]", "Target environment name (defaults to defaultEnv from ev.yaml)").action(async (target) => {
9093
+ const spinner = isTTY ? ora14("Resolving context...").start() : null;
9094
+ try {
9095
+ const resolved = await resolveCurrentContext(target);
9096
+ if (!resolved) {
9097
+ if (spinner) spinner.fail(chalk22.red("No ev.yaml found. Run `ev init` first."));
9098
+ else log("ERROR: No ev.yaml found. Run `ev init` first.");
9099
+ process.exit(1);
9100
+ }
9101
+ const { context, repoRoot } = resolved;
9102
+ const envName = target ?? context.env;
9103
+ const config = await loadEvConfig(process.cwd());
9104
+ if (!config) {
9105
+ if (spinner) spinner.fail(chalk22.red("Could not load ev.yaml"));
9106
+ else log("ERROR: Could not load ev.yaml");
9107
+ process.exit(1);
9108
+ }
9109
+ const cwd = process.cwd();
9110
+ const isAtRoot = cwd === repoRoot;
9111
+ const hasApps = config.apps && Object.keys(config.apps).length > 0;
9112
+ const results = [];
9113
+ if (isAtRoot && hasApps) {
9114
+ if (spinner) spinner.text = "Auditing all apps...";
9115
+ for (const [appName, appConfig] of Object.entries(config.apps ?? {})) {
9116
+ const appDir = join10(repoRoot, appConfig.path);
9117
+ try {
9118
+ await stat3(appDir);
9119
+ } catch {
9120
+ continue;
9121
+ }
9122
+ if (spinner) spinner.text = `Auditing ${appName}:${envName}...`;
9123
+ const result = await auditApp(appName, envName, appDir, context.project);
9124
+ results.push(result);
9125
+ }
9126
+ } else {
9127
+ const appName = context.app ?? "default";
9128
+ const scanPath = context.app && config.apps?.[context.app] ? join10(repoRoot, config.apps[context.app].path) : cwd;
9129
+ if (spinner) spinner.text = `Auditing ${appName}:${envName}...`;
9130
+ const result = await auditApp(appName, envName, scanPath, context.project);
9131
+ results.push(result);
9132
+ }
9133
+ if (spinner) spinner.stop();
9134
+ const totalVars = results.reduce((sum, r2) => sum + r2.total, 0);
9135
+ if (totalVars === 0) {
9136
+ log(isTTY ? chalk22.green(" No environment variable references found in code.") : "No environment variable references found in code.");
9137
+ process.exit(0);
9138
+ }
9139
+ log("");
9140
+ for (const result of results) {
9141
+ printResult(result);
9142
+ log("");
9143
+ }
9144
+ const anyMissing = results.some((r2) => r2.missing.length > 0);
9145
+ if (anyMissing) {
9146
+ log(isTTY ? chalk22.red(" \u2717 Audit failed") : "FAIL");
9147
+ process.exit(1);
9148
+ } else {
9149
+ process.exit(0);
9150
+ }
9151
+ } catch (err) {
9152
+ if (spinner) spinner.fail(chalk22.red(`Audit failed: ${err.message}`));
9153
+ else log(`ERROR: Audit failed: ${err.message}`);
9154
+ process.exit(1);
9155
+ }
9156
+ });
9157
+
9158
+ // src/commands/clean.ts
9159
+ init_dist();
9160
+ init_api_client();
9161
+ init_config();
9162
+ init_auth();
9163
+ import { Command as Command21 } from "commander";
9164
+ import chalk23 from "chalk";
9165
+ import ora15 from "ora";
9166
+ import { join as join11 } from "path";
9167
+ import { readFile as readFile7, writeFile as writeFile6, stat as stat4 } from "fs/promises";
9168
+ import { existsSync as existsSync7 } from "fs";
9169
+ var isTTY2 = process.stdout.isTTY && !process.env.NO_COLOR;
9170
+ function stripAnsi2(str) {
9171
+ const ESC = String.fromCharCode(27);
9172
+ return str.replace(new RegExp(`${ESC}\\[[0-9;]*m`, "g"), "");
9173
+ }
9174
+ function log2(msg) {
9175
+ console.log(isTTY2 ? msg : stripAnsi2(msg));
9176
+ }
9177
+ async function findDeadSecrets(appName, envName, envDir, repoRoot, projectId, envFiles) {
9178
+ const client = await createApiClient();
9179
+ const project = await client.getProject(projectId);
9180
+ const serverApp = project.apps.find((a2) => a2.name.toLowerCase() === appName.toLowerCase());
9181
+ if (!serverApp) {
9182
+ log2(
9183
+ isTTY2 ? chalk23.red(` App "${appName}" not found on server`) : `ERROR: App "${appName}" not found on server`
9184
+ );
9185
+ process.exit(1);
9186
+ }
9187
+ const envs = await client.listEnvironments(serverApp.id);
9188
+ const serverEnv = envs.find((e) => e.name.toLowerCase() === envName.toLowerCase());
9189
+ if (!serverEnv) {
9190
+ log2(
9191
+ isTTY2 ? chalk23.red(` Environment "${envName}" not found for app "${appName}"`) : `ERROR: Environment "${envName}" not found for app "${appName}"`
9192
+ );
9193
+ process.exit(1);
9194
+ }
9195
+ const refs = await scanDir(repoRoot);
9196
+ const referencedKeys = new Set(refs.keys());
9197
+ const localKeyFiles = /* @__PURE__ */ new Map();
9198
+ for (const envFile of envFiles) {
9199
+ const filePath = join11(envDir, envFile);
9200
+ if (existsSync7(filePath)) {
9201
+ const content = await readFile7(filePath, "utf-8");
9202
+ const parsed = parseEnvFile(content);
9203
+ for (const key of Object.keys(parsed)) {
9204
+ if (!localKeyFiles.has(key)) localKeyFiles.set(key, []);
9205
+ localKeyFiles.get(key)?.push(filePath);
9206
+ }
9207
+ }
9208
+ }
9209
+ const response = await client.pullSecrets(serverEnv.id);
9210
+ const remoteKeys = new Set(response.secrets.map((s2) => s2.key));
9211
+ const allKeys = /* @__PURE__ */ new Set([...localKeyFiles.keys(), ...remoteKeys]);
9212
+ const dead = [];
9213
+ for (const key of allKeys) {
9214
+ if (!referencedKeys.has(key)) {
9215
+ dead.push({
9216
+ key,
9217
+ inLocal: localKeyFiles.has(key),
9218
+ inRemote: remoteKeys.has(key)
9219
+ });
9220
+ }
9221
+ }
9222
+ const affectedFiles = /* @__PURE__ */ new Set();
9223
+ for (const d2 of dead) {
9224
+ if (d2.inLocal) {
9225
+ for (const f2 of localKeyFiles.get(d2.key) ?? []) {
9226
+ affectedFiles.add(f2);
9227
+ }
9228
+ }
9229
+ }
9230
+ return {
9231
+ label: `${appName}:${envName}`,
9232
+ dead,
9233
+ localFiles: [...affectedFiles],
9234
+ envName: serverEnv.id,
9235
+ projectId,
9236
+ appName,
9237
+ codeRefCount: referencedKeys.size
9238
+ };
9239
+ }
9240
+ function printDeadTable(result) {
9241
+ const { label, dead } = result;
9242
+ const count = dead.length;
9243
+ if (count === 0) {
9244
+ const msg = ` ${label} \u2014 no unused secrets found`;
9245
+ log2(isTTY2 ? chalk23.green(msg) : msg);
9246
+ return;
9247
+ }
9248
+ const header = ` ${label} \u2014 ${count} unused secret${count === 1 ? "" : "s"} found`;
9249
+ log2(isTTY2 ? chalk23.yellow(header) : header);
9250
+ log2("");
9251
+ const maxKeyLen = Math.max(8, ...dead.map((d2) => d2.key.length));
9252
+ const keyCol = maxKeyLen + 2;
9253
+ const headerLine = " " + "Variable".padEnd(keyCol) + "local remote";
9254
+ log2(isTTY2 ? chalk23.dim(headerLine) : headerLine);
9255
+ const divider = " " + "\u2500".repeat(keyCol + 16);
9256
+ log2(isTTY2 ? chalk23.dim(divider) : divider);
9257
+ for (const d2 of dead) {
9258
+ const localMark = d2.inLocal ? isTTY2 ? chalk23.green("\u2713") : "\u2713" : isTTY2 ? chalk23.dim("\u2013") : "\u2013";
9259
+ const remoteMark = d2.inRemote ? isTTY2 ? chalk23.green("\u2713") : "\u2713" : isTTY2 ? chalk23.dim("\u2013") : "\u2013";
9260
+ const paddedKey = d2.key.padEnd(keyCol);
9261
+ log2(` ${isTTY2 ? chalk23.red(d2.key) + " ".repeat(keyCol - d2.key.length) : paddedKey}${localMark} ${remoteMark}`);
9262
+ }
9263
+ log2("");
9264
+ }
9265
+ async function cleanApp(result, deadSet, envFiles, scanPath, backendConfig) {
9266
+ const isEvBackend = !backendConfig || backendConfig.type === "ev";
9267
+ for (const envFile of envFiles) {
9268
+ const filePath = join11(scanPath, envFile);
9269
+ if (!existsSync7(filePath)) continue;
9270
+ const content = await readFile7(filePath, "utf-8");
9271
+ const parsed = parseEnvFile(content);
9272
+ const filtered = {};
9273
+ for (const [k2, v2] of Object.entries(parsed)) {
9274
+ if (!deadSet.has(k2)) filtered[k2] = v2;
9275
+ }
9276
+ if (Object.keys(filtered).length < Object.keys(parsed).length) {
9277
+ await writeFile6(filePath, serializeEnv(filtered), "utf-8");
9278
+ log2(isTTY2 ? ` ${chalk23.green("\u2713")} Cleaned ${envFile}` : ` \u2713 Cleaned ${envFile}`);
9279
+ }
9280
+ }
9281
+ const deadRemoteKeys = result.dead.filter((d2) => d2.inRemote).map((d2) => d2.key);
9282
+ if (deadRemoteKeys.length > 0) {
9283
+ const client = await createApiClient();
9284
+ const pullResponse = await client.pullSecrets(result.envName);
9285
+ const kept = pullResponse.secrets.filter((s2) => !deadSet.has(s2.key));
9286
+ let secrets;
9287
+ if (isEvBackend) {
9288
+ const projectKey = await getDecryptedProjectKey(result.projectId);
9289
+ secrets = kept.map((s2) => {
9290
+ const decrypted = decryptSecret(s2.encryptedValue, projectKey);
9291
+ const reEncrypted = encryptSecret(decrypted, projectKey);
9292
+ return { key: s2.key, encryptedValue: reEncrypted };
9293
+ });
9294
+ } else {
9295
+ secrets = kept.map((s2) => ({ key: s2.key, encryptedValue: s2.encryptedValue }));
9296
+ }
9297
+ await client.pushSecrets(result.envName, {
9298
+ secrets,
9299
+ prune: true,
9300
+ message: "ev clean: removed unused secrets"
9301
+ });
9302
+ log2(
9303
+ isTTY2 ? ` ${chalk23.green("\u2713")} Removed ${deadRemoteKeys.length} from ${result.label} remote` : ` \u2713 Removed ${deadRemoteKeys.length} from ${result.label} remote`
9304
+ );
9305
+ }
9306
+ }
9307
+ var cleanCommand = new Command21("clean").description("Find and remove unused secrets from local env files and remote environments").argument("[target]", "Target environment name (defaults to defaultEnv from ev.yaml)").action(async (target) => {
9308
+ const spinner = isTTY2 ? ora15("Resolving context...").start() : null;
9309
+ try {
9310
+ const resolved = await resolveCurrentContext(target);
9311
+ if (!resolved) {
9312
+ if (spinner) spinner.fail(chalk23.red("No ev.yaml found. Run `ev init` first."));
9313
+ else log2("ERROR: No ev.yaml found. Run `ev init` first.");
9314
+ process.exit(1);
9315
+ }
9316
+ const { context, repoRoot, backendConfig, envFiles } = resolved;
9317
+ const envName = target ?? context.env;
9318
+ const config = await loadEvConfig(process.cwd());
9319
+ if (!config) {
9320
+ if (spinner) spinner.fail(chalk23.red("Could not load ev.yaml"));
9321
+ else log2("ERROR: Could not load ev.yaml");
9322
+ process.exit(1);
9323
+ }
9324
+ const cwd = process.cwd();
9325
+ const isAtRoot = cwd === repoRoot;
9326
+ const hasApps = config.apps && Object.keys(config.apps).length > 0;
9327
+ const results = [];
9328
+ const scanPaths = /* @__PURE__ */ new Map();
9329
+ if (isAtRoot && hasApps) {
9330
+ if (spinner) spinner.text = "Scanning all apps...";
9331
+ for (const [appName, appConfig] of Object.entries(config.apps ?? {})) {
9332
+ const appDir = join11(repoRoot, appConfig.path);
9333
+ try {
9334
+ await stat4(appDir);
9335
+ } catch {
9336
+ continue;
9337
+ }
9338
+ if (spinner) spinner.text = `Scanning ${appName}:${envName}...`;
9339
+ const result = await findDeadSecrets(
9340
+ appName,
9341
+ envName,
9342
+ appDir,
9343
+ repoRoot,
9344
+ context.project,
9345
+ envFiles
9346
+ );
9347
+ results.push(result);
9348
+ scanPaths.set(appName, appDir);
9349
+ }
9350
+ } else {
9351
+ const appName = context.app ?? "default";
9352
+ const scanPath = context.app && config.apps?.[context.app] ? join11(repoRoot, config.apps[context.app].path) : cwd;
9353
+ if (spinner) spinner.text = `Scanning ${appName}:${envName}...`;
9354
+ const result = await findDeadSecrets(
9355
+ appName,
9356
+ envName,
9357
+ scanPath,
9358
+ repoRoot,
9359
+ context.project,
9360
+ envFiles
9361
+ );
9362
+ results.push(result);
9363
+ scanPaths.set(appName, scanPath);
9364
+ }
9365
+ if (spinner) spinner.stop();
9366
+ const totalCodeRefs = results.reduce((sum, r2) => sum + r2.codeRefCount, 0);
9367
+ if (totalCodeRefs === 0) {
9368
+ log2(
9369
+ "No env var references found in code \u2014 can't determine what's unused."
9370
+ );
9371
+ process.exit(0);
9372
+ }
9373
+ log2("");
9374
+ for (const result of results) {
9375
+ printDeadTable(result);
9376
+ if (result.dead.length === 0) continue;
9377
+ const deadSet = new Set(result.dead.map((d2) => d2.key));
9378
+ const appScanPath = scanPaths.get(result.appName) ?? repoRoot;
9379
+ const answer = await prompt(
9380
+ isTTY2 ? ` Remove these ${result.dead.length} secret${result.dead.length === 1 ? "" : "s"}? (y/N) ` : `Remove these ${result.dead.length} secrets? (y/N) `
9381
+ );
9382
+ if (answer.toLowerCase() !== "y") {
9383
+ log2(isTTY2 ? chalk23.dim(" Skipped.") : " Skipped.");
9384
+ log2("");
9385
+ continue;
9386
+ }
9387
+ log2("");
9388
+ await cleanApp(result, deadSet, envFiles, appScanPath, backendConfig);
9389
+ log2("");
9390
+ }
9391
+ } catch (err) {
9392
+ if (spinner) spinner.fail(chalk23.red(`Clean failed: ${err.message}`));
9393
+ else log2(`ERROR: Clean failed: ${err.message}`);
9394
+ process.exit(1);
9395
+ }
9396
+ });
9397
+
9032
9398
  // src/index.ts
9033
- var program = new Command20();
9034
- program.name("ev").description("Git for env vars \u2014 sync environment variables across teams securely").version("0.4.5");
9399
+ var program = new Command22();
9400
+ program.name("ev").description("Git for env vars \u2014 sync environment variables across teams securely").version("0.5.1");
9035
9401
  program.addCommand(loginCommand);
9036
9402
  program.addCommand(initCommand);
9037
9403
  program.addCommand(pushCommand);
@@ -9051,6 +9417,8 @@ program.addCommand(completionsCommand);
9051
9417
  program.addCommand(scanCommand);
9052
9418
  program.addCommand(updateCommand);
9053
9419
  program.addCommand(deleteCommand);
9420
+ program.addCommand(auditCommand);
9421
+ program.addCommand(cleanCommand);
9054
9422
  async function main() {
9055
9423
  await initCrypto();
9056
9424
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rowlabs/ev",
3
- "version": "0.4.5",
3
+ "version": "0.5.1",
4
4
  "description": "Git for env vars — sync environment variables across teams securely",
5
5
  "type": "module",
6
6
  "bin": {