fullcourtdefense-cli 1.1.13 → 1.1.14

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.
@@ -80,24 +80,27 @@ function normalizeAgentClient(value, fallback) {
80
80
  return value;
81
81
  return fallback;
82
82
  }
83
+ /**
84
+ * Detect which agent client spawned this gateway process at runtime.
85
+ *
86
+ * Claude Code sets `CLAUDECODE=1` and `CLAUDE_CODE_SESSION_ID` in every stdio
87
+ * MCP subprocess it starts, so a gateway launched by Claude Code can self-label
88
+ * even when it was installed with no explicit `--agent-client`. Cursor and
89
+ * Claude Desktop don't expose a runtime marker, so they fall through to the
90
+ * install-time default baked into the MCP config.
91
+ */
92
+ function detectRuntimeAgentClient() {
93
+ if (process.env.CLAUDECODE === '1' || process.env.CLAUDE_CODE_SESSION_ID)
94
+ return 'claude-code';
95
+ return undefined;
96
+ }
97
+ /** Claude Code passes the session id to spawned MCP servers for correlation. */
98
+ function runtimeSessionId() {
99
+ return firstNonEmpty(process.env.CLAUDE_CODE_SESSION_ID, process.env.FCD_SESSION_ID);
100
+ }
83
101
  function firstNonEmpty(...values) {
84
102
  return values.map(value => value?.trim()).find(Boolean);
85
103
  }
86
- function commandOutput(command, args) {
87
- try {
88
- const result = (0, child_process_1.spawnSync)(command, args, {
89
- encoding: 'utf8',
90
- shell: process.platform === 'win32',
91
- stdio: ['ignore', 'pipe', 'ignore'],
92
- });
93
- if (result.status !== 0)
94
- return undefined;
95
- return result.stdout?.trim() || undefined;
96
- }
97
- catch {
98
- return undefined;
99
- }
100
- }
101
104
  function safeIdentityPart(value) {
102
105
  return value
103
106
  .trim()
@@ -106,8 +109,18 @@ function safeIdentityPart(value) {
106
109
  .replace(/^-+|-+$/g, '')
107
110
  .toLowerCase() || 'local-user';
108
111
  }
112
+ /**
113
+ * Resolve the developer identity for runtime attribution.
114
+ *
115
+ * Same behavior for Cursor, Claude Code, and Claude Desktop: prefer an explicit
116
+ * name (flag or env var IT can set per machine), otherwise fall back to
117
+ * `username@hostname` — exactly like the Cursor hook. We intentionally do NOT
118
+ * guess from git email / npm whoami: neither Cursor nor Claude exposes the
119
+ * account email, so the machine-scoped login is the one stable, predictable,
120
+ * collision-free identity.
121
+ */
109
122
  function detectedDeveloperName(args) {
110
- return firstNonEmpty(args.developerName, process.env.FCD_DEVELOPER_NAME, process.env.FULLCOURTDEFENSE_DEVELOPER_NAME, process.env.AGENTGUARD_DEVELOPER_NAME, process.env.USER_EMAIL, commandOutput('git', ['config', '--get', 'user.email']), commandOutput('npm', ['whoami']), safeUserName()) || 'unknown';
123
+ return firstNonEmpty(args.developerName, process.env.FCD_DEVELOPER_NAME, process.env.FULLCOURTDEFENSE_DEVELOPER_NAME, process.env.AGENTGUARD_DEVELOPER_NAME, machineScopedUser()) || 'unknown';
111
124
  }
112
125
  function defaultAgentName(agentClient, developerName, defaults) {
113
126
  if (defaults.agentName)
@@ -120,7 +133,7 @@ function resolveGatewayConfig(args, config, defaults = {}) {
120
133
  const shieldId = args.shieldId || process.env.FCD_SHIELD_ID || process.env.FULLCOURTDEFENSE_SHIELD_ID || process.env.AGENTGUARD_SHIELD_ID || config.shieldId || home.shieldId;
121
134
  if (!shieldId)
122
135
  throw new Error('Missing Shield ID. Pass --shield-id or run fullcourtdefense configure.');
123
- const agentClient = normalizeAgentClient(args.agentClient || process.env.FCD_AGENT_CLIENT, defaults.agentClient || 'cursor');
136
+ const agentClient = normalizeAgentClient(args.agentClient || process.env.FCD_AGENT_CLIENT || detectRuntimeAgentClient(), defaults.agentClient || 'cursor');
124
137
  const developerName = detectedDeveloperName(args);
125
138
  return {
126
139
  shieldId,
@@ -140,15 +153,17 @@ function resolveGatewayConfig(args, config, defaults = {}) {
140
153
  };
141
154
  }
142
155
  function machineMetadata(developerName, agentClient = 'cursor') {
156
+ const sessionId = runtimeSessionId();
143
157
  return {
144
- developerName: developerName || safeUserName(),
158
+ developerName: developerName || machineScopedUser(),
145
159
  machineName: os.hostname(),
146
160
  os: `${os.platform()} ${os.release()}`,
147
161
  username: safeUserName(),
148
162
  ipAddress: firstPrivateIp(),
149
163
  cwd: process.cwd(),
150
- workspacePath: process.env.WORKSPACE_PATH || process.cwd(),
164
+ workspacePath: process.env.WORKSPACE_PATH || process.env.CLAUDE_PROJECT_DIR || process.cwd(),
151
165
  agentClient,
166
+ ...(sessionId ? { sessionId } : {}),
152
167
  gateway: 'agentguard-mcp-gateway',
153
168
  };
154
169
  }
@@ -170,6 +185,25 @@ function safeUserName() {
170
185
  return 'unknown';
171
186
  }
172
187
  }
188
+ /**
189
+ * Machine-scoped identity: `username@hostname` (e.g. `boazl@BOAZ-PC`).
190
+ *
191
+ * Bare OS usernames collide across machines (every laptop has an `admin` /
192
+ * `user`), which breaks per-person attribution and fails SOC 2 CC6.1
193
+ * non-repudiation. Pairing the login with the hostname makes the identity
194
+ * unique per device with zero config, and matches the Cursor hook's format.
195
+ * For verified corporate identity, set `FCD_DEVELOPER_NAME` (e.g. via MDM/IdP).
196
+ */
197
+ function machineScopedUser() {
198
+ const user = safeUserName();
199
+ try {
200
+ const host = os.hostname();
201
+ return host ? `${user}@${host}` : user;
202
+ }
203
+ catch {
204
+ return user;
205
+ }
206
+ }
173
207
  function summarizeToolArgs(args, developerName, agentClient = 'cursor') {
174
208
  const out = { ...machineMetadata(developerName, agentClient) };
175
209
  for (const [key, value] of Object.entries(args || {})) {
package/dist/index.js CHANGED
@@ -11,7 +11,7 @@ const discover_1 = require("./commands/discover");
11
11
  const hook_1 = require("./commands/hook");
12
12
  const installCursorHook_1 = require("./commands/installCursorHook");
13
13
  const mcpGateway_1 = require("./commands/mcpGateway");
14
- const VERSION = '1.1.13';
14
+ const VERSION = '1.1.14';
15
15
  function parseArgs(argv) {
16
16
  const flags = {};
17
17
  let command = '';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fullcourtdefense-cli",
3
- "version": "1.1.13",
3
+ "version": "1.1.14",
4
4
  "description": "Full Court Defense CLI — security scanning for AI agents from your terminal",
5
5
  "main": "dist/index.js",
6
6
  "bin": {