copilot-debugger 0.1.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.
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Copilot Debugger</title>
7
+ <script type="module" crossorigin src="/assets/index-BDdPtMW0.js"></script>
8
+ <link rel="stylesheet" crossorigin href="/assets/index-CX0BuS04.css">
9
+ </head>
10
+ <body>
11
+ <div id="root"></div>
12
+ </body>
13
+ </html>
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ process.env.COPILOT_DEBUGGER_OPEN = process.env.COPILOT_DEBUGGER_OPEN ?? '1';
3
+ await import('./index.js');
4
+ export {};
@@ -0,0 +1,25 @@
1
+ import os from 'node:os';
2
+ import path from 'node:path';
3
+ function defaultWorkspaceStorageRoot() {
4
+ const home = os.homedir();
5
+ switch (os.platform()) {
6
+ case 'win32':
7
+ return path.join(process.env.APPDATA ?? path.join(home, 'AppData', 'Roaming'), 'Code', 'User', 'workspaceStorage');
8
+ case 'linux':
9
+ return path.join(home, '.config', 'Code', 'User', 'workspaceStorage');
10
+ default: // darwin
11
+ return path.join(home, 'Library', 'Application Support', 'Code', 'User', 'workspaceStorage');
12
+ }
13
+ }
14
+ export function loadConfig() {
15
+ const port = Number(process.env.PORT ?? 4317);
16
+ if (!Number.isInteger(port) || port < 1 || port > 65535) {
17
+ throw new Error(`Invalid PORT value: ${process.env.PORT}`);
18
+ }
19
+ return {
20
+ port,
21
+ workspaceStorageRoot: process.env.VSCODE_WORKSPACE_STORAGE_ROOT ?? defaultWorkspaceStorageRoot(),
22
+ directCopilotSessionRoot: process.env.VSCODE_COPILOT_SESSION_ROOT,
23
+ pollIntervalMs: Number(process.env.SESSION_POLL_INTERVAL_MS ?? 30_000)
24
+ };
25
+ }
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from 'node:child_process';
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import express from 'express';
7
+ import { loadConfig } from './config.js';
8
+ import { VsCodeTranscriptSource } from './sources/VsCodeTranscriptSource.js';
9
+ const currentDir = path.dirname(fileURLToPath(import.meta.url));
10
+ const clientDistRoot = path.resolve(currentDir, '..', 'client');
11
+ const clientIndexPath = path.join(clientDistRoot, 'index.html');
12
+ const config = loadConfig();
13
+ const app = express();
14
+ const source = new VsCodeTranscriptSource({
15
+ workspaceStorageRoot: config.workspaceStorageRoot,
16
+ directCopilotSessionRoot: config.directCopilotSessionRoot,
17
+ pollIntervalMs: config.pollIntervalMs
18
+ });
19
+ app.disable('x-powered-by');
20
+ // Guard against DNS rebinding: only accept requests addressed to loopback hosts.
21
+ const ALLOWED_HOSTS = new Set(['127.0.0.1', 'localhost', '[::1]']);
22
+ app.use((request, response, next) => {
23
+ const host = (request.headers.host ?? '').replace(/:\d+$/, '');
24
+ if (!ALLOWED_HOSTS.has(host)) {
25
+ response.status(403).json({ error: 'Forbidden host' });
26
+ return;
27
+ }
28
+ next();
29
+ });
30
+ app.get('/api/health', (_request, response) => {
31
+ const snapshot = source.listSessions();
32
+ response.json({ ok: true, ready: snapshot.lastRefreshAt !== undefined, lastRefreshAt: snapshot.lastRefreshAt });
33
+ });
34
+ app.get('/api/sessions', (request, response) => {
35
+ const snapshot = source.listSessions();
36
+ const q = typeof request.query.q === 'string' ? request.query.q.toLowerCase() : undefined;
37
+ const agent = typeof request.query.agent === 'string' ? request.query.agent.toLowerCase() : undefined;
38
+ const sessions = snapshot.sessions.filter((session) => {
39
+ const matchesQuery = q
40
+ ? [session.id, session.workspaceStorageId, session.firstUserMessage, session.producer]
41
+ .filter(Boolean)
42
+ .some((value) => value?.toLowerCase().includes(q))
43
+ : true;
44
+ const matchesAgent = agent ? session.agents.some((value) => value.toLowerCase() === agent) : true;
45
+ return matchesQuery && matchesAgent;
46
+ });
47
+ response.json({ ...snapshot, sessions, total: sessions.length });
48
+ });
49
+ app.get('/api/sessions/:id', (request, response) => {
50
+ const session = source.getSession(request.params.id);
51
+ if (!session) {
52
+ response.status(404).json({ error: 'Session not found' });
53
+ return;
54
+ }
55
+ response.json(session);
56
+ });
57
+ app.get('/api/sessions/:id/overview', async (request, response) => {
58
+ const session = source.getSession(request.params.id);
59
+ if (!session) {
60
+ response.status(404).json({ error: 'Session not found' });
61
+ return;
62
+ }
63
+ const overview = await source.getSessionOverview(request.params.id);
64
+ if (!overview) {
65
+ response.status(404).json({ error: 'No overview data available (debug log required)' });
66
+ return;
67
+ }
68
+ response.json(overview);
69
+ });
70
+ app.get('/api/sessions/:id/turns', async (request, response) => {
71
+ const session = source.getSession(request.params.id);
72
+ if (!session) {
73
+ response.status(404).json({ error: 'Session not found' });
74
+ return;
75
+ }
76
+ const turns = await source.getSessionTurns(request.params.id);
77
+ response.json({ turns, total: turns.length });
78
+ });
79
+ if (fs.existsSync(clientIndexPath)) {
80
+ app.use(express.static(clientDistRoot));
81
+ app.get('*', (_request, response) => {
82
+ response.sendFile(clientIndexPath);
83
+ });
84
+ }
85
+ const server = app.listen(config.port, '127.0.0.1', () => {
86
+ const url = `http://127.0.0.1:${config.port}`;
87
+ console.log(`Copilot Debugger listening on ${url}`);
88
+ if (process.env.COPILOT_DEBUGGER_OPEN === '1') {
89
+ openBrowser(url);
90
+ }
91
+ });
92
+ source.start().catch((error) => {
93
+ console.error('Session source failed to start', error);
94
+ });
95
+ async function shutdown() {
96
+ await source.stop();
97
+ server.close(() => process.exit(0));
98
+ }
99
+ process.on('SIGINT', () => void shutdown());
100
+ process.on('SIGTERM', () => void shutdown());
101
+ function openBrowser(url) {
102
+ const platform = process.platform;
103
+ const command = platform === 'darwin' ? 'open' : platform === 'win32' ? 'cmd' : 'xdg-open';
104
+ const args = platform === 'win32' ? ['/c', 'start', '', url] : [url];
105
+ const child = spawn(command, args, { detached: true, stdio: 'ignore' });
106
+ child.on('error', (error) => {
107
+ console.warn(`Could not open browser automatically: ${error.message}`);
108
+ });
109
+ child.unref();
110
+ }
@@ -0,0 +1 @@
1
+ export {};