@glasstrace/sdk 0.19.0 → 0.20.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 CHANGED
@@ -155,6 +155,85 @@ GLASSTRACE_SUPPRESS_ACTION_NUDGE=1
155
155
  The nudge never fires in production (detected via `NODE_ENV` or
156
156
  `VERCEL_ENV`) unless `GLASSTRACE_FORCE_ENABLE=true` is also set.
157
157
 
158
+ ## Browser-extension discovery
159
+
160
+ `glasstrace init` writes a small static file at
161
+ `public/.well-known/glasstrace.json` (or `static/.well-known/glasstrace.json`
162
+ on SvelteKit) so the Glasstrace browser extension can discover your
163
+ project's anonymous key without a runtime HTTP handler. The file
164
+ contains only a schema version and the project's anonymous key — it
165
+ is public metadata, not a secret, and should be committed to source
166
+ control alongside the rest of your project.
167
+
168
+ The SDK no longer requires `createDiscoveryHandler` to be wired into
169
+ your server. If you previously registered the handler (for example,
170
+ inside `middleware.ts` or `proxy.ts` on Next.js), you can remove the
171
+ handler code and the extension will read the static file instead.
172
+
173
+ ### Migration: removing the runtime discovery handler
174
+
175
+ **Next.js 15 and earlier (`middleware.ts`):**
176
+
177
+ ```ts
178
+ // Before: middleware.ts
179
+ import { createDiscoveryHandler } from "@glasstrace/sdk";
180
+ import { NextResponse } from "next/server";
181
+
182
+ const discoveryHandler = createDiscoveryHandler(/* getAnonKey */, /* getSessionId */);
183
+
184
+ export async function middleware(req: Request) {
185
+ const response = await discoveryHandler(req);
186
+ if (response !== null) return response;
187
+ return NextResponse.next();
188
+ }
189
+ ```
190
+
191
+ ```ts
192
+ // After: middleware.ts (only the non-Glasstrace logic remains)
193
+ import { NextResponse } from "next/server";
194
+
195
+ export function middleware(_req: Request) {
196
+ return NextResponse.next();
197
+ }
198
+ ```
199
+
200
+ **Next.js 16 and later (`proxy.ts`):**
201
+
202
+ Next.js 16 replaces `middleware.ts` with `proxy.ts`. If your project
203
+ invoked the discovery handler from `middleware.ts`, migrate it to the
204
+ new file convention and drop the handler in the same edit:
205
+
206
+ ```ts
207
+ // Before: proxy.ts (Next 16+)
208
+ import { createDiscoveryHandler } from "@glasstrace/sdk";
209
+ import { NextResponse } from "next/server";
210
+
211
+ const discoveryHandler = createDiscoveryHandler(/* getAnonKey */, /* getSessionId */);
212
+
213
+ export async function proxy(req: Request) {
214
+ const response = await discoveryHandler(req);
215
+ if (response !== null) return response;
216
+ return NextResponse.next();
217
+ }
218
+ ```
219
+
220
+ ```ts
221
+ // After: proxy.ts (Next 16+)
222
+ import { NextResponse } from "next/server";
223
+
224
+ export function proxy(_req: Request) {
225
+ return NextResponse.next();
226
+ }
227
+ ```
228
+
229
+ If `proxy.ts` no longer does anything else, you can delete it entirely.
230
+
231
+ `createDiscoveryHandler` remains available for one more major version
232
+ to avoid breaking integrations that depend on it, but it now prints a
233
+ one-time deprecation warning on first use and will be removed in
234
+ `v1.0.0`. Run `npx glasstrace init` after upgrading to generate the
235
+ static file.
236
+
158
237
  ## Security
159
238
 
160
239
  The SDK transmits your API key exclusively via the `Authorization: Bearer`
@@ -2,14 +2,213 @@ import {
2
2
  isDevApiKey,
3
3
  readEnvLocalApiKey
4
4
  } from "./chunk-O63DJKIJ.js";
5
+ import {
6
+ AnonApiKeySchema
7
+ } from "./chunk-5N2IR4EO.js";
5
8
  import {
6
9
  NEXT_CONFIG_NAMES
7
10
  } from "./chunk-DXRZKKSO.js";
8
11
 
9
12
  // src/cli/uninit.ts
10
- import * as fs from "node:fs";
13
+ import * as fs2 from "node:fs";
11
14
  import * as os from "node:os";
15
+ import * as path2 from "node:path";
16
+
17
+ // src/cli/discovery-file.ts
18
+ import * as fs from "node:fs";
12
19
  import * as path from "node:path";
20
+ var DISCOVERY_FILE_VERSION = 1;
21
+ function resolveStaticRoot(projectRoot) {
22
+ if (isSvelteKitProject(projectRoot)) {
23
+ return {
24
+ absolutePath: path.join(projectRoot, "static"),
25
+ layout: "static"
26
+ };
27
+ }
28
+ return {
29
+ absolutePath: path.join(projectRoot, "public"),
30
+ layout: "public"
31
+ };
32
+ }
33
+ function isSvelteKitProject(projectRoot) {
34
+ const pkgPath = path.join(projectRoot, "package.json");
35
+ let isEsm = false;
36
+ try {
37
+ const pkgContent = fs.readFileSync(pkgPath, "utf-8");
38
+ const parsed = JSON.parse(pkgContent);
39
+ isEsm = parsed.type === "module";
40
+ } catch {
41
+ return false;
42
+ }
43
+ if (!isEsm) return false;
44
+ const svelteConfigJs = path.join(projectRoot, "svelte.config.js");
45
+ const svelteConfigTs = path.join(projectRoot, "svelte.config.ts");
46
+ const appHtml = path.join(projectRoot, "src", "app.html");
47
+ return fs.existsSync(svelteConfigJs) || fs.existsSync(svelteConfigTs) || fs.existsSync(appHtml);
48
+ }
49
+ function relativeDiscoveryPath(layout) {
50
+ return layout === "static" ? "static/.well-known/glasstrace.json" : "public/.well-known/glasstrace.json";
51
+ }
52
+ function readExistingDiscoveryFile(filePath) {
53
+ let raw;
54
+ try {
55
+ raw = fs.readFileSync(filePath, "utf-8");
56
+ } catch {
57
+ return null;
58
+ }
59
+ let parsed;
60
+ try {
61
+ parsed = JSON.parse(raw);
62
+ } catch {
63
+ return null;
64
+ }
65
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
66
+ return null;
67
+ }
68
+ const obj = parsed;
69
+ const versionRaw = obj.version;
70
+ if (typeof versionRaw !== "number" || !Number.isInteger(versionRaw) || versionRaw < 1) {
71
+ return null;
72
+ }
73
+ const keyResult = AnonApiKeySchema.safeParse(obj.key);
74
+ if (!keyResult.success) {
75
+ return null;
76
+ }
77
+ const extras = {};
78
+ for (const [k, v] of Object.entries(obj)) {
79
+ if (k === "version" || k === "key") continue;
80
+ extras[k] = v;
81
+ }
82
+ return { key: keyResult.data, extras };
83
+ }
84
+ function serializeDiscoveryPayload(key, extras) {
85
+ const payload = {
86
+ version: DISCOVERY_FILE_VERSION,
87
+ key,
88
+ ...extras
89
+ };
90
+ return JSON.stringify(payload, null, 2) + "\n";
91
+ }
92
+ function writeDiscoveryFile(projectRoot, anonKey) {
93
+ const { absolutePath: staticRoot, layout } = resolveStaticRoot(projectRoot);
94
+ const wellKnownDir = path.join(staticRoot, ".well-known");
95
+ const filePath = path.join(wellKnownDir, "glasstrace.json");
96
+ let existingAction;
97
+ let extras = {};
98
+ if (fs.existsSync(filePath)) {
99
+ const existing = readExistingDiscoveryFile(filePath);
100
+ if (existing === null) {
101
+ existingAction = "skipped-foreign";
102
+ } else if (existing.key === anonKey) {
103
+ return {
104
+ action: "skipped-matches",
105
+ filePath,
106
+ layout
107
+ };
108
+ } else {
109
+ extras = existing.extras;
110
+ existingAction = "updated-stale";
111
+ }
112
+ } else {
113
+ existingAction = "created";
114
+ }
115
+ const tmpPath = `${filePath}.tmp-${process.pid}`;
116
+ const needsWindowsReplace = process.platform === "win32" && fs.existsSync(filePath);
117
+ const backupPath = needsWindowsReplace ? `${filePath}.bak-${process.pid}` : null;
118
+ try {
119
+ fs.mkdirSync(wellKnownDir, { recursive: true });
120
+ const payload = serializeDiscoveryPayload(anonKey, extras);
121
+ fs.writeFileSync(tmpPath, payload, { encoding: "utf-8" });
122
+ if (backupPath !== null) {
123
+ fs.renameSync(filePath, backupPath);
124
+ try {
125
+ fs.renameSync(tmpPath, filePath);
126
+ } catch (renameErr) {
127
+ try {
128
+ fs.renameSync(backupPath, filePath);
129
+ } catch {
130
+ }
131
+ throw renameErr;
132
+ }
133
+ try {
134
+ fs.unlinkSync(backupPath);
135
+ } catch {
136
+ }
137
+ } else {
138
+ fs.renameSync(tmpPath, filePath);
139
+ }
140
+ return { action: existingAction, filePath, layout };
141
+ } catch (err) {
142
+ try {
143
+ if (fs.existsSync(tmpPath)) {
144
+ fs.unlinkSync(tmpPath);
145
+ }
146
+ } catch {
147
+ }
148
+ return {
149
+ action: "failed",
150
+ filePath,
151
+ layout,
152
+ error: err instanceof Error ? err.message : String(err)
153
+ };
154
+ }
155
+ }
156
+ function removeDiscoveryFile(projectRoot) {
157
+ const { layout: inferredLayout } = resolveStaticRoot(projectRoot);
158
+ const layouts = ["public", "static"];
159
+ const outcomes = [];
160
+ for (const layout of layouts) {
161
+ const staticRoot = path.join(projectRoot, layout);
162
+ const wellKnownDir = path.join(staticRoot, ".well-known");
163
+ const filePath = path.join(wellKnownDir, "glasstrace.json");
164
+ let removed = false;
165
+ try {
166
+ if (fs.existsSync(filePath)) {
167
+ fs.unlinkSync(filePath);
168
+ removed = true;
169
+ }
170
+ } catch (err) {
171
+ return {
172
+ action: "failed",
173
+ filePath,
174
+ layout,
175
+ directoryRemoved: false,
176
+ error: err instanceof Error ? err.message : String(err)
177
+ };
178
+ }
179
+ let directoryRemoved = false;
180
+ if (removed) {
181
+ try {
182
+ if (fs.existsSync(wellKnownDir)) {
183
+ const entries = fs.readdirSync(wellKnownDir);
184
+ if (entries.length === 0) {
185
+ fs.rmdirSync(wellKnownDir);
186
+ directoryRemoved = true;
187
+ }
188
+ }
189
+ } catch {
190
+ }
191
+ }
192
+ outcomes.push({ layout, filePath, removed, directoryRemoved });
193
+ }
194
+ const removals = outcomes.filter((o) => o.removed);
195
+ const chosen = (() => {
196
+ if (removals.length === 0) {
197
+ return outcomes.find((o) => o.layout === inferredLayout) ?? outcomes[0];
198
+ }
199
+ if (removals.length === 1) return removals[0];
200
+ return removals.find((o) => o.layout === inferredLayout) ?? removals[0];
201
+ })();
202
+ const anyDirectoryRemoved = outcomes.some((o) => o.directoryRemoved);
203
+ return {
204
+ action: removals.length > 0 ? "removed" : "not-found",
205
+ filePath: chosen.filePath,
206
+ layout: chosen.layout,
207
+ directoryRemoved: chosen.directoryRemoved || anyDirectoryRemoved
208
+ };
209
+ }
210
+
211
+ // src/cli/uninit.ts
13
212
  var MCP_CONFIG_FILES = [".mcp.json", ".cursor/mcp.json", ".gemini/settings.json"];
14
213
  var AGENT_INFO_FILES = [
15
214
  "CLAUDE.md",
@@ -316,24 +515,24 @@ function processTomlMcpConfig(content) {
316
515
  return { action: "removed-section", content: result + "\n" };
317
516
  }
318
517
  function writeShutdownMarker(projectRoot) {
319
- const dirPath = path.join(projectRoot, ".glasstrace");
320
- if (!fs.existsSync(dirPath)) {
518
+ const dirPath = path2.join(projectRoot, ".glasstrace");
519
+ if (!fs2.existsSync(dirPath)) {
321
520
  return false;
322
521
  }
323
- const markerPath = path.join(dirPath, "shutdown-requested");
522
+ const markerPath = path2.join(dirPath, "shutdown-requested");
324
523
  const tmpPath = `${markerPath}.tmp`;
325
524
  const body = JSON.stringify({ requestedAt: (/* @__PURE__ */ new Date()).toISOString() });
326
525
  try {
327
- fs.writeFileSync(tmpPath, body, { encoding: "utf-8", mode: 384 });
526
+ fs2.writeFileSync(tmpPath, body, { encoding: "utf-8", mode: 384 });
328
527
  try {
329
- fs.chmodSync(tmpPath, 384);
528
+ fs2.chmodSync(tmpPath, 384);
330
529
  } catch {
331
530
  }
332
- fs.renameSync(tmpPath, markerPath);
531
+ fs2.renameSync(tmpPath, markerPath);
333
532
  return true;
334
533
  } catch {
335
534
  try {
336
- fs.unlinkSync(tmpPath);
535
+ fs2.unlinkSync(tmpPath);
337
536
  } catch {
338
537
  }
339
538
  return false;
@@ -374,8 +573,8 @@ async function runUninit(options) {
374
573
  summary.push("Wrote .glasstrace/shutdown-requested marker");
375
574
  }
376
575
  } else {
377
- const dirPath = path.join(projectRoot, ".glasstrace");
378
- if (fs.existsSync(dirPath)) {
576
+ const dirPath = path2.join(projectRoot, ".glasstrace");
577
+ if (fs2.existsSync(dirPath)) {
379
578
  summary.push(`${prefix}Would write .glasstrace/shutdown-requested marker`);
380
579
  }
381
580
  }
@@ -387,11 +586,11 @@ async function runUninit(options) {
387
586
  try {
388
587
  let configHandled = false;
389
588
  for (const name of NEXT_CONFIG_NAMES) {
390
- const configPath = path.join(projectRoot, name);
391
- if (!fs.existsSync(configPath)) {
589
+ const configPath = path2.join(projectRoot, name);
590
+ if (!fs2.existsSync(configPath)) {
392
591
  continue;
393
592
  }
394
- const content = fs.readFileSync(configPath, "utf-8");
593
+ const content = fs2.readFileSync(configPath, "utf-8");
395
594
  if (!content.includes("withGlasstraceConfig")) {
396
595
  continue;
397
596
  }
@@ -401,7 +600,7 @@ async function runUninit(options) {
401
600
  const cleaned = removeGlasstraceConfigImport(unwrapResult.content);
402
601
  const final = cleanLeadingBlankLines(cleaned);
403
602
  if (!dryRun) {
404
- fs.writeFileSync(configPath, final, "utf-8");
603
+ fs2.writeFileSync(configPath, final, "utf-8");
405
604
  }
406
605
  summary.push(`${prefix}Unwrapped withGlasstraceConfig from ${name}`);
407
606
  configHandled = true;
@@ -422,20 +621,20 @@ async function runUninit(options) {
422
621
  );
423
622
  }
424
623
  try {
425
- const instrPath = path.join(projectRoot, "instrumentation.ts");
426
- if (fs.existsSync(instrPath)) {
427
- const content = fs.readFileSync(instrPath, "utf-8");
624
+ const instrPath = path2.join(projectRoot, "instrumentation.ts");
625
+ if (fs2.existsSync(instrPath)) {
626
+ const content = fs2.readFileSync(instrPath, "utf-8");
428
627
  if (content.includes("registerGlasstrace") || content.includes("@glasstrace/sdk")) {
429
628
  if (isInitCreatedInstrumentation(content)) {
430
629
  if (!dryRun) {
431
- fs.unlinkSync(instrPath);
630
+ fs2.unlinkSync(instrPath);
432
631
  }
433
632
  summary.push(`${prefix}Deleted instrumentation.ts (init-created)`);
434
633
  } else {
435
634
  const cleaned = removeRegisterGlasstrace(content);
436
635
  if (cleaned !== content) {
437
636
  if (!dryRun) {
438
- fs.writeFileSync(instrPath, cleaned, "utf-8");
637
+ fs2.writeFileSync(instrPath, cleaned, "utf-8");
439
638
  }
440
639
  summary.push(
441
640
  `${prefix}Removed registerGlasstrace() from instrumentation.ts`
@@ -450,10 +649,10 @@ async function runUninit(options) {
450
649
  );
451
650
  }
452
651
  try {
453
- const glasstraceDir = path.join(projectRoot, ".glasstrace");
454
- if (fs.existsSync(glasstraceDir)) {
652
+ const glasstraceDir = path2.join(projectRoot, ".glasstrace");
653
+ if (fs2.existsSync(glasstraceDir)) {
455
654
  if (!dryRun) {
456
- fs.rmSync(glasstraceDir, { recursive: true, force: true });
655
+ fs2.rmSync(glasstraceDir, { recursive: true, force: true });
457
656
  }
458
657
  summary.push(`${prefix}Removed .glasstrace/ directory`);
459
658
  }
@@ -463,9 +662,38 @@ async function runUninit(options) {
463
662
  );
464
663
  }
465
664
  try {
466
- const envPath = path.join(projectRoot, ".env.local");
467
- if (fs.existsSync(envPath)) {
468
- const content = fs.readFileSync(envPath, "utf-8");
665
+ if (dryRun) {
666
+ for (const previewLayout of ["public", "static"]) {
667
+ const relPath = relativeDiscoveryPath(previewLayout);
668
+ const absPath = path2.join(projectRoot, relPath);
669
+ if (fs2.existsSync(absPath)) {
670
+ summary.push(`${prefix}Would remove ${relPath}`);
671
+ }
672
+ }
673
+ } else {
674
+ const result = removeDiscoveryFile(projectRoot);
675
+ if (result.action === "removed") {
676
+ const relPath = relativeDiscoveryPath(result.layout);
677
+ summary.push(`Removed ${relPath}`);
678
+ if (result.directoryRemoved) {
679
+ const dirRel = relPath.replace(/\/glasstrace\.json$/, "/");
680
+ summary.push(`Removed empty ${dirRel}`);
681
+ }
682
+ } else if (result.action === "failed") {
683
+ warnings.push(
684
+ `Failed to remove ${relativeDiscoveryPath(result.layout)}${result.error !== void 0 ? `: ${result.error}` : ""}`
685
+ );
686
+ }
687
+ }
688
+ } catch (err) {
689
+ warnings.push(
690
+ `Failed to remove discovery file: ${err instanceof Error ? err.message : String(err)}`
691
+ );
692
+ }
693
+ try {
694
+ const envPath = path2.join(projectRoot, ".env.local");
695
+ if (fs2.existsSync(envPath)) {
696
+ const content = fs2.readFileSync(envPath, "utf-8");
469
697
  const existingKey = readEnvLocalApiKey(content);
470
698
  const hasDevKey = isDevApiKey(existingKey);
471
699
  let proceed = true;
@@ -498,12 +726,12 @@ async function runUninit(options) {
498
726
  const result = filtered.join("\n");
499
727
  if (result.trim().length === 0) {
500
728
  if (!dryRun) {
501
- fs.unlinkSync(envPath);
729
+ fs2.unlinkSync(envPath);
502
730
  }
503
731
  summary.push(`${prefix}Deleted .env.local (no remaining entries)`);
504
732
  } else {
505
733
  if (!dryRun) {
506
- fs.writeFileSync(envPath, result, "utf-8");
734
+ fs2.writeFileSync(envPath, result, "utf-8");
507
735
  }
508
736
  let devKeyAnnotation = "";
509
737
  if (devKeyPath === "interactive-confirmed") {
@@ -526,9 +754,9 @@ async function runUninit(options) {
526
754
  );
527
755
  }
528
756
  try {
529
- const gitignorePath = path.join(projectRoot, ".gitignore");
530
- if (fs.existsSync(gitignorePath)) {
531
- const content = fs.readFileSync(gitignorePath, "utf-8");
757
+ const gitignorePath = path2.join(projectRoot, ".gitignore");
758
+ if (fs2.existsSync(gitignorePath)) {
759
+ const content = fs2.readFileSync(gitignorePath, "utf-8");
532
760
  const lines = content.split("\n");
533
761
  const mcpGitignoreEntries = /* @__PURE__ */ new Set([
534
762
  ".glasstrace/",
@@ -544,12 +772,12 @@ async function runUninit(options) {
544
772
  const result = filtered.join("\n");
545
773
  if (result.trim().length === 0) {
546
774
  if (!dryRun) {
547
- fs.unlinkSync(gitignorePath);
775
+ fs2.unlinkSync(gitignorePath);
548
776
  }
549
777
  summary.push(`${prefix}Deleted .gitignore (no remaining entries)`);
550
778
  } else {
551
779
  if (!dryRun) {
552
- fs.writeFileSync(gitignorePath, result, "utf-8");
780
+ fs2.writeFileSync(gitignorePath, result, "utf-8");
553
781
  }
554
782
  summary.push(`${prefix}Removed Glasstrace entries from .gitignore`);
555
783
  }
@@ -562,63 +790,63 @@ async function runUninit(options) {
562
790
  }
563
791
  try {
564
792
  for (const configFile of MCP_CONFIG_FILES) {
565
- const configPath = path.join(projectRoot, configFile);
566
- if (!fs.existsSync(configPath)) {
793
+ const configPath = path2.join(projectRoot, configFile);
794
+ if (!fs2.existsSync(configPath)) {
567
795
  continue;
568
796
  }
569
- const content = fs.readFileSync(configPath, "utf-8");
797
+ const content = fs2.readFileSync(configPath, "utf-8");
570
798
  const result = processJsonMcpConfig(content);
571
799
  if (result.action === "deleted") {
572
800
  if (!dryRun) {
573
- fs.unlinkSync(configPath);
801
+ fs2.unlinkSync(configPath);
574
802
  }
575
803
  summary.push(`${prefix}Deleted ${configFile}`);
576
804
  } else if (result.action === "removed-key" && result.content !== void 0) {
577
805
  if (!dryRun) {
578
- fs.writeFileSync(configPath, result.content, "utf-8");
806
+ fs2.writeFileSync(configPath, result.content, "utf-8");
579
807
  }
580
808
  summary.push(`${prefix}Removed glasstrace from ${configFile}`);
581
809
  }
582
810
  }
583
- const codexConfigPath = path.join(projectRoot, ".codex", "config.toml");
584
- if (fs.existsSync(codexConfigPath)) {
585
- const content = fs.readFileSync(codexConfigPath, "utf-8");
811
+ const codexConfigPath = path2.join(projectRoot, ".codex", "config.toml");
812
+ if (fs2.existsSync(codexConfigPath)) {
813
+ const content = fs2.readFileSync(codexConfigPath, "utf-8");
586
814
  const tomlResult = processTomlMcpConfig(content);
587
815
  if (tomlResult.action === "deleted") {
588
816
  if (!dryRun) {
589
- fs.unlinkSync(codexConfigPath);
817
+ fs2.unlinkSync(codexConfigPath);
590
818
  }
591
819
  summary.push(`${prefix}Deleted .codex/config.toml`);
592
820
  } else if (tomlResult.action === "removed-section" && tomlResult.content !== void 0) {
593
821
  if (!dryRun) {
594
- fs.writeFileSync(codexConfigPath, tomlResult.content, "utf-8");
822
+ fs2.writeFileSync(codexConfigPath, tomlResult.content, "utf-8");
595
823
  }
596
824
  summary.push(`${prefix}Removed glasstrace from .codex/config.toml`);
597
825
  }
598
826
  }
599
- const hasWindsurfMarkers = fs.existsSync(path.join(projectRoot, ".windsurfrules")) || fs.existsSync(path.join(projectRoot, ".windsurf"));
827
+ const hasWindsurfMarkers = fs2.existsSync(path2.join(projectRoot, ".windsurfrules")) || fs2.existsSync(path2.join(projectRoot, ".windsurf"));
600
828
  if (hasWindsurfMarkers) {
601
- const windsurfConfigPath = path.join(
829
+ const windsurfConfigPath = path2.join(
602
830
  os.homedir(),
603
831
  ".codeium",
604
832
  "windsurf",
605
833
  "mcp_config.json"
606
834
  );
607
- if (fs.existsSync(windsurfConfigPath)) {
608
- const content = fs.readFileSync(windsurfConfigPath, "utf-8");
835
+ if (fs2.existsSync(windsurfConfigPath)) {
836
+ const content = fs2.readFileSync(windsurfConfigPath, "utf-8");
609
837
  const windsurfResult = processJsonMcpConfig(content);
610
838
  const home = os.homedir();
611
839
  const displayPath = windsurfConfigPath.startsWith(home) ? "~" + windsurfConfigPath.slice(home.length) : windsurfConfigPath;
612
840
  if (windsurfResult.action === "deleted") {
613
841
  if (!dryRun) {
614
- fs.unlinkSync(windsurfConfigPath);
842
+ fs2.unlinkSync(windsurfConfigPath);
615
843
  }
616
844
  summary.push(
617
845
  `${prefix}Deleted global Windsurf config (${displayPath})`
618
846
  );
619
847
  } else if (windsurfResult.action === "removed-key" && windsurfResult.content !== void 0) {
620
848
  if (!dryRun) {
621
- fs.writeFileSync(windsurfConfigPath, windsurfResult.content, "utf-8");
849
+ fs2.writeFileSync(windsurfConfigPath, windsurfResult.content, "utf-8");
622
850
  }
623
851
  summary.push(
624
852
  `${prefix}Removed glasstrace from global Windsurf config (${displayPath})`
@@ -633,21 +861,21 @@ async function runUninit(options) {
633
861
  }
634
862
  try {
635
863
  for (const infoFile of AGENT_INFO_FILES) {
636
- const filePath = path.join(projectRoot, infoFile);
637
- if (!fs.existsSync(filePath)) {
864
+ const filePath = path2.join(projectRoot, infoFile);
865
+ if (!fs2.existsSync(filePath)) {
638
866
  continue;
639
867
  }
640
- const content = fs.readFileSync(filePath, "utf-8");
868
+ const content = fs2.readFileSync(filePath, "utf-8");
641
869
  const result = removeMarkerSection(content);
642
870
  if (result.removed) {
643
871
  if (result.content.trim().length === 0) {
644
872
  if (!dryRun) {
645
- fs.unlinkSync(filePath);
873
+ fs2.unlinkSync(filePath);
646
874
  }
647
875
  summary.push(`${prefix}Deleted ${infoFile} (only contained Glasstrace section)`);
648
876
  } else {
649
877
  if (!dryRun) {
650
- fs.writeFileSync(filePath, result.content, "utf-8");
878
+ fs2.writeFileSync(filePath, result.content, "utf-8");
651
879
  }
652
880
  summary.push(`${prefix}Removed Glasstrace section from ${infoFile}`);
653
881
  }
@@ -665,6 +893,10 @@ async function runUninit(options) {
665
893
  }
666
894
 
667
895
  export {
896
+ resolveStaticRoot,
897
+ relativeDiscoveryPath,
898
+ writeDiscoveryFile,
899
+ removeDiscoveryFile,
668
900
  skipString,
669
901
  findMatchingDelimiter,
670
902
  findMatchingParen,
@@ -679,4 +911,4 @@ export {
679
911
  writeShutdownMarker,
680
912
  runUninit
681
913
  };
682
- //# sourceMappingURL=chunk-XNDHQN4S.js.map
914
+ //# sourceMappingURL=chunk-6JRI4OGB.js.map