@keywaysh/cli 0.1.12 → 0.1.13

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/cli.js +78 -37
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -12,13 +12,14 @@ import pc11 from "picocolors";
12
12
 
13
13
  // src/cmds/init.ts
14
14
  import pc6 from "picocolors";
15
- import prompts4 from "prompts";
15
+ import prompts5 from "prompts";
16
16
 
17
17
  // src/utils/git.ts
18
18
  import { execSync } from "child_process";
19
19
  import fs from "fs";
20
20
  import path from "path";
21
21
  import pc from "picocolors";
22
+ import prompts from "prompts";
22
23
  function getCurrentRepoFullName() {
23
24
  try {
24
25
  if (!isGitRepository()) {
@@ -87,9 +88,47 @@ function checkEnvGitignore() {
87
88
  return true;
88
89
  }
89
90
  }
90
- function warnIfEnvNotGitignored() {
91
- if (!checkEnvGitignore()) {
92
- console.log(pc.yellow("\u26A0\uFE0F .env files are not in .gitignore - secrets may be committed"));
91
+ function addEnvToGitignore() {
92
+ try {
93
+ const gitRoot = execSync("git rev-parse --show-toplevel", {
94
+ encoding: "utf-8",
95
+ stdio: "pipe"
96
+ }).trim();
97
+ const gitignorePath = path.join(gitRoot, ".gitignore");
98
+ const envEntry = ".env*";
99
+ if (fs.existsSync(gitignorePath)) {
100
+ const content = fs.readFileSync(gitignorePath, "utf-8");
101
+ const newContent = content.endsWith("\n") ? `${content}${envEntry}
102
+ ` : `${content}
103
+ ${envEntry}
104
+ `;
105
+ fs.writeFileSync(gitignorePath, newContent);
106
+ } else {
107
+ fs.writeFileSync(gitignorePath, `${envEntry}
108
+ `);
109
+ }
110
+ return true;
111
+ } catch {
112
+ return false;
113
+ }
114
+ }
115
+ async function warnIfEnvNotGitignored() {
116
+ if (checkEnvGitignore()) {
117
+ return;
118
+ }
119
+ console.log(pc.yellow("\u26A0\uFE0F .env files are not in .gitignore - secrets may be committed"));
120
+ const { addToGitignore } = await prompts({
121
+ type: "confirm",
122
+ name: "addToGitignore",
123
+ message: "Add .env* to .gitignore?",
124
+ initial: true
125
+ });
126
+ if (addToGitignore) {
127
+ if (addEnvToGitignore()) {
128
+ console.log(pc.green("\u2713 Added .env* to .gitignore"));
129
+ } else {
130
+ console.log(pc.red("\u2717 Failed to update .gitignore"));
131
+ }
93
132
  }
94
133
  }
95
134
 
@@ -101,7 +140,7 @@ var INTERNAL_POSTHOG_HOST = "https://eu.i.posthog.com";
101
140
  // package.json
102
141
  var package_default = {
103
142
  name: "@keywaysh/cli",
104
- version: "0.1.12",
143
+ version: "0.1.13",
105
144
  description: "One link to all your secrets",
106
145
  type: "module",
107
146
  bin: {
@@ -719,7 +758,7 @@ var AnalyticsEvents = {
719
758
  // src/cmds/readme.ts
720
759
  import fs3 from "fs";
721
760
  import path3 from "path";
722
- import prompts from "prompts";
761
+ import prompts2 from "prompts";
723
762
  import pc2 from "picocolors";
724
763
  import balanced from "balanced-match";
725
764
  function generateBadge(repo) {
@@ -814,7 +853,7 @@ async function ensureReadme(repoName, cwd) {
814
853
  console.log(pc2.yellow('No README found. Run "keyway readme add-badge" from a repo with a README.'));
815
854
  return null;
816
855
  }
817
- const { confirm } = await prompts(
856
+ const { confirm } = await prompts2(
818
857
  {
819
858
  type: "confirm",
820
859
  name: "confirm",
@@ -864,12 +903,12 @@ async function addBadgeToReadme(silent = false) {
864
903
  import pc5 from "picocolors";
865
904
  import fs4 from "fs";
866
905
  import path4 from "path";
867
- import prompts3 from "prompts";
906
+ import prompts4 from "prompts";
868
907
 
869
908
  // src/cmds/login.ts
870
909
  import pc4 from "picocolors";
871
910
  import readline from "readline";
872
- import prompts2 from "prompts";
911
+ import prompts3 from "prompts";
873
912
 
874
913
  // src/utils/helpers.ts
875
914
  import pc3 from "picocolors";
@@ -1010,7 +1049,7 @@ async function runTokenLogin() {
1010
1049
  await openUrl(url);
1011
1050
  console.log(pc4.gray("Select the detected repo (or scope manually)."));
1012
1051
  console.log(pc4.gray("Permissions: Metadata \u2192 Read-only; Account permissions: None."));
1013
- const { token } = await prompts2(
1052
+ const { token } = await prompts3(
1014
1053
  {
1015
1054
  type: "password",
1016
1055
  name: "token",
@@ -1128,7 +1167,7 @@ async function pushCommand(options) {
1128
1167
  }
1129
1168
  }
1130
1169
  if (!environment && !envFile && isInteractive2 && candidates.length > 0) {
1131
- const { choice } = await prompts3(
1170
+ const { choice } = await prompts4(
1132
1171
  {
1133
1172
  type: "select",
1134
1173
  name: "choice",
@@ -1151,7 +1190,7 @@ async function pushCommand(options) {
1151
1190
  envFile = choice.file;
1152
1191
  environment = choice.env;
1153
1192
  } else if (choice === "custom") {
1154
- const { fileInput } = await prompts3(
1193
+ const { fileInput } = await prompts4(
1155
1194
  {
1156
1195
  type: "text",
1157
1196
  name: "fileInput",
@@ -1201,7 +1240,7 @@ async function pushCommand(options) {
1201
1240
  if (!isInteractive3) {
1202
1241
  throw new Error("Confirmation required. Re-run with --yes in non-interactive environments.");
1203
1242
  }
1204
- const { confirm } = await prompts3(
1243
+ const { confirm } = await prompts4(
1205
1244
  {
1206
1245
  type: "confirm",
1207
1246
  name: "confirm",
@@ -1316,7 +1355,7 @@ async function ensureLoginAndGitHubApp(repoFullName, options = {}) {
1316
1355
  const deviceStart = await startDeviceLogin(repoFullName);
1317
1356
  const installUrl = deviceStart.githubAppInstallUrl || "https://github.com/apps/keyway/installations/new";
1318
1357
  console.log("");
1319
- const { shouldProceed } = await prompts4({
1358
+ const { shouldProceed } = await prompts5({
1320
1359
  type: "confirm",
1321
1360
  name: "shouldProceed",
1322
1361
  message: "Open browser to sign in?",
@@ -1376,7 +1415,7 @@ async function ensureLoginAndGitHubApp(repoFullName, options = {}) {
1376
1415
  console.log(pc6.yellow("\u26A0 GitHub App not installed on this repository"));
1377
1416
  console.log(pc6.gray(" The Keyway GitHub App is required for secure access."));
1378
1417
  console.log("");
1379
- const { shouldInstall } = await prompts4({
1418
+ const { shouldInstall } = await prompts5({
1380
1419
  type: "confirm",
1381
1420
  name: "shouldInstall",
1382
1421
  message: "Open browser to install GitHub App?",
@@ -1445,7 +1484,7 @@ async function ensureGitHubAppInstalledOnly(repoFullName, accessToken) {
1445
1484
  console.log(pc6.gray(` Install the Keyway GitHub App: ${status.installUrl}`));
1446
1485
  throw new Error("GitHub App installation required.");
1447
1486
  }
1448
- const { shouldInstall } = await prompts4({
1487
+ const { shouldInstall } = await prompts5({
1449
1488
  type: "confirm",
1450
1489
  name: "shouldInstall",
1451
1490
  message: "Open browser to install Keyway GitHub App?",
@@ -1519,7 +1558,7 @@ async function initCommand(options = {}) {
1519
1558
  if (envCandidates.length > 0 && isInteractive2) {
1520
1559
  console.log(pc6.gray(` Found ${envCandidates.length} env file(s): ${envCandidates.map((c) => c.file).join(", ")}
1521
1560
  `));
1522
- const { shouldPush } = await prompts4({
1561
+ const { shouldPush } = await prompts5({
1523
1562
  type: "confirm",
1524
1563
  name: "shouldPush",
1525
1564
  message: "Push secrets now?",
@@ -1577,7 +1616,7 @@ async function initCommand(options = {}) {
1577
1616
  import pc7 from "picocolors";
1578
1617
  import fs5 from "fs";
1579
1618
  import path5 from "path";
1580
- import prompts5 from "prompts";
1619
+ import prompts6 from "prompts";
1581
1620
  async function pullCommand(options) {
1582
1621
  try {
1583
1622
  const environment = options.env || "development";
@@ -1602,7 +1641,7 @@ async function pullCommand(options) {
1602
1641
  } else if (!isInteractive2) {
1603
1642
  throw new Error(`File ${envFile} exists. Re-run with --yes to overwrite or choose a different --file.`);
1604
1643
  } else {
1605
- const { confirm } = await prompts5(
1644
+ const { confirm } = await prompts6(
1606
1645
  {
1607
1646
  type: "confirm",
1608
1647
  name: "confirm",
@@ -1962,7 +2001,7 @@ Summary: ${formatSummary(results)}`);
1962
2001
 
1963
2002
  // src/cmds/connect.ts
1964
2003
  import pc9 from "picocolors";
1965
- import prompts6 from "prompts";
2004
+ import prompts7 from "prompts";
1966
2005
  var TOKEN_AUTH_PROVIDERS = ["railway"];
1967
2006
  function getTokenCreationUrl(provider) {
1968
2007
  switch (provider) {
@@ -1979,7 +2018,7 @@ async function connectWithTokenFlow(accessToken, provider, displayName) {
1979
2018
  console.log(pc9.yellow(` Do NOT use "No workspace" - it won't have access to your projects.`));
1980
2019
  }
1981
2020
  await openUrl(tokenUrl);
1982
- const { token } = await prompts6({
2021
+ const { token } = await prompts7({
1983
2022
  type: "password",
1984
2023
  name: "token",
1985
2024
  message: `${displayName} API Token:`
@@ -2056,7 +2095,7 @@ async function connectCommand(provider, options = {}) {
2056
2095
  const { connections } = await getConnections(accessToken);
2057
2096
  const existingConnection = connections.find((c) => c.provider === provider.toLowerCase());
2058
2097
  if (existingConnection) {
2059
- const { reconnect } = await prompts6({
2098
+ const { reconnect } = await prompts7({
2060
2099
  type: "confirm",
2061
2100
  name: "reconnect",
2062
2101
  message: `You're already connected to ${providerInfo.displayName}. Reconnect?`,
@@ -2128,7 +2167,7 @@ async function disconnectCommand(provider, options = {}) {
2128
2167
  return;
2129
2168
  }
2130
2169
  const providerName = provider.charAt(0).toUpperCase() + provider.slice(1);
2131
- const { confirm } = await prompts6({
2170
+ const { confirm } = await prompts7({
2132
2171
  type: "confirm",
2133
2172
  name: "confirm",
2134
2173
  message: `Disconnect from ${providerName}?`,
@@ -2158,7 +2197,7 @@ async function disconnectCommand(provider, options = {}) {
2158
2197
 
2159
2198
  // src/cmds/sync.ts
2160
2199
  import pc10 from "picocolors";
2161
- import prompts7 from "prompts";
2200
+ import prompts8 from "prompts";
2162
2201
  function mapToVercelEnvironment(keywayEnv) {
2163
2202
  const mapping = {
2164
2203
  production: "production",
@@ -2289,7 +2328,7 @@ async function promptProjectSelection(projects, repoFullName) {
2289
2328
  }
2290
2329
  return { title, value: p.id };
2291
2330
  });
2292
- const { projectChoice } = await prompts7({
2331
+ const { projectChoice } = await prompts8({
2293
2332
  type: "select",
2294
2333
  name: "projectChoice",
2295
2334
  message: "Select a project:",
@@ -2320,7 +2359,7 @@ async function syncCommand(provider, options = {}) {
2320
2359
  if (!vaultExists) {
2321
2360
  console.log(pc10.yellow(`
2322
2361
  No vault found for ${repoFullName}.`));
2323
- const { shouldCreate } = await prompts7({
2362
+ const { shouldCreate } = await prompts8({
2324
2363
  type: "confirm",
2325
2364
  name: "shouldCreate",
2326
2365
  message: "Create vault now?",
@@ -2348,7 +2387,7 @@ No vault found for ${repoFullName}.`));
2348
2387
  const providerDisplayName = provider.charAt(0).toUpperCase() + provider.slice(1);
2349
2388
  console.log(pc10.yellow(`
2350
2389
  Not connected to ${providerDisplayName}.`));
2351
- const { shouldConnect } = await prompts7({
2390
+ const { shouldConnect } = await prompts8({
2352
2391
  type: "confirm",
2353
2392
  name: "shouldConnect",
2354
2393
  message: `Connect to ${providerDisplayName} now?`,
@@ -2407,7 +2446,7 @@ Connection to ${providerDisplayName} failed.`));
2407
2446
  } else if (autoMatch && autoMatch.matchType === "partial_name") {
2408
2447
  const partialDisplayName = getProjectDisplayName(autoMatch.project);
2409
2448
  console.log(pc10.yellow(`Detected project: ${partialDisplayName} (partial match)`));
2410
- const { useDetected } = await prompts7({
2449
+ const { useDetected } = await prompts8({
2411
2450
  type: "confirm",
2412
2451
  name: "useDetected",
2413
2452
  message: `Use ${partialDisplayName}?`,
@@ -2431,7 +2470,7 @@ Connection to ${providerDisplayName} failed.`));
2431
2470
  console.log(pc10.yellow(` Project linked to: ${selectedProject.linkedRepo}`));
2432
2471
  }
2433
2472
  console.log("");
2434
- const { continueAnyway } = await prompts7({
2473
+ const { continueAnyway } = await prompts8({
2435
2474
  type: "confirm",
2436
2475
  name: "continueAnyway",
2437
2476
  message: "Continue anyway?",
@@ -2462,7 +2501,7 @@ Connection to ${providerDisplayName} failed.`));
2462
2501
  console.log(pc10.yellow(` Project linked to: ${selectedProject.linkedRepo}`));
2463
2502
  }
2464
2503
  console.log("");
2465
- const { continueAnyway } = await prompts7({
2504
+ const { continueAnyway } = await prompts8({
2466
2505
  type: "confirm",
2467
2506
  name: "continueAnyway",
2468
2507
  message: "Are you sure you want to sync with this project?",
@@ -2483,7 +2522,7 @@ Connection to ${providerDisplayName} failed.`));
2483
2522
  if (needsEnvPrompt || needsDirectionPrompt) {
2484
2523
  if (needsEnvPrompt) {
2485
2524
  const vaultEnvs = await getVaultEnvironments(accessToken, repoFullName);
2486
- const { selectedEnv } = await prompts7({
2525
+ const { selectedEnv } = await prompts8({
2487
2526
  type: "select",
2488
2527
  name: "selectedEnv",
2489
2528
  message: "Keyway environment:",
@@ -2507,7 +2546,7 @@ Connection to ${providerDisplayName} failed.`));
2507
2546
  providerEnv = selectedProject.environments[0];
2508
2547
  console.log(pc10.gray(`Using ${providerName} environment: ${providerEnv}`));
2509
2548
  } else {
2510
- const { selectedProviderEnv } = await prompts7({
2549
+ const { selectedProviderEnv } = await prompts8({
2511
2550
  type: "select",
2512
2551
  name: "selectedProviderEnv",
2513
2552
  message: `${providerName} environment:`,
@@ -2553,7 +2592,7 @@ Connection to ${providerDisplayName} failed.`));
2553
2592
  } else if (diff.providerCount === 0 && diff.keywayCount > 0) {
2554
2593
  defaultDirection = 0;
2555
2594
  }
2556
- const { selectedDirection } = await prompts7({
2595
+ const { selectedDirection } = await prompts8({
2557
2596
  type: "select",
2558
2597
  name: "selectedDirection",
2559
2598
  message: "Sync direction:",
@@ -2584,7 +2623,7 @@ Connection to ${providerDisplayName} failed.`));
2584
2623
  console.log(pc10.yellow(`
2585
2624
  \u26A0\uFE0F Your Keyway vault is empty for "${keywayEnv}", but ${providerName} has ${status.providerSecretCount} secrets.`));
2586
2625
  console.log(pc10.gray(` (Use --environment to sync a different environment)`));
2587
- const { importFirst } = await prompts7({
2626
+ const { importFirst } = await prompts8({
2588
2627
  type: "confirm",
2589
2628
  name: "importFirst",
2590
2629
  message: `Import secrets from ${providerName} first?`,
@@ -2675,7 +2714,7 @@ async function executeSyncOperation(accessToken, repoFullName, connectionId, pro
2675
2714
  console.log("");
2676
2715
  if (!skipConfirm) {
2677
2716
  const target = direction === "push" ? providerName : "Keyway";
2678
- const { confirm } = await prompts7({
2717
+ const { confirm } = await prompts8({
2679
2718
  type: "confirm",
2680
2719
  name: "confirm",
2681
2720
  message: `Apply ${totalChanges} changes to ${target}?`,
@@ -2733,7 +2772,6 @@ ${pc11.gray(TAGLINE)}
2733
2772
  `);
2734
2773
  };
2735
2774
  showBanner();
2736
- warnIfEnvNotGitignored();
2737
2775
  program.name("keyway").description(TAGLINE).version(package_default.version);
2738
2776
  program.command("init").description("Initialize a vault for the current repository").option("--no-login-prompt", "Fail instead of prompting to login if unauthenticated").action(async (options) => {
2739
2777
  await initCommand(options);
@@ -2765,7 +2803,10 @@ program.command("disconnect <provider>").description("Disconnect from a provider
2765
2803
  program.command("sync <provider>").description("Sync secrets with a provider (e.g., vercel)").option("--push", "Export secrets from Keyway to provider").option("--pull", "Import secrets from provider to Keyway").option("-e, --environment <env>", "Keyway environment (default: production)").option("--provider-env <env>", "Provider environment (default: production)").option("--project <project>", "Provider project name or ID").option("--allow-delete", "Allow deleting secrets not in source").option("-y, --yes", "Skip confirmation prompt").option("--no-login-prompt", "Fail instead of prompting to login if unauthenticated").action(async (provider, options) => {
2766
2804
  await syncCommand(provider, options);
2767
2805
  });
2768
- program.parseAsync().catch((error) => {
2806
+ (async () => {
2807
+ await warnIfEnvNotGitignored();
2808
+ await program.parseAsync();
2809
+ })().catch((error) => {
2769
2810
  console.error(pc11.red("Error:"), error.message || error);
2770
2811
  process.exit(1);
2771
2812
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keywaysh/cli",
3
- "version": "0.1.12",
3
+ "version": "0.1.13",
4
4
  "description": "One link to all your secrets",
5
5
  "type": "module",
6
6
  "bin": {