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.
- package/dist/commands/mcpGateway.js +53 -19
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -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,
|
|
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 ||
|
|
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.
|
|
14
|
+
const VERSION = '1.1.14';
|
|
15
15
|
function parseArgs(argv) {
|
|
16
16
|
const flags = {};
|
|
17
17
|
let command = '';
|