@newrelic/preflight 1.15.6 → 1.16.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/dist/alerts/alert-snapshot-collector.d.ts +2 -3
- package/dist/alerts/alert-snapshot-collector.d.ts.map +1 -1
- package/dist/alerts/alert-snapshot-collector.js +1 -1
- package/dist/alerts/alert-snapshot-collector.js.map +1 -1
- package/dist/config.d.ts +3 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -0
- package/dist/config.js.map +1 -1
- package/dist/dashboard/dashboard-server.d.ts.map +1 -1
- package/dist/dashboard/dashboard-server.js +15 -0
- package/dist/dashboard/dashboard-server.js.map +1 -1
- package/dist/dashboard/routes/api-handler.d.ts +26 -18
- package/dist/dashboard/routes/api-handler.d.ts.map +1 -1
- package/dist/dashboard/routes/api-handler.js +71 -23
- package/dist/dashboard/routes/api-handler.js.map +1 -1
- package/dist/data/copilot-pricing/README.md +42 -0
- package/dist/data/copilot-pricing/pricing.json +54 -0
- package/dist/data/copilot-sdk-extension/extension.mjs +123 -0
- package/dist/deploy/data-paths.d.ts +6 -4
- package/dist/deploy/data-paths.d.ts.map +1 -1
- package/dist/deploy/data-paths.js +5 -3
- package/dist/deploy/data-paths.js.map +1 -1
- package/dist/hooks/collector-script.d.ts +12 -3
- package/dist/hooks/collector-script.d.ts.map +1 -1
- package/dist/hooks/collector-script.js +70 -18
- package/dist/hooks/collector-script.js.map +1 -1
- package/dist/hooks/copilot-sdk-usage-mapper.d.ts +45 -0
- package/dist/hooks/copilot-sdk-usage-mapper.d.ts.map +1 -0
- package/dist/hooks/copilot-sdk-usage-mapper.js +73 -0
- package/dist/hooks/copilot-sdk-usage-mapper.js.map +1 -0
- package/dist/hooks/copilot-usage-watcher.d.ts +109 -0
- package/dist/hooks/copilot-usage-watcher.d.ts.map +1 -0
- package/dist/hooks/copilot-usage-watcher.js +436 -0
- package/dist/hooks/copilot-usage-watcher.js.map +1 -0
- package/dist/hooks/parent-transcript-watcher.d.ts +11 -1
- package/dist/hooks/parent-transcript-watcher.d.ts.map +1 -1
- package/dist/hooks/parent-transcript-watcher.js +71 -15
- package/dist/hooks/parent-transcript-watcher.js.map +1 -1
- package/dist/hooks/subagent-watcher.d.ts +14 -0
- package/dist/hooks/subagent-watcher.d.ts.map +1 -1
- package/dist/hooks/subagent-watcher.js +50 -6
- package/dist/hooks/subagent-watcher.js.map +1 -1
- package/dist/index.d.ts +11 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +162 -70
- package/dist/index.js.map +1 -1
- package/dist/metrics/budget-tracker.d.ts +4 -3
- package/dist/metrics/budget-tracker.d.ts.map +1 -1
- package/dist/metrics/budget-tracker.js +4 -3
- package/dist/metrics/budget-tracker.js.map +1 -1
- package/dist/metrics/copilot-pricing-overlay.d.ts +42 -0
- package/dist/metrics/copilot-pricing-overlay.d.ts.map +1 -0
- package/dist/metrics/copilot-pricing-overlay.js +97 -0
- package/dist/metrics/copilot-pricing-overlay.js.map +1 -0
- package/dist/metrics/cost-tracker.d.ts +14 -0
- package/dist/metrics/cost-tracker.d.ts.map +1 -1
- package/dist/metrics/cost-tracker.js +2 -0
- package/dist/metrics/cost-tracker.js.map +1 -1
- package/dist/metrics/git-efficiency-tracker.d.ts +12 -1
- package/dist/metrics/git-efficiency-tracker.d.ts.map +1 -1
- package/dist/metrics/git-efficiency-tracker.js +18 -3
- package/dist/metrics/git-efficiency-tracker.js.map +1 -1
- package/dist/metrics/instruction-drift-tracker.d.ts.map +1 -1
- package/dist/metrics/instruction-drift-tracker.js +1 -2
- package/dist/metrics/instruction-drift-tracker.js.map +1 -1
- package/dist/metrics/local-session-aggregator.d.ts +164 -0
- package/dist/metrics/local-session-aggregator.d.ts.map +1 -0
- package/dist/metrics/local-session-aggregator.js +444 -0
- package/dist/metrics/local-session-aggregator.js.map +1 -0
- package/dist/platforms/copilot-adapter.d.ts +1 -1
- package/dist/platforms/copilot-adapter.d.ts.map +1 -1
- package/dist/platforms/copilot-adapter.js +64 -10
- package/dist/platforms/copilot-adapter.js.map +1 -1
- package/dist/platforms/copilot-sdk-adapter.d.ts +24 -0
- package/dist/platforms/copilot-sdk-adapter.d.ts.map +1 -0
- package/dist/platforms/copilot-sdk-adapter.js +173 -0
- package/dist/platforms/copilot-sdk-adapter.js.map +1 -0
- package/dist/platforms/index.d.ts +1 -0
- package/dist/platforms/index.d.ts.map +1 -1
- package/dist/platforms/index.js +1 -0
- package/dist/platforms/index.js.map +1 -1
- package/dist/platforms/platform-registry.d.ts.map +1 -1
- package/dist/platforms/platform-registry.js +13 -11
- package/dist/platforms/platform-registry.js.map +1 -1
- package/dist/storage/session-store.d.ts +43 -10
- package/dist/storage/session-store.d.ts.map +1 -1
- package/dist/storage/session-store.js +238 -20
- package/dist/storage/session-store.js.map +1 -1
- package/dist/web/assets/{index-BasTEtUs.js → index-Bwc4qaqj.js} +1 -1
- package/dist/web/assets/index-DwdSz9gz.css +2 -0
- package/dist/web/index.html +2 -2
- package/package.json +2 -2
- package/dist/web/assets/index-BI-2ELf2.css +0 -2
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maps GitHub Copilot SDK `assistant.usage` events to Preflight's `mode:
|
|
3
|
+
* 'token'` buffer-line contract, and resolves the buffer file path — the
|
|
4
|
+
* shared, tested logic behind `copilot-sdk-extension/extension.mjs` (a
|
|
5
|
+
* hand-maintained plain-JS mirror of this file; Copilot SDK extensions must
|
|
6
|
+
* be written in JavaScript, so the logic can't be imported directly — see
|
|
7
|
+
* that file's header comment).
|
|
8
|
+
*
|
|
9
|
+
* Field mapping verified against two real `assistant.usage` events captured
|
|
10
|
+
* live from a Copilot SDK session, via the Copilot CLI host (see
|
|
11
|
+
* copilot-sdk-usage-mapper.test.ts):
|
|
12
|
+
* the SDK's `inputTokens` is inclusive of BOTH cache-read and cache-write
|
|
13
|
+
* (unlike VS Code's debug log, which is cache-read-inclusive only) — proven
|
|
14
|
+
* by cross-checking against the SDK's own `copilotUsage.tokenDetails`
|
|
15
|
+
* per-token-type breakdown on both samples.
|
|
16
|
+
*/
|
|
17
|
+
import { homedir } from 'node:os';
|
|
18
|
+
import { resolve } from 'node:path';
|
|
19
|
+
const SESSION_ID_RE = /^[a-zA-Z0-9_-]{1,128}$/;
|
|
20
|
+
function num(value) {
|
|
21
|
+
return typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : 0;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Maps an `assistant.usage` event's `data` payload to a `mode: 'token'`
|
|
25
|
+
* buffer line. Returns `null` when the payload is missing the two fields a
|
|
26
|
+
* usable line requires — `model` (priced downstream) and `apiCallId` (the
|
|
27
|
+
* stable per-call dedup key `HookEventProcessor.handleTokenEvent()` uses) —
|
|
28
|
+
* rather than emitting a line with fabricated values. Never throws.
|
|
29
|
+
*/
|
|
30
|
+
export function mapAssistantUsageEvent(data, sessionId, timestampMs) {
|
|
31
|
+
if (typeof data !== 'object' || data === null)
|
|
32
|
+
return null;
|
|
33
|
+
const d = data;
|
|
34
|
+
const model = typeof d.model === 'string' && d.model.length > 0 ? d.model : null;
|
|
35
|
+
if (!model)
|
|
36
|
+
return null;
|
|
37
|
+
const apiCallId = typeof d.apiCallId === 'string' && d.apiCallId.length > 0 ? d.apiCallId : null;
|
|
38
|
+
if (!apiCallId)
|
|
39
|
+
return null;
|
|
40
|
+
const inputTokensRaw = num(d.inputTokens);
|
|
41
|
+
const cacheReadTokens = num(d.cacheReadTokens);
|
|
42
|
+
const cacheCreationTokens = num(d.cacheWriteTokens);
|
|
43
|
+
return {
|
|
44
|
+
mode: 'token',
|
|
45
|
+
tool: 'copilot-sdk-usage',
|
|
46
|
+
timestamp: timestampMs,
|
|
47
|
+
sessionId,
|
|
48
|
+
messageId: apiCallId,
|
|
49
|
+
model,
|
|
50
|
+
// SDK's inputTokens is inclusive of both cache-read and cache-write —
|
|
51
|
+
// subtract both so the uncached remainder isn't double-billed at the
|
|
52
|
+
// base input rate (see header comment).
|
|
53
|
+
inputTokens: Math.max(0, inputTokensRaw - cacheReadTokens - cacheCreationTokens),
|
|
54
|
+
outputTokens: num(d.outputTokens),
|
|
55
|
+
cacheReadTokens,
|
|
56
|
+
cacheCreationTokens,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/** Storage dir for cursor/buffer files — same env var and default as collector-script.ts. */
|
|
60
|
+
export function resolveStoragePath() {
|
|
61
|
+
return process.env.NEW_RELIC_AI_MCP_STORAGE_PATH ?? resolve(homedir(), '.newrelic-preflight');
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Resolves `<storagePath>/buffer-<sessionId>.jsonl`. Validates `sessionId`
|
|
65
|
+
* against the same pattern collector-script.ts uses, returning `null` for an
|
|
66
|
+
* invalid id rather than letting it escape the storage dir.
|
|
67
|
+
*/
|
|
68
|
+
export function resolveBufferPath(storagePath, sessionId) {
|
|
69
|
+
if (!SESSION_ID_RE.test(sessionId))
|
|
70
|
+
return null;
|
|
71
|
+
return resolve(storagePath, `buffer-${sessionId}.jsonl`);
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=copilot-sdk-usage-mapper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copilot-sdk-usage-mapper.js","sourceRoot":"","sources":["../../src/hooks/copilot-sdk-usage-mapper.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,aAAa,GAAG,wBAAwB,CAAC;AAe/C,SAAS,GAAG,CAAC,KAAc;IACzB,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AACvF,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CACpC,IAAa,EACb,SAAiB,EACjB,WAAmB;IAEnB,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC3D,MAAM,CAAC,GAAG,IAA+B,CAAC;IAE1C,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IACjF,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IAExB,MAAM,SAAS,GAAG,OAAO,CAAC,CAAC,SAAS,KAAK,QAAQ,IAAI,CAAC,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;IACjG,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC;IAE5B,MAAM,cAAc,GAAG,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;IAC1C,MAAM,eAAe,GAAG,GAAG,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC;IAC/C,MAAM,mBAAmB,GAAG,GAAG,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC;IAEpD,OAAO;QACL,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,mBAAmB;QACzB,SAAS,EAAE,WAAW;QACtB,SAAS;QACT,SAAS,EAAE,SAAS;QACpB,KAAK;QACL,sEAAsE;QACtE,qEAAqE;QACrE,wCAAwC;QACxC,WAAW,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,cAAc,GAAG,eAAe,GAAG,mBAAmB,CAAC;QAChF,YAAY,EAAE,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC;QACjC,eAAe;QACf,mBAAmB;KACpB,CAAC;AACJ,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,kBAAkB;IAChC,OAAO,OAAO,CAAC,GAAG,CAAC,6BAA6B,IAAI,OAAO,CAAC,OAAO,EAAE,EAAE,qBAAqB,CAAC,CAAC;AAChG,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,WAAmB,EAAE,SAAiB;IACtE,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC;IAChD,OAAO,OAAO,CAAC,WAAW,EAAE,UAAU,SAAS,QAAQ,CAAC,CAAC;AAC3D,CAAC"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copilot Usage Watcher — polls VS Code Copilot agent debug logs and emits
|
|
3
|
+
* one `mode: 'token'` line per LLM request into the session's hook buffer,
|
|
4
|
+
* giving Copilot sessions token-exact cost (the same fidelity
|
|
5
|
+
* ParentTranscriptWatcher gives Claude Code sessions) instead of
|
|
6
|
+
* content-size estimation.
|
|
7
|
+
*
|
|
8
|
+
* Source of truth: VS Code writes per-request records to
|
|
9
|
+
* `<userDataDir>/workspaceStorage/<hash>/GitHub.copilot-chat/debug-logs/<sessionId>/main.jsonl`,
|
|
10
|
+
* where `<sessionId>` is the same chat session id VS Code sends as
|
|
11
|
+
* `session_id` in agent hook payloads — so events emitted here land in the
|
|
12
|
+
* same `buffer-<sessionId>.jsonl` the hook collector writes to. Each usage
|
|
13
|
+
* record is `{ type: 'llm_request', sid, ts, attrs: { model, inputTokens,
|
|
14
|
+
* outputTokens, cachedTokens, responseId, copilotUsageNanoAiu, ... } }`.
|
|
15
|
+
* This path + schema are produced by VS Code's `chatDebugFileLoggerService.ts`
|
|
16
|
+
* (formerly `microsoft/vscode-copilot-chat`, now merged into
|
|
17
|
+
* `microsoft/vscode` under `extensions/copilot/` and archived); the record
|
|
18
|
+
* shape is documented in its `otel-data-flow.html` and cross-checked against a
|
|
19
|
+
* real main.jsonl (VS Code 1.109 / Copilot Chat).
|
|
20
|
+
* The hooks doc notes transcript/debug-log formats are not a stable API
|
|
21
|
+
* (code.visualstudio.com/docs/copilot/customization/hooks) — same stability
|
|
22
|
+
* tier as the Claude Code transcript format the parent/subagent watchers
|
|
23
|
+
* already depend on; schema drift degrades to zero emissions, never a crash.
|
|
24
|
+
*
|
|
25
|
+
* Tailing/cursor mechanics mirror `ParentTranscriptWatcher`: durable byte
|
|
26
|
+
* cursor per session (`.copilot-usage-pos-<sessionId>`), bounded reads per
|
|
27
|
+
* poll, bounded partial-line carry, duplicate emissions after a crash are
|
|
28
|
+
* deduped downstream by (sessionId, messageId) in
|
|
29
|
+
* `HookEventProcessor.handleTokenEvent()` — `responseId` serves as the
|
|
30
|
+
* message id.
|
|
31
|
+
*/
|
|
32
|
+
import type { LocalStore } from '../storage/local-store.js';
|
|
33
|
+
/**
|
|
34
|
+
* Default VS Code `workspaceStorage` roots per OS (stable + Insiders), plus
|
|
35
|
+
* the remote-server roots used by WSL/SSH/dev-container sessions.
|
|
36
|
+
* Sourced from VS Code's documented user-data locations
|
|
37
|
+
* (code.visualstudio.com/docs/configure/command-line#_advanced-cli-options).
|
|
38
|
+
*/
|
|
39
|
+
export declare function defaultWorkspaceStorageRoots(): string[];
|
|
40
|
+
export interface CopilotUsageWatcherOptions {
|
|
41
|
+
/** Storage path for cursor state + buffers (defaults to ~/.newrelic-preflight). */
|
|
42
|
+
readonly storagePath?: string;
|
|
43
|
+
/** VS Code workspaceStorage roots to scan; defaults to per-OS conventions. */
|
|
44
|
+
readonly workspaceStorageRoots?: string[];
|
|
45
|
+
/** Poll interval in ms. Default 2000. */
|
|
46
|
+
readonly pollIntervalMs?: number;
|
|
47
|
+
/** Cold-scan eligibility window (mtime) for discovered logs. Default 24h. */
|
|
48
|
+
readonly discoveryHours?: number;
|
|
49
|
+
/**
|
|
50
|
+
* If provided, only this session's debug log is processed (the `--stdio`
|
|
51
|
+
* case). Default: every recent session log (matches `--local` semantics).
|
|
52
|
+
*/
|
|
53
|
+
readonly parentSessionId?: string;
|
|
54
|
+
/** LocalStore, used to exclude sessions already owned by a live --stdio watcher in unscoped mode. */
|
|
55
|
+
readonly localStore?: LocalStore;
|
|
56
|
+
}
|
|
57
|
+
export interface CopilotUsageWatcherHealth {
|
|
58
|
+
readonly filesWatched: number;
|
|
59
|
+
readonly linesRead: number;
|
|
60
|
+
readonly bytesRead: number;
|
|
61
|
+
readonly parseErrors: number;
|
|
62
|
+
/**
|
|
63
|
+
* True when at least one VS Code workspaceStorage root exists but no Copilot
|
|
64
|
+
* `debug-logs` directory was found in any of them — the signature of the
|
|
65
|
+
* off-by-default `github.copilot.chat.agentDebugLog.fileLogging.enabled`
|
|
66
|
+
* setting. Lets callers distinguish "integration broken" from "prerequisite
|
|
67
|
+
* not enabled" instead of silently reporting zero cost. False when no root
|
|
68
|
+
* exists at all (VS Code absent / wrong OS path — cannot conclude).
|
|
69
|
+
*/
|
|
70
|
+
readonly debugLoggingLikelyDisabled: boolean;
|
|
71
|
+
}
|
|
72
|
+
export declare class CopilotUsageWatcher {
|
|
73
|
+
private readonly storagePath;
|
|
74
|
+
private readonly workspaceStorageRoots;
|
|
75
|
+
private readonly pollIntervalMs;
|
|
76
|
+
private readonly discoveryHours;
|
|
77
|
+
private readonly parentSessionFilter;
|
|
78
|
+
private readonly localStore;
|
|
79
|
+
private intervalId;
|
|
80
|
+
private readonly partialByPath;
|
|
81
|
+
private readonly decoderByPath;
|
|
82
|
+
private filesWatched;
|
|
83
|
+
private linesRead;
|
|
84
|
+
private bytesRead;
|
|
85
|
+
private parseErrors;
|
|
86
|
+
private debugLoggingLikelyDisabled;
|
|
87
|
+
private warnedDebugLoggingDisabled;
|
|
88
|
+
constructor(options?: CopilotUsageWatcherOptions);
|
|
89
|
+
start(): void;
|
|
90
|
+
stop(): void;
|
|
91
|
+
getHealth(): CopilotUsageWatcherHealth;
|
|
92
|
+
/** One discovery + tail pass. Exported for direct testing. */
|
|
93
|
+
poll(): void;
|
|
94
|
+
private discoverFiles;
|
|
95
|
+
private tailFile;
|
|
96
|
+
private evictStalePartials;
|
|
97
|
+
/**
|
|
98
|
+
* Parse a debug-log JSONL line, returning non-null only for an
|
|
99
|
+
* `llm_request` record carrying a model, responseId, and token counts.
|
|
100
|
+
*/
|
|
101
|
+
private tryParseLine;
|
|
102
|
+
private cursorPath;
|
|
103
|
+
private readCursor;
|
|
104
|
+
private writeCursor;
|
|
105
|
+
/** Append into the session's hook buffer, same path convention as the collector. */
|
|
106
|
+
private appendToBuffer;
|
|
107
|
+
private recordError;
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=copilot-usage-watcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copilot-usage-watcher.d.ts","sourceRoot":"","sources":["../../src/hooks/copilot-usage-watcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAmBH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAiC5D;;;;;GAKG;AACH,wBAAgB,4BAA4B,IAAI,MAAM,EAAE,CAiBvD;AAMD,MAAM,WAAW,0BAA0B;IACzC,mFAAmF;IACnF,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,8EAA8E;IAC9E,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1C,yCAAyC;IACzC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,6EAA6E;IAC7E,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC;;;OAGG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,qGAAqG;IACrG,QAAQ,CAAC,UAAU,CAAC,EAAE,UAAU,CAAC;CAClC;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;;;;;;;OAOG;IACH,QAAQ,CAAC,0BAA0B,EAAE,OAAO,CAAC;CAC9C;AAqBD,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAW;IACjD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAgB;IACpD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAyB;IAEpD,OAAO,CAAC,UAAU,CAA+C;IACjE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA6B;IAC3D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAoC;IAElE,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,0BAA0B,CAAS;IAC3C,OAAO,CAAC,0BAA0B,CAAS;gBAE/B,OAAO,GAAE,0BAA+B;IASpD,KAAK,IAAI,IAAI;IAYb,IAAI,IAAI,IAAI;IAOZ,SAAS,IAAI,yBAAyB;IAUtC,8DAA8D;IAC9D,IAAI,IAAI,IAAI;IAiBZ,OAAO,CAAC,aAAa;IAuErB,OAAO,CAAC,QAAQ;IA+FhB,OAAO,CAAC,kBAAkB;IAW1B;;;OAGG;IACH,OAAO,CAAC,YAAY;IAqCpB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,UAAU;IAgBlB,OAAO,CAAC,WAAW;IAWnB,oFAAoF;IACpF,OAAO,CAAC,cAAc;IAiBtB,OAAO,CAAC,WAAW;CAIpB"}
|
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copilot Usage Watcher — polls VS Code Copilot agent debug logs and emits
|
|
3
|
+
* one `mode: 'token'` line per LLM request into the session's hook buffer,
|
|
4
|
+
* giving Copilot sessions token-exact cost (the same fidelity
|
|
5
|
+
* ParentTranscriptWatcher gives Claude Code sessions) instead of
|
|
6
|
+
* content-size estimation.
|
|
7
|
+
*
|
|
8
|
+
* Source of truth: VS Code writes per-request records to
|
|
9
|
+
* `<userDataDir>/workspaceStorage/<hash>/GitHub.copilot-chat/debug-logs/<sessionId>/main.jsonl`,
|
|
10
|
+
* where `<sessionId>` is the same chat session id VS Code sends as
|
|
11
|
+
* `session_id` in agent hook payloads — so events emitted here land in the
|
|
12
|
+
* same `buffer-<sessionId>.jsonl` the hook collector writes to. Each usage
|
|
13
|
+
* record is `{ type: 'llm_request', sid, ts, attrs: { model, inputTokens,
|
|
14
|
+
* outputTokens, cachedTokens, responseId, copilotUsageNanoAiu, ... } }`.
|
|
15
|
+
* This path + schema are produced by VS Code's `chatDebugFileLoggerService.ts`
|
|
16
|
+
* (formerly `microsoft/vscode-copilot-chat`, now merged into
|
|
17
|
+
* `microsoft/vscode` under `extensions/copilot/` and archived); the record
|
|
18
|
+
* shape is documented in its `otel-data-flow.html` and cross-checked against a
|
|
19
|
+
* real main.jsonl (VS Code 1.109 / Copilot Chat).
|
|
20
|
+
* The hooks doc notes transcript/debug-log formats are not a stable API
|
|
21
|
+
* (code.visualstudio.com/docs/copilot/customization/hooks) — same stability
|
|
22
|
+
* tier as the Claude Code transcript format the parent/subagent watchers
|
|
23
|
+
* already depend on; schema drift degrades to zero emissions, never a crash.
|
|
24
|
+
*
|
|
25
|
+
* Tailing/cursor mechanics mirror `ParentTranscriptWatcher`: durable byte
|
|
26
|
+
* cursor per session (`.copilot-usage-pos-<sessionId>`), bounded reads per
|
|
27
|
+
* poll, bounded partial-line carry, duplicate emissions after a crash are
|
|
28
|
+
* deduped downstream by (sessionId, messageId) in
|
|
29
|
+
* `HookEventProcessor.handleTokenEvent()` — `responseId` serves as the
|
|
30
|
+
* message id.
|
|
31
|
+
*/
|
|
32
|
+
import { appendFileSync, closeSync, existsSync, mkdirSync, openSync, readdirSync, readFileSync, readSync, statSync, writeFileSync, } from 'node:fs';
|
|
33
|
+
import { homedir } from 'node:os';
|
|
34
|
+
import { join } from 'node:path';
|
|
35
|
+
import { StringDecoder } from 'node:string_decoder';
|
|
36
|
+
import { createLogger } from '../shared/index.js';
|
|
37
|
+
const logger = createLogger('copilot-usage-watcher');
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
// Constants
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
const DEFAULT_POLL_INTERVAL_MS = 2_000;
|
|
42
|
+
const DEFAULT_DISCOVERY_HOURS = 24;
|
|
43
|
+
// Copilot debug-log lines embed full inputMessages payloads and routinely run
|
|
44
|
+
// hundreds of KiB — far larger than transcript lines. A bigger window keeps a
|
|
45
|
+
// single record from spanning many polls (each carry-over persists the whole
|
|
46
|
+
// partial line into the cursor file).
|
|
47
|
+
const MAX_BYTES_PER_POLL = 1024 * 1024;
|
|
48
|
+
/** See SubagentWatcher's identical constant for the OOM-bug rationale this guards against. */
|
|
49
|
+
const MAX_PARTIAL_LINE_BYTES = 4 * 1024 * 1024; // 4 MiB — sized to the larger log lines
|
|
50
|
+
const SESSION_ID_RE = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/;
|
|
51
|
+
const COPILOT_LOG_SUBPATH = join('GitHub.copilot-chat', 'debug-logs');
|
|
52
|
+
/**
|
|
53
|
+
* VS Code Remote (WSL, SSH, dev containers, Codespaces) `workspaceStorage`
|
|
54
|
+
* roots. When the Copilot Chat extension runs in a remote extension host, it
|
|
55
|
+
* writes debug logs under the server's user-data dir rather than the desktop
|
|
56
|
+
* one, so these must be searched alongside the local roots or remote sessions
|
|
57
|
+
* silently yield zero token events (and therefore $0.00 cost).
|
|
58
|
+
*/
|
|
59
|
+
function remoteWorkspaceStorageRoots(home) {
|
|
60
|
+
return ['.vscode-server', '.vscode-server-insiders', '.vscode-remote'].map((dir) => join(home, dir, 'data', 'User', 'workspaceStorage'));
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Default VS Code `workspaceStorage` roots per OS (stable + Insiders), plus
|
|
64
|
+
* the remote-server roots used by WSL/SSH/dev-container sessions.
|
|
65
|
+
* Sourced from VS Code's documented user-data locations
|
|
66
|
+
* (code.visualstudio.com/docs/configure/command-line#_advanced-cli-options).
|
|
67
|
+
*/
|
|
68
|
+
export function defaultWorkspaceStorageRoots() {
|
|
69
|
+
const home = homedir();
|
|
70
|
+
const variants = ['Code', 'Code - Insiders'];
|
|
71
|
+
const remote = remoteWorkspaceStorageRoots(home);
|
|
72
|
+
if (process.platform === 'darwin') {
|
|
73
|
+
return [
|
|
74
|
+
...variants.map((v) => join(home, 'Library', 'Application Support', v, 'User', 'workspaceStorage')),
|
|
75
|
+
...remote,
|
|
76
|
+
];
|
|
77
|
+
}
|
|
78
|
+
if (process.platform === 'win32') {
|
|
79
|
+
const appData = process.env.APPDATA ?? join(home, 'AppData', 'Roaming');
|
|
80
|
+
return [...variants.map((v) => join(appData, v, 'User', 'workspaceStorage')), ...remote];
|
|
81
|
+
}
|
|
82
|
+
return [...variants.map((v) => join(home, '.config', v, 'User', 'workspaceStorage')), ...remote];
|
|
83
|
+
}
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
// CopilotUsageWatcher
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
export class CopilotUsageWatcher {
|
|
88
|
+
storagePath;
|
|
89
|
+
workspaceStorageRoots;
|
|
90
|
+
pollIntervalMs;
|
|
91
|
+
discoveryHours;
|
|
92
|
+
parentSessionFilter;
|
|
93
|
+
localStore;
|
|
94
|
+
intervalId = null;
|
|
95
|
+
partialByPath = new Map();
|
|
96
|
+
decoderByPath = new Map();
|
|
97
|
+
filesWatched = 0;
|
|
98
|
+
linesRead = 0;
|
|
99
|
+
bytesRead = 0;
|
|
100
|
+
parseErrors = 0;
|
|
101
|
+
debugLoggingLikelyDisabled = false;
|
|
102
|
+
warnedDebugLoggingDisabled = false;
|
|
103
|
+
constructor(options = {}) {
|
|
104
|
+
this.storagePath = options.storagePath ?? join(homedir(), '.newrelic-preflight');
|
|
105
|
+
this.workspaceStorageRoots = options.workspaceStorageRoots ?? defaultWorkspaceStorageRoots();
|
|
106
|
+
this.pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
107
|
+
this.discoveryHours = options.discoveryHours ?? DEFAULT_DISCOVERY_HOURS;
|
|
108
|
+
this.parentSessionFilter = options.parentSessionId ?? null;
|
|
109
|
+
this.localStore = options.localStore;
|
|
110
|
+
}
|
|
111
|
+
start() {
|
|
112
|
+
if (this.intervalId !== null)
|
|
113
|
+
return;
|
|
114
|
+
this.intervalId = setInterval(() => {
|
|
115
|
+
try {
|
|
116
|
+
this.poll();
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
this.recordError(err);
|
|
120
|
+
}
|
|
121
|
+
}, this.pollIntervalMs);
|
|
122
|
+
this.intervalId.unref?.();
|
|
123
|
+
}
|
|
124
|
+
stop() {
|
|
125
|
+
if (this.intervalId !== null) {
|
|
126
|
+
clearInterval(this.intervalId);
|
|
127
|
+
this.intervalId = null;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
getHealth() {
|
|
131
|
+
return {
|
|
132
|
+
filesWatched: this.filesWatched,
|
|
133
|
+
linesRead: this.linesRead,
|
|
134
|
+
bytesRead: this.bytesRead,
|
|
135
|
+
parseErrors: this.parseErrors,
|
|
136
|
+
debugLoggingLikelyDisabled: this.debugLoggingLikelyDisabled,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/** One discovery + tail pass. Exported for direct testing. */
|
|
140
|
+
poll() {
|
|
141
|
+
const files = this.discoverFiles();
|
|
142
|
+
this.filesWatched = files.length;
|
|
143
|
+
this.evictStalePartials(files);
|
|
144
|
+
for (const file of files) {
|
|
145
|
+
try {
|
|
146
|
+
this.tailFile(file.path, file.sessionId);
|
|
147
|
+
}
|
|
148
|
+
catch (err) {
|
|
149
|
+
this.recordError(err);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// -------------------------------------------------------------------------
|
|
154
|
+
// Discovery
|
|
155
|
+
// -------------------------------------------------------------------------
|
|
156
|
+
discoverFiles() {
|
|
157
|
+
const out = [];
|
|
158
|
+
const cutoffMs = Date.now() - this.discoveryHours * 3_600_000;
|
|
159
|
+
// Unscoped (--local) mode: skip sessions a live --stdio process already
|
|
160
|
+
// tails, so two processes never race on the same cursor file — identical
|
|
161
|
+
// to ParentTranscriptWatcher's unfiltered discovery.
|
|
162
|
+
const liveOwnedSessionIds = this.parentSessionFilter === null
|
|
163
|
+
? (this.localStore?.getActiveSessionIdsFromHeartbeats() ?? null)
|
|
164
|
+
: null;
|
|
165
|
+
let anyRootExists = false;
|
|
166
|
+
let anyDebugLogDir = false;
|
|
167
|
+
for (const root of this.workspaceStorageRoots) {
|
|
168
|
+
if (!existsSync(root))
|
|
169
|
+
continue;
|
|
170
|
+
anyRootExists = true;
|
|
171
|
+
let workspaceHashes;
|
|
172
|
+
try {
|
|
173
|
+
workspaceHashes = readdirSync(root);
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
for (const hash of workspaceHashes) {
|
|
179
|
+
const logsDir = join(root, hash, COPILOT_LOG_SUBPATH);
|
|
180
|
+
if (!existsSync(logsDir))
|
|
181
|
+
continue;
|
|
182
|
+
anyDebugLogDir = true;
|
|
183
|
+
let sessionDirs;
|
|
184
|
+
try {
|
|
185
|
+
sessionDirs = readdirSync(logsDir);
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
for (const sessionId of sessionDirs) {
|
|
191
|
+
if (!SESSION_ID_RE.test(sessionId))
|
|
192
|
+
continue;
|
|
193
|
+
if (this.parentSessionFilter !== null && sessionId !== this.parentSessionFilter) {
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
if (liveOwnedSessionIds?.has(sessionId))
|
|
197
|
+
continue;
|
|
198
|
+
const logPath = join(logsDir, sessionId, 'main.jsonl');
|
|
199
|
+
try {
|
|
200
|
+
const st = statSync(logPath);
|
|
201
|
+
if (!st.isFile())
|
|
202
|
+
continue;
|
|
203
|
+
// Scoped mode always tails its own session; unscoped applies the
|
|
204
|
+
// recency window so old logs don't trigger unbounded cold scans.
|
|
205
|
+
if (this.parentSessionFilter === null && st.mtimeMs < cutoffMs)
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
out.push({ path: logPath, sessionId });
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
// A VS Code install is present (root exists) yet no Copilot debug-logs
|
|
216
|
+
// directory anywhere ⇒ the off-by-default fileLogging setting is almost
|
|
217
|
+
// certainly not enabled. Warn once so a zero-cost session reads as a
|
|
218
|
+
// missing prerequisite rather than a broken integration.
|
|
219
|
+
this.debugLoggingLikelyDisabled = anyRootExists && !anyDebugLogDir;
|
|
220
|
+
if (this.debugLoggingLikelyDisabled && !this.warnedDebugLoggingDisabled) {
|
|
221
|
+
this.warnedDebugLoggingDisabled = true;
|
|
222
|
+
logger.warn('No Copilot debug-logs directory found; token-exact cost is unavailable. ' +
|
|
223
|
+
'Enable "github.copilot.chat.agentDebugLog.fileLogging.enabled" in VS Code ' +
|
|
224
|
+
'settings.json and reload the window.');
|
|
225
|
+
}
|
|
226
|
+
return out;
|
|
227
|
+
}
|
|
228
|
+
// -------------------------------------------------------------------------
|
|
229
|
+
// Tailing
|
|
230
|
+
// -------------------------------------------------------------------------
|
|
231
|
+
tailFile(path, dirSessionId) {
|
|
232
|
+
const cursorPath = this.cursorPath(dirSessionId);
|
|
233
|
+
const persisted = this.readCursor(cursorPath);
|
|
234
|
+
const memPartial = this.partialByPath.get(path);
|
|
235
|
+
const startCursor = memPartial !== undefined
|
|
236
|
+
? { bytePos: persisted.bytePos, partialLine: memPartial }
|
|
237
|
+
: persisted;
|
|
238
|
+
let size;
|
|
239
|
+
try {
|
|
240
|
+
size = statSync(path).size;
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
// Truncated/rotated file: restart from 0.
|
|
246
|
+
const fromPos = startCursor.bytePos > size ? 0 : startCursor.bytePos;
|
|
247
|
+
if (fromPos >= size)
|
|
248
|
+
return;
|
|
249
|
+
const toRead = Math.min(size - fromPos, MAX_BYTES_PER_POLL);
|
|
250
|
+
const buf = Buffer.alloc(toRead);
|
|
251
|
+
let actuallyRead = 0;
|
|
252
|
+
let fd = null;
|
|
253
|
+
try {
|
|
254
|
+
fd = openSync(path, 'r');
|
|
255
|
+
actuallyRead = readSync(fd, buf, 0, toRead, fromPos);
|
|
256
|
+
}
|
|
257
|
+
catch (err) {
|
|
258
|
+
this.recordError(err);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
finally {
|
|
262
|
+
if (fd !== null)
|
|
263
|
+
closeSync(fd);
|
|
264
|
+
}
|
|
265
|
+
if (actuallyRead <= 0)
|
|
266
|
+
return;
|
|
267
|
+
this.bytesRead += actuallyRead;
|
|
268
|
+
let decoder = this.decoderByPath.get(path);
|
|
269
|
+
if (!decoder) {
|
|
270
|
+
decoder = new StringDecoder('utf8');
|
|
271
|
+
this.decoderByPath.set(path, decoder);
|
|
272
|
+
}
|
|
273
|
+
const combined = startCursor.partialLine + decoder.write(buf.subarray(0, actuallyRead));
|
|
274
|
+
const nextBytePos = fromPos + actuallyRead;
|
|
275
|
+
const lastNewline = combined.lastIndexOf('\n');
|
|
276
|
+
let lines = [];
|
|
277
|
+
let newPartial = combined;
|
|
278
|
+
if (lastNewline >= 0) {
|
|
279
|
+
lines = combined.slice(0, lastNewline).split('\n');
|
|
280
|
+
newPartial = combined.slice(lastNewline + 1);
|
|
281
|
+
}
|
|
282
|
+
if (newPartial.length > MAX_PARTIAL_LINE_BYTES) {
|
|
283
|
+
newPartial = '';
|
|
284
|
+
this.parseErrors += 1;
|
|
285
|
+
}
|
|
286
|
+
for (const line of lines) {
|
|
287
|
+
if (!line)
|
|
288
|
+
continue;
|
|
289
|
+
this.linesRead += 1;
|
|
290
|
+
const parsed = this.tryParseLine(line);
|
|
291
|
+
if (parsed === null)
|
|
292
|
+
continue;
|
|
293
|
+
const sessionId = parsed.sessionId ?? dirSessionId;
|
|
294
|
+
this.appendToBuffer(sessionId, {
|
|
295
|
+
mode: 'token',
|
|
296
|
+
tool: 'copilot-usage',
|
|
297
|
+
timestamp: parsed.timestampMs,
|
|
298
|
+
sessionId,
|
|
299
|
+
messageId: parsed.responseId,
|
|
300
|
+
model: parsed.model,
|
|
301
|
+
// VS Code's inputTokens is cache-INCLUSIVE (verified against a real
|
|
302
|
+
// main.jsonl: each request's cachedTokens ≈ the previous request's
|
|
303
|
+
// inputTokens). CostTracker follows the Anthropic convention where
|
|
304
|
+
// input excludes cache reads, so emit only the uncached remainder —
|
|
305
|
+
// otherwise cached tokens are double-billed at full input rate.
|
|
306
|
+
inputTokens: Math.max(0, parsed.inputTokens - parsed.cachedTokens),
|
|
307
|
+
outputTokens: parsed.outputTokens,
|
|
308
|
+
// `cachedTokens` is the only cache figure the debug-log schema exposes,
|
|
309
|
+
// and it is cache-READ (confirmed against a real main.jsonl: it tracks
|
|
310
|
+
// the previous request's input). KNOWN ESTIMATION GAP: VS Code does not
|
|
311
|
+
// surface cache-CREATION (cache-write) tokens separately, so any turn
|
|
312
|
+
// with a fresh cache write has those tokens folded into `inputTokens`
|
|
313
|
+
// above and billed at the base input rate instead of the premium
|
|
314
|
+
// `cacheCreationPerMTok` rate (src/shared/pricing.ts). This causes a
|
|
315
|
+
// small, consistent UNDER-billing on cache-write turns. We report 0
|
|
316
|
+
// rather than guess a split the source doesn't provide; revisit if VS
|
|
317
|
+
// Code adds a cache-creation field to the schema.
|
|
318
|
+
cacheReadTokens: parsed.cachedTokens,
|
|
319
|
+
cacheCreationTokens: 0,
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
this.writeCursor(cursorPath, nextBytePos, newPartial);
|
|
323
|
+
if (newPartial.length > 0)
|
|
324
|
+
this.partialByPath.set(path, newPartial);
|
|
325
|
+
else
|
|
326
|
+
this.partialByPath.delete(path);
|
|
327
|
+
}
|
|
328
|
+
evictStalePartials(files) {
|
|
329
|
+
if (this.partialByPath.size === 0 && this.decoderByPath.size === 0)
|
|
330
|
+
return;
|
|
331
|
+
const live = new Set(files.map((f) => f.path));
|
|
332
|
+
for (const path of this.partialByPath.keys()) {
|
|
333
|
+
if (!live.has(path))
|
|
334
|
+
this.partialByPath.delete(path);
|
|
335
|
+
}
|
|
336
|
+
for (const path of this.decoderByPath.keys()) {
|
|
337
|
+
if (!live.has(path))
|
|
338
|
+
this.decoderByPath.delete(path);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Parse a debug-log JSONL line, returning non-null only for an
|
|
343
|
+
* `llm_request` record carrying a model, responseId, and token counts.
|
|
344
|
+
*/
|
|
345
|
+
tryParseLine(line) {
|
|
346
|
+
let parsed;
|
|
347
|
+
try {
|
|
348
|
+
parsed = JSON.parse(line);
|
|
349
|
+
}
|
|
350
|
+
catch {
|
|
351
|
+
this.parseErrors += 1;
|
|
352
|
+
return null;
|
|
353
|
+
}
|
|
354
|
+
if (!parsed || typeof parsed !== 'object')
|
|
355
|
+
return null;
|
|
356
|
+
const obj = parsed;
|
|
357
|
+
if (obj.type !== 'llm_request')
|
|
358
|
+
return null;
|
|
359
|
+
const attrs = obj.attrs;
|
|
360
|
+
if (!attrs || typeof attrs !== 'object')
|
|
361
|
+
return null;
|
|
362
|
+
const a = attrs;
|
|
363
|
+
const model = typeof a.model === 'string' && a.model.length > 0 ? a.model : null;
|
|
364
|
+
if (!model)
|
|
365
|
+
return null;
|
|
366
|
+
const responseId = typeof a.responseId === 'string' && a.responseId.length > 0 ? a.responseId : null;
|
|
367
|
+
if (!responseId)
|
|
368
|
+
return null;
|
|
369
|
+
const timestampMs = typeof obj.ts === 'number' && Number.isFinite(obj.ts) && obj.ts > 0 ? obj.ts : Date.now();
|
|
370
|
+
const sid = typeof obj.sid === 'string' && SESSION_ID_RE.test(obj.sid) ? obj.sid : null;
|
|
371
|
+
return {
|
|
372
|
+
timestampMs,
|
|
373
|
+
sessionId: sid,
|
|
374
|
+
responseId,
|
|
375
|
+
model,
|
|
376
|
+
inputTokens: num(a.inputTokens),
|
|
377
|
+
outputTokens: num(a.outputTokens),
|
|
378
|
+
cachedTokens: num(a.cachedTokens),
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
// -------------------------------------------------------------------------
|
|
382
|
+
// Buffer + cursor I/O
|
|
383
|
+
// -------------------------------------------------------------------------
|
|
384
|
+
cursorPath(sessionId) {
|
|
385
|
+
return join(this.storagePath, `.copilot-usage-pos-${sessionId}`);
|
|
386
|
+
}
|
|
387
|
+
readCursor(cursorPath) {
|
|
388
|
+
if (!existsSync(cursorPath))
|
|
389
|
+
return { bytePos: 0, partialLine: '' };
|
|
390
|
+
try {
|
|
391
|
+
const parsed = JSON.parse(readFileSync(cursorPath, 'utf-8').trim());
|
|
392
|
+
const bytePos = typeof parsed.bytePos === 'number' && parsed.bytePos >= 0 ? parsed.bytePos : 0;
|
|
393
|
+
const partialLine = typeof parsed.partialLine === 'string' ? parsed.partialLine : '';
|
|
394
|
+
return { bytePos, partialLine };
|
|
395
|
+
}
|
|
396
|
+
catch {
|
|
397
|
+
return { bytePos: 0, partialLine: '' };
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
writeCursor(cursorPath, bytePos, partialLine) {
|
|
401
|
+
try {
|
|
402
|
+
if (!existsSync(this.storagePath)) {
|
|
403
|
+
mkdirSync(this.storagePath, { recursive: true, mode: 0o700 });
|
|
404
|
+
}
|
|
405
|
+
writeFileSync(cursorPath, JSON.stringify({ bytePos, partialLine }), { mode: 0o600 });
|
|
406
|
+
}
|
|
407
|
+
catch (err) {
|
|
408
|
+
this.recordError(err);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
/** Append into the session's hook buffer, same path convention as the collector. */
|
|
412
|
+
appendToBuffer(sessionId, event) {
|
|
413
|
+
try {
|
|
414
|
+
if (!existsSync(this.storagePath)) {
|
|
415
|
+
mkdirSync(this.storagePath, { recursive: true, mode: 0o700 });
|
|
416
|
+
}
|
|
417
|
+
appendFileSync(join(this.storagePath, `buffer-${sessionId}.jsonl`), JSON.stringify(event) + '\n', {
|
|
418
|
+
mode: 0o600,
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
catch (err) {
|
|
422
|
+
this.recordError(err);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
recordError(err) {
|
|
426
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
427
|
+
logger.warn('CopilotUsageWatcher error', { message: message.slice(0, 200) });
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
// ---------------------------------------------------------------------------
|
|
431
|
+
// Helpers
|
|
432
|
+
// ---------------------------------------------------------------------------
|
|
433
|
+
function num(v) {
|
|
434
|
+
return typeof v === 'number' && Number.isFinite(v) && v >= 0 ? v : 0;
|
|
435
|
+
}
|
|
436
|
+
//# sourceMappingURL=copilot-usage-watcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copilot-usage-watcher.js","sourceRoot":"","sources":["../../src/hooks/copilot-usage-watcher.ts"],"names":[],"mappings":"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"}
|