@glasstrace/sdk 1.9.1 → 1.10.1
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/async-context/index.cjs +44 -0
- package/dist/async-context/index.cjs.map +1 -1
- package/dist/async-context/index.js +2 -2
- package/dist/{chunk-JHUNLPSS.js → chunk-6RKS3DNA.js} +45 -1
- package/dist/{chunk-JHUNLPSS.js.map → chunk-6RKS3DNA.js.map} +1 -1
- package/dist/{chunk-HD6JIFKN.js → chunk-BSVWJSVX.js} +2 -2
- package/dist/{chunk-QOHKZOKB.js → chunk-D54FMQHF.js} +2 -2
- package/dist/chunk-I2DVVSKW.js +419 -0
- package/dist/chunk-I2DVVSKW.js.map +1 -0
- package/dist/{chunk-H6WJ63X2.js → chunk-M5GO2SSO.js} +2 -2
- package/dist/{chunk-QEXRCXSY.js → chunk-OXM2BZMF.js} +2 -2
- package/dist/{chunk-M6EWJCAT.js → chunk-QVTONMVZ.js} +2 -2
- package/dist/{chunk-DKV53A2C.js → chunk-RL43PU2X.js} +2 -2
- package/dist/{chunk-KI7YJ7XD.js → chunk-TJ46YOGJ.js} +42 -22
- package/dist/chunk-TJ46YOGJ.js.map +1 -0
- package/dist/{chunk-GWIEUBFR.js → chunk-UMGZJYC4.js} +3 -3
- package/dist/{chunk-2F2MGFLO.js → chunk-WQF7ZQOM.js} +39 -405
- package/dist/chunk-WQF7ZQOM.js.map +1 -0
- package/dist/{chunk-QXITSNYM.js → chunk-XG6WR2KS.js} +3 -3
- package/dist/cli/init.cjs +51 -25
- package/dist/cli/init.cjs.map +1 -1
- package/dist/cli/init.js +8 -8
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/mcp-add.cjs +42 -22
- package/dist/cli/mcp-add.cjs.map +1 -1
- package/dist/cli/mcp-add.js +4 -4
- package/dist/cli/mcp-add.js.map +1 -1
- package/dist/cli/uninit.js +3 -3
- package/dist/cli/upgrade-instructions.cjs +39 -21
- package/dist/cli/upgrade-instructions.cjs.map +1 -1
- package/dist/cli/upgrade-instructions.js +4 -4
- package/dist/cli/upgrade-instructions.js.map +1 -1
- package/dist/cli/validate.cjs.map +1 -1
- package/dist/cli/validate.js +2 -2
- package/dist/edge-entry.cjs +44 -0
- package/dist/edge-entry.cjs.map +1 -1
- package/dist/edge-entry.js +4 -4
- package/dist/index.cjs +58 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +10 -8
- package/dist/index.js.map +1 -1
- package/dist/middleware/index.cjs +44 -0
- package/dist/middleware/index.cjs.map +1 -1
- package/dist/middleware/index.js +2 -2
- package/dist/node-entry.cjs +58 -5
- package/dist/node-entry.cjs.map +1 -1
- package/dist/node-entry.js +12 -10
- package/dist/node-subpath.cjs.map +1 -1
- package/dist/node-subpath.js +3 -3
- package/dist/{source-map-uploader-MMJ2WCL4.js → source-map-uploader-CLYCE2TZ.js} +3 -3
- package/dist/trpc/index.cjs +15164 -503
- package/dist/trpc/index.cjs.map +1 -1
- package/dist/trpc/index.d.cts +62 -1
- package/dist/trpc/index.d.ts +62 -1
- package/dist/trpc/index.js +200 -1
- package/dist/trpc/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-2F2MGFLO.js.map +0 -1
- package/dist/chunk-KI7YJ7XD.js.map +0 -1
- /package/dist/{chunk-HD6JIFKN.js.map → chunk-BSVWJSVX.js.map} +0 -0
- /package/dist/{chunk-QOHKZOKB.js.map → chunk-D54FMQHF.js.map} +0 -0
- /package/dist/{chunk-H6WJ63X2.js.map → chunk-M5GO2SSO.js.map} +0 -0
- /package/dist/{chunk-QEXRCXSY.js.map → chunk-OXM2BZMF.js.map} +0 -0
- /package/dist/{chunk-M6EWJCAT.js.map → chunk-QVTONMVZ.js.map} +0 -0
- /package/dist/{chunk-DKV53A2C.js.map → chunk-RL43PU2X.js.map} +0 -0
- /package/dist/{chunk-GWIEUBFR.js.map → chunk-UMGZJYC4.js.map} +0 -0
- /package/dist/{chunk-QXITSNYM.js.map → chunk-XG6WR2KS.js.map} +0 -0
- /package/dist/{source-map-uploader-MMJ2WCL4.js.map → source-map-uploader-CLYCE2TZ.js.map} +0 -0
package/dist/cli/mcp-add.js
CHANGED
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
detectAgents,
|
|
3
3
|
generateInfoSection,
|
|
4
4
|
generateMcpConfig
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-TJ46YOGJ.js";
|
|
6
6
|
import {
|
|
7
7
|
MCP_ENDPOINT,
|
|
8
8
|
identityFingerprint,
|
|
@@ -10,8 +10,8 @@ import {
|
|
|
10
10
|
readMcpMarker,
|
|
11
11
|
resolveEffectiveMcpCredential,
|
|
12
12
|
writeMcpMarker
|
|
13
|
-
} from "../chunk-
|
|
14
|
-
import "../chunk-
|
|
13
|
+
} from "../chunk-RL43PU2X.js";
|
|
14
|
+
import "../chunk-6RKS3DNA.js";
|
|
15
15
|
import {
|
|
16
16
|
formatAgentName
|
|
17
17
|
} from "../chunk-NB7GJE4S.js";
|
|
@@ -168,7 +168,7 @@ async function mcpAdd(options) {
|
|
|
168
168
|
const bearer = resolved.effective.key;
|
|
169
169
|
for (const agent of targetAgents) {
|
|
170
170
|
const name = formatAgentName(agent.name);
|
|
171
|
-
const sdkVersion = true ? "1.
|
|
171
|
+
const sdkVersion = true ? "1.10.1" : "0.0.0-dev";
|
|
172
172
|
if (agent.name !== "generic") {
|
|
173
173
|
const cliSuccess = await registerViaCli(agent, bearer);
|
|
174
174
|
if (cliSuccess) {
|
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\";\n\n// Declare the tsup-injected SDK version literal. Replaced at build time\n// via `define` in tsup.config.ts. Falls back to \"0.0.0-dev\" when running\n// under vitest where no tsup build step has populated the constant.\ndeclare const __SDK_VERSION__: string;\nimport {\n isAnonApiKey,\n identityFingerprint,\n MCP_ENDPOINT,\n readMcpMarker,\n resolveEffectiveMcpCredential,\n writeMcpMarker,\n type EffectiveMcpCredential,\n} from \"../mcp-runtime.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 type { DetectedAgent } from \"../agent-detection/detect.js\";\nimport { 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 * **Anon keys only, by design.** This path passes the bearer token to\n * vendor CLIs (Claude, Gemini) as a process argument; on multi-user\n * hosts, process arguments are visible via `ps` and `/proc`. Anon keys\n * are non-secret project identifiers — exposing one in a process\n * listing is acceptable. A claimed dev/account key absolutely is not.\n *\n * Two layers of enforcement:\n *\n * 1. The `bearer` parameter is typed `string` rather than `AnonApiKey`\n * only because the function is called through CLI plumbing where\n * the brand is erased; callers must verify the source upstream.\n * 2. A runtime `isAnonApiKey` guard at the top of the function\n * short-circuits with `false` if the value fails strict\n * `AnonApiKeySchema` validation. This defends against accidental\n * `string`-typed paths that erase the brand and against any\n * future caller that forgets the upstream check.\n *\n * Codex's CLI registration does not embed the bearer (it writes\n * `bearer_token_env_var = \"GLASSTRACE_API_KEY\"` and reads the\n * actual token from the environment), so it is unaffected by this\n * constraint.\n */\n/** @internal Exported for unit testing of the runtime anon-only guard. */\nexport async function registerViaCli(\n agent: DetectedAgent,\n bearer: string,\n): Promise<boolean> {\n if (!agent.cliAvailable) {\n return false;\n }\n\n // Layer 2: runtime guard. If the bearer is not a strictly-valid\n // anon key, refuse to put it in process arguments. The caller is\n // expected to fall through to the file-config path, which writes\n // 0o600 files and never exposes the bearer to other processes.\n if (agent.name !== \"codex\" && !isAnonApiKey(bearer)) {\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 ${bearer}` },\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 ${bearer}`,\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 * Returns whether the on-disk marker describes the same underlying\n * credential as the resolver's effective credential.\n *\n * Compares on `credentialHash` only — the credential identity, not\n * the on-disk *source* it was loaded from. The source can shift\n * without the key actually changing (e.g., a user copies the same\n * key from `.glasstrace/claimed-key` into `.env.local`, or vice\n * versa), and treating that shift as a credential change would\n * falsely trigger the claim-transition refresh on a project whose\n * `mcp.json` already embeds the correct bearer.\n *\n * Treats `unknown-version` and `corrupted` markers as not-matching\n * (forces a re-run that overwrites them with v2). Treats `absent`\n * markers as not-matching too — the caller's existing\n * `fs.existsSync(markerPath)` short-circuit catches that case before\n * calling here, so this branch only fires when a marker read itself\n * failed.\n */\nasync function markerMatchesEffective(\n projectRoot: string,\n effective: EffectiveMcpCredential,\n): Promise<boolean> {\n const state = await readMcpMarker(projectRoot);\n if (state.status !== \"valid\") return false;\n return state.credentialHash === identityFingerprint(effective.key);\n}\n\n/**\n * Registers the Glasstrace MCP server with detected AI coding agents.\n *\n * For each agent, attempts native CLI registration first (anon keys\n * only, see {@link registerViaCli}), then falls back to file-based\n * configuration. The marker file at `.glasstrace/mcp-connected`\n * records the effective credential's source and identity fingerprint\n * so a later run can detect a project that has transitioned from\n * anon to account/dev-key and prompt a refresh.\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: Resolve the effective credential. Replaces the prior\n // anon-only `readAnonKey` path; dev-key-only projects (no\n // .glasstrace/anon_key on disk) now run too.\n const resolved = await resolveEffectiveMcpCredential(projectRoot);\n if (resolved.effective === null) {\n return {\n exitCode: 1,\n results: [],\n messages: [\"Error: Run `glasstrace init` first to generate an API key.\"],\n };\n }\n\n // Optional: surface the claimed-key-only warning so the user knows\n // to copy the key into .env.local for normal use.\n if (resolved.warnings.includes(\"claimed-key-only\")) {\n messages.push(\n \"Note: dev key was loaded from .glasstrace/claimed-key. Copy it into .env.local so your app and Codex pick it up automatically.\",\n );\n }\n\n // Step 2: Marker check.\n //\n // Two short-circuits:\n // - Marker absent and not --force: the legacy \"MCP already\n // configured\" message is no longer applicable here, but absence\n // of the marker means we proceed to register. (The original\n // behaviour was to short-circuit if the marker existed; the\n // short-circuit now also requires the marker to actually match\n // the effective credential.)\n // - Marker present, matches effective credential, and not --force:\n // already configured for this credential, no work to do.\n // - Marker present, mismatches effective credential, regardless of\n // --force: the project has transitioned credentials; treat as\n // unconfigured and re-register.\n const markerPath = path.join(projectRoot, \".glasstrace\", \"mcp-connected\");\n if (fs.existsSync(markerPath) && !force) {\n if (await markerMatchesEffective(projectRoot, resolved.effective)) {\n return {\n exitCode: 0,\n results: [],\n messages: [\"MCP already configured. Use --force to reconfigure.\"],\n };\n }\n // Mismatch: project has transitioned credentials. Fall through to\n // re-register so MCP queries see the same scope as ingestion.\n messages.push(\n \"Detected a credential change since MCP was last configured. Refreshing MCP config so queries use the current account credential.\",\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 // The generic helper backs `.glasstrace/mcp.json`, the file\n // validation/debug tooling reads directly. ALWAYS include it in the\n // target list — when non-generic agents like Claude/Cursor are\n // detected, the helper used to be silently dropped, which left the\n // generic config stale after a credential change. `detectAgents`\n // contract guarantees the generic entry is always appended last\n // (see `agent-detection/detect.ts`), so we rely on that here rather\n // than synthesising a fallback.\n const genericAgent = agents.find((a) => a.name === \"generic\");\n const targetAgents: DetectedAgent[] = genericAgent\n ? [...detectedNonGeneric, genericAgent]\n : detectedNonGeneric;\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 && resolved.effective.source === \"anon\") {\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. The bearer used in the embedded\n // configs and in vendor CLI invocations is the resolver's effective\n // credential. registerViaCli is anon-only by design (see its\n // docstring); when the effective credential is a dev key, that path\n // returns false and the file-config branch takes over.\n const results: AgentResult[] = [];\n const bearer = resolved.effective.key;\n\n for (const agent of targetAgents) {\n const name = formatAgentName(agent.name);\n\n const sdkVersion =\n typeof __SDK_VERSION__ === \"string\" ? __SDK_VERSION__ : \"0.0.0-dev\";\n\n // Try CLI registration first (not applicable for generic)\n if (agent.name !== \"generic\") {\n const cliSuccess = await registerViaCli(agent, bearer);\n if (cliSuccess) {\n // Still inject info section if applicable\n const infoContent = generateInfoSection(agent, MCP_ENDPOINT, sdkVersion);\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, bearer);\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, sdkVersion);\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: Update marker if at least one succeeded. Marker records\n // the effective credential's source and fingerprint (raw key never\n // touches disk via this path), so a later run can detect a\n // credential change.\n const anySuccess = results.some((r) => r.success);\n\n if (anySuccess) {\n await writeMcpMarker(projectRoot, {\n credentialSource: resolved.effective.source,\n credentialHash: identityFingerprint(resolved.effective.key),\n });\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 // Exit code reflects whether the originally-detected non-generic\n // agents succeeded. The generic helper is always in `results` now —\n // letting its success alone mask a complete failure of the agents\n // the user actually has installed would silently break automation\n // that bisects on `mcp add` exit code. Preserves the pre-fix\n // contract: a run where Claude/Cursor failed still exits non-zero,\n // even when the generic helper write succeeded.\n const detectedNonGenericResults = results.filter((r) =>\n detectedNonGeneric.some((a) => a.name === r.agent),\n );\n const allDetectedNonGenericFailed =\n detectedNonGeneric.length > 0 &&\n !detectedNonGenericResults.some((r) => r.success);\n\n if (allDetectedNonGenericFailed) {\n messages.push(\n \"\",\n \"All detected agent registrations failed. Check errors above.\",\n );\n return { exitCode: 1, results, messages };\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\";\n\n// Declare the tsup-injected SDK version literal. Replaced at build time\n// via `define` in tsup.config.ts. Falls back to \"0.0.0-dev\" when running\n// under vitest where no tsup build step has populated the constant.\ndeclare const __SDK_VERSION__: string;\nimport {\n isAnonApiKey,\n identityFingerprint,\n MCP_ENDPOINT,\n readMcpMarker,\n resolveEffectiveMcpCredential,\n writeMcpMarker,\n type EffectiveMcpCredential,\n} from \"../mcp-runtime.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 type { DetectedAgent } from \"../agent-detection/detect.js\";\nimport { 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 * **Anon keys only, by design.** This path passes the bearer token to\n * vendor CLIs (Claude, Gemini) as a process argument; on multi-user\n * hosts, process arguments are visible via `ps` and `/proc`. Anon keys\n * are non-secret project identifiers — exposing one in a process\n * listing is acceptable. A claimed dev/account key absolutely is not.\n *\n * Two layers of enforcement:\n *\n * 1. The `bearer` parameter is typed `string` rather than `AnonApiKey`\n * only because the function is called through CLI plumbing where\n * the brand is erased; callers must verify the source upstream.\n * 2. A runtime `isAnonApiKey` guard at the top of the function\n * short-circuits with `false` if the value fails strict\n * `AnonApiKeySchema` validation. This defends against accidental\n * `string`-typed paths that erase the brand and against any\n * future caller that forgets the upstream check.\n *\n * Codex's CLI registration does not embed the bearer (it writes\n * `bearer_token_env_var = \"GLASSTRACE_API_KEY\"` and reads the\n * actual token from the environment), so it is unaffected by this\n * constraint.\n */\n/** @internal Exported for unit testing of the runtime anon-only guard. */\nexport async function registerViaCli(\n agent: DetectedAgent,\n bearer: string,\n): Promise<boolean> {\n if (!agent.cliAvailable) {\n return false;\n }\n\n // Layer 2: runtime guard. If the bearer is not a strictly-valid\n // anon key, refuse to put it in process arguments. The caller is\n // expected to fall through to the file-config path, which writes\n // 0o600 files and never exposes the bearer to other processes.\n if (agent.name !== \"codex\" && !isAnonApiKey(bearer)) {\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 ${bearer}` },\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 ${bearer}`,\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 * Returns whether the on-disk marker describes the same underlying\n * credential as the resolver's effective credential.\n *\n * Compares on `credentialHash` only — the credential identity, not\n * the on-disk *source* it was loaded from. The source can shift\n * without the key actually changing (e.g., a user copies the same\n * key from `.glasstrace/claimed-key` into `.env.local`, or vice\n * versa), and treating that shift as a credential change would\n * falsely trigger the claim-transition refresh on a project whose\n * `mcp.json` already embeds the correct bearer.\n *\n * Treats `unknown-version` and `corrupted` markers as not-matching\n * (forces a re-run that overwrites them with v2). Treats `absent`\n * markers as not-matching too — the caller's existing\n * `fs.existsSync(markerPath)` short-circuit catches that case before\n * calling here, so this branch only fires when a marker read itself\n * failed.\n */\nasync function markerMatchesEffective(\n projectRoot: string,\n effective: EffectiveMcpCredential,\n): Promise<boolean> {\n const state = await readMcpMarker(projectRoot);\n if (state.status !== \"valid\") return false;\n return state.credentialHash === identityFingerprint(effective.key);\n}\n\n/**\n * Registers the Glasstrace MCP server with detected AI coding agents.\n *\n * For each agent, attempts native CLI registration first (anon keys\n * only, see {@link registerViaCli}), then falls back to file-based\n * configuration. The marker file at `.glasstrace/mcp-connected`\n * records the effective credential's source and identity fingerprint\n * so a later run can detect a project that has transitioned from\n * anon to account/dev-key and prompt a refresh.\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: Resolve the effective credential. Replaces the prior\n // anon-only `readAnonKey` path; dev-key-only projects (no\n // .glasstrace/anon_key on disk) now run too.\n const resolved = await resolveEffectiveMcpCredential(projectRoot);\n if (resolved.effective === null) {\n return {\n exitCode: 1,\n results: [],\n messages: [\"Error: Run `glasstrace init` first to generate an API key.\"],\n };\n }\n\n // Optional: surface the claimed-key-only warning so the user knows\n // to copy the key into .env.local for normal use.\n if (resolved.warnings.includes(\"claimed-key-only\")) {\n messages.push(\n \"Note: dev key was loaded from .glasstrace/claimed-key. Copy it into .env.local so your app and Codex pick it up automatically.\",\n );\n }\n\n // Step 2: Marker check.\n //\n // Two short-circuits:\n // - Marker absent and not --force: the legacy \"MCP already\n // configured\" message is no longer applicable here, but absence\n // of the marker means we proceed to register. (The original\n // behaviour was to short-circuit if the marker existed; the\n // short-circuit now also requires the marker to actually match\n // the effective credential.)\n // - Marker present, matches effective credential, and not --force:\n // already configured for this credential, no work to do.\n // - Marker present, mismatches effective credential, regardless of\n // --force: the project has transitioned credentials; treat as\n // unconfigured and re-register.\n const markerPath = path.join(projectRoot, \".glasstrace\", \"mcp-connected\");\n if (fs.existsSync(markerPath) && !force) {\n if (await markerMatchesEffective(projectRoot, resolved.effective)) {\n return {\n exitCode: 0,\n results: [],\n messages: [\"MCP already configured. Use --force to reconfigure.\"],\n };\n }\n // Mismatch: project has transitioned credentials. Fall through to\n // re-register so MCP queries see the same scope as ingestion.\n messages.push(\n \"Detected a credential change since MCP was last configured. Refreshing MCP config so queries use the current account credential.\",\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 // The generic helper backs `.glasstrace/mcp.json`, the file\n // validation/debug tooling reads directly. ALWAYS include it in the\n // target list — when non-generic agents like Claude/Cursor are\n // detected, the helper used to be silently dropped, which left the\n // generic config stale after a credential change. `detectAgents`\n // contract guarantees the generic entry is always appended last\n // (see `agent-detection/detect.ts`), so we rely on that here rather\n // than synthesising a fallback.\n const genericAgent = agents.find((a) => a.name === \"generic\");\n const targetAgents: DetectedAgent[] = genericAgent\n ? [...detectedNonGeneric, genericAgent]\n : detectedNonGeneric;\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 && resolved.effective.source === \"anon\") {\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. The bearer used in the embedded\n // configs and in vendor CLI invocations is the resolver's effective\n // credential. registerViaCli is anon-only by design (see its\n // docstring); when the effective credential is a dev key, that path\n // returns false and the file-config branch takes over.\n const results: AgentResult[] = [];\n const bearer = resolved.effective.key;\n\n for (const agent of targetAgents) {\n const name = formatAgentName(agent.name);\n\n const sdkVersion =\n typeof __SDK_VERSION__ === \"string\" ? __SDK_VERSION__ : \"0.0.0-dev\";\n\n // Try CLI registration first (not applicable for generic)\n if (agent.name !== \"generic\") {\n const cliSuccess = await registerViaCli(agent, bearer);\n if (cliSuccess) {\n // Still inject info section if applicable\n const infoContent = generateInfoSection(agent, MCP_ENDPOINT, sdkVersion);\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, bearer);\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, sdkVersion);\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: Update marker if at least one succeeded. Marker records\n // the effective credential's source and fingerprint (raw key never\n // touches disk via this path), so a later run can detect a\n // credential change.\n const anySuccess = results.some((r) => r.success);\n\n if (anySuccess) {\n await writeMcpMarker(projectRoot, {\n credentialSource: resolved.effective.source,\n credentialHash: identityFingerprint(resolved.effective.key),\n });\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 // Exit code reflects whether the originally-detected non-generic\n // agents succeeded. The generic helper is always in `results` now —\n // letting its success alone mask a complete failure of the agents\n // the user actually has installed would silently break automation\n // that bisects on `mcp add` exit code. Preserves the pre-fix\n // contract: a run where Claude/Cursor failed still exits non-zero,\n // even when the generic helper write succeeded.\n const detectedNonGenericResults = results.filter((r) =>\n detectedNonGeneric.some((a) => a.name === r.agent),\n );\n const allDetectedNonGenericFailed =\n detectedNonGeneric.length > 0 &&\n !detectedNonGenericResults.some((r) => r.success);\n\n if (allDetectedNonGenericFailed) {\n messages.push(\n \"\",\n \"All detected agent registrations failed. Check errors above.\",\n );\n return { exitCode: 1, results, messages };\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/uninit.js
CHANGED
|
@@ -12,9 +12,9 @@ import {
|
|
|
12
12
|
unwrapCJSExport,
|
|
13
13
|
unwrapExport,
|
|
14
14
|
writeShutdownMarker
|
|
15
|
-
} from "../chunk-
|
|
16
|
-
import "../chunk-
|
|
17
|
-
import "../chunk-
|
|
15
|
+
} from "../chunk-UMGZJYC4.js";
|
|
16
|
+
import "../chunk-RL43PU2X.js";
|
|
17
|
+
import "../chunk-6RKS3DNA.js";
|
|
18
18
|
import "../chunk-NB7GJE4S.js";
|
|
19
19
|
import "../chunk-ZBQQXVHD.js";
|
|
20
20
|
import "../chunk-NSBPE2FW.js";
|
|
@@ -182,6 +182,43 @@ async function detectAgents(projectRoot) {
|
|
|
182
182
|
return detected;
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
+
// src/agent-detection/agent-instruction-text.ts
|
|
186
|
+
function buildAgentInstructionBody() {
|
|
187
|
+
return [
|
|
188
|
+
"",
|
|
189
|
+
"## Glasstrace MCP \u2014 Runtime Debugging Evidence",
|
|
190
|
+
"",
|
|
191
|
+
"Glasstrace is connected via MCP (`glasstrace`). It captures runtime traces from this app and exposes them through tools that disambiguate bugs source alone cannot resolve.",
|
|
192
|
+
"",
|
|
193
|
+
"### Call Glasstrace FIRST when:",
|
|
194
|
+
"- A value passed at runtime is suspect: wrong role, locale, timezone, template, tenant id, or any enum/config that branches downstream.",
|
|
195
|
+
"- The bug is in a side-effect operation: email sent, calendar event created, webhook dispatched, queued job, `after()` callback.",
|
|
196
|
+
"- The user reports a symptom but you do not yet know which code path actually executed.",
|
|
197
|
+
"- You need to confirm a route or procedure ran in the relevant time window before doing a source dive.",
|
|
198
|
+
"",
|
|
199
|
+
"### SKIP Glasstrace when:",
|
|
200
|
+
"- The bug is statically obvious from source (N+1 query, missing await, redundant query, type error, missing null check).",
|
|
201
|
+
"- The change is a refactor whose correctness does not depend on runtime behavior.",
|
|
202
|
+
"- You already have a precise traceId from another source.",
|
|
203
|
+
"",
|
|
204
|
+
"### Workflow",
|
|
205
|
+
"1. Start with `find_trace_candidates`. Pass whatever route or procedure name is natural \u2014 the server normalizes vocabulary and, on miss, returns close matches and a sample of routes actually present in the window.",
|
|
206
|
+
"2. Take the highest-confidence candidate's `suggestedFollowups` and pass them straight to `get_trace` or `get_root_cause`.",
|
|
207
|
+
"3. For side-effect bugs, read `sideEffectSummary` in the `get_trace` / `get_root_cause` response. The allowlisted fields (`templateKey`, `providerOperation`, `role`, `locale`, `timezone`, `status`, `phase`) are the ones that disambiguate payload bugs.",
|
|
208
|
+
"4. If a tool returns empty, READ the response's `closeMatches`, `recentRoutesSample`, and `recoveryActions` before pivoting to source. Empty results carry `notAbsenceProof: true` \u2014 they are never proof the bug did not occur.",
|
|
209
|
+
"",
|
|
210
|
+
"### Tools",
|
|
211
|
+
"- `find_trace_candidates` \u2014 discovery, vocabulary-tolerant filter",
|
|
212
|
+
"- `get_trace` \u2014 exact trace by `traceId`",
|
|
213
|
+
"- `get_root_cause` \u2014 root-cause analysis for a `traceId`",
|
|
214
|
+
"- `get_session_timeline` \u2014 events for a session",
|
|
215
|
+
"- `get_latest_error` / `get_error_list` \u2014 recent server errors",
|
|
216
|
+
"",
|
|
217
|
+
"Side-effect evidence is allowlisted and compact by design. Fields you don't see may have been omitted by policy, not absent at runtime.",
|
|
218
|
+
""
|
|
219
|
+
].join("\n");
|
|
220
|
+
}
|
|
221
|
+
|
|
185
222
|
// src/agent-detection/configs.ts
|
|
186
223
|
var SDK_VERSION_STAMP_PATTERN = /^[A-Za-z0-9.+-]+$/;
|
|
187
224
|
function htmlMarkers(sdkVersion) {
|
|
@@ -208,26 +245,7 @@ function generateInfoSection(agent, endpoint, sdkVersion) {
|
|
|
208
245
|
"sdkVersion must match [A-Za-z0-9.+\\-]+ (semver-shaped, no whitespace, no angle brackets)"
|
|
209
246
|
);
|
|
210
247
|
}
|
|
211
|
-
const content =
|
|
212
|
-
"",
|
|
213
|
-
"## Glasstrace MCP Integration",
|
|
214
|
-
"",
|
|
215
|
-
`Glasstrace is configured as an MCP server at: ${endpoint}`,
|
|
216
|
-
"",
|
|
217
|
-
"Glasstrace MCP is available when runtime evidence would materially reduce uncertainty. Use it when there is a failing request, stack trace, unclear runtime behavior, race/data-flow symptom, side effect, or performance issue that source inspection alone does not explain. For a current error, `get_latest_error` or `get_error_list` is usually the cheapest orientation call. For a known route/procedure with no exact error, use `find_trace_candidates` and follow returned exact `get_trace` or `get_root_cause` arguments only if the candidates look relevant. Do not call trace tools for trivial source-local fixes. Treat **no candidates** or **no_traces_found** as a scoped retrieval result, not proof the bug is absent.",
|
|
218
|
-
"",
|
|
219
|
-
"Available tools:",
|
|
220
|
-
"- `get_latest_error` - Get the most recent error trace from the current session",
|
|
221
|
-
"- `find_trace_candidates` - First-contact route/procedure/URL candidate selection when you have a route fragment, tRPC procedure, method, status, or rough recent activity window but not the exact trace ID. Returns candidate traces plus suggested `get_trace` / `get_root_cause` follow-up call arguments. Candidate discovery, not root-cause proof.",
|
|
222
|
-
"- `get_error_list` - List recent errors with filtering and pagination",
|
|
223
|
-
"- `get_trace` - Get a specific trace by ID or URL pattern",
|
|
224
|
-
"- `get_root_cause` - Get the root cause analysis for a specific error trace (requires a `traceId` from `get_latest_error`, `get_error_list`, or `get_trace`)",
|
|
225
|
-
"- `get_test_suggestions` - Get test suggestions for a specific error trace (requires a `traceId` from `get_latest_error`, `get_error_list`, or `get_trace`)",
|
|
226
|
-
"- `get_session_timeline` - Get the timeline of all traces in the current session",
|
|
227
|
-
"",
|
|
228
|
-
"To refresh this managed section after a `@glasstrace/sdk` upgrade, run: `npx glasstrace upgrade-instructions`. To reconfigure MCP credentials, run: `npx glasstrace mcp add`.",
|
|
229
|
-
""
|
|
230
|
-
].join("\n");
|
|
248
|
+
const content = buildAgentInstructionBody();
|
|
231
249
|
switch (agent.name) {
|
|
232
250
|
case "claude": {
|
|
233
251
|
const m = htmlMarkers(sdkVersion);
|
|
@@ -395,7 +413,7 @@ async function runUpgradeInstructions(options) {
|
|
|
395
413
|
);
|
|
396
414
|
return { exitCode: 1, refreshed, skipped, warnings, errors };
|
|
397
415
|
}
|
|
398
|
-
const sdkVersion = true ? "1.
|
|
416
|
+
const sdkVersion = true ? "1.10.1" : "0.0.0-dev";
|
|
399
417
|
for (const agent of agents) {
|
|
400
418
|
if (agent.infoFilePath === null) {
|
|
401
419
|
continue;
|