@glasstrace/sdk 0.2.3 → 0.4.0
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/{chunk-CUFIV225.js → chunk-EC5IINUT.js} +86 -201
- package/dist/chunk-EC5IINUT.js.map +1 -0
- package/dist/chunk-STECO33B.js +675 -0
- package/dist/chunk-STECO33B.js.map +1 -0
- package/dist/chunk-TJ6ETQPH.js +172 -0
- package/dist/chunk-TJ6ETQPH.js.map +1 -0
- package/dist/cli/init.cjs +12476 -10887
- package/dist/cli/init.cjs.map +1 -1
- package/dist/cli/init.js +193 -184
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/mcp-add.cjs +14651 -0
- package/dist/cli/mcp-add.cjs.map +1 -0
- package/dist/cli/mcp-add.d.cts +46 -0
- package/dist/cli/mcp-add.d.ts +46 -0
- package/dist/cli/mcp-add.js +243 -0
- package/dist/cli/mcp-add.js.map +1 -0
- package/dist/index.cjs +3 -3
- package/dist/index.js +19 -66
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-CUFIV225.js.map +0 -1
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Describes an AI coding agent detected in a project.
|
|
3
|
+
*/
|
|
4
|
+
interface DetectedAgent {
|
|
5
|
+
name: "claude" | "codex" | "gemini" | "cursor" | "windsurf" | "generic";
|
|
6
|
+
mcpConfigPath: string | null;
|
|
7
|
+
infoFilePath: string | null;
|
|
8
|
+
cliAvailable: boolean;
|
|
9
|
+
registrationCommand: string | null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** Options for the mcp add command. */
|
|
13
|
+
interface McpAddOptions {
|
|
14
|
+
force?: boolean;
|
|
15
|
+
dryRun?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/** Result of the mcp add command. */
|
|
18
|
+
interface McpAddResult {
|
|
19
|
+
exitCode: number;
|
|
20
|
+
results: AgentResult[];
|
|
21
|
+
messages: string[];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Result of a single agent registration attempt.
|
|
25
|
+
*/
|
|
26
|
+
interface AgentResult {
|
|
27
|
+
agent: DetectedAgent["name"];
|
|
28
|
+
success: boolean;
|
|
29
|
+
method: "cli" | "file" | "skipped";
|
|
30
|
+
message: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Registers the Glasstrace MCP server with detected AI coding agents.
|
|
34
|
+
*
|
|
35
|
+
* For each agent, attempts native CLI registration first, then falls back
|
|
36
|
+
* to file-based configuration. Creates a marker file on success to enable
|
|
37
|
+
* idempotent re-runs.
|
|
38
|
+
*
|
|
39
|
+
* Returns a structured result instead of calling process.exit(), so the
|
|
40
|
+
* CLI entry point can decide how to handle the outcome.
|
|
41
|
+
*
|
|
42
|
+
* @param options - Control flags for force and dry-run modes.
|
|
43
|
+
*/
|
|
44
|
+
declare function mcpAdd(options?: McpAddOptions): Promise<McpAddResult>;
|
|
45
|
+
|
|
46
|
+
export { type McpAddOptions, type McpAddResult, mcpAdd };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Describes an AI coding agent detected in a project.
|
|
3
|
+
*/
|
|
4
|
+
interface DetectedAgent {
|
|
5
|
+
name: "claude" | "codex" | "gemini" | "cursor" | "windsurf" | "generic";
|
|
6
|
+
mcpConfigPath: string | null;
|
|
7
|
+
infoFilePath: string | null;
|
|
8
|
+
cliAvailable: boolean;
|
|
9
|
+
registrationCommand: string | null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** Options for the mcp add command. */
|
|
13
|
+
interface McpAddOptions {
|
|
14
|
+
force?: boolean;
|
|
15
|
+
dryRun?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/** Result of the mcp add command. */
|
|
18
|
+
interface McpAddResult {
|
|
19
|
+
exitCode: number;
|
|
20
|
+
results: AgentResult[];
|
|
21
|
+
messages: string[];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Result of a single agent registration attempt.
|
|
25
|
+
*/
|
|
26
|
+
interface AgentResult {
|
|
27
|
+
agent: DetectedAgent["name"];
|
|
28
|
+
success: boolean;
|
|
29
|
+
method: "cli" | "file" | "skipped";
|
|
30
|
+
message: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Registers the Glasstrace MCP server with detected AI coding agents.
|
|
34
|
+
*
|
|
35
|
+
* For each agent, attempts native CLI registration first, then falls back
|
|
36
|
+
* to file-based configuration. Creates a marker file on success to enable
|
|
37
|
+
* idempotent re-runs.
|
|
38
|
+
*
|
|
39
|
+
* Returns a structured result instead of calling process.exit(), so the
|
|
40
|
+
* CLI entry point can decide how to handle the outcome.
|
|
41
|
+
*
|
|
42
|
+
* @param options - Control flags for force and dry-run modes.
|
|
43
|
+
*/
|
|
44
|
+
declare function mcpAdd(options?: McpAddOptions): Promise<McpAddResult>;
|
|
45
|
+
|
|
46
|
+
export { type McpAddOptions, type McpAddResult, mcpAdd };
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import {
|
|
2
|
+
detectAgents,
|
|
3
|
+
generateInfoSection,
|
|
4
|
+
generateMcpConfig,
|
|
5
|
+
injectInfoSection,
|
|
6
|
+
scaffoldMcpMarker,
|
|
7
|
+
updateGitignore,
|
|
8
|
+
writeMcpConfig
|
|
9
|
+
} from "../chunk-STECO33B.js";
|
|
10
|
+
import {
|
|
11
|
+
readAnonKey
|
|
12
|
+
} from "../chunk-EC5IINUT.js";
|
|
13
|
+
import "../chunk-PZ5AY32C.js";
|
|
14
|
+
|
|
15
|
+
// src/cli/mcp-add.ts
|
|
16
|
+
import { execFile as execFileCb } from "child_process";
|
|
17
|
+
import * as fs from "fs";
|
|
18
|
+
import * as path from "path";
|
|
19
|
+
import { promisify } from "util";
|
|
20
|
+
var execFileAsync = promisify(execFileCb);
|
|
21
|
+
var MCP_ENDPOINT = "https://api.glasstrace.dev/mcp";
|
|
22
|
+
function formatAgentName(name) {
|
|
23
|
+
const displayNames = {
|
|
24
|
+
claude: "Claude Code",
|
|
25
|
+
codex: "Codex",
|
|
26
|
+
gemini: "Gemini",
|
|
27
|
+
cursor: "Cursor",
|
|
28
|
+
windsurf: "Windsurf",
|
|
29
|
+
generic: "Generic"
|
|
30
|
+
};
|
|
31
|
+
return displayNames[name];
|
|
32
|
+
}
|
|
33
|
+
async function registerViaCli(agent, anonKey) {
|
|
34
|
+
if (!agent.cliAvailable) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
switch (agent.name) {
|
|
39
|
+
case "claude": {
|
|
40
|
+
const payload = JSON.stringify({
|
|
41
|
+
type: "http",
|
|
42
|
+
url: MCP_ENDPOINT,
|
|
43
|
+
headers: { Authorization: `Bearer ${anonKey}` }
|
|
44
|
+
});
|
|
45
|
+
await execFileAsync("claude", [
|
|
46
|
+
"mcp",
|
|
47
|
+
"add-json",
|
|
48
|
+
"glasstrace",
|
|
49
|
+
payload,
|
|
50
|
+
"--scope",
|
|
51
|
+
"project"
|
|
52
|
+
]);
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
case "codex": {
|
|
56
|
+
await execFileAsync("codex", [
|
|
57
|
+
"mcp",
|
|
58
|
+
"add",
|
|
59
|
+
"glasstrace",
|
|
60
|
+
"--url",
|
|
61
|
+
MCP_ENDPOINT
|
|
62
|
+
]);
|
|
63
|
+
const configPath = agent.mcpConfigPath;
|
|
64
|
+
if (configPath !== null && fs.existsSync(configPath)) {
|
|
65
|
+
const content = fs.readFileSync(configPath, "utf-8");
|
|
66
|
+
if (!content.includes("bearer_token_env_var")) {
|
|
67
|
+
const appendContent = content.endsWith("\n") ? "" : "\n";
|
|
68
|
+
fs.writeFileSync(
|
|
69
|
+
configPath,
|
|
70
|
+
content + appendContent + 'bearer_token_env_var = "GLASSTRACE_API_KEY"\n',
|
|
71
|
+
"utf-8"
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
process.stderr.write(
|
|
76
|
+
" Note: Set GLASSTRACE_API_KEY environment variable for Codex authentication.\n"
|
|
77
|
+
);
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
case "gemini": {
|
|
81
|
+
await execFileAsync("gemini", [
|
|
82
|
+
"mcp",
|
|
83
|
+
"add",
|
|
84
|
+
"--transport",
|
|
85
|
+
"http",
|
|
86
|
+
"--header",
|
|
87
|
+
`Authorization: Bearer ${anonKey}`,
|
|
88
|
+
"glasstrace",
|
|
89
|
+
MCP_ENDPOINT
|
|
90
|
+
]);
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
default:
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
} catch {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async function mcpAdd(options) {
|
|
101
|
+
const force = options?.force ?? false;
|
|
102
|
+
const dryRun = options?.dryRun ?? false;
|
|
103
|
+
const projectRoot = process.cwd();
|
|
104
|
+
const messages = [];
|
|
105
|
+
const anonKey = await readAnonKey(projectRoot);
|
|
106
|
+
if (anonKey === null) {
|
|
107
|
+
return {
|
|
108
|
+
exitCode: 1,
|
|
109
|
+
results: [],
|
|
110
|
+
messages: ["Error: Run `glasstrace init` first to generate an API key."]
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
const markerPath = path.join(projectRoot, ".glasstrace", "mcp-connected");
|
|
114
|
+
if (fs.existsSync(markerPath) && !force) {
|
|
115
|
+
return {
|
|
116
|
+
exitCode: 0,
|
|
117
|
+
results: [],
|
|
118
|
+
messages: ["MCP already configured. Use --force to reconfigure."]
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
const agents = await detectAgents(projectRoot);
|
|
122
|
+
const detectedNonGeneric = agents.filter((a) => a.name !== "generic");
|
|
123
|
+
const targetAgents = detectedNonGeneric.length > 0 ? detectedNonGeneric : agents.filter((a) => a.name === "generic");
|
|
124
|
+
if (dryRun) {
|
|
125
|
+
messages.push("Dry run: would perform the following actions:", "");
|
|
126
|
+
for (const agent of targetAgents) {
|
|
127
|
+
const name = formatAgentName(agent.name);
|
|
128
|
+
if (agent.cliAvailable) {
|
|
129
|
+
messages.push(
|
|
130
|
+
` ${name}: Register via CLI (${agent.name} mcp add)`
|
|
131
|
+
);
|
|
132
|
+
} else if (agent.mcpConfigPath !== null) {
|
|
133
|
+
messages.push(
|
|
134
|
+
` ${name}: Write config to ${agent.mcpConfigPath}`
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
if (agent.infoFilePath !== null) {
|
|
138
|
+
messages.push(
|
|
139
|
+
` ${name}: Inject info section into ${agent.infoFilePath}`
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
messages.push(
|
|
144
|
+
"",
|
|
145
|
+
" Update .gitignore with MCP config paths",
|
|
146
|
+
" Create .glasstrace/mcp-connected marker"
|
|
147
|
+
);
|
|
148
|
+
return { exitCode: 0, results: [], messages };
|
|
149
|
+
}
|
|
150
|
+
const results = [];
|
|
151
|
+
for (const agent of targetAgents) {
|
|
152
|
+
const name = formatAgentName(agent.name);
|
|
153
|
+
if (agent.name !== "generic") {
|
|
154
|
+
const cliSuccess = await registerViaCli(agent, anonKey);
|
|
155
|
+
if (cliSuccess) {
|
|
156
|
+
const infoContent = generateInfoSection(agent, MCP_ENDPOINT);
|
|
157
|
+
if (infoContent !== "") {
|
|
158
|
+
await injectInfoSection(agent, infoContent, projectRoot);
|
|
159
|
+
}
|
|
160
|
+
results.push({
|
|
161
|
+
agent: agent.name,
|
|
162
|
+
success: true,
|
|
163
|
+
method: "cli",
|
|
164
|
+
message: `${name}: Registered via CLI`
|
|
165
|
+
});
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (agent.mcpConfigPath !== null) {
|
|
170
|
+
try {
|
|
171
|
+
const configContent = generateMcpConfig(agent, MCP_ENDPOINT, anonKey);
|
|
172
|
+
await writeMcpConfig(agent, configContent, projectRoot);
|
|
173
|
+
if (fs.existsSync(agent.mcpConfigPath)) {
|
|
174
|
+
const infoContent = generateInfoSection(agent, MCP_ENDPOINT);
|
|
175
|
+
if (infoContent !== "") {
|
|
176
|
+
await injectInfoSection(agent, infoContent, projectRoot);
|
|
177
|
+
}
|
|
178
|
+
results.push({
|
|
179
|
+
agent: agent.name,
|
|
180
|
+
success: true,
|
|
181
|
+
method: "file",
|
|
182
|
+
message: `${name}: Configured via ${agent.mcpConfigPath}`
|
|
183
|
+
});
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
results.push({
|
|
187
|
+
agent: agent.name,
|
|
188
|
+
success: false,
|
|
189
|
+
method: "file",
|
|
190
|
+
message: `${name}: Failed to write config to ${agent.mcpConfigPath} (permission denied)`
|
|
191
|
+
});
|
|
192
|
+
continue;
|
|
193
|
+
} catch (err) {
|
|
194
|
+
results.push({
|
|
195
|
+
agent: agent.name,
|
|
196
|
+
success: false,
|
|
197
|
+
method: "file",
|
|
198
|
+
message: `${name}: Failed - ${err instanceof Error ? err.message : String(err)}`
|
|
199
|
+
});
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
results.push({
|
|
204
|
+
agent: agent.name,
|
|
205
|
+
success: false,
|
|
206
|
+
method: "skipped",
|
|
207
|
+
message: `${name}: No registration method available`
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
await updateGitignore(
|
|
211
|
+
[".mcp.json", ".cursor/mcp.json", ".gemini/settings.json", ".codex/config.toml"],
|
|
212
|
+
projectRoot
|
|
213
|
+
);
|
|
214
|
+
const anySuccess = results.some((r) => r.success);
|
|
215
|
+
if (anySuccess) {
|
|
216
|
+
await scaffoldMcpMarker(projectRoot, anonKey);
|
|
217
|
+
}
|
|
218
|
+
messages.push("", "MCP registration summary:");
|
|
219
|
+
for (const result of results) {
|
|
220
|
+
const icon = result.success ? "+" : "-";
|
|
221
|
+
messages.push(` [${icon}] ${result.message}`);
|
|
222
|
+
}
|
|
223
|
+
if (results.length === 0) {
|
|
224
|
+
messages.push(
|
|
225
|
+
" No agents detected. Place agent marker files (e.g., CLAUDE.md, .cursor/) in your project."
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
if (!anySuccess && results.length > 0) {
|
|
229
|
+
messages.push(
|
|
230
|
+
"",
|
|
231
|
+
"All agent registrations failed. Check errors above."
|
|
232
|
+
);
|
|
233
|
+
return { exitCode: 1, results, messages };
|
|
234
|
+
}
|
|
235
|
+
if (anySuccess) {
|
|
236
|
+
messages.push("", "MCP registration complete.");
|
|
237
|
+
}
|
|
238
|
+
return { exitCode: 0, results, messages };
|
|
239
|
+
}
|
|
240
|
+
export {
|
|
241
|
+
mcpAdd
|
|
242
|
+
};
|
|
243
|
+
//# sourceMappingURL=mcp-add.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/cli/mcp-add.ts"],"sourcesContent":["import { execFile as execFileCb } from \"node:child_process\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { readAnonKey } from \"../anon-key.js\";\nimport { detectAgents } from \"../agent-detection/detect.js\";\nimport { generateMcpConfig, generateInfoSection } from \"../agent-detection/configs.js\";\nimport {\n writeMcpConfig,\n injectInfoSection,\n updateGitignore,\n} from \"../agent-detection/inject.js\";\nimport { scaffoldMcpMarker } from \"./scaffolder.js\";\nimport type { DetectedAgent } from \"../agent-detection/detect.js\";\n\nconst execFileAsync = promisify(execFileCb);\n\n/** Glasstrace MCP endpoint for agent configuration. */\nconst MCP_ENDPOINT = \"https://api.glasstrace.dev/mcp\";\n\n/** Maps internal agent name to a human-readable display name. */\nfunction formatAgentName(name: DetectedAgent[\"name\"]): string {\n const displayNames: Record<DetectedAgent[\"name\"], string> = {\n claude: \"Claude Code\",\n codex: \"Codex\",\n gemini: \"Gemini\",\n cursor: \"Cursor\",\n windsurf: \"Windsurf\",\n generic: \"Generic\",\n };\n return displayNames[name];\n}\n\n/** Options for the mcp add command. */\nexport interface McpAddOptions {\n force?: boolean;\n dryRun?: boolean;\n}\n\n/** Result of the mcp add command. */\nexport interface McpAddResult {\n exitCode: number;\n results: AgentResult[];\n messages: string[];\n}\n\n/**\n * Result of a single agent registration attempt.\n */\ninterface AgentResult {\n agent: DetectedAgent[\"name\"];\n success: boolean;\n method: \"cli\" | \"file\" | \"skipped\";\n message: string;\n}\n\n/**\n * Attempts CLI-based MCP registration for agents that support it.\n * Returns true if the CLI command succeeded.\n *\n * Note: anonymous keys are passed in process arguments for CLI registration.\n * This is acceptable because anon keys are non-secret identifiers (not\n * credentials) designed for semi-public use. They identify a project\n * but cannot be used to access user data.\n */\nasync function registerViaCli(\n agent: DetectedAgent,\n anonKey: string,\n): Promise<boolean> {\n if (!agent.cliAvailable) {\n return false;\n }\n\n try {\n switch (agent.name) {\n case \"claude\": {\n const payload = JSON.stringify({\n type: \"http\",\n url: MCP_ENDPOINT,\n headers: { Authorization: `Bearer ${anonKey}` },\n });\n await execFileAsync(\"claude\", [\n \"mcp\",\n \"add-json\",\n \"glasstrace\",\n payload,\n \"--scope\",\n \"project\",\n ]);\n return true;\n }\n\n case \"codex\": {\n await execFileAsync(\"codex\", [\n \"mcp\",\n \"add\",\n \"glasstrace\",\n \"--url\",\n MCP_ENDPOINT,\n ]);\n // Ensure .codex/config.toml has bearer_token_env_var\n const configPath = agent.mcpConfigPath;\n if (configPath !== null && fs.existsSync(configPath)) {\n const content = fs.readFileSync(configPath, \"utf-8\");\n if (!content.includes(\"bearer_token_env_var\")) {\n const appendContent =\n content.endsWith(\"\\n\") ? \"\" : \"\\n\";\n fs.writeFileSync(\n configPath,\n content +\n appendContent +\n 'bearer_token_env_var = \"GLASSTRACE_API_KEY\"\\n',\n \"utf-8\",\n );\n }\n }\n process.stderr.write(\n \" Note: Set GLASSTRACE_API_KEY environment variable for Codex authentication.\\n\",\n );\n return true;\n }\n\n case \"gemini\": {\n await execFileAsync(\"gemini\", [\n \"mcp\",\n \"add\",\n \"--transport\",\n \"http\",\n \"--header\",\n `Authorization: Bearer ${anonKey}`,\n \"glasstrace\",\n MCP_ENDPOINT,\n ]);\n return true;\n }\n\n default:\n return false;\n }\n } catch {\n return false;\n }\n}\n\n/**\n * Registers the Glasstrace MCP server with detected AI coding agents.\n *\n * For each agent, attempts native CLI registration first, then falls back\n * to file-based configuration. Creates a marker file on success to enable\n * idempotent re-runs.\n *\n * Returns a structured result instead of calling process.exit(), so the\n * CLI entry point can decide how to handle the outcome.\n *\n * @param options - Control flags for force and dry-run modes.\n */\nexport async function mcpAdd(options?: McpAddOptions): Promise<McpAddResult> {\n const force = options?.force ?? false;\n const dryRun = options?.dryRun ?? false;\n const projectRoot = process.cwd();\n const messages: string[] = [];\n\n // Step 1: Read anon key\n const anonKey = await readAnonKey(projectRoot);\n if (anonKey === null) {\n return {\n exitCode: 1,\n results: [],\n messages: [\"Error: Run `glasstrace init` first to generate an API key.\"],\n };\n }\n\n // Step 2: Check marker file\n const markerPath = path.join(projectRoot, \".glasstrace\", \"mcp-connected\");\n if (fs.existsSync(markerPath) && !force) {\n return {\n exitCode: 0,\n results: [],\n messages: [\"MCP already configured. Use --force to reconfigure.\"],\n };\n }\n\n // Step 3: Detect agents\n const agents = await detectAgents(projectRoot);\n const detectedNonGeneric = agents.filter((a) => a.name !== \"generic\");\n\n // If no specific agents found, include the generic fallback so the command\n // still produces a usable .glasstrace/mcp.json (matching init behavior).\n const targetAgents =\n detectedNonGeneric.length > 0\n ? detectedNonGeneric\n : agents.filter((a) => a.name === \"generic\");\n\n if (dryRun) {\n messages.push(\"Dry run: would perform the following actions:\", \"\");\n for (const agent of targetAgents) {\n const name = formatAgentName(agent.name);\n if (agent.cliAvailable) {\n messages.push(\n ` ${name}: Register via CLI (${agent.name} mcp add)`,\n );\n } else if (agent.mcpConfigPath !== null) {\n messages.push(\n ` ${name}: Write config to ${agent.mcpConfigPath}`,\n );\n }\n if (agent.infoFilePath !== null) {\n messages.push(\n ` ${name}: Inject info section into ${agent.infoFilePath}`,\n );\n }\n }\n messages.push(\n \"\",\n \" Update .gitignore with MCP config paths\",\n \" Create .glasstrace/mcp-connected marker\",\n );\n return { exitCode: 0, results: [], messages };\n }\n\n // Step 4: Register with each agent\n const results: AgentResult[] = [];\n\n for (const agent of targetAgents) {\n const name = formatAgentName(agent.name);\n\n // Try CLI registration first (not applicable for generic)\n if (agent.name !== \"generic\") {\n const cliSuccess = await registerViaCli(agent, anonKey);\n if (cliSuccess) {\n // Still inject info section if applicable\n const infoContent = generateInfoSection(agent, MCP_ENDPOINT);\n if (infoContent !== \"\") {\n await injectInfoSection(agent, infoContent, projectRoot);\n }\n results.push({\n agent: agent.name,\n success: true,\n method: \"cli\",\n message: `${name}: Registered via CLI`,\n });\n continue;\n }\n }\n\n // Fall back to file-based config\n if (agent.mcpConfigPath !== null) {\n try {\n const configContent = generateMcpConfig(agent, MCP_ENDPOINT, anonKey);\n await writeMcpConfig(agent, configContent, projectRoot);\n\n // Verify the config was written (writeMcpConfig swallows permission errors)\n if (fs.existsSync(agent.mcpConfigPath)) {\n const infoContent = generateInfoSection(agent, MCP_ENDPOINT);\n if (infoContent !== \"\") {\n await injectInfoSection(agent, infoContent, projectRoot);\n }\n results.push({\n agent: agent.name,\n success: true,\n method: \"file\",\n message: `${name}: Configured via ${agent.mcpConfigPath}`,\n });\n continue;\n }\n\n // writeMcpConfig returned without throwing but file doesn't exist\n // (permission denied handled gracefully inside writeMcpConfig)\n results.push({\n agent: agent.name,\n success: false,\n method: \"file\",\n message: `${name}: Failed to write config to ${agent.mcpConfigPath} (permission denied)`,\n });\n continue;\n } catch (err) {\n results.push({\n agent: agent.name,\n success: false,\n method: \"file\",\n message: `${name}: Failed - ${err instanceof Error ? err.message : String(err)}`,\n });\n continue;\n }\n }\n\n results.push({\n agent: agent.name,\n success: false,\n method: \"skipped\",\n message: `${name}: No registration method available`,\n });\n }\n\n // Step 5: Update gitignore\n await updateGitignore(\n [\".mcp.json\", \".cursor/mcp.json\", \".gemini/settings.json\", \".codex/config.toml\"],\n projectRoot,\n );\n\n // Step 6: Create marker file if at least one succeeded\n const anySuccess = results.some((r) => r.success);\n\n if (anySuccess) {\n await scaffoldMcpMarker(projectRoot, anonKey);\n }\n\n // Step 7: Build summary messages\n messages.push(\"\", \"MCP registration summary:\");\n for (const result of results) {\n const icon = result.success ? \"+\" : \"-\";\n messages.push(` [${icon}] ${result.message}`);\n }\n\n if (results.length === 0) {\n messages.push(\n \" No agents detected. Place agent marker files (e.g., CLAUDE.md, .cursor/) in your project.\",\n );\n }\n\n if (!anySuccess && results.length > 0) {\n messages.push(\n \"\",\n \"All agent registrations failed. Check errors above.\",\n );\n return { exitCode: 1, results, messages };\n }\n\n if (anySuccess) {\n messages.push(\"\", \"MCP registration complete.\");\n }\n\n return { exitCode: 0, results, messages };\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.cjs
CHANGED
|
@@ -19143,7 +19143,7 @@ function registerGlasstrace(options) {
|
|
|
19143
19143
|
if (config2.verbose) {
|
|
19144
19144
|
console.info("[glasstrace] Background init firing.");
|
|
19145
19145
|
}
|
|
19146
|
-
await performInit(config2, anonKey, "0.
|
|
19146
|
+
await performInit(config2, anonKey, "0.4.0");
|
|
19147
19147
|
maybeInstallConsoleCapture();
|
|
19148
19148
|
} catch (err) {
|
|
19149
19149
|
console.warn(
|
|
@@ -19163,7 +19163,7 @@ function registerGlasstrace(options) {
|
|
|
19163
19163
|
if (config2.verbose) {
|
|
19164
19164
|
console.info("[glasstrace] Background init firing.");
|
|
19165
19165
|
}
|
|
19166
|
-
await performInit(config2, anonKey, "0.
|
|
19166
|
+
await performInit(config2, anonKey, "0.4.0");
|
|
19167
19167
|
maybeInstallConsoleCapture();
|
|
19168
19168
|
} catch (err) {
|
|
19169
19169
|
console.warn(
|
|
@@ -19185,7 +19185,7 @@ function registerGlasstrace(options) {
|
|
|
19185
19185
|
if (config2.verbose) {
|
|
19186
19186
|
console.info("[glasstrace] Background init firing.");
|
|
19187
19187
|
}
|
|
19188
|
-
await performInit(config2, anonKeyForInit, "0.
|
|
19188
|
+
await performInit(config2, anonKeyForInit, "0.4.0");
|
|
19189
19189
|
maybeInstallConsoleCapture();
|
|
19190
19190
|
} catch (err) {
|
|
19191
19191
|
console.warn(
|
package/dist/index.js
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
buildImportGraph,
|
|
3
|
+
discoverTestFiles,
|
|
4
|
+
extractImports
|
|
5
|
+
} from "./chunk-TJ6ETQPH.js";
|
|
6
|
+
import {
|
|
3
7
|
DEFAULT_CAPTURE_CONFIG,
|
|
4
8
|
GLASSTRACE_ATTRIBUTE_NAMES,
|
|
5
9
|
SdkCachedConfigSchema,
|
|
6
10
|
SdkInitResponseSchema,
|
|
7
11
|
SessionIdSchema,
|
|
8
12
|
SourceMapUploadResponseSchema,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
extractImports
|
|
13
|
-
} from "./chunk-CUFIV225.js";
|
|
13
|
+
getOrCreateAnonKey,
|
|
14
|
+
readAnonKey
|
|
15
|
+
} from "./chunk-EC5IINUT.js";
|
|
14
16
|
import {
|
|
15
17
|
INVALID_SPAN_CONTEXT,
|
|
16
18
|
SamplingDecision,
|
|
@@ -180,60 +182,11 @@ function classifyFetchTarget(url) {
|
|
|
180
182
|
return "unknown";
|
|
181
183
|
}
|
|
182
184
|
|
|
183
|
-
// src/anon-key.ts
|
|
184
|
-
import { readFile, writeFile, mkdir, chmod } from "fs/promises";
|
|
185
|
-
import { join } from "path";
|
|
186
|
-
var GLASSTRACE_DIR = ".glasstrace";
|
|
187
|
-
var ANON_KEY_FILE = "anon_key";
|
|
188
|
-
var ephemeralKeyCache = /* @__PURE__ */ new Map();
|
|
189
|
-
async function readAnonKey(projectRoot) {
|
|
190
|
-
const root = projectRoot ?? process.cwd();
|
|
191
|
-
const keyPath = join(root, GLASSTRACE_DIR, ANON_KEY_FILE);
|
|
192
|
-
try {
|
|
193
|
-
const content = await readFile(keyPath, "utf-8");
|
|
194
|
-
const result = AnonApiKeySchema.safeParse(content);
|
|
195
|
-
if (result.success) {
|
|
196
|
-
return result.data;
|
|
197
|
-
}
|
|
198
|
-
} catch {
|
|
199
|
-
}
|
|
200
|
-
const cached = ephemeralKeyCache.get(root);
|
|
201
|
-
if (cached !== void 0) {
|
|
202
|
-
return cached;
|
|
203
|
-
}
|
|
204
|
-
return null;
|
|
205
|
-
}
|
|
206
|
-
async function getOrCreateAnonKey(projectRoot) {
|
|
207
|
-
const root = projectRoot ?? process.cwd();
|
|
208
|
-
const dirPath = join(root, GLASSTRACE_DIR);
|
|
209
|
-
const keyPath = join(dirPath, ANON_KEY_FILE);
|
|
210
|
-
const existingKey = await readAnonKey(root);
|
|
211
|
-
if (existingKey !== null) {
|
|
212
|
-
return existingKey;
|
|
213
|
-
}
|
|
214
|
-
const cached = ephemeralKeyCache.get(root);
|
|
215
|
-
if (cached !== void 0) {
|
|
216
|
-
return cached;
|
|
217
|
-
}
|
|
218
|
-
const newKey = createAnonApiKey();
|
|
219
|
-
try {
|
|
220
|
-
await mkdir(dirPath, { recursive: true, mode: 448 });
|
|
221
|
-
await writeFile(keyPath, newKey, "utf-8");
|
|
222
|
-
await chmod(keyPath, 384);
|
|
223
|
-
} catch (err) {
|
|
224
|
-
ephemeralKeyCache.set(root, newKey);
|
|
225
|
-
console.warn(
|
|
226
|
-
`[glasstrace] Failed to persist anonymous key to ${keyPath}: ${err instanceof Error ? err.message : String(err)}. Using ephemeral key.`
|
|
227
|
-
);
|
|
228
|
-
}
|
|
229
|
-
return newKey;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
185
|
// src/init-client.ts
|
|
233
186
|
import { readFileSync } from "fs";
|
|
234
|
-
import { writeFile
|
|
235
|
-
import { join
|
|
236
|
-
var
|
|
187
|
+
import { writeFile, mkdir } from "fs/promises";
|
|
188
|
+
import { join } from "path";
|
|
189
|
+
var GLASSTRACE_DIR = ".glasstrace";
|
|
237
190
|
var CONFIG_FILE = "config";
|
|
238
191
|
var TWENTY_FOUR_HOURS_MS = 24 * 60 * 60 * 1e3;
|
|
239
192
|
var INIT_TIMEOUT_MS = 1e4;
|
|
@@ -241,7 +194,7 @@ var currentConfig = null;
|
|
|
241
194
|
var rateLimitBackoff = false;
|
|
242
195
|
function loadCachedConfig(projectRoot) {
|
|
243
196
|
const root = projectRoot ?? process.cwd();
|
|
244
|
-
const configPath =
|
|
197
|
+
const configPath = join(root, GLASSTRACE_DIR, CONFIG_FILE);
|
|
245
198
|
try {
|
|
246
199
|
const content = readFileSync(configPath, "utf-8");
|
|
247
200
|
const parsed = JSON.parse(content);
|
|
@@ -264,15 +217,15 @@ function loadCachedConfig(projectRoot) {
|
|
|
264
217
|
}
|
|
265
218
|
async function saveCachedConfig(response, projectRoot) {
|
|
266
219
|
const root = projectRoot ?? process.cwd();
|
|
267
|
-
const dirPath =
|
|
268
|
-
const configPath =
|
|
220
|
+
const dirPath = join(root, GLASSTRACE_DIR);
|
|
221
|
+
const configPath = join(dirPath, CONFIG_FILE);
|
|
269
222
|
try {
|
|
270
|
-
await
|
|
223
|
+
await mkdir(dirPath, { recursive: true });
|
|
271
224
|
const cached = {
|
|
272
225
|
response,
|
|
273
226
|
cachedAt: Date.now()
|
|
274
227
|
};
|
|
275
|
-
await
|
|
228
|
+
await writeFile(configPath, JSON.stringify(cached), "utf-8");
|
|
276
229
|
} catch (err) {
|
|
277
230
|
console.warn(
|
|
278
231
|
`[glasstrace] Failed to cache config to ${configPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -3589,7 +3542,7 @@ function registerGlasstrace(options) {
|
|
|
3589
3542
|
if (config.verbose) {
|
|
3590
3543
|
console.info("[glasstrace] Background init firing.");
|
|
3591
3544
|
}
|
|
3592
|
-
await performInit(config, anonKey, "0.
|
|
3545
|
+
await performInit(config, anonKey, "0.4.0");
|
|
3593
3546
|
maybeInstallConsoleCapture();
|
|
3594
3547
|
} catch (err) {
|
|
3595
3548
|
console.warn(
|
|
@@ -3609,7 +3562,7 @@ function registerGlasstrace(options) {
|
|
|
3609
3562
|
if (config.verbose) {
|
|
3610
3563
|
console.info("[glasstrace] Background init firing.");
|
|
3611
3564
|
}
|
|
3612
|
-
await performInit(config, anonKey, "0.
|
|
3565
|
+
await performInit(config, anonKey, "0.4.0");
|
|
3613
3566
|
maybeInstallConsoleCapture();
|
|
3614
3567
|
} catch (err) {
|
|
3615
3568
|
console.warn(
|
|
@@ -3631,7 +3584,7 @@ function registerGlasstrace(options) {
|
|
|
3631
3584
|
if (config.verbose) {
|
|
3632
3585
|
console.info("[glasstrace] Background init firing.");
|
|
3633
3586
|
}
|
|
3634
|
-
await performInit(config, anonKeyForInit, "0.
|
|
3587
|
+
await performInit(config, anonKeyForInit, "0.4.0");
|
|
3635
3588
|
maybeInstallConsoleCapture();
|
|
3636
3589
|
} catch (err) {
|
|
3637
3590
|
console.warn(
|