@envseal/cli 0.1.6 → 0.1.7

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.
@@ -1,7 +1,7 @@
1
1
  import { existsSync, statSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import { SepError } from '@envseal/protocol';
4
- import { inspectDotenvGitSafety, loadManifest, projectPaths, readHookHeartbeat } from '@envseal/core';
4
+ import { inspectDotenvGitSafety, loadManifest, projectPaths, readHookDecisions, readHookHeartbeat } 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';
@@ -49,6 +49,7 @@ export async function doctor(root, json) {
49
49
  const egress = manifest?.policy?.egress;
50
50
  const inspection = inspectPrimaryHostWiring(root, host.id, { probe: true });
51
51
  const hookLastRan = readHookHeartbeat(root);
52
+ const hookDecisions = readHookDecisions(root);
52
53
  const output = {
53
54
  projectRoot: root,
54
55
  manifestPath,
@@ -72,6 +73,7 @@ export async function doctor(root, json) {
72
73
  egressPolicy: egress ?? { mode: 'warn', allow: [] },
73
74
  hookFailClosed,
74
75
  hookLastRan,
76
+ hookDecisions,
75
77
  missingRequiredCount: status.missingRequired.length,
76
78
  missingRequired: status.missingRequired,
77
79
  rotationOverdue: status.entries
@@ -85,6 +87,9 @@ export async function doctor(root, json) {
85
87
  status: inspection.mcp.status,
86
88
  message: inspection.mcp.message,
87
89
  commandOk: inspection.mcp.commandOk,
90
+ ...(inspection.mcp.otherServers === undefined
91
+ ? {}
92
+ : { otherServers: inspection.mcp.otherServers }),
88
93
  },
89
94
  }),
90
95
  };
@@ -102,6 +107,13 @@ export async function doctor(root, json) {
102
107
  console.log(`Egress policy: ${egress?.mode === 'allowlist' ? `allowlist (${egress.allow.length} allowed host${egress.allow.length === 1 ? '' : 's'})` : 'warn (default)'}`);
103
108
  console.log(`Hook on internal error: ${hookFailClosed ? 'fail-closed' : 'fail-open (default)'}`);
104
109
  console.log(`Hook heartbeat: ${describeHeartbeatAge(hookLastRan)}`);
110
+ if (hookDecisions !== null) {
111
+ console.log(`Hook decisions (approx): ${hookDecisions.allow} allow, ${hookDecisions.deny} deny`);
112
+ }
113
+ const siblings = inspection.mcp?.otherServers ?? [];
114
+ if (siblings.length > 0) {
115
+ console.log(`Other MCP servers (outside envseal read protection): ${siblings.join(', ')}`);
116
+ }
105
117
  console.log(`Missing required keys: ${status.missingRequired.length}`);
106
118
  if (status.missingRequired.length > 0) {
107
119
  for (const key of status.missingRequired) {
@@ -79,11 +79,20 @@ export function inspectCopilotSettings(root, options = {}) {
79
79
  };
80
80
  }
81
81
  const entry = list.find((item) => entryName(item) === ENVSEAL_MCP_SERVER_NAME);
82
+ // Names only, never entry values: a sibling argv or env block can hold a
83
+ // real credential, and doctor output must not become a new exfil channel.
84
+ // List configs can repeat a name; report each server once.
85
+ const otherServers = [
86
+ ...new Set(list
87
+ .map((item) => entryName(item))
88
+ .filter((name) => name !== undefined && name !== ENVSEAL_MCP_SERVER_NAME)),
89
+ ].sort();
82
90
  if (entry === undefined || isEmptyEnvsealStub(entry) || !looksLikeEnvsealServer(entry)) {
83
91
  return {
84
92
  wired: false,
85
93
  status: entry === undefined ? 'missing' : 'stub',
86
94
  commandOk: null,
95
+ otherServers,
87
96
  message: `.vscode/settings.json github.copilot.mcp has no working envseal-mcp. ${hint}`,
88
97
  };
89
98
  }
@@ -94,6 +103,6 @@ export function inspectCopilotSettings(root, options = {}) {
94
103
  message =
95
104
  'Copilot MCP is configured, but the launch command did not report a version. Run `envseal init`. [VERIFY]';
96
105
  }
97
- return { wired: true, status: 'wired', commandOk, message };
106
+ return { wired: true, status: 'wired', commandOk, otherServers, message };
98
107
  }
99
108
  //# sourceMappingURL=copilot.js.map
@@ -13,7 +13,19 @@ export type McpInspection = {
13
13
  message: string;
14
14
  /** null when the launch command was not probed (npx is not side-effect free). */
15
15
  commandOk: boolean | null;
16
+ /**
17
+ * Names of co-registered MCP servers besides envseal-mcp. Names only, never
18
+ * entry values: a sibling argv or env block can hold a real credential, and
19
+ * doctor output must not become a new exfil channel. Undefined when the host
20
+ * config shape carries no enumerable server list.
21
+ */
22
+ otherServers?: string[];
16
23
  };
24
+ /**
25
+ * Sorted names of every server in a `mcpServers`-style map except envseal-mcp.
26
+ * Keys only: callers must never forward the entry values.
27
+ */
28
+ export declare function siblingServerNames(servers: Record<string, unknown>): string[];
17
29
  /**
18
30
  * Launch argv a host can spawn without a global `envseal-mcp` on PATH.
19
31
  * Project MCP uses the workspace as cwd — never bake `--project` in.
@@ -4,6 +4,15 @@ import { spawnSync } from 'node:child_process';
4
4
  export const ENVSEAL_MCP_PACKAGE = '@envseal/mcp-server';
5
5
  export const ENVSEAL_MCP_SERVER_NAME = 'envseal-mcp';
6
6
  export const NPX_ARGS = ['-y', ENVSEAL_MCP_PACKAGE];
7
+ /**
8
+ * Sorted names of every server in a `mcpServers`-style map except envseal-mcp.
9
+ * Keys only: callers must never forward the entry values.
10
+ */
11
+ export function siblingServerNames(servers) {
12
+ return Object.keys(servers)
13
+ .filter((name) => name !== ENVSEAL_MCP_SERVER_NAME)
14
+ .sort();
15
+ }
7
16
  /**
8
17
  * Launch argv a host can spawn without a global `envseal-mcp` on PATH.
9
18
  * Project MCP uses the workspace as cwd — never bake `--project` in.
@@ -203,11 +212,13 @@ export function inspectMcpServersFile(path, label, options = {}) {
203
212
  }
204
213
  const entry = servers[ENVSEAL_MCP_SERVER_NAME];
205
214
  const kind = classifyEntry(entry);
215
+ const otherServers = siblingServerNames(servers);
206
216
  if (kind === 'missing') {
207
217
  return {
208
218
  wired: false,
209
219
  status: 'missing',
210
220
  commandOk: null,
221
+ otherServers,
211
222
  message: `${label} has no envseal-mcp. ${hint}`,
212
223
  };
213
224
  }
@@ -216,6 +227,7 @@ export function inspectMcpServersFile(path, label, options = {}) {
216
227
  wired: false,
217
228
  status: 'stub',
218
229
  commandOk: null,
230
+ otherServers,
219
231
  message: `${label} envseal-mcp is the empty envseal-mcp stub (not on PATH for the host). ${hint}`,
220
232
  };
221
233
  }
@@ -225,7 +237,7 @@ export function inspectMcpServersFile(path, label, options = {}) {
225
237
  if (commandOk === false) {
226
238
  message = `MCP is configured in ${label}, but the launch command did not report a version. Run \`envseal init\` if the host cannot connect.`;
227
239
  }
228
- return { wired: true, status: 'wired', commandOk, message };
240
+ return { wired: true, status: 'wired', commandOk, otherServers, message };
229
241
  }
230
242
  /** Map an inspection to the doctor `agentWiring.mcp` field. */
231
243
  export function mcpWiringState(inspection) {
@@ -1,6 +1,6 @@
1
1
  import { existsSync, readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
- import { ENVSEAL_MCP_SERVER_NAME, isRecord, mcpLaunch, nextEnvsealEntry, parseJsonObject, writeJson, classifyEntry, probeVersion, } from './mcp.js';
3
+ import { ENVSEAL_MCP_SERVER_NAME, isRecord, mcpLaunch, nextEnvsealEntry, parseJsonObject, writeJson, classifyEntry, probeVersion, siblingServerNames, } from './mcp.js';
4
4
  function zedHint(platform) {
5
5
  const launch = mcpLaunch(platform);
6
6
  return `Run \`envseal init\` to merge { "mcp": { "${ENVSEAL_MCP_SERVER_NAME}": ${JSON.stringify(launch)} } } into .zed/settings.json. [VERIFY]`;
@@ -69,11 +69,13 @@ export function inspectZedSettings(root, options = {}) {
69
69
  }
70
70
  const entry = mcp[ENVSEAL_MCP_SERVER_NAME];
71
71
  const kind = classifyEntry(entry);
72
+ const otherServers = siblingServerNames(mcp);
72
73
  if (kind === 'missing') {
73
74
  return {
74
75
  wired: false,
75
76
  status: 'missing',
76
77
  commandOk: null,
78
+ otherServers,
77
79
  message: `.zed/settings.json has no envseal-mcp. ${hint}`,
78
80
  };
79
81
  }
@@ -82,6 +84,7 @@ export function inspectZedSettings(root, options = {}) {
82
84
  wired: false,
83
85
  status: 'stub',
84
86
  commandOk: null,
87
+ otherServers,
85
88
  message: `.zed/settings.json envseal-mcp is the empty stub. ${hint}`,
86
89
  };
87
90
  }
@@ -92,6 +95,6 @@ export function inspectZedSettings(root, options = {}) {
92
95
  message =
93
96
  'Zed MCP is configured, but the launch command did not report a version. Run `envseal init`. [VERIFY]';
94
97
  }
95
- return { wired: true, status: 'wired', commandOk, message };
98
+ return { wired: true, status: 'wired', commandOk, otherServers, message };
96
99
  }
97
100
  //# sourceMappingURL=zed.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@envseal/cli",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
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/protocol": "0.1.6",
27
- "@envseal/prompters": "0.1.6",
28
- "@envseal/registry": "0.1.6",
29
- "@envseal/core": "0.1.6",
30
- "@envseal/mcp-server": "0.1.6",
31
- "@envseal/http-server": "0.1.6",
32
- "@envseal/detector": "0.1.6"
26
+ "@envseal/core": "0.1.7",
27
+ "@envseal/prompters": "0.1.7",
28
+ "@envseal/protocol": "0.1.7",
29
+ "@envseal/detector": "0.1.7",
30
+ "@envseal/registry": "0.1.7",
31
+ "@envseal/http-server": "0.1.7",
32
+ "@envseal/mcp-server": "0.1.7"
33
33
  },
34
34
  "repository": {
35
35
  "type": "git",