@akshxy/envgit 0.5.1 → 0.6.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.
@@ -0,0 +1,83 @@
1
+ import { search, input, password, confirm } from '@inquirer/prompts';
2
+
3
+ // Fuzzy match — returns true if all chars of query appear in order in str
4
+ function fuzzy(str, query) {
5
+ if (!query) return true;
6
+ let si = 0;
7
+ const s = str.toLowerCase();
8
+ const q = query.toLowerCase();
9
+ for (let i = 0; i < q.length; i++) {
10
+ si = s.indexOf(q[i], si);
11
+ if (si === -1) return false;
12
+ si++;
13
+ }
14
+ return true;
15
+ }
16
+
17
+ /**
18
+ * Interactive fuzzy key picker.
19
+ * @param {Record<string,string>} vars — existing key/value pairs
20
+ * @param {string} message
21
+ * @param {{ allowNew?: boolean }} opts
22
+ * @returns {Promise<string>} chosen key name
23
+ */
24
+ export async function pickKey(vars, message = 'Select a key', { allowNew = false } = {}) {
25
+ const keys = Object.keys(vars).sort();
26
+
27
+ return search({
28
+ message,
29
+ source(term) {
30
+ const matches = keys.filter(k => fuzzy(k, term));
31
+ const choices = matches.map(k => ({ name: k, value: k }));
32
+
33
+ // If the typed term isn't an exact match and allowNew is set, offer to create it
34
+ if (allowNew && term && !keys.includes(term)) {
35
+ choices.unshift({ name: `+ create "${term}"`, value: term });
36
+ }
37
+
38
+ return choices;
39
+ },
40
+ });
41
+ }
42
+
43
+ /**
44
+ * Interactive fuzzy environment picker.
45
+ * @param {string[]} envs
46
+ * @param {string} message
47
+ * @returns {Promise<string>} chosen env name
48
+ */
49
+ export async function pickEnv(envs, message = 'Select an environment') {
50
+ return search({
51
+ message,
52
+ source(term) {
53
+ return envs
54
+ .filter(e => fuzzy(e, term))
55
+ .map(e => ({ name: e, value: e }));
56
+ },
57
+ });
58
+ }
59
+
60
+ /**
61
+ * Prompt for a secret value (masked by default).
62
+ */
63
+ export async function promptValue(keyName, existing) {
64
+ const msg = existing !== undefined
65
+ ? `New value for ${keyName} (current: ${'*'.repeat(Math.min(existing.length, 8))})`
66
+ : `Value for ${keyName}`;
67
+
68
+ return password({ message: msg, mask: false });
69
+ }
70
+
71
+ /**
72
+ * Prompt for a plain (visible) string.
73
+ */
74
+ export async function promptInput(message, defaultValue) {
75
+ return input({ message, default: defaultValue });
76
+ }
77
+
78
+ /**
79
+ * Prompt for confirmation.
80
+ */
81
+ export async function promptConfirm(message, defaultValue = false) {
82
+ return confirm({ message, default: defaultValue });
83
+ }
package/src/keystore.js CHANGED
@@ -46,7 +46,7 @@ export function loadKey(projectRoot) {
46
46
  }
47
47
  // Project found, but this machine doesn't have the key yet
48
48
  console.error(
49
- `No key found for this project. Ask your team for the key, then run:\n envgit keygen --set <key>`
49
+ `No key found for this project. Ask a teammate to run:\n envgit share\nthen run the join command they send you.`
50
50
  );
51
51
  process.exit(1);
52
52
  }
@@ -59,7 +59,7 @@ export function loadKey(projectRoot) {
59
59
  }
60
60
 
61
61
  console.error(
62
- `No key found for this project. Ask your team for the key, then run:\n envgit keygen --set <key>`
62
+ `No key found for this project. Ask a teammate to run:\n envgit share\nthen run the join command they send you.`
63
63
  );
64
64
  process.exit(1);
65
65
  }
package/src/ui.js CHANGED
@@ -1,9 +1,34 @@
1
1
  import chalk from 'chalk';
2
2
 
3
- export const ok = (msg) => console.log(chalk.green(`✓ ${msg}`));
4
- export const fail = (msg) => console.log(chalk.red(`✗ ${msg}`));
5
- export const warn = (msg) => console.log(chalk.yellow(`⚠ ${msg}`));
3
+ export const ok = (msg) => console.log(chalk.green(`✓ ${msg}`));
4
+ export const fail = (msg) => console.log(chalk.red(`✗ ${msg}`));
5
+ export const warn = (msg) => console.log(chalk.yellow(`⚠ ${msg}`));
6
6
  export const fatal = (msg) => { console.error(chalk.red(`✗ ${msg}`)); process.exit(1); };
7
+ export const dim = (text) => chalk.dim(text);
8
+ export const bold = (text) => chalk.bold(text);
9
+
10
+ // Plain label (non-env uses)
7
11
  export const label = (text) => chalk.cyan(`[${text}]`);
8
- export const dim = (text) => chalk.dim(text);
9
- export const bold = (text) => chalk.bold(text);
12
+
13
+ // Well-known env signals only these get special colors
14
+ const PROD_RE = /^(prod|production)$/i;
15
+ const STAGING_RE = /^(staging|stage|uat|preprod|pre-prod|preview)$/i;
16
+ const DEV_RE = /^(dev|development|local|localhost)$/i;
17
+
18
+ // Any other env name gets a stable unique color derived from the name —
19
+ // "john", "feature-x", "client-abc", "solo" each always render the same color
20
+ const PALETTE = [
21
+ 'cyan', 'blue', 'magenta', 'white',
22
+ 'cyanBright', 'blueBright', 'magentaBright', 'whiteBright',
23
+ ];
24
+ function hashColor(name) {
25
+ const n = [...name].reduce((acc, c) => acc * 31 + c.charCodeAt(0), 7);
26
+ return PALETTE[Math.abs(n) % PALETTE.length];
27
+ }
28
+
29
+ export function envLabel(name) {
30
+ if (PROD_RE.test(name)) return chalk.bold.red(`[${name}]`);
31
+ if (STAGING_RE.test(name)) return chalk.bold.yellow(`[${name}]`);
32
+ if (DEV_RE.test(name)) return chalk.bold.green(`[${name}]`);
33
+ return chalk.bold[hashColor(name)](`[${name}]`);
34
+ }
@@ -1,71 +0,0 @@
1
- import { generateKey } from '../crypto.js';
2
- import { findProjectRoot, saveKey, loadKey, globalKeyPath } from '../keystore.js';
3
- import { loadConfig } from '../config.js';
4
- import { ok, warn, fatal, bold, dim } from '../ui.js';
5
-
6
- export async function keygen(options) {
7
- const projectRoot = findProjectRoot();
8
-
9
- if (options.show) {
10
- if (!projectRoot) {
11
- fatal('No envgit project found — cannot show key.');
12
- }
13
- let key;
14
- try {
15
- key = loadKey(projectRoot);
16
- } catch (e) {
17
- fatal(e.message);
18
- }
19
- const hint = key.slice(0, 8);
20
- console.log('');
21
- console.log(bold('Current key:'));
22
- console.log(` ${key}`);
23
- console.log('');
24
- console.log(dim(`Hint (first 8 chars): ${hint}`));
25
- console.log(dim('Share via a secure channel (not git, not chat).'));
26
- console.log(dim('Teammate saves it with: envgit keygen --set <key>'));
27
- console.log('');
28
- return;
29
- }
30
-
31
- if (options.set) {
32
- const key = options.set;
33
- const decoded = Buffer.from(key, 'base64');
34
- if (decoded.length !== 32) {
35
- fatal(`Invalid key — must decode to exactly 32 bytes (got ${decoded.length}). Generate one with: envgit keygen`);
36
- }
37
- if (!projectRoot) {
38
- fatal('No envgit project found. Run envgit init first, or clone a repo that uses envgit.');
39
- }
40
- const keyPath = saveKey(projectRoot, key);
41
- ok(`Key saved for this project`);
42
- console.log(dim(` Stored at: ${keyPath}`));
43
- console.log(dim(` Hint: ${key.slice(0, 8)}`));
44
- console.log('');
45
- console.log(dim('Run `envgit verify` to confirm it works.'));
46
- console.log('');
47
- return;
48
- }
49
-
50
- // Generate new key
51
- const key = generateKey();
52
- const hint = key.slice(0, 8);
53
-
54
- if (projectRoot) {
55
- const keyPath = saveKey(projectRoot, key);
56
- ok(`New key generated`);
57
- console.log(dim(` Stored at: ${keyPath}`));
58
- } else {
59
- console.log('');
60
- console.log(bold('Generated key (no project found — not saved):'));
61
- }
62
-
63
- console.log('');
64
- console.log(bold('Key:'));
65
- console.log(` ${key}`);
66
- console.log('');
67
- console.log(dim(`Hint (first 8 chars): ${hint}`));
68
- console.log(dim('Share via a secure channel (not git, not chat).'));
69
- console.log(dim('Teammate saves it with: envgit keygen --set <key>'));
70
- console.log('');
71
- }