copilot-tracer 1.0.9 → 1.0.10

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.
Files changed (2) hide show
  1. package/dist/setup.js +96 -12
  2. package/package.json +1 -1
package/dist/setup.js CHANGED
@@ -5,8 +5,9 @@
5
5
  * 1. Detect copilot CLI (which copilot)
6
6
  * 2. Detect VS Code installation + built-in copilot (v1.99+)
7
7
  * 3. Inject OTEL env vars into:
8
- * - Shell profile (~/.zshrc / ~/.bashrc / ~/.zprofile)
9
- * - VS Code settings.json (terminal.integrated.env.osx)
8
+ * - Shell profile: ~/.zshrc / ~/.bashrc / ~/.zprofile (macOS/Linux),
9
+ * $PROFILE — Documents\PowerShell\Microsoft.PowerShell_profile.ps1 (Windows)
10
+ * - VS Code settings.json (terminal.integrated.env.osx / .linux / .windows)
10
11
  * 4. Print a summary and next steps
11
12
  */
12
13
  import { execSync } from 'child_process';
@@ -23,7 +24,10 @@ const OTEL_TRACES_EXPORTER_KEY = 'OTEL_TRACES_EXPORTER';
23
24
  const OTEL_PROTOCOL_KEY = 'OTEL_EXPORTER_OTLP_PROTOCOL';
24
25
  const OTEL_LOG_PROMPTS_KEY = 'OTEL_LOG_USER_PROMPTS';
25
26
  const OTEL_LOG_RESPONSES_KEY = 'OTEL_LOG_ASSISTANT_RESPONSES';
26
- function otelEnvBlock(port) {
27
+ function isWindows() {
28
+ return process.platform === 'win32';
29
+ }
30
+ function otelEnvBlockPosix(port) {
27
31
  return [
28
32
  `# >>> copilot-tracer OTLP config (auto-added) >>>`,
29
33
  `export ${OTEL_ENDPOINT_KEY}=http://localhost:${port}`,
@@ -75,6 +79,53 @@ function otelEnvBlock(port) {
75
79
  `# <<< copilot-tracer <<<`,
76
80
  ].join('\n');
77
81
  }
82
+ function otelEnvBlockPowerShell(port) {
83
+ return [
84
+ `# >>> copilot-tracer OTLP config (auto-added) >>>`,
85
+ `$env:${OTEL_ENDPOINT_KEY} = "http://localhost:${port}"`,
86
+ `$env:${OTEL_CONTENT_KEY} = "true"`,
87
+ `$env:${OTEL_ENABLED_KEY} = "true"`,
88
+ `$env:${CLAUDE_TELEMETRY_KEY} = "1"`,
89
+ `$env:${CLAUDE_TRACES_KEY} = "1"`,
90
+ `$env:${OTEL_LOGS_EXPORTER_KEY} = "otlp"`,
91
+ `$env:${OTEL_TRACES_EXPORTER_KEY} = "otlp"`,
92
+ `$env:${OTEL_PROTOCOL_KEY} = "http/json"`,
93
+ `$env:${OTEL_LOG_PROMPTS_KEY} = "1"`,
94
+ `$env:${OTEL_LOG_RESPONSES_KEY} = "1"`,
95
+ ``,
96
+ `# Tag every copilot prompt with the terminal folder it ran from, so the`,
97
+ `# tracer can attribute it to the right project. OTLP carries no working-dir,`,
98
+ `# so we inject it via OTEL_RESOURCE_ATTRIBUTES (percent-encoded).`,
99
+ `function copilot {`,
100
+ ` $_wd = (Get-Location).Path.Replace('\\','/')`,
101
+ ` $_wd = [uri]::EscapeDataString($_wd).Replace('%2F','/')`,
102
+ ` if ($env:OTEL_RESOURCE_ATTRIBUTES) {`,
103
+ ` $parts = $env:OTEL_RESOURCE_ATTRIBUTES -split ',' | Where-Object { $_ -notmatch '^github\\.copilot\\.working_dir=' }`,
104
+ ` $env:OTEL_RESOURCE_ATTRIBUTES = $parts -join ','`,
105
+ ` if ($env:OTEL_RESOURCE_ATTRIBUTES) { $env:OTEL_RESOURCE_ATTRIBUTES += ',' }`,
106
+ ` }`,
107
+ ` $env:OTEL_RESOURCE_ATTRIBUTES = "$($env:OTEL_RESOURCE_ATTRIBUTES)github.copilot.working_dir=$_wd"`,
108
+ ` $cmd = Get-Command copilot -CommandType Application,ExternalScript -ErrorAction SilentlyContinue | Select-Object -First 1`,
109
+ ` if ($cmd) { & $cmd.Source @args } else { & copilot.cmd @args }`,
110
+ `}`,
111
+ ``,
112
+ `function claude {`,
113
+ ` $_wd = (Get-Location).Path.Replace('\\','/')`,
114
+ ` $_wd = [uri]::EscapeDataString($_wd).Replace('%2F','/')`,
115
+ ` if ($env:OTEL_RESOURCE_ATTRIBUTES) {`,
116
+ ` $env:OTEL_RESOURCE_ATTRIBUTES += ",claude_code.working_dir=$_wd"`,
117
+ ` } else {`,
118
+ ` $env:OTEL_RESOURCE_ATTRIBUTES = "claude_code.working_dir=$_wd"`,
119
+ ` }`,
120
+ ` $cmd = Get-Command claude -CommandType Application,ExternalScript -ErrorAction SilentlyContinue | Select-Object -First 1`,
121
+ ` if ($cmd) { & $cmd.Source @args } else { & claude.cmd @args }`,
122
+ `}`,
123
+ `# <<< copilot-tracer <<<`,
124
+ ].join('\n');
125
+ }
126
+ function otelEnvBlock(port) {
127
+ return isWindows() ? otelEnvBlockPowerShell(port) : otelEnvBlockPosix(port);
128
+ }
78
129
  function vscodeEnvBlock(port) {
79
130
  return {
80
131
  [OTEL_ENDPOINT_KEY]: `http://localhost:${port}`,
@@ -225,15 +276,33 @@ function detectVSCode() {
225
276
  return { found: true, version, hasBuiltinCopilot };
226
277
  }
227
278
  catch {
228
- // Try app bundle directly
229
- const appPath = '/Applications/Visual Studio Code.app';
230
- if (fs.existsSync(appPath)) {
279
+ // Try well-known install locations directly
280
+ const appPath = process.platform === 'darwin'
281
+ ? '/Applications/Visual Studio Code.app'
282
+ : isWindows()
283
+ ? path.join(process.env.LOCALAPPDATA ?? path.join(os.homedir(), 'AppData', 'Local'), 'Programs', 'Microsoft VS Code', 'Code.exe')
284
+ : null;
285
+ if (appPath && fs.existsSync(appPath)) {
231
286
  return { found: true, hasBuiltinCopilot: true, version: 'unknown (app found)' };
232
287
  }
233
288
  return { found: false, hasBuiltinCopilot: false };
234
289
  }
235
290
  }
236
291
  function detectShellProfile() {
292
+ if (isWindows()) {
293
+ const candidates = [
294
+ // PowerShell 7+ ($PROFILE for `pwsh`)
295
+ path.join(os.homedir(), 'Documents', 'PowerShell', 'Microsoft.PowerShell_profile.ps1'),
296
+ // Windows PowerShell 5.1 ($PROFILE for `powershell.exe`)
297
+ path.join(os.homedir(), 'Documents', 'WindowsPowerShell', 'Microsoft.PowerShell_profile.ps1'),
298
+ ];
299
+ for (const p of candidates) {
300
+ if (fs.existsSync(p))
301
+ return p;
302
+ }
303
+ // Default to the PowerShell 7+ profile (create it)
304
+ return candidates[0];
305
+ }
237
306
  const candidates = [
238
307
  path.join(os.homedir(), '.zshrc'),
239
308
  path.join(os.homedir(), '.zprofile'),
@@ -247,10 +316,22 @@ function detectShellProfile() {
247
316
  // Default to .zshrc (create it)
248
317
  return path.join(os.homedir(), '.zshrc');
249
318
  }
319
+ /** VS Code's per-platform key for `terminal.integrated.env.*`. */
320
+ function getVSCodeEnvKey() {
321
+ if (process.platform === 'darwin')
322
+ return 'terminal.integrated.env.osx';
323
+ if (isWindows())
324
+ return 'terminal.integrated.env.windows';
325
+ return 'terminal.integrated.env.linux';
326
+ }
250
327
  function getVSCodeSettingsPath() {
251
328
  if (process.platform === 'darwin') {
252
329
  return path.join(os.homedir(), 'Library/Application Support/Code/User/settings.json');
253
330
  }
331
+ if (isWindows()) {
332
+ const appData = process.env.APPDATA ?? path.join(os.homedir(), 'AppData', 'Roaming');
333
+ return path.join(appData, 'Code', 'User', 'settings.json');
334
+ }
254
335
  // Linux (and WSL)
255
336
  return path.join(os.homedir(), '.config/Code/User/settings.json');
256
337
  }
@@ -258,6 +339,9 @@ function getVSCodeSettingsPath() {
258
339
  function patchShellProfile(profilePath, port) {
259
340
  const content = fs.existsSync(profilePath) ? fs.readFileSync(profilePath, 'utf8') : '';
260
341
  const block = otelEnvBlock(port);
342
+ // The PowerShell profile dir (Documents\PowerShell or \WindowsPowerShell) often
343
+ // doesn't exist yet — unlike ~/.zshrc's parent, which is always the home dir.
344
+ fs.mkdirSync(path.dirname(profilePath), { recursive: true });
261
345
  // Already has our block?
262
346
  if (content.includes('copilot-tracer OTLP config')) {
263
347
  // Only skip if the embedded block is byte-identical to what we'd generate now —
@@ -287,7 +371,7 @@ function patchVSCodeSettings(settingsPath, port) {
287
371
  catch {
288
372
  return { action: 'skipped', reason: 'could not parse settings.json' };
289
373
  }
290
- const envKey = 'terminal.integrated.env.osx';
374
+ const envKey = getVSCodeEnvKey();
291
375
  const existing = (settings[envKey] ?? {});
292
376
  const newEnv = vscodeEnvBlock(port);
293
377
  // Already set correctly only if every current key/value is present — checking a
@@ -342,16 +426,17 @@ export function runSetup(port, silent = false) {
342
426
  if (profilePath) {
343
427
  const result = patchShellProfile(profilePath, port);
344
428
  const label = path.basename(profilePath);
429
+ const reload = isWindows() ? `. ${profilePath}` : `source ${profilePath}`;
345
430
  if (result.action === 'added') {
346
431
  console.log(`\n${CHECK} Shell profile patched: ${label}`);
347
432
  console.log(` ${ARROW} Added Copilot + Claude Code OTLP env vars`);
348
433
  console.log(` ${ARROW} Added copilot() wrapper — tags each prompt with the terminal folder`);
349
- console.log(` ${ARROW} Run: source ${profilePath}`);
434
+ console.log(` ${ARROW} Run: ${reload}`);
350
435
  }
351
436
  else if (result.action === 'updated') {
352
437
  console.log(`\n${CHECK} Shell profile updated: ${label}`);
353
438
  console.log(` ${ARROW} Updated port to ${port} + Claude Code/Copilot OTLP config`);
354
- console.log(` ${ARROW} Run: source ${profilePath}`);
439
+ console.log(` ${ARROW} Run: ${reload}`);
355
440
  }
356
441
  else {
357
442
  console.log(`\n${CHECK} Shell profile: already configured (${label})`);
@@ -363,7 +448,7 @@ export function runSetup(port, silent = false) {
363
448
  const result = patchVSCodeSettings(settingsPath, port);
364
449
  if (result.action === 'added') {
365
450
  console.log(`\n${CHECK} VS Code settings patched`);
366
- console.log(` ${ARROW} Added terminal.integrated.env.osx with OTEL vars`);
451
+ console.log(` ${ARROW} Added ${getVSCodeEnvKey()} with OTEL vars`);
367
452
  console.log(` ${ARROW} Restart VS Code to apply`);
368
453
  }
369
454
  else if (result.action === 'updated') {
@@ -411,10 +496,9 @@ export function runSetup(port, silent = false) {
411
496
  process.env[OTEL_LOG_RESPONSES_KEY] = '1';
412
497
  // 7. Summary
413
498
  if (!silent) {
414
- const profileBase = profilePath ? path.basename(profilePath) : '.zshrc';
415
499
  console.log('\n────────────────────────────────────────────────');
416
500
  console.log(' One manual step required:\n');
417
- console.log(` source ~/${profileBase}`);
501
+ console.log(` ${isWindows() ? `. ${profilePath}` : `source ${profilePath}`}`);
418
502
  console.log(` (opens a new terminal already? — env is already active there)`);
419
503
  if (vscode.found)
420
504
  console.log('\n Restart VS Code once to pick up the new terminal env.');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "copilot-tracer",
3
- "version": "1.0.9",
3
+ "version": "1.0.10",
4
4
  "description": "Real-time tracing and prompt-refinement companion for GitHub Copilot CLI and VS Code Copilot — tracks tokens, AI credits, tool calls, and refined prompts with console and web UI",
5
5
  "keywords": [
6
6
  "copilot",