copilot-tracer 1.0.9 → 1.0.11
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/setup.js +105 -15
- 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
|
|
9
|
-
*
|
|
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
|
|
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}`,
|
|
@@ -205,9 +256,15 @@ export function patchClaudeSettings(settingsPath, port) {
|
|
|
205
256
|
// ── Detection helpers ─────────────────────────────────────────────────────────
|
|
206
257
|
function detectCopilotCli() {
|
|
207
258
|
try {
|
|
208
|
-
const p = execSync('which copilot', { encoding: 'utf8' }).trim();
|
|
209
|
-
|
|
210
|
-
|
|
259
|
+
const p = execSync(isWindows() ? 'where copilot' : 'which copilot', { encoding: 'utf8' }).trim().split('\n')[0];
|
|
260
|
+
let version;
|
|
261
|
+
try {
|
|
262
|
+
version = execSync('copilot --version', { encoding: 'utf8' }).trim().split('\n')[0];
|
|
263
|
+
}
|
|
264
|
+
catch {
|
|
265
|
+
// version lookup is best-effort; presence of the binary is what matters
|
|
266
|
+
}
|
|
267
|
+
return { found: true, path: p, version };
|
|
211
268
|
}
|
|
212
269
|
catch {
|
|
213
270
|
return { found: false };
|
|
@@ -225,15 +282,33 @@ function detectVSCode() {
|
|
|
225
282
|
return { found: true, version, hasBuiltinCopilot };
|
|
226
283
|
}
|
|
227
284
|
catch {
|
|
228
|
-
// Try
|
|
229
|
-
const appPath =
|
|
230
|
-
|
|
285
|
+
// Try well-known install locations directly
|
|
286
|
+
const appPath = process.platform === 'darwin'
|
|
287
|
+
? '/Applications/Visual Studio Code.app'
|
|
288
|
+
: isWindows()
|
|
289
|
+
? path.join(process.env.LOCALAPPDATA ?? path.join(os.homedir(), 'AppData', 'Local'), 'Programs', 'Microsoft VS Code', 'Code.exe')
|
|
290
|
+
: null;
|
|
291
|
+
if (appPath && fs.existsSync(appPath)) {
|
|
231
292
|
return { found: true, hasBuiltinCopilot: true, version: 'unknown (app found)' };
|
|
232
293
|
}
|
|
233
294
|
return { found: false, hasBuiltinCopilot: false };
|
|
234
295
|
}
|
|
235
296
|
}
|
|
236
297
|
function detectShellProfile() {
|
|
298
|
+
if (isWindows()) {
|
|
299
|
+
const candidates = [
|
|
300
|
+
// PowerShell 7+ ($PROFILE for `pwsh`)
|
|
301
|
+
path.join(os.homedir(), 'Documents', 'PowerShell', 'Microsoft.PowerShell_profile.ps1'),
|
|
302
|
+
// Windows PowerShell 5.1 ($PROFILE for `powershell.exe`)
|
|
303
|
+
path.join(os.homedir(), 'Documents', 'WindowsPowerShell', 'Microsoft.PowerShell_profile.ps1'),
|
|
304
|
+
];
|
|
305
|
+
for (const p of candidates) {
|
|
306
|
+
if (fs.existsSync(p))
|
|
307
|
+
return p;
|
|
308
|
+
}
|
|
309
|
+
// Default to the PowerShell 7+ profile (create it)
|
|
310
|
+
return candidates[0];
|
|
311
|
+
}
|
|
237
312
|
const candidates = [
|
|
238
313
|
path.join(os.homedir(), '.zshrc'),
|
|
239
314
|
path.join(os.homedir(), '.zprofile'),
|
|
@@ -247,10 +322,22 @@ function detectShellProfile() {
|
|
|
247
322
|
// Default to .zshrc (create it)
|
|
248
323
|
return path.join(os.homedir(), '.zshrc');
|
|
249
324
|
}
|
|
325
|
+
/** VS Code's per-platform key for `terminal.integrated.env.*`. */
|
|
326
|
+
function getVSCodeEnvKey() {
|
|
327
|
+
if (process.platform === 'darwin')
|
|
328
|
+
return 'terminal.integrated.env.osx';
|
|
329
|
+
if (isWindows())
|
|
330
|
+
return 'terminal.integrated.env.windows';
|
|
331
|
+
return 'terminal.integrated.env.linux';
|
|
332
|
+
}
|
|
250
333
|
function getVSCodeSettingsPath() {
|
|
251
334
|
if (process.platform === 'darwin') {
|
|
252
335
|
return path.join(os.homedir(), 'Library/Application Support/Code/User/settings.json');
|
|
253
336
|
}
|
|
337
|
+
if (isWindows()) {
|
|
338
|
+
const appData = process.env.APPDATA ?? path.join(os.homedir(), 'AppData', 'Roaming');
|
|
339
|
+
return path.join(appData, 'Code', 'User', 'settings.json');
|
|
340
|
+
}
|
|
254
341
|
// Linux (and WSL)
|
|
255
342
|
return path.join(os.homedir(), '.config/Code/User/settings.json');
|
|
256
343
|
}
|
|
@@ -258,6 +345,9 @@ function getVSCodeSettingsPath() {
|
|
|
258
345
|
function patchShellProfile(profilePath, port) {
|
|
259
346
|
const content = fs.existsSync(profilePath) ? fs.readFileSync(profilePath, 'utf8') : '';
|
|
260
347
|
const block = otelEnvBlock(port);
|
|
348
|
+
// The PowerShell profile dir (Documents\PowerShell or \WindowsPowerShell) often
|
|
349
|
+
// doesn't exist yet — unlike ~/.zshrc's parent, which is always the home dir.
|
|
350
|
+
fs.mkdirSync(path.dirname(profilePath), { recursive: true });
|
|
261
351
|
// Already has our block?
|
|
262
352
|
if (content.includes('copilot-tracer OTLP config')) {
|
|
263
353
|
// Only skip if the embedded block is byte-identical to what we'd generate now —
|
|
@@ -287,7 +377,7 @@ function patchVSCodeSettings(settingsPath, port) {
|
|
|
287
377
|
catch {
|
|
288
378
|
return { action: 'skipped', reason: 'could not parse settings.json' };
|
|
289
379
|
}
|
|
290
|
-
const envKey =
|
|
380
|
+
const envKey = getVSCodeEnvKey();
|
|
291
381
|
const existing = (settings[envKey] ?? {});
|
|
292
382
|
const newEnv = vscodeEnvBlock(port);
|
|
293
383
|
// Already set correctly only if every current key/value is present — checking a
|
|
@@ -342,16 +432,17 @@ export function runSetup(port, silent = false) {
|
|
|
342
432
|
if (profilePath) {
|
|
343
433
|
const result = patchShellProfile(profilePath, port);
|
|
344
434
|
const label = path.basename(profilePath);
|
|
435
|
+
const reload = isWindows() ? `. ${profilePath}` : `source ${profilePath}`;
|
|
345
436
|
if (result.action === 'added') {
|
|
346
437
|
console.log(`\n${CHECK} Shell profile patched: ${label}`);
|
|
347
438
|
console.log(` ${ARROW} Added Copilot + Claude Code OTLP env vars`);
|
|
348
439
|
console.log(` ${ARROW} Added copilot() wrapper — tags each prompt with the terminal folder`);
|
|
349
|
-
console.log(` ${ARROW} Run:
|
|
440
|
+
console.log(` ${ARROW} Run: ${reload}`);
|
|
350
441
|
}
|
|
351
442
|
else if (result.action === 'updated') {
|
|
352
443
|
console.log(`\n${CHECK} Shell profile updated: ${label}`);
|
|
353
444
|
console.log(` ${ARROW} Updated port to ${port} + Claude Code/Copilot OTLP config`);
|
|
354
|
-
console.log(` ${ARROW} Run:
|
|
445
|
+
console.log(` ${ARROW} Run: ${reload}`);
|
|
355
446
|
}
|
|
356
447
|
else {
|
|
357
448
|
console.log(`\n${CHECK} Shell profile: already configured (${label})`);
|
|
@@ -363,7 +454,7 @@ export function runSetup(port, silent = false) {
|
|
|
363
454
|
const result = patchVSCodeSettings(settingsPath, port);
|
|
364
455
|
if (result.action === 'added') {
|
|
365
456
|
console.log(`\n${CHECK} VS Code settings patched`);
|
|
366
|
-
console.log(` ${ARROW} Added
|
|
457
|
+
console.log(` ${ARROW} Added ${getVSCodeEnvKey()} with OTEL vars`);
|
|
367
458
|
console.log(` ${ARROW} Restart VS Code to apply`);
|
|
368
459
|
}
|
|
369
460
|
else if (result.action === 'updated') {
|
|
@@ -411,10 +502,9 @@ export function runSetup(port, silent = false) {
|
|
|
411
502
|
process.env[OTEL_LOG_RESPONSES_KEY] = '1';
|
|
412
503
|
// 7. Summary
|
|
413
504
|
if (!silent) {
|
|
414
|
-
const profileBase = profilePath ? path.basename(profilePath) : '.zshrc';
|
|
415
505
|
console.log('\n────────────────────────────────────────────────');
|
|
416
506
|
console.log(' One manual step required:\n');
|
|
417
|
-
console.log(` source
|
|
507
|
+
console.log(` ${isWindows() ? `. ${profilePath}` : `source ${profilePath}`}`);
|
|
418
508
|
console.log(` (opens a new terminal already? — env is already active there)`);
|
|
419
509
|
if (vscode.found)
|
|
420
510
|
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.
|
|
3
|
+
"version": "1.0.11",
|
|
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",
|