@envseal/cli 0.1.2 → 0.1.3

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.2';
16
+ const VERSION = '0.1.3';
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') {
@@ -1,5 +1,6 @@
1
1
  import { createInterface } from 'node:readline';
2
2
  import { SepError } from '@envseal/protocol';
3
+ import { useConfirmationBody } from '@envseal/core';
3
4
  import { emit, fail } from '../output.js';
4
5
  import { createBroker } from '../cli-utils.js';
5
6
  import { loadManifest, projectPaths } from '@envseal/core';
@@ -12,9 +13,11 @@ import { finish } from '../exit.js';
12
13
  * prompt reads from the controlling terminal, so it still works when stdout is
13
14
  * being piped.
14
15
  *
15
- * Fails closed: no TTY and no --yes means no approval. That is the right
16
- * default, because the alternative is a CI job silently handing credentials to
17
- * whatever it was told to run.
16
+ * The dialog body comes from the SAME useConfirmationBody the MCP and SDK
17
+ * bindings render one escaping/fingerprint implementation for every
18
+ * surface. Fails closed: no TTY and no --yes means no approval. That is the
19
+ * right default, because the alternative is a CI job silently handing
20
+ * credentials to whatever it was told to run.
18
21
  */
19
22
  async function confirmInteractive(info) {
20
23
  if (process.env.ENVSEAL_ASSUME_YES === '1')
@@ -23,23 +26,18 @@ async function confirmInteractive(info) {
23
26
  // Distinct from a refusal. Reporting "the user denied the confirmation" when
24
27
  // no human was ever asked is actively misleading, and it is the shell-only
25
28
  // agents of Tier 4 that hit this path — the ones this binding exists for.
29
+ // Deliberately silent about HOW to proceed headlessly: this message lands in
30
+ // agent-visible stderr, and advertising the bypass here handed every reader
31
+ // a one-liner to skip the dialog entirely (documented in docs/ci.md instead).
26
32
  throw new SepError({
27
33
  code: 'SEP_NO_INTERACTIVE_SURFACE',
28
34
  userMessage: 'envseal run needs confirmation before injecting secrets, but there is no terminal to ask on. ' +
29
- 'Re-run in an interactive shell, or pass --yes (or set ENVSEAL_ASSUME_YES=1) to approve non-interactively.',
35
+ 'Nothing was run and no value was read. Re-run it yourself in an interactive shell to review and ' +
36
+ 'approve the command; see docs/ci.md for the supported headless pipeline setup.',
30
37
  });
31
38
  }
32
- const lines = [
33
- '',
34
- 'envseal is about to run a command with secrets in its environment:',
35
- ` command: ${info.command.join(' ')}`,
36
- ` keys: ${info.keys.join(', ')}`,
37
- ];
38
- if (info.networkEgress) {
39
- lines.push(' WARNING: this command can make network requests, so it could send', ' these values somewhere. Only continue if you trust it.');
40
- }
41
- lines.push('');
42
- process.stderr.write(lines.join('\n'));
39
+ const body = useConfirmationBody(info, process.cwd());
40
+ process.stderr.write(`\n${body}\n\n`);
43
41
  const rl = createInterface({ input: process.stdin, output: process.stderr });
44
42
  try {
45
43
  const answer = await new Promise((resolve) => {
@@ -34,6 +34,7 @@ export function exitCodeForError(e) {
34
34
  case 'SEP_FORMAT_INVALID':
35
35
  case 'SEP_RATE_LIMITED':
36
36
  case 'SEP_TICKET_UNKNOWN':
37
+ case 'SEP_TARGET_CHANGED':
37
38
  case 'SEP_CONFIRMATION_DENIED':
38
39
  return EXIT.UNSATISFIED;
39
40
  default: {
@@ -17,14 +17,22 @@ export function createStubPrompter(value) {
17
17
  return {
18
18
  id: 'ide',
19
19
  available: async () => true,
20
- prompt: async (req) => ({
21
- ticket: req.ticket,
22
- results: req.keys.map((k) => ({
23
- key: k.key,
24
- outcome: 'entered',
25
- value: secretFromUtf8(value),
26
- })),
27
- }),
20
+ prompt: async (req) => {
21
+ // Announce the stub on stderr, mirroring probe-approval.ts's
22
+ // ENVSEAL_TEST_APPROVAL notice: any answer that did not come from a
23
+ // human must be visible in the transcript. A model that discovers the
24
+ // two-variable gate cannot use it as a silent approval oracle.
25
+ process.stderr.write(`ENVSEAL_TEST_MODE: prompter is a STUB — key(s) [${req.keys.map((k) => k.key).join(', ')}] ` +
26
+ `answered from ENVSEAL_TEST_PROMPTER_VALUE with no UI.\n`);
27
+ return {
28
+ ticket: req.ticket,
29
+ results: req.keys.map((k) => ({
30
+ key: k.key,
31
+ outcome: 'entered',
32
+ value: secretFromUtf8(value),
33
+ })),
34
+ };
35
+ },
28
36
  cancel: async () => {
29
37
  /* nothing to tear down */
30
38
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@envseal/cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
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/prompters": "0.1.2",
27
- "@envseal/registry": "0.1.2",
28
- "@envseal/detector": "0.1.2",
29
- "@envseal/mcp-server": "0.1.2",
30
- "@envseal/protocol": "0.1.2",
31
- "@envseal/core": "0.1.2",
32
- "@envseal/http-server": "0.1.2"
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"
33
33
  },
34
34
  "repository": {
35
35
  "type": "git",