@envseal/cli 0.1.3 → 0.1.4

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.
package/dist/bin.js CHANGED
@@ -13,7 +13,7 @@ import { doctor } from './commands/doctor.js';
13
13
  import { revoke } from './commands/revoke.js';
14
14
  import { mcp } from './commands/mcp.js';
15
15
  import { init } from './commands/init.js';
16
- const VERSION = '0.1.3';
16
+ const VERSION = '0.1.4';
17
17
  async function main() {
18
18
  const argv = process.argv.slice(2);
19
19
  if (argv.length === 0 || argv[0] === '--help' || argv[0] === '-h') {
@@ -112,7 +112,7 @@ async function main() {
112
112
  finish(EXIT.USAGE);
113
113
  break;
114
114
  }
115
- await revoke(root, key, json);
115
+ await revoke(root, key, json, parsed.flags.yes === true);
116
116
  break;
117
117
  }
118
118
  case 'mcp': {
@@ -12,6 +12,7 @@ type BrokerOpts = ConstructorParameters<typeof Broker>[0];
12
12
  export declare function createBroker(root: string, opts?: {
13
13
  onConfirm?: BrokerOpts['onConfirm'];
14
14
  onApprovalNeeded?: BrokerOpts['onApprovalNeeded'];
15
+ onRevokeConfirm?: BrokerOpts['onRevokeConfirm'];
15
16
  }): Promise<Broker>;
16
17
  /**
17
18
  * Whether there is a human we could put a question to.
package/dist/cli-utils.js CHANGED
@@ -25,6 +25,7 @@ export async function createBroker(root, opts) {
25
25
  prompter,
26
26
  onConfirm: opts?.onConfirm,
27
27
  onApprovalNeeded: opts?.onApprovalNeeded,
28
+ onRevokeConfirm: opts?.onRevokeConfirm,
28
29
  });
29
30
  registerDisposable(() => broker.dispose());
30
31
  return broker;
@@ -206,10 +207,12 @@ file permissions, missing required keys.
206
207
 
207
208
  --json Output as JSON.
208
209
  --project <path> Project root (default: auto-detect).`,
209
- revoke: `Usage: envseal revoke <KEY> [--json] [--project <path>]
210
+ revoke: `Usage: envseal revoke <KEY> [--yes] [--json] [--project <path>]
210
211
 
211
212
  Remove a key from its sink and report the provider's rotation URL.
213
+ Asks for confirmation first; --yes (or ENVSEAL_ASSUME_YES=1) pre-approves it.
212
214
 
215
+ --yes Skip the confirmation prompt.
213
216
  --json Output as JSON.
214
217
  --project <path> Project root (default: auto-detect).`,
215
218
  mcp: `Usage: envseal mcp
@@ -1,7 +1,7 @@
1
1
  import { existsSync, statSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
- import { readFileSync } from 'node:fs';
4
3
  import { SepError } from '@envseal/protocol';
4
+ import { inspectDotenvGitSafety, projectPaths } from '@envseal/core';
5
5
  import { emit, fail } from '../output.js';
6
6
  import { EXIT } from '../exit-codes.js';
7
7
  import { detectHost } from '../host.js';
@@ -25,12 +25,9 @@ export async function doctor(root, json) {
25
25
  const status = await broker.describe();
26
26
  const gitignorePath = join(root, '.gitignore');
27
27
  const envPath = join(root, '.env');
28
- // Check gitignore
29
- let gitignoreCovers = false;
30
- if (existsSync(gitignorePath)) {
31
- const gitignoreContent = readFileSync(gitignorePath, 'utf-8');
32
- gitignoreCovers = gitignoreContent.includes('.env');
33
- }
28
+ const gitSafety = inspectDotenvGitSafety(projectPaths(root));
29
+ const gitignoreCovers = gitSafety.ignored;
30
+ const hookFailClosed = process.env.ENVSEAL_HOOK_FAIL_CLOSED === '1';
34
31
  // Check .env permissions
35
32
  let envFileOk = false;
36
33
  if (existsSync(envPath)) {
@@ -54,9 +51,10 @@ export async function doctor(root, json) {
54
51
  },
55
52
  envFile: {
56
53
  exists: existsSync(envPath),
57
- isTracked: false,
54
+ isTracked: gitSafety.tracked,
58
55
  permissionsOk: envFileOk,
59
56
  },
57
+ hookFailClosed,
60
58
  missingRequiredCount: status.missingRequired.length,
61
59
  missingRequired: status.missingRequired,
62
60
  };
@@ -66,6 +64,7 @@ export async function doctor(root, json) {
66
64
  console.log(` ${host.reason}`);
67
65
  console.log(` ${host.recommendation}`);
68
66
  console.log(`Gitignore covers .env: ${gitignoreCovers ? 'yes' : 'no'}`);
67
+ console.log(`Hook on internal error: ${hookFailClosed ? 'fail-closed' : 'fail-open (default)'}`);
69
68
  console.log(`Missing required keys: ${status.missingRequired.length}`);
70
69
  if (status.missingRequired.length > 0) {
71
70
  for (const key of status.missingRequired) {
@@ -1,2 +1,2 @@
1
- export declare function revoke(root: string, key: string, json: boolean): Promise<void>;
1
+ export declare function revoke(root: string, key: string, json: boolean, assumeYes?: boolean): Promise<void>;
2
2
  //# sourceMappingURL=revoke.d.ts.map
@@ -1,10 +1,48 @@
1
+ import { createInterface } from 'node:readline';
2
+ import { SepError } from '@envseal/protocol';
3
+ import { revokeConfirmationBody } from '@envseal/core';
1
4
  import { emit, fail } from '../output.js';
2
5
  import { EXIT } from '../exit-codes.js';
3
6
  import { createBroker } from '../cli-utils.js';
4
7
  import { finish } from '../exit.js';
5
- export async function revoke(root, key, json) {
8
+ /**
9
+ * Ask the user to approve removing stored credentials.
10
+ *
11
+ * Without this the broker's revoke confirmation callback is absent and
12
+ * `revoke` always throws SEP_CONFIRMATION_DENIED. Mirrors run.ts: same
13
+ * TTY/--yes/ENVSEAL_ASSUME_YES gate, same fail-closed default for headless.
14
+ */
15
+ async function confirmRevokeInteractive(keys) {
16
+ if (process.env.ENVSEAL_ASSUME_YES === '1')
17
+ return true;
18
+ if (!process.stdin.isTTY) {
19
+ throw new SepError({
20
+ code: 'SEP_NO_INTERACTIVE_SURFACE',
21
+ userMessage: 'envseal revoke needs confirmation before removing stored credentials, but there is no terminal to ask on. ' +
22
+ 'Nothing was removed. Re-run it yourself in an interactive shell to review and ' +
23
+ 'approve the revocation; see docs/ci.md for the supported headless pipeline setup.',
24
+ });
25
+ }
26
+ const body = revokeConfirmationBody(keys, process.cwd());
27
+ process.stderr.write(`\n${body}\n\n`);
28
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
29
+ try {
30
+ const answer = await new Promise((resolve) => {
31
+ rl.question('Continue? [y/N] ', resolve);
32
+ });
33
+ return /^y(es)?$/i.test(answer.trim());
34
+ }
35
+ finally {
36
+ rl.close();
37
+ }
38
+ }
39
+ export async function revoke(root, key, json, assumeYes = false) {
6
40
  try {
7
- const broker = await createBroker(root);
41
+ const broker = await createBroker(root, {
42
+ onRevokeConfirm: assumeYes
43
+ ? async () => true
44
+ : async (keys) => confirmRevokeInteractive(keys),
45
+ });
8
46
  const results = await broker.revoke({
9
47
  keys: [key],
10
48
  });
@@ -22,6 +22,7 @@ export function exitCodeForError(e) {
22
22
  case 'SEP_GITIGNORE_UNSAFE':
23
23
  case 'SEP_PROBE_NOT_APPROVED':
24
24
  case 'SEP_VALUE_IN_REQUEST':
25
+ case 'SEP_PATTERN_UNSAFE':
25
26
  return EXIT.USAGE;
26
27
  case 'SEP_NO_INTERACTIVE_SURFACE':
27
28
  return EXIT.NO_SURFACE;
@@ -36,6 +37,7 @@ export function exitCodeForError(e) {
36
37
  case 'SEP_TICKET_UNKNOWN':
37
38
  case 'SEP_TARGET_CHANGED':
38
39
  case 'SEP_CONFIRMATION_DENIED':
40
+ case 'SEP_KEYS_MISSING':
39
41
  return EXIT.UNSATISFIED;
40
42
  default: {
41
43
  const _exhaustive = code;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@envseal/cli",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "main": "./dist/index.js",
@@ -23,13 +23,13 @@
23
23
  "provenance": true
24
24
  },
25
25
  "dependencies": {
26
- "@envseal/core": "0.1.3",
27
- "@envseal/prompters": "0.1.3",
28
- "@envseal/protocol": "0.1.3",
29
- "@envseal/registry": "0.1.3",
30
- "@envseal/detector": "0.1.3",
31
- "@envseal/mcp-server": "0.1.3",
32
- "@envseal/http-server": "0.1.3"
26
+ "@envseal/protocol": "0.1.4",
27
+ "@envseal/core": "0.1.4",
28
+ "@envseal/prompters": "0.1.4",
29
+ "@envseal/mcp-server": "0.1.4",
30
+ "@envseal/detector": "0.1.4",
31
+ "@envseal/http-server": "0.1.4",
32
+ "@envseal/registry": "0.1.4"
33
33
  },
34
34
  "repository": {
35
35
  "type": "git",