@glasstrace/sdk 0.13.5 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-DF52INSK.js → chunk-ECEN724Y.js} +2 -2
- package/dist/{chunk-7MHQRVVW.js → chunk-ERGEG4ZQ.js} +2 -3
- package/dist/chunk-ERGEG4ZQ.js.map +1 -0
- package/dist/{chunk-AMFO5UL4.js → chunk-UPS5BGER.js} +2 -2
- package/dist/{chunk-UJ74MD4Y.js → chunk-YMEXDDTA.js} +7 -3
- package/dist/{chunk-UJ74MD4Y.js.map → chunk-YMEXDDTA.js.map} +1 -1
- package/dist/cli/init.cjs +68 -3
- package/dist/cli/init.cjs.map +1 -1
- package/dist/cli/init.js +3 -3
- package/dist/cli/mcp-add.cjs +2 -1
- package/dist/cli/mcp-add.cjs.map +1 -1
- package/dist/cli/mcp-add.js +2 -2
- package/dist/cli/status.cjs +65 -1
- package/dist/cli/status.cjs.map +1 -1
- package/dist/cli/status.d.cts +22 -1
- package/dist/cli/status.d.ts +22 -1
- package/dist/cli/status.js +65 -1
- package/dist/cli/status.js.map +1 -1
- package/dist/index.cjs +825 -62
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +71 -2
- package/dist/index.d.ts +71 -2
- package/dist/index.js +815 -64
- package/dist/index.js.map +1 -1
- package/dist/{source-map-uploader-EWA2XQI4.js → source-map-uploader-W6VPGY26.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-7MHQRVVW.js.map +0 -1
- /package/dist/{chunk-DF52INSK.js.map → chunk-ECEN724Y.js.map} +0 -0
- /package/dist/{chunk-AMFO5UL4.js.map → chunk-UPS5BGER.js.map} +0 -0
- /package/dist/{source-map-uploader-EWA2XQI4.js.map → source-map-uploader-W6VPGY26.js.map} +0 -0
package/dist/cli/mcp-add.js
CHANGED
package/dist/cli/status.cjs
CHANGED
|
@@ -64,7 +64,8 @@ function runStatus(options) {
|
|
|
64
64
|
configWrapped: checkConfigWrapped(root),
|
|
65
65
|
anonKey: checkAnonKey(root),
|
|
66
66
|
mcpConfigured: checkMcpConfigured(root),
|
|
67
|
-
agents: checkAgents(root)
|
|
67
|
+
agents: checkAgents(root),
|
|
68
|
+
runtime: readRuntimeState(root)
|
|
68
69
|
};
|
|
69
70
|
}
|
|
70
71
|
function checkInstalled(root) {
|
|
@@ -155,6 +156,69 @@ function checkAgents(root) {
|
|
|
155
156
|
}
|
|
156
157
|
return found;
|
|
157
158
|
}
|
|
159
|
+
var STALE_THRESHOLD_MS = 3e4;
|
|
160
|
+
function readRuntimeState(root) {
|
|
161
|
+
const empty = {
|
|
162
|
+
available: false,
|
|
163
|
+
stale: false,
|
|
164
|
+
coreState: null,
|
|
165
|
+
authState: null,
|
|
166
|
+
otelState: null,
|
|
167
|
+
otelScenario: null,
|
|
168
|
+
updatedAt: null,
|
|
169
|
+
pid: null
|
|
170
|
+
};
|
|
171
|
+
try {
|
|
172
|
+
const filePath = path.join(root, ".glasstrace", "runtime-state.json");
|
|
173
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
174
|
+
const parsed = JSON.parse(content);
|
|
175
|
+
const updatedAt = typeof parsed.updatedAt === "string" ? parsed.updatedAt : null;
|
|
176
|
+
const pid = typeof parsed.pid === "number" ? parsed.pid : null;
|
|
177
|
+
const core = parsed.core;
|
|
178
|
+
const auth = parsed.auth;
|
|
179
|
+
const otel = parsed.otel;
|
|
180
|
+
const coreState = typeof core?.state === "string" ? core.state : null;
|
|
181
|
+
const authState = typeof auth?.state === "string" ? auth.state : null;
|
|
182
|
+
const otelState = typeof otel?.state === "string" ? otel.state : null;
|
|
183
|
+
const otelScenario = typeof otel?.scenario === "string" ? otel.scenario : null;
|
|
184
|
+
let stale = false;
|
|
185
|
+
if (coreState === "SHUTDOWN") {
|
|
186
|
+
stale = false;
|
|
187
|
+
} else if (updatedAt) {
|
|
188
|
+
const updatedMs = new Date(updatedAt).getTime();
|
|
189
|
+
const age = Number.isFinite(updatedMs) ? Date.now() - updatedMs : Infinity;
|
|
190
|
+
if (age > STALE_THRESHOLD_MS) {
|
|
191
|
+
if (pid && pid > 0) {
|
|
192
|
+
try {
|
|
193
|
+
process.kill(pid, 0);
|
|
194
|
+
stale = false;
|
|
195
|
+
} catch (err) {
|
|
196
|
+
const code = err?.code;
|
|
197
|
+
if (code === "EPERM") {
|
|
198
|
+
stale = false;
|
|
199
|
+
} else {
|
|
200
|
+
stale = true;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
} else {
|
|
204
|
+
stale = true;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return {
|
|
209
|
+
available: true,
|
|
210
|
+
stale,
|
|
211
|
+
coreState,
|
|
212
|
+
authState,
|
|
213
|
+
otelState,
|
|
214
|
+
otelScenario,
|
|
215
|
+
updatedAt,
|
|
216
|
+
pid
|
|
217
|
+
};
|
|
218
|
+
} catch {
|
|
219
|
+
return empty;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
158
222
|
// Annotate the CommonJS export names for ESM import in node:
|
|
159
223
|
0 && (module.exports = {
|
|
160
224
|
runStatus
|
package/dist/cli/status.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cli/status.ts","../../src/cli/constants.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 */\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}\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 };\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","import type { DetectedAgent } from \"../agent-detection/detect.js\";\n\n/** Glasstrace MCP endpoint for agent configuration. */\nexport const MCP_ENDPOINT = \"https://api.glasstrace.dev/mcp\";\n\n/** Next.js config file names in priority order. */\nexport const NEXT_CONFIG_NAMES = [\"next.config.ts\", \"next.config.js\", \"next.config.mjs\"] as const;\n\n/** Maps internal agent name to a human-readable display name. */\nexport function formatAgentName(name: DetectedAgent[\"name\"]): string {\n const displayNames: Record<DetectedAgent[\"name\"], string> = {\n claude: \"Claude Code\",\n codex: \"Codex\",\n gemini: \"Gemini\",\n cursor: \"Cursor\",\n windsurf: \"Windsurf\",\n generic: \"Generic\",\n };\n return displayNames[name];\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAAoB;AACpB,WAAsB;;;ACKf,IAAM,oBAAoB,CAAC,kBAAkB,kBAAkB,iBAAiB;;;ADGvF,IAAM,iBAAiB,CAAC,aAAa,oBAAoB,yBAAyB,sBAAsB;AAKxG,IAAM,iBAAiB,CAAC,oBAAoB;AAK5C,IAAM,mBAAmB;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AACF;AAKA,IAAM,wBAAwB;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAmCO,SAAS,UAAU,SAAsC;AAC9D,QAAM,OAAO,QAAQ;AAErB,SAAO;AAAA,IACL,WAAW,eAAe,IAAI;AAAA,IAC9B,aAAa,iBAAiB,IAAI;AAAA,IAClC,iBAAiB,qBAAqB,IAAI;AAAA,IAC1C,eAAe,mBAAmB,IAAI;AAAA,IACtC,SAAS,aAAa,IAAI;AAAA,IAC1B,eAAe,mBAAmB,IAAI;AAAA,IACtC,QAAQ,YAAY,IAAI;AAAA,EAC1B;AACF;AAEA,SAAS,eAAe,MAAuB;AAC7C,MAAI;AACF,UAAM,UAAe,UAAK,MAAM,cAAc;AAC9C,UAAM,UAAa,gBAAa,SAAS,OAAO;AAChD,UAAM,MAAM,KAAK,MAAM,OAAO;AAC9B,UAAM,OAAO,IAAI,cAAc;AAC/B,UAAM,UAAU,IAAI,iBAAiB;AACrC,WACG,QAAQ,QAAQ,qBAAqB,QACrC,WAAW,QAAQ,qBAAqB;AAAA,EAE7C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,iBAAiB,MAAuB;AAC/C,MAAI;AACF,WAAU,YAAc,UAAK,MAAM,aAAa,CAAC,EAAE,YAAY;AAAA,EACjE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,qBAAqB,MAAuB;AACnD,aAAW,QAAQ,uBAAuB;AACxC,QAAI;AACF,YAAM,UAAa,gBAAkB,UAAK,MAAM,IAAI,GAAG,OAAO;AAC9D,UAAI,QAAQ,SAAS,oBAAoB,GAAG;AAC1C,eAAO;AAAA,MACT;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,mBAAmB,MAAuB;AACjD,aAAW,QAAQ,mBAAmB;AACpC,QAAI;AACF,YAAM,UAAa,gBAAkB,UAAK,MAAM,IAAI,GAAG,OAAO;AAC9D,UAAI,QAAQ,SAAS,sBAAsB,GAAG;AAC5C,eAAO;AAAA,MACT;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,aAAa,MAAuB;AAC3C,MAAI;AACF,WAAU,YAAc,UAAK,MAAM,eAAe,UAAU,CAAC,EAAE,OAAO;AAAA,EACxE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,mBAAmB,MAAuB;AAEjD,aAAW,QAAQ,gBAAgB;AACjC,QAAI;AACF,YAAM,UAAa,gBAAkB,UAAK,MAAM,IAAI,GAAG,OAAO;AAC9D,YAAM,SAAS,KAAK,MAAM,OAAO;AACjC,YAAM,aAAa,OAAO,YAAY;AACtC,UAAI,cAAc,OAAO,eAAe,YAAY,gBAAgB,YAAY;AAC9E,eAAO;AAAA,MACT;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAGA,aAAW,QAAQ,gBAAgB;AACjC,QAAI;AACF,YAAM,UAAa,gBAAkB,UAAK,MAAM,IAAI,GAAG,OAAO;AAC9D,UAAI,QAAQ,SAAS,0BAA0B,GAAG;AAChD,eAAO;AAAA,MACT;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,YAAY,MAAwB;AAC3C,QAAM,QAAkB,CAAC;AACzB,aAAW,QAAQ,kBAAkB;AACnC,QAAI;AACF,YAAM,UAAa,gBAAkB,UAAK,MAAM,IAAI,GAAG,OAAO;AAC9D,YAAM,iBACJ,QAAQ,SAAS,+BAA+B,KAChD,QAAQ,SAAS,6BAA6B;AAChD,YAAM,iBACJ,QAAQ,SAAS,wBAAwB,KACzC,QAAQ,SAAS,sBAAsB;AACzC,UAAI,kBAAkB,gBAAgB;AACpC,cAAM,KAAK,IAAI;AAAA,MACjB;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO;AACT;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/cli/status.ts","../../src/cli/constants.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","import type { DetectedAgent } from \"../agent-detection/detect.js\";\n\n/** Glasstrace MCP endpoint for agent configuration. */\nexport const MCP_ENDPOINT = \"https://api.glasstrace.dev/mcp\";\n\n/** Next.js config file names in priority order. */\nexport const NEXT_CONFIG_NAMES = [\"next.config.ts\", \"next.config.js\", \"next.config.mjs\"] as const;\n\n/** Maps internal agent name to a human-readable display name. */\nexport function formatAgentName(name: DetectedAgent[\"name\"]): string {\n const displayNames: Record<DetectedAgent[\"name\"], string> = {\n claude: \"Claude Code\",\n codex: \"Codex\",\n gemini: \"Gemini\",\n cursor: \"Cursor\",\n windsurf: \"Windsurf\",\n generic: \"Generic\",\n };\n return displayNames[name];\n}\n"],"mappings":"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","names":[]}
|
package/dist/cli/status.d.cts
CHANGED
|
@@ -3,6 +3,25 @@
|
|
|
3
3
|
* This interface is the public contract for AI agents — fields may be added
|
|
4
4
|
* but never removed or renamed without a major version bump.
|
|
5
5
|
*/
|
|
6
|
+
/** Runtime state snapshot read from .glasstrace/runtime-state.json. */
|
|
7
|
+
interface RuntimeStateSnapshot {
|
|
8
|
+
/** Whether the runtime state file exists and was readable. */
|
|
9
|
+
available: boolean;
|
|
10
|
+
/** Whether the process that wrote the state is likely still running. */
|
|
11
|
+
stale: boolean;
|
|
12
|
+
/** Core lifecycle state (e.g., "ACTIVE", "KEY_PENDING", "SHUTDOWN"). */
|
|
13
|
+
coreState: string | null;
|
|
14
|
+
/** Auth lifecycle state (e.g., "ANONYMOUS", "AUTHENTICATED"). */
|
|
15
|
+
authState: string | null;
|
|
16
|
+
/** OTel coexistence state (e.g., "OWNS_PROVIDER", "AUTO_ATTACHED"). */
|
|
17
|
+
otelState: string | null;
|
|
18
|
+
/** OTel scenario (e.g., "A", "B-auto"). */
|
|
19
|
+
otelScenario: string | null;
|
|
20
|
+
/** When the state was last written. */
|
|
21
|
+
updatedAt: string | null;
|
|
22
|
+
/** PID of the process that wrote the state. */
|
|
23
|
+
pid: number | null;
|
|
24
|
+
}
|
|
6
25
|
interface StatusResult {
|
|
7
26
|
/** Whether @glasstrace/sdk is in package.json dependencies or devDependencies. */
|
|
8
27
|
installed: boolean;
|
|
@@ -18,6 +37,8 @@ interface StatusResult {
|
|
|
18
37
|
mcpConfigured: boolean;
|
|
19
38
|
/** Which agent info files have glasstrace marker sections. */
|
|
20
39
|
agents: string[];
|
|
40
|
+
/** Runtime state from the running SDK process (if available). */
|
|
41
|
+
runtime: RuntimeStateSnapshot;
|
|
21
42
|
}
|
|
22
43
|
/**
|
|
23
44
|
* Options for the status command.
|
|
@@ -31,4 +52,4 @@ interface StatusOptions {
|
|
|
31
52
|
*/
|
|
32
53
|
declare function runStatus(options: StatusOptions): StatusResult;
|
|
33
54
|
|
|
34
|
-
export { type StatusOptions, type StatusResult, runStatus };
|
|
55
|
+
export { type RuntimeStateSnapshot, type StatusOptions, type StatusResult, runStatus };
|
package/dist/cli/status.d.ts
CHANGED
|
@@ -3,6 +3,25 @@
|
|
|
3
3
|
* This interface is the public contract for AI agents — fields may be added
|
|
4
4
|
* but never removed or renamed without a major version bump.
|
|
5
5
|
*/
|
|
6
|
+
/** Runtime state snapshot read from .glasstrace/runtime-state.json. */
|
|
7
|
+
interface RuntimeStateSnapshot {
|
|
8
|
+
/** Whether the runtime state file exists and was readable. */
|
|
9
|
+
available: boolean;
|
|
10
|
+
/** Whether the process that wrote the state is likely still running. */
|
|
11
|
+
stale: boolean;
|
|
12
|
+
/** Core lifecycle state (e.g., "ACTIVE", "KEY_PENDING", "SHUTDOWN"). */
|
|
13
|
+
coreState: string | null;
|
|
14
|
+
/** Auth lifecycle state (e.g., "ANONYMOUS", "AUTHENTICATED"). */
|
|
15
|
+
authState: string | null;
|
|
16
|
+
/** OTel coexistence state (e.g., "OWNS_PROVIDER", "AUTO_ATTACHED"). */
|
|
17
|
+
otelState: string | null;
|
|
18
|
+
/** OTel scenario (e.g., "A", "B-auto"). */
|
|
19
|
+
otelScenario: string | null;
|
|
20
|
+
/** When the state was last written. */
|
|
21
|
+
updatedAt: string | null;
|
|
22
|
+
/** PID of the process that wrote the state. */
|
|
23
|
+
pid: number | null;
|
|
24
|
+
}
|
|
6
25
|
interface StatusResult {
|
|
7
26
|
/** Whether @glasstrace/sdk is in package.json dependencies or devDependencies. */
|
|
8
27
|
installed: boolean;
|
|
@@ -18,6 +37,8 @@ interface StatusResult {
|
|
|
18
37
|
mcpConfigured: boolean;
|
|
19
38
|
/** Which agent info files have glasstrace marker sections. */
|
|
20
39
|
agents: string[];
|
|
40
|
+
/** Runtime state from the running SDK process (if available). */
|
|
41
|
+
runtime: RuntimeStateSnapshot;
|
|
21
42
|
}
|
|
22
43
|
/**
|
|
23
44
|
* Options for the status command.
|
|
@@ -31,4 +52,4 @@ interface StatusOptions {
|
|
|
31
52
|
*/
|
|
32
53
|
declare function runStatus(options: StatusOptions): StatusResult;
|
|
33
54
|
|
|
34
|
-
export { type StatusOptions, type StatusResult, runStatus };
|
|
55
|
+
export { type RuntimeStateSnapshot, type StatusOptions, type StatusResult, runStatus };
|
package/dist/cli/status.js
CHANGED
|
@@ -33,7 +33,8 @@ function runStatus(options) {
|
|
|
33
33
|
configWrapped: checkConfigWrapped(root),
|
|
34
34
|
anonKey: checkAnonKey(root),
|
|
35
35
|
mcpConfigured: checkMcpConfigured(root),
|
|
36
|
-
agents: checkAgents(root)
|
|
36
|
+
agents: checkAgents(root),
|
|
37
|
+
runtime: readRuntimeState(root)
|
|
37
38
|
};
|
|
38
39
|
}
|
|
39
40
|
function checkInstalled(root) {
|
|
@@ -124,6 +125,69 @@ function checkAgents(root) {
|
|
|
124
125
|
}
|
|
125
126
|
return found;
|
|
126
127
|
}
|
|
128
|
+
var STALE_THRESHOLD_MS = 3e4;
|
|
129
|
+
function readRuntimeState(root) {
|
|
130
|
+
const empty = {
|
|
131
|
+
available: false,
|
|
132
|
+
stale: false,
|
|
133
|
+
coreState: null,
|
|
134
|
+
authState: null,
|
|
135
|
+
otelState: null,
|
|
136
|
+
otelScenario: null,
|
|
137
|
+
updatedAt: null,
|
|
138
|
+
pid: null
|
|
139
|
+
};
|
|
140
|
+
try {
|
|
141
|
+
const filePath = path.join(root, ".glasstrace", "runtime-state.json");
|
|
142
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
143
|
+
const parsed = JSON.parse(content);
|
|
144
|
+
const updatedAt = typeof parsed.updatedAt === "string" ? parsed.updatedAt : null;
|
|
145
|
+
const pid = typeof parsed.pid === "number" ? parsed.pid : null;
|
|
146
|
+
const core = parsed.core;
|
|
147
|
+
const auth = parsed.auth;
|
|
148
|
+
const otel = parsed.otel;
|
|
149
|
+
const coreState = typeof core?.state === "string" ? core.state : null;
|
|
150
|
+
const authState = typeof auth?.state === "string" ? auth.state : null;
|
|
151
|
+
const otelState = typeof otel?.state === "string" ? otel.state : null;
|
|
152
|
+
const otelScenario = typeof otel?.scenario === "string" ? otel.scenario : null;
|
|
153
|
+
let stale = false;
|
|
154
|
+
if (coreState === "SHUTDOWN") {
|
|
155
|
+
stale = false;
|
|
156
|
+
} else if (updatedAt) {
|
|
157
|
+
const updatedMs = new Date(updatedAt).getTime();
|
|
158
|
+
const age = Number.isFinite(updatedMs) ? Date.now() - updatedMs : Infinity;
|
|
159
|
+
if (age > STALE_THRESHOLD_MS) {
|
|
160
|
+
if (pid && pid > 0) {
|
|
161
|
+
try {
|
|
162
|
+
process.kill(pid, 0);
|
|
163
|
+
stale = false;
|
|
164
|
+
} catch (err) {
|
|
165
|
+
const code = err?.code;
|
|
166
|
+
if (code === "EPERM") {
|
|
167
|
+
stale = false;
|
|
168
|
+
} else {
|
|
169
|
+
stale = true;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
} else {
|
|
173
|
+
stale = true;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return {
|
|
178
|
+
available: true,
|
|
179
|
+
stale,
|
|
180
|
+
coreState,
|
|
181
|
+
authState,
|
|
182
|
+
otelState,
|
|
183
|
+
otelScenario,
|
|
184
|
+
updatedAt,
|
|
185
|
+
pid
|
|
186
|
+
};
|
|
187
|
+
} catch {
|
|
188
|
+
return empty;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
127
191
|
export {
|
|
128
192
|
runStatus
|
|
129
193
|
};
|
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 */\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}\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 };\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"],"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":";;;;;;;;AAAA;AAAA,YAAY,QAAQ;AACpB,YAAY,UAAU;AAQtB,IAAM,iBAAiB,CAAC,aAAa,oBAAoB,yBAAyB,sBAAsB;AAKxG,IAAM,iBAAiB,CAAC,oBAAoB;AAK5C,IAAM,mBAAmB;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AACF;AAKA,IAAM,wBAAwB;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAyDO,SAAS,UAAU,SAAsC;AAC9D,QAAM,OAAO,QAAQ;AAErB,SAAO;AAAA,IACL,WAAW,eAAe,IAAI;AAAA,IAC9B,aAAa,iBAAiB,IAAI;AAAA,IAClC,iBAAiB,qBAAqB,IAAI;AAAA,IAC1C,eAAe,mBAAmB,IAAI;AAAA,IACtC,SAAS,aAAa,IAAI;AAAA,IAC1B,eAAe,mBAAmB,IAAI;AAAA,IACtC,QAAQ,YAAY,IAAI;AAAA,IACxB,SAAS,iBAAiB,IAAI;AAAA,EAChC;AACF;AAEA,SAAS,eAAe,MAAuB;AAC7C,MAAI;AACF,UAAM,UAAe,UAAK,MAAM,cAAc;AAC9C,UAAM,UAAa,gBAAa,SAAS,OAAO;AAChD,UAAM,MAAM,KAAK,MAAM,OAAO;AAC9B,UAAM,OAAO,IAAI,cAAc;AAC/B,UAAM,UAAU,IAAI,iBAAiB;AACrC,WACG,QAAQ,QAAQ,qBAAqB,QACrC,WAAW,QAAQ,qBAAqB;AAAA,EAE7C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,iBAAiB,MAAuB;AAC/C,MAAI;AACF,WAAU,YAAc,UAAK,MAAM,aAAa,CAAC,EAAE,YAAY;AAAA,EACjE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,qBAAqB,MAAuB;AACnD,aAAW,QAAQ,uBAAuB;AACxC,QAAI;AACF,YAAM,UAAa,gBAAkB,UAAK,MAAM,IAAI,GAAG,OAAO;AAC9D,UAAI,QAAQ,SAAS,oBAAoB,GAAG;AAC1C,eAAO;AAAA,MACT;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,mBAAmB,MAAuB;AACjD,aAAW,QAAQ,mBAAmB;AACpC,QAAI;AACF,YAAM,UAAa,gBAAkB,UAAK,MAAM,IAAI,GAAG,OAAO;AAC9D,UAAI,QAAQ,SAAS,sBAAsB,GAAG;AAC5C,eAAO;AAAA,MACT;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,aAAa,MAAuB;AAC3C,MAAI;AACF,WAAU,YAAc,UAAK,MAAM,eAAe,UAAU,CAAC,EAAE,OAAO;AAAA,EACxE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,mBAAmB,MAAuB;AAEjD,aAAW,QAAQ,gBAAgB;AACjC,QAAI;AACF,YAAM,UAAa,gBAAkB,UAAK,MAAM,IAAI,GAAG,OAAO;AAC9D,YAAM,SAAS,KAAK,MAAM,OAAO;AACjC,YAAM,aAAa,OAAO,YAAY;AACtC,UAAI,cAAc,OAAO,eAAe,YAAY,gBAAgB,YAAY;AAC9E,eAAO;AAAA,MACT;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAGA,aAAW,QAAQ,gBAAgB;AACjC,QAAI;AACF,YAAM,UAAa,gBAAkB,UAAK,MAAM,IAAI,GAAG,OAAO;AAC9D,UAAI,QAAQ,SAAS,0BAA0B,GAAG;AAChD,eAAO;AAAA,MACT;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,YAAY,MAAwB;AAC3C,QAAM,QAAkB,CAAC;AACzB,aAAW,QAAQ,kBAAkB;AACnC,QAAI;AACF,YAAM,UAAa,gBAAkB,UAAK,MAAM,IAAI,GAAG,OAAO;AAC9D,YAAM,iBACJ,QAAQ,SAAS,+BAA+B,KAChD,QAAQ,SAAS,6BAA6B;AAChD,YAAM,iBACJ,QAAQ,SAAS,wBAAwB,KACzC,QAAQ,SAAS,sBAAsB;AACzC,UAAI,kBAAkB,gBAAgB;AACpC,cAAM,KAAK,IAAI;AAAA,MACjB;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO;AACT;AAEA,IAAM,qBAAqB;AAE3B,SAAS,iBAAiB,MAAoC;AAC5D,QAAM,QAA8B;AAAA,IAClC,WAAW;AAAA,IACX,OAAO;AAAA,IACP,WAAW;AAAA,IACX,WAAW;AAAA,IACX,WAAW;AAAA,IACX,cAAc;AAAA,IACd,WAAW;AAAA,IACX,KAAK;AAAA,EACP;AAEA,MAAI;AACF,UAAM,WAAgB,UAAK,MAAM,eAAe,oBAAoB;AACpE,UAAM,UAAa,gBAAa,UAAU,OAAO;AACjD,UAAM,SAAS,KAAK,MAAM,OAAO;AAEjC,UAAM,YAAY,OAAO,OAAO,cAAc,WAAW,OAAO,YAAY;AAC5E,UAAM,MAAM,OAAO,OAAO,QAAQ,WAAW,OAAO,MAAM;AAC1D,UAAM,OAAO,OAAO;AACpB,UAAM,OAAO,OAAO;AACpB,UAAM,OAAO,OAAO;AAEpB,UAAM,YAAY,OAAO,MAAM,UAAU,WAAW,KAAK,QAAQ;AACjE,UAAM,YAAY,OAAO,MAAM,UAAU,WAAW,KAAK,QAAQ;AACjE,UAAM,YAAY,OAAO,MAAM,UAAU,WAAW,KAAK,QAAQ;AACjE,UAAM,eAAe,OAAO,MAAM,aAAa,WAAW,KAAK,WAAW;AAG1E,QAAI,QAAQ;AACZ,QAAI,cAAc,YAAY;AAC5B,cAAQ;AAAA,IACV,WAAW,WAAW;AACpB,YAAM,YAAY,IAAI,KAAK,SAAS,EAAE,QAAQ;AAC9C,YAAM,MAAM,OAAO,SAAS,SAAS,IAAI,KAAK,IAAI,IAAI,YAAY;AAClE,UAAI,MAAM,oBAAoB;AAE5B,YAAI,OAAO,MAAM,GAAG;AAClB,cAAI;AACF,oBAAQ,KAAK,KAAK,CAAC;AAInB,oBAAQ;AAAA,UACV,SAAS,KAAc;AACrB,kBAAM,OAAQ,KAA2B;AACzC,gBAAI,SAAS,SAAS;AACpB,sBAAQ;AAAA,YACV,OAAO;AACL,sBAAQ;AAAA,YACV;AAAA,UACF;AAAA,QACF,OAAO;AACL,kBAAQ;AAAA,QACV;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,MACL,WAAW;AAAA,MACX;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
|