@glasstrace/sdk 0.14.2 → 0.16.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/README.md +84 -1
- package/dist/adapters/drizzle.js +2 -5
- package/dist/adapters/drizzle.js.map +1 -1
- package/dist/{chunk-PD2SKFQQ.js → chunk-55FBXXER.js} +4 -8
- package/dist/{chunk-PD2SKFQQ.js.map → chunk-55FBXXER.js.map} +1 -1
- package/dist/chunk-5C2TJFLB.js +851 -0
- package/dist/chunk-5C2TJFLB.js.map +1 -0
- package/dist/{chunk-YMEXDDTA.js → chunk-7JBKXSBU.js} +3 -99
- package/dist/chunk-7JBKXSBU.js.map +1 -0
- package/dist/{chunk-2LDBR3F3.js → chunk-BANTDXUT.js} +15 -74
- package/dist/chunk-BANTDXUT.js.map +1 -0
- package/dist/{chunk-WV3NIPWJ.js → chunk-CTJI2YKA.js} +23 -288
- package/dist/chunk-CTJI2YKA.js.map +1 -0
- package/dist/{chunk-WK7MPK2T.js → chunk-DQ25VOKK.js} +1 -89
- package/dist/chunk-DQ25VOKK.js.map +1 -0
- package/dist/{chunk-BL3YDC6V.js → chunk-DXRZKKSO.js} +1 -6
- package/dist/{chunk-BL3YDC6V.js.map → chunk-DXRZKKSO.js.map} +1 -1
- package/dist/{chunk-BGZ7J74D.js → chunk-NSBPE2FW.js} +2 -16
- package/dist/chunk-O63DJKIJ.js +460 -0
- package/dist/chunk-O63DJKIJ.js.map +1 -0
- package/dist/{chunk-ECEN724Y.js → chunk-TM5NKZTO.js} +4 -8
- package/dist/{chunk-ECEN724Y.js.map → chunk-TM5NKZTO.js.map} +1 -1
- package/dist/chunk-VUZCLMIX.js +57 -0
- package/dist/chunk-VUZCLMIX.js.map +1 -0
- package/dist/{chunk-OSXIUKD5.js → chunk-WZXVS2EO.js} +1 -6
- package/dist/{chunk-OSXIUKD5.js.map → chunk-WZXVS2EO.js.map} +1 -1
- package/dist/{chunk-ARAOZCZT.js → chunk-XNDHQN4S.js} +122 -24
- package/dist/chunk-XNDHQN4S.js.map +1 -0
- package/dist/cli/init.cjs +1110 -255
- package/dist/cli/init.cjs.map +1 -1
- package/dist/cli/init.d.cts +86 -1
- package/dist/cli/init.d.ts +86 -1
- package/dist/cli/init.js +277 -66
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/mcp-add.cjs +16 -16
- package/dist/cli/mcp-add.cjs.map +1 -1
- package/dist/cli/mcp-add.js +12 -13
- package/dist/cli/mcp-add.js.map +1 -1
- package/dist/cli/status.cjs +2 -2
- package/dist/cli/status.js +4 -7
- package/dist/cli/status.js.map +1 -1
- package/dist/cli/uninit.cjs +138 -20
- package/dist/cli/uninit.cjs.map +1 -1
- package/dist/cli/uninit.d.cts +38 -8
- package/dist/cli/uninit.d.ts +38 -8
- package/dist/cli/uninit.js +8 -5
- package/dist/cli/validate.cjs +135 -0
- package/dist/cli/validate.cjs.map +1 -0
- package/dist/cli/validate.d.cts +60 -0
- package/dist/cli/validate.d.ts +60 -0
- package/dist/cli/validate.js +100 -0
- package/dist/cli/validate.js.map +1 -0
- package/dist/{esm-MDK7CZID.js → esm-KBPHCVB4.js} +3 -3
- package/dist/{getMachineId-bsd-4NIRBWME.js → getMachineId-bsd-345PYXFX.js} +4 -7
- package/dist/{getMachineId-bsd-4NIRBWME.js.map → getMachineId-bsd-345PYXFX.js.map} +1 -1
- package/dist/{getMachineId-darwin-2XNOCCJQ.js → getMachineId-darwin-5L2D25AD.js} +4 -7
- package/dist/{getMachineId-darwin-2XNOCCJQ.js.map → getMachineId-darwin-5L2D25AD.js.map} +1 -1
- package/dist/{getMachineId-linux-V6YSQEY7.js → getMachineId-linux-KJR4P5HN.js} +3 -6
- package/dist/{getMachineId-linux-V6YSQEY7.js.map → getMachineId-linux-KJR4P5HN.js.map} +1 -1
- package/dist/{getMachineId-unsupported-4FKBJNVO.js → getMachineId-unsupported-NDNXDYDY.js} +3 -6
- package/dist/{getMachineId-unsupported-4FKBJNVO.js.map → getMachineId-unsupported-NDNXDYDY.js.map} +1 -1
- package/dist/{getMachineId-win-WLRZBKVG.js → getMachineId-win-T7PJNJXG.js} +4 -7
- package/dist/{getMachineId-win-WLRZBKVG.js.map → getMachineId-win-T7PJNJXG.js.map} +1 -1
- package/dist/index.cjs +519 -494
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +47 -6
- package/dist/index.d.ts +47 -6
- package/dist/index.js +250 -719
- package/dist/index.js.map +1 -1
- package/dist/{monorepo-YILKGQXQ.js → monorepo-N5Z63XP7.js} +4 -4
- package/dist/{source-map-uploader-3GWUQDTS.js → source-map-uploader-MUZPI2S5.js} +5 -4
- package/dist/source-map-uploader-MUZPI2S5.js.map +1 -0
- package/package.json +6 -4
- package/dist/chunk-2LDBR3F3.js.map +0 -1
- package/dist/chunk-ARAOZCZT.js.map +0 -1
- package/dist/chunk-BGZ7J74D.js.map +0 -1
- package/dist/chunk-UPS5BGER.js +0 -182
- package/dist/chunk-UPS5BGER.js.map +0 -1
- package/dist/chunk-WK7MPK2T.js.map +0 -1
- package/dist/chunk-WV3NIPWJ.js.map +0 -1
- package/dist/chunk-YMEXDDTA.js.map +0 -1
- /package/dist/{esm-MDK7CZID.js.map → chunk-NSBPE2FW.js.map} +0 -0
- /package/dist/{monorepo-YILKGQXQ.js.map → esm-KBPHCVB4.js.map} +0 -0
- /package/dist/{source-map-uploader-3GWUQDTS.js.map → monorepo-N5Z63XP7.js.map} +0 -0
package/dist/cli/mcp-add.js
CHANGED
|
@@ -3,28 +3,27 @@ import {
|
|
|
3
3
|
generateInfoSection,
|
|
4
4
|
generateMcpConfig,
|
|
5
5
|
injectInfoSection,
|
|
6
|
-
scaffoldMcpMarker,
|
|
7
6
|
updateGitignore,
|
|
8
7
|
writeMcpConfig
|
|
9
|
-
} from "../chunk-
|
|
8
|
+
} from "../chunk-CTJI2YKA.js";
|
|
10
9
|
import {
|
|
11
10
|
readAnonKey
|
|
12
|
-
} from "../chunk-
|
|
13
|
-
import "../chunk-
|
|
11
|
+
} from "../chunk-TM5NKZTO.js";
|
|
12
|
+
import "../chunk-7JBKXSBU.js";
|
|
13
|
+
import {
|
|
14
|
+
scaffoldMcpMarker
|
|
15
|
+
} from "../chunk-O63DJKIJ.js";
|
|
14
16
|
import {
|
|
15
17
|
MCP_ENDPOINT,
|
|
16
18
|
formatAgentName
|
|
17
|
-
} from "../chunk-
|
|
18
|
-
import
|
|
19
|
-
init_esm_shims
|
|
20
|
-
} from "../chunk-BGZ7J74D.js";
|
|
19
|
+
} from "../chunk-DXRZKKSO.js";
|
|
20
|
+
import "../chunk-NSBPE2FW.js";
|
|
21
21
|
|
|
22
22
|
// src/cli/mcp-add.ts
|
|
23
|
-
|
|
24
|
-
import
|
|
25
|
-
import * as
|
|
26
|
-
import
|
|
27
|
-
import { promisify } from "util";
|
|
23
|
+
import { execFile as execFileCb } from "node:child_process";
|
|
24
|
+
import * as fs from "node:fs";
|
|
25
|
+
import * as path from "node:path";
|
|
26
|
+
import { promisify } from "node:util";
|
|
28
27
|
var execFileAsync = promisify(execFileCb);
|
|
29
28
|
async function registerViaCli(agent, anonKey) {
|
|
30
29
|
if (!agent.cliAvailable) {
|
package/dist/cli/mcp-add.js.map
CHANGED
|
@@ -1 +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\";\nimport { MCP_ENDPOINT, formatAgentName } from \"./constants.js\";\n\nconst execFileAsync = promisify(execFileCb);\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":[]}
|
|
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\";\nimport { MCP_ENDPOINT, formatAgentName } from \"./constants.js\";\n\nconst execFileAsync = promisify(execFileCb);\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/cli/status.cjs
CHANGED
|
@@ -33,8 +33,8 @@ __export(status_exports, {
|
|
|
33
33
|
runStatus: () => runStatus
|
|
34
34
|
});
|
|
35
35
|
module.exports = __toCommonJS(status_exports);
|
|
36
|
-
var fs = __toESM(require("fs"), 1);
|
|
37
|
-
var path = __toESM(require("path"), 1);
|
|
36
|
+
var fs = __toESM(require("node:fs"), 1);
|
|
37
|
+
var path = __toESM(require("node:path"), 1);
|
|
38
38
|
|
|
39
39
|
// src/cli/constants.ts
|
|
40
40
|
var NEXT_CONFIG_NAMES = ["next.config.ts", "next.config.js", "next.config.mjs"];
|
package/dist/cli/status.js
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
NEXT_CONFIG_NAMES
|
|
3
|
-
} from "../chunk-
|
|
4
|
-
import
|
|
5
|
-
init_esm_shims
|
|
6
|
-
} from "../chunk-BGZ7J74D.js";
|
|
3
|
+
} from "../chunk-DXRZKKSO.js";
|
|
4
|
+
import "../chunk-NSBPE2FW.js";
|
|
7
5
|
|
|
8
6
|
// src/cli/status.ts
|
|
9
|
-
|
|
10
|
-
import * as
|
|
11
|
-
import * as path from "path";
|
|
7
|
+
import * as fs from "node:fs";
|
|
8
|
+
import * as path from "node:path";
|
|
12
9
|
var MCP_JSON_FILES = [".mcp.json", ".cursor/mcp.json", ".gemini/settings.json", ".glasstrace/mcp.json"];
|
|
13
10
|
var MCP_TOML_FILES = [".codex/config.toml"];
|
|
14
11
|
var AGENT_INFO_FILES = [
|
package/dist/cli/status.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cli/status.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { NEXT_CONFIG_NAMES } from \"./constants.js\";\n\n/**\n * JSON-based MCP config files that init may create.\n * Includes .glasstrace/mcp.json (CI/generic fallback) in addition to the\n * agent-specific files that uninit.ts handles.\n */\nconst MCP_JSON_FILES = [\".mcp.json\", \".cursor/mcp.json\", \".gemini/settings.json\", \".glasstrace/mcp.json\"] as const;\n\n/**\n * TOML-based MCP config files (Codex uses this format).\n */\nconst MCP_TOML_FILES = [\".codex/config.toml\"] as const;\n\n/**\n * Agent info files that may contain glasstrace marker sections.\n */\nconst AGENT_INFO_FILES = [\n \"CLAUDE.md\",\n \"codex.md\",\n \".cursorrules\",\n] as const;\n\n/**\n * Instrumentation file names in priority order.\n */\nconst INSTRUMENTATION_FILES = [\n \"instrumentation.ts\",\n \"instrumentation.js\",\n \"instrumentation.mjs\",\n \"src/instrumentation.ts\",\n \"src/instrumentation.js\",\n \"src/instrumentation.mjs\",\n] as const;\n\n/**\n * Machine-readable SDK configuration state.\n * This interface is the public contract for AI agents — fields may be added\n * but never removed or renamed without a major version bump.\n */\n/** Runtime state snapshot read from .glasstrace/runtime-state.json. */\nexport interface RuntimeStateSnapshot {\n /** Whether the runtime state file exists and was readable. */\n available: boolean;\n /** Whether the process that wrote the state is likely still running. */\n stale: boolean;\n /** Core lifecycle state (e.g., \"ACTIVE\", \"KEY_PENDING\", \"SHUTDOWN\"). */\n coreState: string | null;\n /** Auth lifecycle state (e.g., \"ANONYMOUS\", \"AUTHENTICATED\"). */\n authState: string | null;\n /** OTel coexistence state (e.g., \"OWNS_PROVIDER\", \"AUTO_ATTACHED\"). */\n otelState: string | null;\n /** OTel scenario (e.g., \"A\", \"B-auto\"). */\n otelScenario: string | null;\n /** When the state was last written. */\n updatedAt: string | null;\n /** PID of the process that wrote the state. */\n pid: number | null;\n}\n\nexport interface StatusResult {\n /** Whether @glasstrace/sdk is in package.json dependencies or devDependencies. */\n installed: boolean;\n /** Whether the .glasstrace/ directory exists. */\n initialized: boolean;\n /** Whether an instrumentation file exists with registerGlasstrace(). */\n instrumentation: boolean;\n /** Whether next.config is wrapped with withGlasstraceConfig(). */\n configWrapped: boolean;\n /** Whether .glasstrace/anon_key exists. */\n anonKey: boolean;\n /** Whether any MCP config file has a glasstrace server entry. */\n mcpConfigured: boolean;\n /** Which agent info files have glasstrace marker sections. */\n agents: string[];\n /** Runtime state from the running SDK process (if available). */\n runtime: RuntimeStateSnapshot;\n}\n\n/**\n * Options for the status command.\n */\nexport interface StatusOptions {\n projectRoot: string;\n}\n\n/**\n * Checks SDK configuration state by reading filesystem markers.\n * This function is read-only — it never modifies files or creates directories.\n */\nexport function runStatus(options: StatusOptions): StatusResult {\n const root = options.projectRoot;\n\n return {\n installed: checkInstalled(root),\n initialized: checkInitialized(root),\n instrumentation: checkInstrumentation(root),\n configWrapped: checkConfigWrapped(root),\n anonKey: checkAnonKey(root),\n mcpConfigured: checkMcpConfigured(root),\n agents: checkAgents(root),\n runtime: readRuntimeState(root),\n };\n}\n\nfunction checkInstalled(root: string): boolean {\n try {\n const pkgPath = path.join(root, \"package.json\");\n const content = fs.readFileSync(pkgPath, \"utf-8\");\n const pkg = JSON.parse(content) as Record<string, unknown>;\n const deps = pkg[\"dependencies\"] as Record<string, unknown> | undefined;\n const devDeps = pkg[\"devDependencies\"] as Record<string, unknown> | undefined;\n return (\n (deps != null && \"@glasstrace/sdk\" in deps) ||\n (devDeps != null && \"@glasstrace/sdk\" in devDeps)\n );\n } catch {\n return false;\n }\n}\n\nfunction checkInitialized(root: string): boolean {\n try {\n return fs.statSync(path.join(root, \".glasstrace\")).isDirectory();\n } catch {\n return false;\n }\n}\n\nfunction checkInstrumentation(root: string): boolean {\n for (const name of INSTRUMENTATION_FILES) {\n try {\n const content = fs.readFileSync(path.join(root, name), \"utf-8\");\n if (content.includes(\"registerGlasstrace\")) {\n return true;\n }\n } catch {\n // File doesn't exist or is unreadable — try next\n }\n }\n return false;\n}\n\nfunction checkConfigWrapped(root: string): boolean {\n for (const name of NEXT_CONFIG_NAMES) {\n try {\n const content = fs.readFileSync(path.join(root, name), \"utf-8\");\n if (content.includes(\"withGlasstraceConfig\")) {\n return true;\n }\n } catch {\n // File doesn't exist or is unreadable — try next\n }\n }\n return false;\n}\n\nfunction checkAnonKey(root: string): boolean {\n try {\n return fs.statSync(path.join(root, \".glasstrace\", \"anon_key\")).isFile();\n } catch {\n return false;\n }\n}\n\nfunction checkMcpConfigured(root: string): boolean {\n // Check JSON-based MCP config files\n for (const name of MCP_JSON_FILES) {\n try {\n const content = fs.readFileSync(path.join(root, name), \"utf-8\");\n const parsed = JSON.parse(content) as Record<string, unknown>;\n const mcpServers = parsed[\"mcpServers\"] as Record<string, unknown> | undefined;\n if (mcpServers && typeof mcpServers === \"object\" && \"glasstrace\" in mcpServers) {\n return true;\n }\n } catch {\n // File doesn't exist, is unreadable, or has invalid JSON — try next\n }\n }\n\n // Check TOML-based MCP config files (Codex)\n for (const name of MCP_TOML_FILES) {\n try {\n const content = fs.readFileSync(path.join(root, name), \"utf-8\");\n if (content.includes(\"[mcp_servers.glasstrace]\")) {\n return true;\n }\n } catch {\n // File doesn't exist or is unreadable — try next\n }\n }\n\n return false;\n}\n\nfunction checkAgents(root: string): string[] {\n const found: string[] = [];\n for (const name of AGENT_INFO_FILES) {\n try {\n const content = fs.readFileSync(path.join(root, name), \"utf-8\");\n const hasHtmlMarkers =\n content.includes(\"<!-- glasstrace:mcp:start -->\") &&\n content.includes(\"<!-- glasstrace:mcp:end -->\");\n const hasHashMarkers =\n content.includes(\"# glasstrace:mcp:start\") &&\n content.includes(\"# glasstrace:mcp:end\");\n if (hasHtmlMarkers || hasHashMarkers) {\n found.push(name);\n }\n } catch {\n // File doesn't exist or is unreadable — skip\n }\n }\n return found;\n}\n\nconst STALE_THRESHOLD_MS = 30_000; // 30 seconds\n\nfunction readRuntimeState(root: string): RuntimeStateSnapshot {\n const empty: RuntimeStateSnapshot = {\n available: false,\n stale: false,\n coreState: null,\n authState: null,\n otelState: null,\n otelScenario: null,\n updatedAt: null,\n pid: null,\n };\n\n try {\n const filePath = path.join(root, \".glasstrace\", \"runtime-state.json\");\n const content = fs.readFileSync(filePath, \"utf-8\");\n const parsed = JSON.parse(content) as Record<string, unknown>;\n\n const updatedAt = typeof parsed.updatedAt === \"string\" ? parsed.updatedAt : null;\n const pid = typeof parsed.pid === \"number\" ? parsed.pid : null;\n const core = parsed.core as Record<string, unknown> | undefined;\n const auth = parsed.auth as Record<string, unknown> | undefined;\n const otel = parsed.otel as Record<string, unknown> | undefined;\n\n const coreState = typeof core?.state === \"string\" ? core.state : null;\n const authState = typeof auth?.state === \"string\" ? auth.state : null;\n const otelState = typeof otel?.state === \"string\" ? otel.state : null;\n const otelScenario = typeof otel?.scenario === \"string\" ? otel.scenario : null;\n\n // Staleness detection\n let stale = false;\n if (coreState === \"SHUTDOWN\") {\n stale = false; // Clean shutdown — not stale, just finished\n } else if (updatedAt) {\n const updatedMs = new Date(updatedAt).getTime();\n const age = Number.isFinite(updatedMs) ? Date.now() - updatedMs : Infinity;\n if (age > STALE_THRESHOLD_MS) {\n // Check if the process is still running\n if (pid && pid > 0) {\n try {\n process.kill(pid, 0); // Signal 0 = existence check\n // If we get here, process exists. EPERM would also throw,\n // but with code \"EPERM\" — meaning the process exists but\n // we lack permission. Both mean \"not stale.\"\n stale = false;\n } catch (err: unknown) {\n const code = (err as { code?: string })?.code;\n if (code === \"EPERM\") {\n stale = false; // Process exists, we just can't signal it\n } else {\n stale = true; // ESRCH or other — process gone\n }\n }\n } else {\n stale = true; // No valid PID — can't verify\n }\n }\n }\n\n return {\n available: true,\n stale,\n coreState,\n authState,\n otelState,\n otelScenario,\n updatedAt,\n pid,\n };\n } catch {\n return empty;\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/cli/status.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { NEXT_CONFIG_NAMES } from \"./constants.js\";\n\n/**\n * JSON-based MCP config files that init may create.\n * Includes .glasstrace/mcp.json (CI/generic fallback) in addition to the\n * agent-specific files that uninit.ts handles.\n */\nconst MCP_JSON_FILES = [\".mcp.json\", \".cursor/mcp.json\", \".gemini/settings.json\", \".glasstrace/mcp.json\"] as const;\n\n/**\n * TOML-based MCP config files (Codex uses this format).\n */\nconst MCP_TOML_FILES = [\".codex/config.toml\"] as const;\n\n/**\n * Agent info files that may contain glasstrace marker sections.\n */\nconst AGENT_INFO_FILES = [\n \"CLAUDE.md\",\n \"codex.md\",\n \".cursorrules\",\n] as const;\n\n/**\n * Instrumentation file names in priority order.\n */\nconst INSTRUMENTATION_FILES = [\n \"instrumentation.ts\",\n \"instrumentation.js\",\n \"instrumentation.mjs\",\n \"src/instrumentation.ts\",\n \"src/instrumentation.js\",\n \"src/instrumentation.mjs\",\n] as const;\n\n/**\n * Machine-readable SDK configuration state.\n * This interface is the public contract for AI agents — fields may be added\n * but never removed or renamed without a major version bump.\n */\n/** Runtime state snapshot read from .glasstrace/runtime-state.json. */\nexport interface RuntimeStateSnapshot {\n /** Whether the runtime state file exists and was readable. */\n available: boolean;\n /** Whether the process that wrote the state is likely still running. */\n stale: boolean;\n /** Core lifecycle state (e.g., \"ACTIVE\", \"KEY_PENDING\", \"SHUTDOWN\"). */\n coreState: string | null;\n /** Auth lifecycle state (e.g., \"ANONYMOUS\", \"AUTHENTICATED\"). */\n authState: string | null;\n /** OTel coexistence state (e.g., \"OWNS_PROVIDER\", \"AUTO_ATTACHED\"). */\n otelState: string | null;\n /** OTel scenario (e.g., \"A\", \"B-auto\"). */\n otelScenario: string | null;\n /** When the state was last written. */\n updatedAt: string | null;\n /** PID of the process that wrote the state. */\n pid: number | null;\n}\n\nexport interface StatusResult {\n /** Whether @glasstrace/sdk is in package.json dependencies or devDependencies. */\n installed: boolean;\n /** Whether the .glasstrace/ directory exists. */\n initialized: boolean;\n /** Whether an instrumentation file exists with registerGlasstrace(). */\n instrumentation: boolean;\n /** Whether next.config is wrapped with withGlasstraceConfig(). */\n configWrapped: boolean;\n /** Whether .glasstrace/anon_key exists. */\n anonKey: boolean;\n /** Whether any MCP config file has a glasstrace server entry. */\n mcpConfigured: boolean;\n /** Which agent info files have glasstrace marker sections. */\n agents: string[];\n /** Runtime state from the running SDK process (if available). */\n runtime: RuntimeStateSnapshot;\n}\n\n/**\n * Options for the status command.\n */\nexport interface StatusOptions {\n projectRoot: string;\n}\n\n/**\n * Checks SDK configuration state by reading filesystem markers.\n * This function is read-only — it never modifies files or creates directories.\n */\nexport function runStatus(options: StatusOptions): StatusResult {\n const root = options.projectRoot;\n\n return {\n installed: checkInstalled(root),\n initialized: checkInitialized(root),\n instrumentation: checkInstrumentation(root),\n configWrapped: checkConfigWrapped(root),\n anonKey: checkAnonKey(root),\n mcpConfigured: checkMcpConfigured(root),\n agents: checkAgents(root),\n runtime: readRuntimeState(root),\n };\n}\n\nfunction checkInstalled(root: string): boolean {\n try {\n const pkgPath = path.join(root, \"package.json\");\n const content = fs.readFileSync(pkgPath, \"utf-8\");\n const pkg = JSON.parse(content) as Record<string, unknown>;\n const deps = pkg[\"dependencies\"] as Record<string, unknown> | undefined;\n const devDeps = pkg[\"devDependencies\"] as Record<string, unknown> | undefined;\n return (\n (deps != null && \"@glasstrace/sdk\" in deps) ||\n (devDeps != null && \"@glasstrace/sdk\" in devDeps)\n );\n } catch {\n return false;\n }\n}\n\nfunction checkInitialized(root: string): boolean {\n try {\n return fs.statSync(path.join(root, \".glasstrace\")).isDirectory();\n } catch {\n return false;\n }\n}\n\nfunction checkInstrumentation(root: string): boolean {\n for (const name of INSTRUMENTATION_FILES) {\n try {\n const content = fs.readFileSync(path.join(root, name), \"utf-8\");\n if (content.includes(\"registerGlasstrace\")) {\n return true;\n }\n } catch {\n // File doesn't exist or is unreadable — try next\n }\n }\n return false;\n}\n\nfunction checkConfigWrapped(root: string): boolean {\n for (const name of NEXT_CONFIG_NAMES) {\n try {\n const content = fs.readFileSync(path.join(root, name), \"utf-8\");\n if (content.includes(\"withGlasstraceConfig\")) {\n return true;\n }\n } catch {\n // File doesn't exist or is unreadable — try next\n }\n }\n return false;\n}\n\nfunction checkAnonKey(root: string): boolean {\n try {\n return fs.statSync(path.join(root, \".glasstrace\", \"anon_key\")).isFile();\n } catch {\n return false;\n }\n}\n\nfunction checkMcpConfigured(root: string): boolean {\n // Check JSON-based MCP config files\n for (const name of MCP_JSON_FILES) {\n try {\n const content = fs.readFileSync(path.join(root, name), \"utf-8\");\n const parsed = JSON.parse(content) as Record<string, unknown>;\n const mcpServers = parsed[\"mcpServers\"] as Record<string, unknown> | undefined;\n if (mcpServers && typeof mcpServers === \"object\" && \"glasstrace\" in mcpServers) {\n return true;\n }\n } catch {\n // File doesn't exist, is unreadable, or has invalid JSON — try next\n }\n }\n\n // Check TOML-based MCP config files (Codex)\n for (const name of MCP_TOML_FILES) {\n try {\n const content = fs.readFileSync(path.join(root, name), \"utf-8\");\n if (content.includes(\"[mcp_servers.glasstrace]\")) {\n return true;\n }\n } catch {\n // File doesn't exist or is unreadable — try next\n }\n }\n\n return false;\n}\n\nfunction checkAgents(root: string): string[] {\n const found: string[] = [];\n for (const name of AGENT_INFO_FILES) {\n try {\n const content = fs.readFileSync(path.join(root, name), \"utf-8\");\n const hasHtmlMarkers =\n content.includes(\"<!-- glasstrace:mcp:start -->\") &&\n content.includes(\"<!-- glasstrace:mcp:end -->\");\n const hasHashMarkers =\n content.includes(\"# glasstrace:mcp:start\") &&\n content.includes(\"# glasstrace:mcp:end\");\n if (hasHtmlMarkers || hasHashMarkers) {\n found.push(name);\n }\n } catch {\n // File doesn't exist or is unreadable — skip\n }\n }\n return found;\n}\n\nconst STALE_THRESHOLD_MS = 30_000; // 30 seconds\n\nfunction readRuntimeState(root: string): RuntimeStateSnapshot {\n const empty: RuntimeStateSnapshot = {\n available: false,\n stale: false,\n coreState: null,\n authState: null,\n otelState: null,\n otelScenario: null,\n updatedAt: null,\n pid: null,\n };\n\n try {\n const filePath = path.join(root, \".glasstrace\", \"runtime-state.json\");\n const content = fs.readFileSync(filePath, \"utf-8\");\n const parsed = JSON.parse(content) as Record<string, unknown>;\n\n const updatedAt = typeof parsed.updatedAt === \"string\" ? parsed.updatedAt : null;\n const pid = typeof parsed.pid === \"number\" ? parsed.pid : null;\n const core = parsed.core as Record<string, unknown> | undefined;\n const auth = parsed.auth as Record<string, unknown> | undefined;\n const otel = parsed.otel as Record<string, unknown> | undefined;\n\n const coreState = typeof core?.state === \"string\" ? core.state : null;\n const authState = typeof auth?.state === \"string\" ? auth.state : null;\n const otelState = typeof otel?.state === \"string\" ? otel.state : null;\n const otelScenario = typeof otel?.scenario === \"string\" ? otel.scenario : null;\n\n // Staleness detection\n let stale = false;\n if (coreState === \"SHUTDOWN\") {\n stale = false; // Clean shutdown — not stale, just finished\n } else if (updatedAt) {\n const updatedMs = new Date(updatedAt).getTime();\n const age = Number.isFinite(updatedMs) ? Date.now() - updatedMs : Infinity;\n if (age > STALE_THRESHOLD_MS) {\n // Check if the process is still running\n if (pid && pid > 0) {\n try {\n process.kill(pid, 0); // Signal 0 = existence check\n // If we get here, process exists. EPERM would also throw,\n // but with code \"EPERM\" — meaning the process exists but\n // we lack permission. Both mean \"not stale.\"\n stale = false;\n } catch (err: unknown) {\n const code = (err as { code?: string })?.code;\n if (code === \"EPERM\") {\n stale = false; // Process exists, we just can't signal it\n } else {\n stale = true; // ESRCH or other — process gone\n }\n }\n } else {\n stale = true; // No valid PID — can't verify\n }\n }\n }\n\n return {\n available: true,\n stale,\n coreState,\n authState,\n otelState,\n otelScenario,\n updatedAt,\n pid,\n };\n } catch {\n return empty;\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/cli/uninit.cjs
CHANGED
|
@@ -41,16 +41,36 @@ __export(uninit_exports, {
|
|
|
41
41
|
runUninit: () => runUninit,
|
|
42
42
|
skipString: () => skipString,
|
|
43
43
|
unwrapCJSExport: () => unwrapCJSExport,
|
|
44
|
-
unwrapExport: () => unwrapExport
|
|
44
|
+
unwrapExport: () => unwrapExport,
|
|
45
|
+
writeShutdownMarker: () => writeShutdownMarker
|
|
45
46
|
});
|
|
46
47
|
module.exports = __toCommonJS(uninit_exports);
|
|
47
|
-
var fs = __toESM(require("fs"), 1);
|
|
48
|
-
var os = __toESM(require("os"), 1);
|
|
49
|
-
var path = __toESM(require("path"), 1);
|
|
48
|
+
var fs = __toESM(require("node:fs"), 1);
|
|
49
|
+
var os = __toESM(require("node:os"), 1);
|
|
50
|
+
var path = __toESM(require("node:path"), 1);
|
|
50
51
|
|
|
51
52
|
// src/cli/constants.ts
|
|
52
53
|
var NEXT_CONFIG_NAMES = ["next.config.ts", "next.config.js", "next.config.mjs"];
|
|
53
54
|
|
|
55
|
+
// src/cli/scaffolder.ts
|
|
56
|
+
function readEnvLocalApiKey(content) {
|
|
57
|
+
let last = null;
|
|
58
|
+
const regex = /^\s*GLASSTRACE_API_KEY\s*=\s*(.*)$/gm;
|
|
59
|
+
let match;
|
|
60
|
+
while ((match = regex.exec(content)) !== null) {
|
|
61
|
+
const raw = match[1].trim();
|
|
62
|
+
if (raw === "") continue;
|
|
63
|
+
const unquoted = raw.replace(/^(['"])(.*)\1$/, "$2");
|
|
64
|
+
if (unquoted === "" || unquoted === "your_key_here") continue;
|
|
65
|
+
last = unquoted;
|
|
66
|
+
}
|
|
67
|
+
return last;
|
|
68
|
+
}
|
|
69
|
+
function isDevApiKey(value) {
|
|
70
|
+
if (value === null || value === void 0) return false;
|
|
71
|
+
return value.trim().startsWith("gt_dev_");
|
|
72
|
+
}
|
|
73
|
+
|
|
54
74
|
// src/cli/uninit.ts
|
|
55
75
|
var MCP_CONFIG_FILES = [".mcp.json", ".cursor/mcp.json", ".gemini/settings.json"];
|
|
56
76
|
var AGENT_INFO_FILES = [
|
|
@@ -357,12 +377,75 @@ function processTomlMcpConfig(content) {
|
|
|
357
377
|
}
|
|
358
378
|
return { action: "removed-section", content: result + "\n" };
|
|
359
379
|
}
|
|
380
|
+
function writeShutdownMarker(projectRoot) {
|
|
381
|
+
const dirPath = path.join(projectRoot, ".glasstrace");
|
|
382
|
+
if (!fs.existsSync(dirPath)) {
|
|
383
|
+
return false;
|
|
384
|
+
}
|
|
385
|
+
const markerPath = path.join(dirPath, "shutdown-requested");
|
|
386
|
+
const tmpPath = `${markerPath}.tmp`;
|
|
387
|
+
const body = JSON.stringify({ requestedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
388
|
+
try {
|
|
389
|
+
fs.writeFileSync(tmpPath, body, { encoding: "utf-8", mode: 384 });
|
|
390
|
+
try {
|
|
391
|
+
fs.chmodSync(tmpPath, 384);
|
|
392
|
+
} catch {
|
|
393
|
+
}
|
|
394
|
+
fs.renameSync(tmpPath, markerPath);
|
|
395
|
+
return true;
|
|
396
|
+
} catch {
|
|
397
|
+
try {
|
|
398
|
+
fs.unlinkSync(tmpPath);
|
|
399
|
+
} catch {
|
|
400
|
+
}
|
|
401
|
+
return false;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
async function defaultPrompt(question, defaultValue) {
|
|
405
|
+
if (!process.stdin.isTTY) return defaultValue;
|
|
406
|
+
const readline = await import("node:readline");
|
|
407
|
+
const rl = readline.createInterface({
|
|
408
|
+
input: process.stdin,
|
|
409
|
+
output: process.stdout
|
|
410
|
+
});
|
|
411
|
+
return new Promise((resolve) => {
|
|
412
|
+
const suffix = defaultValue ? " [Y/n] " : " [y/N] ";
|
|
413
|
+
rl.question(question + suffix, (answer) => {
|
|
414
|
+
rl.close();
|
|
415
|
+
const trimmed = answer.trim().toLowerCase();
|
|
416
|
+
if (trimmed === "") {
|
|
417
|
+
resolve(defaultValue);
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
resolve(trimmed === "y" || trimmed === "yes");
|
|
421
|
+
});
|
|
422
|
+
});
|
|
423
|
+
}
|
|
360
424
|
async function runUninit(options) {
|
|
361
425
|
const { projectRoot, dryRun } = options;
|
|
426
|
+
const force = options.force === true;
|
|
427
|
+
const prompt = options.prompt ?? defaultPrompt;
|
|
362
428
|
const summary = [];
|
|
363
429
|
const warnings = [];
|
|
364
430
|
const errors = [];
|
|
365
431
|
const prefix = dryRun ? "[dry run] " : "";
|
|
432
|
+
try {
|
|
433
|
+
if (!dryRun) {
|
|
434
|
+
const markerWritten = writeShutdownMarker(projectRoot);
|
|
435
|
+
if (markerWritten) {
|
|
436
|
+
summary.push("Wrote .glasstrace/shutdown-requested marker");
|
|
437
|
+
}
|
|
438
|
+
} else {
|
|
439
|
+
const dirPath = path.join(projectRoot, ".glasstrace");
|
|
440
|
+
if (fs.existsSync(dirPath)) {
|
|
441
|
+
summary.push(`${prefix}Would write .glasstrace/shutdown-requested marker`);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
} catch (err) {
|
|
445
|
+
warnings.push(
|
|
446
|
+
`Shutdown marker write failed: ${err instanceof Error ? err.message : String(err)}`
|
|
447
|
+
);
|
|
448
|
+
}
|
|
366
449
|
try {
|
|
367
450
|
let configHandled = false;
|
|
368
451
|
for (const name of NEXT_CONFIG_NAMES) {
|
|
@@ -445,23 +528,57 @@ async function runUninit(options) {
|
|
|
445
528
|
const envPath = path.join(projectRoot, ".env.local");
|
|
446
529
|
if (fs.existsSync(envPath)) {
|
|
447
530
|
const content = fs.readFileSync(envPath, "utf-8");
|
|
448
|
-
const
|
|
449
|
-
const
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
if (
|
|
456
|
-
|
|
457
|
-
fs.unlinkSync(envPath);
|
|
458
|
-
}
|
|
459
|
-
summary.push(`${prefix}Deleted .env.local (no remaining entries)`);
|
|
531
|
+
const existingKey = readEnvLocalApiKey(content);
|
|
532
|
+
const hasDevKey = isDevApiKey(existingKey);
|
|
533
|
+
let proceed = true;
|
|
534
|
+
let devKeyPath = "none";
|
|
535
|
+
if (hasDevKey) {
|
|
536
|
+
if (dryRun) {
|
|
537
|
+
devKeyPath = "dry-run-preview";
|
|
538
|
+
} else if (force) {
|
|
539
|
+
devKeyPath = "force-bypass";
|
|
460
540
|
} else {
|
|
461
|
-
|
|
462
|
-
|
|
541
|
+
const confirmed = await prompt(
|
|
542
|
+
".env.local contains a claimed Glasstrace developer API key (gt_dev_...). Removing it will require you to re-authenticate. Continue?",
|
|
543
|
+
false
|
|
544
|
+
);
|
|
545
|
+
proceed = confirmed;
|
|
546
|
+
if (confirmed) devKeyPath = "interactive-confirmed";
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
if (!proceed) {
|
|
550
|
+
warnings.push(
|
|
551
|
+
"Preserved GLASSTRACE_API_KEY in .env.local (claimed dev key; re-run with --force to remove)"
|
|
552
|
+
);
|
|
553
|
+
} else {
|
|
554
|
+
const lines = content.split("\n");
|
|
555
|
+
const filtered = lines.filter((line) => {
|
|
556
|
+
const trimmed = line.trim();
|
|
557
|
+
return !(/^\s*#?\s*GLASSTRACE_API_KEY\s*=/.test(trimmed) || /^\s*#?\s*GLASSTRACE_COVERAGE_MAP\s*=/.test(trimmed));
|
|
558
|
+
});
|
|
559
|
+
if (filtered.length !== lines.length) {
|
|
560
|
+
const result = filtered.join("\n");
|
|
561
|
+
if (result.trim().length === 0) {
|
|
562
|
+
if (!dryRun) {
|
|
563
|
+
fs.unlinkSync(envPath);
|
|
564
|
+
}
|
|
565
|
+
summary.push(`${prefix}Deleted .env.local (no remaining entries)`);
|
|
566
|
+
} else {
|
|
567
|
+
if (!dryRun) {
|
|
568
|
+
fs.writeFileSync(envPath, result, "utf-8");
|
|
569
|
+
}
|
|
570
|
+
let devKeyAnnotation = "";
|
|
571
|
+
if (devKeyPath === "interactive-confirmed") {
|
|
572
|
+
devKeyAnnotation = " (dev key confirmed)";
|
|
573
|
+
} else if (devKeyPath === "force-bypass") {
|
|
574
|
+
devKeyAnnotation = " (dev key removed via --force)";
|
|
575
|
+
} else if (devKeyPath === "dry-run-preview") {
|
|
576
|
+
devKeyAnnotation = " (dev key would be removed; real run would require confirmation)";
|
|
577
|
+
}
|
|
578
|
+
summary.push(
|
|
579
|
+
`${prefix}Removed GLASSTRACE entries from .env.local${devKeyAnnotation}`
|
|
580
|
+
);
|
|
463
581
|
}
|
|
464
|
-
summary.push(`${prefix}Removed GLASSTRACE entries from .env.local`);
|
|
465
582
|
}
|
|
466
583
|
}
|
|
467
584
|
}
|
|
@@ -621,6 +738,7 @@ async function runUninit(options) {
|
|
|
621
738
|
runUninit,
|
|
622
739
|
skipString,
|
|
623
740
|
unwrapCJSExport,
|
|
624
|
-
unwrapExport
|
|
741
|
+
unwrapExport,
|
|
742
|
+
writeShutdownMarker
|
|
625
743
|
});
|
|
626
744
|
//# sourceMappingURL=uninit.cjs.map
|