hacklab 0.21.3 → 0.22.1
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 +101 -10
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +3 -3
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/scan.d.ts.map +1 -1
- package/dist/commands/scan.js +4 -0
- package/dist/commands/scan.js.map +1 -1
- package/dist/commands/setup.js +3 -1
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/sync.js +11 -4
- package/dist/commands/sync.js.map +1 -1
- package/dist/config.d.ts +4 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/prompt-consent.d.ts +4 -6
- package/dist/prompt-consent.d.ts.map +1 -1
- package/dist/prompt-consent.js +16 -9
- package/dist/prompt-consent.js.map +1 -1
- package/dist/prompt-stats.d.ts +14 -2
- package/dist/prompt-stats.d.ts.map +1 -1
- package/dist/prompt-stats.js +83 -26
- package/dist/prompt-stats.js.map +1 -1
- package/dist/scanners/antigravity.d.ts +41 -0
- package/dist/scanners/antigravity.d.ts.map +1 -0
- package/dist/scanners/antigravity.js +210 -0
- package/dist/scanners/antigravity.js.map +1 -0
- package/dist/scanners/github-copilot-telemetry.d.ts +23 -0
- package/dist/scanners/github-copilot-telemetry.d.ts.map +1 -0
- package/dist/scanners/github-copilot-telemetry.js +220 -0
- package/dist/scanners/github-copilot-telemetry.js.map +1 -0
- package/dist/scanners/github-copilot.d.ts +68 -0
- package/dist/scanners/github-copilot.d.ts.map +1 -0
- package/dist/scanners/github-copilot.js +262 -0
- package/dist/scanners/github-copilot.js.map +1 -0
- package/dist/scanners/incremental.d.ts +19 -4
- package/dist/scanners/incremental.d.ts.map +1 -1
- package/dist/scanners/incremental.js +104 -23
- package/dist/scanners/incremental.js.map +1 -1
- package/dist/scanners/index.d.ts.map +1 -1
- package/dist/scanners/index.js +16 -2
- package/dist/scanners/index.js.map +1 -1
- package/dist/scanners/util.d.ts +1 -1
- package/dist/scanners/util.d.ts.map +1 -1
- package/dist/scanners/util.js.map +1 -1
- package/dist/sync.d.ts +2 -0
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +4 -0
- package/dist/sync.js.map +1 -1
- package/package.json +3 -1
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { basename, join, resolve } from 'node:path';
|
|
4
|
+
import { parse as parseJsonc } from 'jsonc-parser';
|
|
5
|
+
import { queryDb } from './sqlite.js';
|
|
6
|
+
import { findFiles } from './util.js';
|
|
7
|
+
function object(value) {
|
|
8
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
9
|
+
? value
|
|
10
|
+
: {};
|
|
11
|
+
}
|
|
12
|
+
/** Discover only Copilot-owned exports; never sweep generic agent telemetry. */
|
|
13
|
+
export async function copilotTelemetrySources(userDirs) {
|
|
14
|
+
const root = process.env.COPILOT_HOME || join(homedir(), '.copilot');
|
|
15
|
+
const files = new Set(await findFiles(join(root, 'otel'), '.jsonl'));
|
|
16
|
+
const explicit = process.env.COPILOT_OTEL_FILE_EXPORTER_PATH?.trim();
|
|
17
|
+
if (explicit)
|
|
18
|
+
files.add(resolve(explicit));
|
|
19
|
+
const databases = new Set();
|
|
20
|
+
for (const userDir of userDirs) {
|
|
21
|
+
const settings = [
|
|
22
|
+
join(userDir, 'settings.json'),
|
|
23
|
+
...(await findFiles(join(userDir, 'profiles'), 'settings.json')),
|
|
24
|
+
];
|
|
25
|
+
for (const path of settings) {
|
|
26
|
+
try {
|
|
27
|
+
const config = object(parseJsonc(await readFile(path, 'utf8')));
|
|
28
|
+
const outfile = config['github.copilot.chat.otel.outfile'];
|
|
29
|
+
if (typeof outfile === 'string' && outfile.trim())
|
|
30
|
+
files.add(resolve(outfile.trim()));
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
if (error.code !== 'ENOENT') {
|
|
34
|
+
console.warn(`Copilot telemetry settings unavailable: ${error instanceof Error ? error.message : String(error)}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const candidates = [
|
|
39
|
+
...(await findFiles(join(userDir, 'globalStorage', 'github.copilot-chat'), '.db')),
|
|
40
|
+
...(await findFiles(join(userDir, 'profiles'), '.db')),
|
|
41
|
+
];
|
|
42
|
+
for (const path of candidates) {
|
|
43
|
+
if (/[\\/]globalStorage[\\/]github\.copilot-chat[\\/]agent-traces\.db$/i.test(path))
|
|
44
|
+
databases.add(path);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return { files: [...files], databases: [...databases] };
|
|
48
|
+
}
|
|
49
|
+
function hrTime(value) {
|
|
50
|
+
if (!Array.isArray(value) ||
|
|
51
|
+
value.length !== 2 ||
|
|
52
|
+
!Number.isSafeInteger(value[0]) ||
|
|
53
|
+
!Number.isSafeInteger(value[1]) ||
|
|
54
|
+
value[0] < 0 ||
|
|
55
|
+
value[1] < 0 ||
|
|
56
|
+
value[1] >= 1_000_000_000)
|
|
57
|
+
return NaN;
|
|
58
|
+
return value[0] * 1000 + Math.floor(value[1] / 1_000_000);
|
|
59
|
+
}
|
|
60
|
+
/** Direct ReadableSpan JSON from Copilot's file exporter, not aggregate metrics. */
|
|
61
|
+
function fileSpan(line) {
|
|
62
|
+
try {
|
|
63
|
+
const record = object(JSON.parse(line));
|
|
64
|
+
const context = object(record._spanContext ?? record.spanContext);
|
|
65
|
+
const parent = object(record.parentSpanContext);
|
|
66
|
+
const traceId = record.traceId ?? context.traceId;
|
|
67
|
+
const spanId = record.spanId ?? context.spanId;
|
|
68
|
+
const parentSpanId = record.parentSpanId ?? parent.spanId ?? '';
|
|
69
|
+
if (typeof traceId !== 'string' ||
|
|
70
|
+
!traceId ||
|
|
71
|
+
typeof spanId !== 'string' ||
|
|
72
|
+
!spanId ||
|
|
73
|
+
typeof parentSpanId !== 'string')
|
|
74
|
+
return null;
|
|
75
|
+
const endTime = hrTime(record.endTime);
|
|
76
|
+
if (!Number.isFinite(endTime) || endTime <= 0)
|
|
77
|
+
return null;
|
|
78
|
+
return {
|
|
79
|
+
traceId,
|
|
80
|
+
spanId,
|
|
81
|
+
parentSpanId,
|
|
82
|
+
endTime,
|
|
83
|
+
attributes: object(record.attributes),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// Partial lines and the exporter's known '{}' serialization failure carry no usage.
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* VS Code's SQLiteSpanExporter preserves SDK trace/span IDs, including embedded
|
|
93
|
+
* CLI spans. Count completed native Copilot trace leaves only, deduplicating the
|
|
94
|
+
* DB and file exports. Require the owning agent span: a Claude/Codex delegate's
|
|
95
|
+
* usage belongs to its native scanner, and a model called Claude is not itself
|
|
96
|
+
* evidence that the harness was Claude Code.
|
|
97
|
+
* Sources: microsoft/vscode extensions/copilot/src/platform/otel/node/{
|
|
98
|
+
* fileExporters.ts,sqlite/sqliteSpanExporter.ts,sqlite/otelSqliteStore.ts}.
|
|
99
|
+
*/
|
|
100
|
+
export async function readCopilotTelemetry(sources) {
|
|
101
|
+
const spans = new Map();
|
|
102
|
+
for (const path of sources.files) {
|
|
103
|
+
try {
|
|
104
|
+
for (const line of (await readFile(path, 'utf8')).split('\n')) {
|
|
105
|
+
const span = fileSpan(line);
|
|
106
|
+
if (span)
|
|
107
|
+
spans.set(`${span.traceId}:${span.spanId}`, span);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
if (error.code !== 'ENOENT') {
|
|
112
|
+
console.warn(`Copilot telemetry unavailable in ${basename(path)}: ${error instanceof Error ? error.message : String(error)}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
for (const path of sources.databases) {
|
|
117
|
+
try {
|
|
118
|
+
const rows = await queryDb(path, `SELECT span_id, trace_id, parent_span_id,
|
|
119
|
+
end_time_ms, operation_name, agent_name, conversation_id, chat_session_id,
|
|
120
|
+
response_model, request_model, input_tokens, output_tokens,
|
|
121
|
+
(SELECT value FROM span_attributes
|
|
122
|
+
WHERE span_id = s.span_id AND key = 'gen_ai.agent.id') AS agent_id
|
|
123
|
+
FROM spans AS s`);
|
|
124
|
+
for (const row of rows) {
|
|
125
|
+
const [spanId, traceId, parentSpanId, endTime, operation, agent, conversation, chatSession, responseModel, requestModel, input, output, agentId,] = row;
|
|
126
|
+
if (typeof spanId !== 'string' ||
|
|
127
|
+
!spanId ||
|
|
128
|
+
typeof traceId !== 'string' ||
|
|
129
|
+
!traceId ||
|
|
130
|
+
typeof endTime !== 'number' ||
|
|
131
|
+
!Number.isFinite(endTime) ||
|
|
132
|
+
endTime <= 0)
|
|
133
|
+
continue;
|
|
134
|
+
spans.set(`${traceId}:${spanId}`, {
|
|
135
|
+
spanId,
|
|
136
|
+
traceId,
|
|
137
|
+
parentSpanId: typeof parentSpanId === 'string' ? parentSpanId : '',
|
|
138
|
+
endTime,
|
|
139
|
+
attributes: {
|
|
140
|
+
'gen_ai.operation.name': operation,
|
|
141
|
+
'gen_ai.agent.name': agent,
|
|
142
|
+
'gen_ai.agent.id': agentId,
|
|
143
|
+
'gen_ai.conversation.id': conversation || chatSession,
|
|
144
|
+
'gen_ai.response.model': responseModel,
|
|
145
|
+
'gen_ai.request.model': requestModel,
|
|
146
|
+
'gen_ai.usage.input_tokens': input,
|
|
147
|
+
'gen_ai.usage.output_tokens': output,
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
console.warn(`Copilot trace database unavailable: ${error instanceof Error ? error.message : String(error)}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
const inferences = [];
|
|
157
|
+
for (const span of spans.values()) {
|
|
158
|
+
const attributes = span.attributes;
|
|
159
|
+
if (attributes['gen_ai.operation.name'] !== 'chat')
|
|
160
|
+
continue;
|
|
161
|
+
let session = attributes['gen_ai.conversation.id'] ??
|
|
162
|
+
attributes['copilot_chat.chat_session_id'];
|
|
163
|
+
let native = false;
|
|
164
|
+
let delegated = false;
|
|
165
|
+
let cursor = span;
|
|
166
|
+
const seen = new Set();
|
|
167
|
+
while (cursor && !seen.has(cursor.spanId)) {
|
|
168
|
+
seen.add(cursor.spanId);
|
|
169
|
+
if (cursor.attributes['gen_ai.agent.id'] === 'github.copilot.default')
|
|
170
|
+
native = true;
|
|
171
|
+
const agent = cursor.attributes['gen_ai.agent.name'];
|
|
172
|
+
if (typeof agent === 'string') {
|
|
173
|
+
const name = agent.toLowerCase();
|
|
174
|
+
if (name === 'github copilot chat' ||
|
|
175
|
+
name === 'copilot' ||
|
|
176
|
+
name === 'copilotcli' ||
|
|
177
|
+
name === 'github-copilot')
|
|
178
|
+
native = true;
|
|
179
|
+
if (name === 'claude' ||
|
|
180
|
+
name === 'claude-code' ||
|
|
181
|
+
name === 'claude_code' ||
|
|
182
|
+
name === 'codex')
|
|
183
|
+
delegated = true;
|
|
184
|
+
}
|
|
185
|
+
session ||=
|
|
186
|
+
cursor.attributes['gen_ai.conversation.id'] ??
|
|
187
|
+
cursor.attributes['copilot_chat.chat_session_id'];
|
|
188
|
+
cursor = spans.get(`${span.traceId}:${cursor.parentSpanId}`);
|
|
189
|
+
}
|
|
190
|
+
if (!native || delegated || typeof session !== 'string' || !session)
|
|
191
|
+
continue;
|
|
192
|
+
const model = attributes['gen_ai.response.model'] || attributes['gen_ai.request.model'];
|
|
193
|
+
const input = attributes['gen_ai.usage.input_tokens'];
|
|
194
|
+
const output = attributes['gen_ai.usage.output_tokens'];
|
|
195
|
+
if (typeof model !== 'string' ||
|
|
196
|
+
!model ||
|
|
197
|
+
typeof input !== 'number' ||
|
|
198
|
+
typeof output !== 'number' ||
|
|
199
|
+
!Number.isSafeInteger(input) ||
|
|
200
|
+
input < 0 ||
|
|
201
|
+
!Number.isSafeInteger(output) ||
|
|
202
|
+
output < 0)
|
|
203
|
+
continue;
|
|
204
|
+
// GenAI input includes cache subsets; output includes reasoning subsets.
|
|
205
|
+
const tokens = input + output;
|
|
206
|
+
const date = new Date(span.endTime);
|
|
207
|
+
if (!Number.isSafeInteger(tokens) ||
|
|
208
|
+
tokens <= 0 ||
|
|
209
|
+
!Number.isFinite(date.getTime()))
|
|
210
|
+
continue;
|
|
211
|
+
inferences.push({
|
|
212
|
+
sessionId: session,
|
|
213
|
+
model,
|
|
214
|
+
timestamp: date.toISOString(),
|
|
215
|
+
tokens,
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
return inferences;
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=github-copilot-telemetry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-copilot-telemetry.js","sourceRoot":"","sources":["../../src/scanners/github-copilot-telemetry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { ScanResult } from './util.js';
|
|
2
|
+
/** A user-authored Copilot prompt retained by a local Copilot transcript. */
|
|
3
|
+
export type GitHubCopilotPrompt = {
|
|
4
|
+
/** Stable across scans and distinct from other harness session ids. */
|
|
5
|
+
sessionId: string;
|
|
6
|
+
/** The prompt text. Send only under the existing full-consent flow. */
|
|
7
|
+
text: string;
|
|
8
|
+
/** The event's recorded instant, or null when the source did not record one. */
|
|
9
|
+
timestamp: string | null;
|
|
10
|
+
};
|
|
11
|
+
type CopilotSource = 'vscode' | 'cli';
|
|
12
|
+
type ParseOptions = {
|
|
13
|
+
source: CopilotSource;
|
|
14
|
+
/** Required for Copilot CLI events, whose session id is the parent directory name. */
|
|
15
|
+
sessionId?: string;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Source: microsoft/vscode, extensions/copilot/src/extension/chat/vscode-node/
|
|
19
|
+
* sessionTranscriptService.ts (session.start + user.message JSONL).
|
|
20
|
+
* VS Code's built-in GitHub Copilot extension writes these JSONL transcripts
|
|
21
|
+
* under workspace storage. We intentionally name only its extension storage,
|
|
22
|
+
* never generic VS Code chat persistence, so another chat provider cannot be
|
|
23
|
+
* mistaken for Copilot.
|
|
24
|
+
*/
|
|
25
|
+
export declare function githubCopilotVsCodeWorkspaceStorageRoots(): string[];
|
|
26
|
+
/** Copilot CLI uses this state root; COPILOT_HOME takes precedence at scan time. */
|
|
27
|
+
export declare function githubCopilotCliSessionStateDir(): string;
|
|
28
|
+
/**
|
|
29
|
+
* Local files deliberately written by GitHub Copilot only.
|
|
30
|
+
*
|
|
31
|
+
* VS Code transcript persistence is currently limited to the extension's
|
|
32
|
+
* `github.copilot-chat/transcripts` directory. Copilot CLI persists one
|
|
33
|
+
* `events.jsonl` file per session. Neither generic VS Code workspace state nor
|
|
34
|
+
* exported chats are read, avoiding duplicate/snapshot records and unrelated
|
|
35
|
+
* providers' chats.
|
|
36
|
+
*/
|
|
37
|
+
export declare function githubCopilotFiles(): Promise<string[]>;
|
|
38
|
+
/**
|
|
39
|
+
* Parse the documented JSONL event shape written by VS Code's Copilot
|
|
40
|
+
* transcript service and the Copilot CLI SDK. Synthetic SDK user messages
|
|
41
|
+
* (for example tool/skill traffic) are excluded; only absent or `user` sources
|
|
42
|
+
* represent a person-entered prompt.
|
|
43
|
+
*/
|
|
44
|
+
export declare function parseGitHubCopilotJsonl(content: string, options: ParseOptions): GitHubCopilotPrompt[];
|
|
45
|
+
/**
|
|
46
|
+
* Read raw local prompts only for the consented prompt pipeline. Token scanning
|
|
47
|
+
* below uses numeric usage records independently of prompt-sharing consent.
|
|
48
|
+
*/
|
|
49
|
+
export declare function scanGitHubCopilotPrompts(): Promise<GitHubCopilotPrompt[]>;
|
|
50
|
+
export declare function githubCopilotTokenFiles(): Promise<string[]>;
|
|
51
|
+
export type CopilotUsageSnapshot = {
|
|
52
|
+
sessionId: string;
|
|
53
|
+
model: string;
|
|
54
|
+
timestamp: string;
|
|
55
|
+
tokens: number;
|
|
56
|
+
messages: number;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* SDK session.shutdown is durable, unlike ephemeral assistant.usage events.
|
|
60
|
+
* modelMetrics is cumulative across resumes. Input already includes both cache
|
|
61
|
+
* buckets, output already includes reasoning; agentMetrics is a breakdown of
|
|
62
|
+
* these same modelMetrics, not additional usage.
|
|
63
|
+
* Sources: github/copilot-sdk generated/session-events.ts; ccusage.com/guide/copilot/
|
|
64
|
+
*/
|
|
65
|
+
export declare function parseGitHubCopilotUsage(content: string, fallbackSessionId: string): CopilotUsageSnapshot[];
|
|
66
|
+
export declare function scanGitHubCopilot(): Promise<ScanResult>;
|
|
67
|
+
export {};
|
|
68
|
+
//# sourceMappingURL=github-copilot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-copilot.d.ts","sourceRoot":"","sources":["../../src/scanners/github-copilot.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAG3C,6EAA6E;AAC7E,MAAM,MAAM,mBAAmB,GAAG;IAChC,uEAAuE;IACvE,SAAS,EAAE,MAAM,CAAA;IACjB,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAA;IACZ,gFAAgF;IAChF,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CACzB,CAAA;AAED,KAAK,aAAa,GAAG,QAAQ,GAAG,KAAK,CAAA;AAErC,KAAK,YAAY,GAAG;IAClB,MAAM,EAAE,aAAa,CAAA;IACrB,sFAAsF;IACtF,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;;;;;GAOG;AACH,wBAAgB,wCAAwC,IAAI,MAAM,EAAE,CAuCnE;AAED,oFAAoF;AACpF,wBAAgB,+BAA+B,IAAI,MAAM,CAKxD;AAED;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAsB5D;AAcD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,YAAY,GACpB,mBAAmB,EAAE,CAwDvB;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,IAAI,OAAO,CACvD,mBAAmB,EAAE,CACtB,CAoBA;AAQD,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CASjE;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAMD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EACf,iBAAiB,EAAE,MAAM,GACxB,oBAAoB,EAAE,CA4DxB;AAED,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,UAAU,CAAC,CA8D7D"}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import { readdir, readFile } from 'node:fs/promises';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { basename, dirname, join } from 'node:path';
|
|
4
|
+
import { copilotTelemetrySources, readCopilotTelemetry, } from './github-copilot-telemetry.js';
|
|
5
|
+
import { findFiles, TokenCollector } from './util.js';
|
|
6
|
+
/**
|
|
7
|
+
* Source: microsoft/vscode, extensions/copilot/src/extension/chat/vscode-node/
|
|
8
|
+
* sessionTranscriptService.ts (session.start + user.message JSONL).
|
|
9
|
+
* VS Code's built-in GitHub Copilot extension writes these JSONL transcripts
|
|
10
|
+
* under workspace storage. We intentionally name only its extension storage,
|
|
11
|
+
* never generic VS Code chat persistence, so another chat provider cannot be
|
|
12
|
+
* mistaken for Copilot.
|
|
13
|
+
*/
|
|
14
|
+
export function githubCopilotVsCodeWorkspaceStorageRoots() {
|
|
15
|
+
const home = homedir();
|
|
16
|
+
switch (process.platform) {
|
|
17
|
+
case 'win32': {
|
|
18
|
+
const appData = process.env.APPDATA || join(home, 'AppData', 'Roaming');
|
|
19
|
+
return [
|
|
20
|
+
join(appData, 'Code', 'User', 'workspaceStorage'),
|
|
21
|
+
join(appData, 'Code - Insiders', 'User', 'workspaceStorage'),
|
|
22
|
+
];
|
|
23
|
+
}
|
|
24
|
+
case 'darwin':
|
|
25
|
+
return [
|
|
26
|
+
join(home, 'Library', 'Application Support', 'Code', 'User', 'workspaceStorage'),
|
|
27
|
+
join(home, 'Library', 'Application Support', 'Code - Insiders', 'User', 'workspaceStorage'),
|
|
28
|
+
];
|
|
29
|
+
case 'linux': {
|
|
30
|
+
const configHome = process.env.XDG_CONFIG_HOME || join(home, '.config');
|
|
31
|
+
return [
|
|
32
|
+
join(configHome, 'Code', 'User', 'workspaceStorage'),
|
|
33
|
+
join(configHome, 'Code - Insiders', 'User', 'workspaceStorage'),
|
|
34
|
+
];
|
|
35
|
+
}
|
|
36
|
+
default:
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/** Copilot CLI uses this state root; COPILOT_HOME takes precedence at scan time. */
|
|
41
|
+
export function githubCopilotCliSessionStateDir() {
|
|
42
|
+
return join(process.env.COPILOT_HOME || join(homedir(), '.copilot'), 'session-state');
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Local files deliberately written by GitHub Copilot only.
|
|
46
|
+
*
|
|
47
|
+
* VS Code transcript persistence is currently limited to the extension's
|
|
48
|
+
* `github.copilot-chat/transcripts` directory. Copilot CLI persists one
|
|
49
|
+
* `events.jsonl` file per session. Neither generic VS Code workspace state nor
|
|
50
|
+
* exported chats are read, avoiding duplicate/snapshot records and unrelated
|
|
51
|
+
* providers' chats.
|
|
52
|
+
*/
|
|
53
|
+
export async function githubCopilotFiles() {
|
|
54
|
+
const vscodeFiles = await Promise.all(githubCopilotVsCodeWorkspaceStorageRoots().map(async (root) => {
|
|
55
|
+
let workspaces;
|
|
56
|
+
try {
|
|
57
|
+
workspaces = await readdir(root);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
return (await Promise.all(workspaces.map((workspace) => findFiles(join(root, workspace, 'github.copilot-chat', 'transcripts'), '.jsonl')))).flat();
|
|
63
|
+
}));
|
|
64
|
+
return [...vscodeFiles.flat(), ...(await githubCopilotSessionFiles())];
|
|
65
|
+
}
|
|
66
|
+
/** Keep a source prefix because VS Code and the CLI may reuse a UUID. */
|
|
67
|
+
function prefixedSessionId(source, sessionId) {
|
|
68
|
+
return `github_copilot:${source}:${sessionId}`;
|
|
69
|
+
}
|
|
70
|
+
function timestamp(value) {
|
|
71
|
+
if (typeof value !== 'string' && typeof value !== 'number')
|
|
72
|
+
return null;
|
|
73
|
+
const instant = typeof value === 'number' ? value : Date.parse(value);
|
|
74
|
+
const date = new Date(instant);
|
|
75
|
+
return Number.isFinite(date.getTime()) ? date.toISOString() : null;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Parse the documented JSONL event shape written by VS Code's Copilot
|
|
79
|
+
* transcript service and the Copilot CLI SDK. Synthetic SDK user messages
|
|
80
|
+
* (for example tool/skill traffic) are excluded; only absent or `user` sources
|
|
81
|
+
* represent a person-entered prompt.
|
|
82
|
+
*/
|
|
83
|
+
export function parseGitHubCopilotJsonl(content, options) {
|
|
84
|
+
let sessionId = options.sessionId?.trim() || '';
|
|
85
|
+
let sawVsCodeTranscript = options.source !== 'vscode';
|
|
86
|
+
const prompts = [];
|
|
87
|
+
for (const line of content.split('\n')) {
|
|
88
|
+
if (!line.trim())
|
|
89
|
+
continue;
|
|
90
|
+
let event;
|
|
91
|
+
try {
|
|
92
|
+
event = JSON.parse(line);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (!event || typeof event !== 'object')
|
|
98
|
+
continue;
|
|
99
|
+
const record = event;
|
|
100
|
+
if (record.type === 'session.start') {
|
|
101
|
+
if (record.data?.producer === 'copilot-agent')
|
|
102
|
+
sawVsCodeTranscript = true;
|
|
103
|
+
if (typeof record.data?.sessionId === 'string' &&
|
|
104
|
+
record.data.sessionId.trim()) {
|
|
105
|
+
sessionId = record.data.sessionId.trim();
|
|
106
|
+
}
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (record.type !== 'user.message' ||
|
|
110
|
+
!sawVsCodeTranscript ||
|
|
111
|
+
!sessionId ||
|
|
112
|
+
prefixedSessionId(options.source, sessionId).length > 128)
|
|
113
|
+
continue;
|
|
114
|
+
if (typeof record.data?.source === 'string' &&
|
|
115
|
+
record.data.source.toLowerCase() !== 'user') {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (typeof record.data?.content !== 'string' || !record.data.content.trim())
|
|
119
|
+
continue;
|
|
120
|
+
prompts.push({
|
|
121
|
+
sessionId: prefixedSessionId(options.source, sessionId),
|
|
122
|
+
text: record.data.content,
|
|
123
|
+
timestamp: timestamp(record.timestamp),
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
return prompts;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Read raw local prompts only for the consented prompt pipeline. Token scanning
|
|
130
|
+
* below uses numeric usage records independently of prompt-sharing consent.
|
|
131
|
+
*/
|
|
132
|
+
export async function scanGitHubCopilotPrompts() {
|
|
133
|
+
const files = await githubCopilotFiles();
|
|
134
|
+
const prompts = await Promise.all(files.map(async (path) => {
|
|
135
|
+
let content;
|
|
136
|
+
try {
|
|
137
|
+
content = await readFile(path, 'utf8');
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
return [];
|
|
141
|
+
}
|
|
142
|
+
const cliMatch = path.match(/[\\/]session-state[\\/]([^\\/]+)[\\/]events\.jsonl$/i);
|
|
143
|
+
return parseGitHubCopilotJsonl(content, {
|
|
144
|
+
source: cliMatch ? 'cli' : 'vscode',
|
|
145
|
+
sessionId: cliMatch?.[1],
|
|
146
|
+
});
|
|
147
|
+
}));
|
|
148
|
+
return prompts.flat();
|
|
149
|
+
}
|
|
150
|
+
async function githubCopilotSessionFiles() {
|
|
151
|
+
return (await findFiles(githubCopilotCliSessionStateDir(), '.jsonl')).filter((path) => /[\\/]session-state[\\/][^\\/]+[\\/]events\.jsonl$/i.test(path));
|
|
152
|
+
}
|
|
153
|
+
export async function githubCopilotTokenFiles() {
|
|
154
|
+
const telemetry = await copilotTelemetrySources(githubCopilotVsCodeWorkspaceStorageRoots().map((root) => dirname(root)));
|
|
155
|
+
return [
|
|
156
|
+
...(await githubCopilotSessionFiles()),
|
|
157
|
+
...telemetry.files,
|
|
158
|
+
...telemetry.databases.flatMap((path) => [path, `${path}-wal`]),
|
|
159
|
+
];
|
|
160
|
+
}
|
|
161
|
+
function tokenCount(value) {
|
|
162
|
+
return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* SDK session.shutdown is durable, unlike ephemeral assistant.usage events.
|
|
166
|
+
* modelMetrics is cumulative across resumes. Input already includes both cache
|
|
167
|
+
* buckets, output already includes reasoning; agentMetrics is a breakdown of
|
|
168
|
+
* these same modelMetrics, not additional usage.
|
|
169
|
+
* Sources: github/copilot-sdk generated/session-events.ts; ccusage.com/guide/copilot/
|
|
170
|
+
*/
|
|
171
|
+
export function parseGitHubCopilotUsage(content, fallbackSessionId) {
|
|
172
|
+
let sessionId = fallbackSessionId;
|
|
173
|
+
const snapshots = [];
|
|
174
|
+
for (const line of content.split('\n')) {
|
|
175
|
+
let event;
|
|
176
|
+
try {
|
|
177
|
+
event = JSON.parse(line);
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
if (!event || typeof event !== 'object')
|
|
183
|
+
continue;
|
|
184
|
+
if (event.type === 'session.start' &&
|
|
185
|
+
typeof event.data?.sessionId === 'string') {
|
|
186
|
+
sessionId = event.data.sessionId.trim() || sessionId;
|
|
187
|
+
}
|
|
188
|
+
if (event.type !== 'session.shutdown' || event.agentId)
|
|
189
|
+
continue;
|
|
190
|
+
const at = timestamp(event.timestamp);
|
|
191
|
+
const metrics = event.data?.modelMetrics;
|
|
192
|
+
if (!at ||
|
|
193
|
+
!sessionId ||
|
|
194
|
+
!metrics ||
|
|
195
|
+
typeof metrics !== 'object' ||
|
|
196
|
+
Array.isArray(metrics))
|
|
197
|
+
continue;
|
|
198
|
+
for (const [model, metric] of Object.entries(metrics)) {
|
|
199
|
+
const input = metric?.usage?.inputTokens;
|
|
200
|
+
const output = metric?.usage?.outputTokens;
|
|
201
|
+
if (!model || !tokenCount(input) || !tokenCount(output))
|
|
202
|
+
continue;
|
|
203
|
+
const tokens = input + output;
|
|
204
|
+
if (!Number.isSafeInteger(tokens))
|
|
205
|
+
continue;
|
|
206
|
+
snapshots.push({
|
|
207
|
+
sessionId,
|
|
208
|
+
model,
|
|
209
|
+
timestamp: at,
|
|
210
|
+
tokens,
|
|
211
|
+
messages: tokenCount(metric?.requests?.count)
|
|
212
|
+
? metric.requests.count
|
|
213
|
+
: 0,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return snapshots;
|
|
218
|
+
}
|
|
219
|
+
export async function scanGitHubCopilot() {
|
|
220
|
+
const snapshots = [];
|
|
221
|
+
for (const path of await githubCopilotSessionFiles()) {
|
|
222
|
+
try {
|
|
223
|
+
snapshots.push(...parseGitHubCopilotUsage(await readFile(path, 'utf8'), basename(dirname(path))));
|
|
224
|
+
}
|
|
225
|
+
catch (error) {
|
|
226
|
+
console.warn(`Copilot usage unavailable in ${basename(dirname(path))}: ${error instanceof Error ? error.message : String(error)}`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
snapshots.sort((a, b) => a.timestamp.localeCompare(b.timestamp));
|
|
230
|
+
const collector = new TokenCollector('github_copilot');
|
|
231
|
+
const previous = new Map();
|
|
232
|
+
const latestShutdown = new Map();
|
|
233
|
+
for (const snapshot of snapshots) {
|
|
234
|
+
const key = JSON.stringify([snapshot.sessionId, snapshot.model]);
|
|
235
|
+
latestShutdown.set(JSON.stringify([
|
|
236
|
+
snapshot.sessionId,
|
|
237
|
+
snapshot.model.replace(/-1m(?:-internal)?$/, ''),
|
|
238
|
+
]), snapshot.timestamp);
|
|
239
|
+
const before = previous.get(key) ?? { tokens: 0, messages: 0 };
|
|
240
|
+
const tokens = Math.max(before.tokens, snapshot.tokens);
|
|
241
|
+
const messages = Math.max(before.messages, snapshot.messages);
|
|
242
|
+
if (tokens > before.tokens) {
|
|
243
|
+
collector.addDaily(snapshot.timestamp.slice(0, 10), snapshot.model, tokens - before.tokens, messages - before.messages);
|
|
244
|
+
}
|
|
245
|
+
previous.set(key, { tokens, messages });
|
|
246
|
+
}
|
|
247
|
+
const telemetry = await copilotTelemetrySources(githubCopilotVsCodeWorkspaceStorageRoots().map((root) => dirname(root)));
|
|
248
|
+
for (const inference of await readCopilotTelemetry(telemetry)) {
|
|
249
|
+
const key = JSON.stringify([
|
|
250
|
+
inference.sessionId,
|
|
251
|
+
inference.model.replace(/-1m(?:-internal)?$/, ''),
|
|
252
|
+
]);
|
|
253
|
+
const shutdown = latestShutdown.get(key);
|
|
254
|
+
// A later resumed call is new; earlier calls already belong to the durable
|
|
255
|
+
// cumulative snapshot. Never add two representations of the same usage.
|
|
256
|
+
if (shutdown && inference.timestamp <= shutdown)
|
|
257
|
+
continue;
|
|
258
|
+
collector.addDaily(inference.timestamp.slice(0, 10), inference.model, inference.tokens, 1);
|
|
259
|
+
}
|
|
260
|
+
return collector.result();
|
|
261
|
+
}
|
|
262
|
+
//# sourceMappingURL=github-copilot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-copilot.js","sourceRoot":"","sources":["../../src/scanners/github-copilot.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type PromptActivityAggregate, type PromptLine } from '../prompt-stats.js';
|
|
1
|
+
import { IDE_PROMPT_SOURCES, type PromptActivityAggregate, type PromptLine, type ScannedPromptActivity } from '../prompt-stats.js';
|
|
2
2
|
import { type AggregateScan, type PromptActivity, type ScanResult, type TokensMessages, type Tool, type UsageLine } from './index.js';
|
|
3
3
|
/**
|
|
4
4
|
* Bumping this makes `loadScanState` return null, which re-reads every log
|
|
@@ -21,12 +21,15 @@ import { type AggregateScan, type PromptActivity, type ScanResult, type TokensMe
|
|
|
21
21
|
* the wipe this time. For anyone still on an older CLI it costs one extra
|
|
22
22
|
* rescan and nothing else.
|
|
23
23
|
*
|
|
24
|
+
* 5: prompt activity is partitioned by source, so rebuilding a Claude log or
|
|
25
|
+
* rotating an IDE transcript cannot erase or double-count another harness.
|
|
26
|
+
*
|
|
24
27
|
* Token totals are unaffected: they travel as cumulative absolutes that the
|
|
25
28
|
* server diffs against its own per-machine snapshot, and `state.uploaded` is
|
|
26
29
|
* only the tick's "nothing moved" short-circuit — so a rebuild re-baselines
|
|
27
30
|
* rather than double-counting.
|
|
28
31
|
*/
|
|
29
|
-
export declare const SCAN_STATE_VERSION =
|
|
32
|
+
export declare const SCAN_STATE_VERSION = 5;
|
|
30
33
|
/** How long a session is kept in the state after its last prompt. */
|
|
31
34
|
export declare const PROMPT_SESSION_RETENTION_DAYS = 45;
|
|
32
35
|
/** How long a per-day prompt tally is kept. Matches the wire's date cap. */
|
|
@@ -93,6 +96,9 @@ export type ScanState = {
|
|
|
93
96
|
dirty: string[];
|
|
94
97
|
/** Prompt sessions and per-day counts, plus what's outstanding. */
|
|
95
98
|
prompts: PromptState;
|
|
99
|
+
promptSources: Record<string, PromptActivityAggregate>;
|
|
100
|
+
/** Prompt-only snapshots are re-read only when their file set changes. */
|
|
101
|
+
promptFiles: Record<string, Record<string, FileState>>;
|
|
96
102
|
/** Cumulative totals as of the last accepted upload. */
|
|
97
103
|
uploaded: {
|
|
98
104
|
toolTotals: Record<string, number>;
|
|
@@ -153,10 +159,17 @@ export type SqliteSource = {
|
|
|
153
159
|
dbPath: () => string;
|
|
154
160
|
scan: () => Promise<ScanResult>;
|
|
155
161
|
};
|
|
162
|
+
export type SnapshotSource = {
|
|
163
|
+
tool: Tool;
|
|
164
|
+
files: () => Promise<string[]>;
|
|
165
|
+
scan: () => Promise<ScanResult>;
|
|
166
|
+
};
|
|
156
167
|
export type TickSources = {
|
|
157
168
|
jsonl: JsonlSource[];
|
|
158
169
|
codex: CodexSource;
|
|
159
170
|
sqlite: SqliteSource[];
|
|
171
|
+
prompts?: typeof IDE_PROMPT_SOURCES;
|
|
172
|
+
snapshots?: SnapshotSource[];
|
|
160
173
|
};
|
|
161
174
|
/** The real harnesses. Injectable so the tick can be tested against a tmp dir. */
|
|
162
175
|
export declare function defaultSources(): TickSources;
|
|
@@ -177,7 +190,9 @@ export type TickOutcome = {
|
|
|
177
190
|
* cold start (fresh install, or a state file this version can't read) is
|
|
178
191
|
* exactly the case where we have no evidence any of it ever reached the server.
|
|
179
192
|
*/
|
|
180
|
-
export declare function runTick(prev: ScanState | null, sources?: TickSources
|
|
193
|
+
export declare function runTick(prev: ScanState | null, sources?: TickSources, options?: {
|
|
194
|
+
promptActivity?: boolean;
|
|
195
|
+
}): Promise<TickOutcome>;
|
|
181
196
|
/** Cumulative per-tool and per-model totals for this machine. */
|
|
182
197
|
export declare function cumulativeTotals(state: ScanState): {
|
|
183
198
|
toolTotals: Record<string, number>;
|
|
@@ -219,7 +234,7 @@ export declare function markUploaded(state: ScanState): void;
|
|
|
219
234
|
export type StagedPrompts =
|
|
220
235
|
/** A prompt scan ran — re-base the prompt state on its aggregate. */
|
|
221
236
|
{
|
|
222
|
-
scanned:
|
|
237
|
+
scanned: ScannedPromptActivity;
|
|
223
238
|
}
|
|
224
239
|
/** The user is at the `none` tier — drop the prompt state entirely. */
|
|
225
240
|
| {
|