@newrelic/preflight 1.70.0 → 1.72.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.
- package/README.md +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +7 -0
- package/dist/config.js.map +1 -1
- package/dist/dashboard/routes/api-handler.d.ts.map +1 -1
- package/dist/dashboard/routes/api-handler.js +11 -16
- package/dist/dashboard/routes/api-handler.js.map +1 -1
- package/dist/dashboard/subagent-timeline-store.d.ts +12 -1
- package/dist/dashboard/subagent-timeline-store.d.ts.map +1 -1
- package/dist/dashboard/subagent-timeline-store.js +32 -4
- package/dist/dashboard/subagent-timeline-store.js.map +1 -1
- package/dist/dashboard/workflow-store.d.ts.map +1 -1
- package/dist/dashboard/workflow-store.js +2 -3
- package/dist/dashboard/workflow-store.js.map +1 -1
- package/dist/hooks/collector-script.d.ts.map +1 -1
- package/dist/hooks/collector-script.js +14 -8
- package/dist/hooks/collector-script.js.map +1 -1
- package/dist/hooks/copilot-app-usage-watcher.d.ts.map +1 -1
- package/dist/hooks/copilot-app-usage-watcher.js +3 -4
- package/dist/hooks/copilot-app-usage-watcher.js.map +1 -1
- package/dist/hooks/copilot-usage-watcher.d.ts +25 -5
- package/dist/hooks/copilot-usage-watcher.d.ts.map +1 -1
- package/dist/hooks/copilot-usage-watcher.js +254 -92
- package/dist/hooks/copilot-usage-watcher.js.map +1 -1
- package/dist/hooks/event-processor.d.ts +24 -11
- package/dist/hooks/event-processor.d.ts.map +1 -1
- package/dist/hooks/event-processor.js +139 -45
- package/dist/hooks/event-processor.js.map +1 -1
- package/dist/hooks/index.d.ts +1 -1
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +1 -1
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/parent-transcript-watcher.d.ts +1 -1
- package/dist/hooks/parent-transcript-watcher.d.ts.map +1 -1
- package/dist/hooks/parent-transcript-watcher.js +11 -6
- package/dist/hooks/parent-transcript-watcher.js.map +1 -1
- package/dist/hooks/session-resolver.d.ts.map +1 -1
- package/dist/hooks/session-resolver.js +2 -1
- package/dist/hooks/session-resolver.js.map +1 -1
- package/dist/hooks/subagent-watcher.d.ts +21 -15
- package/dist/hooks/subagent-watcher.d.ts.map +1 -1
- package/dist/hooks/subagent-watcher.js +89 -73
- package/dist/hooks/subagent-watcher.js.map +1 -1
- package/dist/hooks/throttled-warning.d.ts +5 -5
- package/dist/hooks/throttled-warning.d.ts.map +1 -1
- package/dist/hooks/throttled-warning.js +14 -12
- package/dist/hooks/throttled-warning.js.map +1 -1
- package/dist/hooks/workflow-watcher.d.ts.map +1 -1
- package/dist/hooks/workflow-watcher.js +2 -2
- package/dist/hooks/workflow-watcher.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +57 -62
- package/dist/index.js.map +1 -1
- package/dist/install/cli.d.ts.map +1 -1
- package/dist/install/cli.js +131 -17
- package/dist/install/cli.js.map +1 -1
- package/dist/install/copilot-install-helper.d.ts +1 -0
- package/dist/install/copilot-install-helper.d.ts.map +1 -1
- package/dist/install/copilot-install-helper.js +82 -26
- package/dist/install/copilot-install-helper.js.map +1 -1
- package/dist/install/credentials.d.ts +23 -0
- package/dist/install/credentials.d.ts.map +1 -0
- package/dist/install/credentials.js +57 -0
- package/dist/install/credentials.js.map +1 -0
- package/dist/install/diagnostics.d.ts.map +1 -1
- package/dist/install/diagnostics.js +6 -3
- package/dist/install/diagnostics.js.map +1 -1
- package/dist/install/install-helper.d.ts +9 -0
- package/dist/install/install-helper.d.ts.map +1 -1
- package/dist/install/install-helper.js +42 -17
- package/dist/install/install-helper.js.map +1 -1
- package/dist/install/schedule.d.ts.map +1 -1
- package/dist/install/schedule.js +4 -1
- package/dist/install/schedule.js.map +1 -1
- package/dist/install/setup-wizard.d.ts.map +1 -1
- package/dist/install/setup-wizard.js +3 -3
- package/dist/install/setup-wizard.js.map +1 -1
- package/dist/install/target-user.d.ts +33 -0
- package/dist/install/target-user.d.ts.map +1 -0
- package/dist/install/target-user.js +121 -0
- package/dist/install/target-user.js.map +1 -0
- package/dist/lib/claude-config-dir.d.ts +3 -0
- package/dist/lib/claude-config-dir.d.ts.map +1 -0
- package/dist/lib/claude-config-dir.js +10 -0
- package/dist/lib/claude-config-dir.js.map +1 -0
- package/dist/lib/claude-settings-env.d.ts +18 -0
- package/dist/lib/claude-settings-env.d.ts.map +1 -0
- package/dist/lib/claude-settings-env.js +78 -0
- package/dist/lib/claude-settings-env.js.map +1 -0
- package/dist/lib/date.d.ts +7 -0
- package/dist/lib/date.d.ts.map +1 -1
- package/dist/lib/date.js +11 -1
- package/dist/lib/date.js.map +1 -1
- package/dist/lib/subagent-transcript-parser.d.ts +2 -0
- package/dist/lib/subagent-transcript-parser.d.ts.map +1 -1
- package/dist/lib/subagent-transcript-parser.js +5 -0
- package/dist/lib/subagent-transcript-parser.js.map +1 -1
- package/dist/lib/transcript-types.d.ts +4 -0
- package/dist/lib/transcript-types.d.ts.map +1 -1
- package/dist/metrics/cost-forecast.d.ts +2 -0
- package/dist/metrics/cost-forecast.d.ts.map +1 -1
- package/dist/metrics/cost-forecast.js +46 -24
- package/dist/metrics/cost-forecast.js.map +1 -1
- package/dist/metrics/cost-tracker.d.ts +3 -3
- package/dist/metrics/cost-tracker.js +3 -3
- package/dist/metrics/efficiency-score.d.ts +2 -2
- package/dist/metrics/efficiency-score.js +2 -2
- package/dist/metrics/first-save-baselines.d.ts +60 -0
- package/dist/metrics/first-save-baselines.d.ts.map +1 -0
- package/dist/metrics/first-save-baselines.js +77 -0
- package/dist/metrics/first-save-baselines.js.map +1 -0
- package/dist/metrics/local-session-aggregator.d.ts +6 -5
- package/dist/metrics/local-session-aggregator.d.ts.map +1 -1
- package/dist/metrics/local-session-aggregator.js +34 -6
- package/dist/metrics/local-session-aggregator.js.map +1 -1
- package/dist/metrics/model-pricing.d.ts +1 -1
- package/dist/metrics/model-pricing.d.ts.map +1 -1
- package/dist/metrics/model-pricing.js +2 -2
- package/dist/metrics/model-pricing.js.map +1 -1
- package/dist/metrics/model-usage-tracker.d.ts +2 -2
- package/dist/metrics/model-usage-tracker.js +2 -2
- package/dist/metrics/pricing-init.d.ts.map +1 -1
- package/dist/metrics/pricing-init.js +5 -3
- package/dist/metrics/pricing-init.js.map +1 -1
- package/dist/metrics/pricing-overlay.d.ts.map +1 -1
- package/dist/metrics/pricing-overlay.js +1 -4
- package/dist/metrics/pricing-overlay.js.map +1 -1
- package/dist/metrics/pricing-refresh.d.ts +16 -4
- package/dist/metrics/pricing-refresh.d.ts.map +1 -1
- package/dist/metrics/pricing-refresh.js +47 -1
- package/dist/metrics/pricing-refresh.js.map +1 -1
- package/dist/metrics/quality-proxy-tracker.d.ts +2 -2
- package/dist/metrics/quality-proxy-tracker.js +2 -2
- package/dist/metrics/session-tracker.d.ts +5 -1
- package/dist/metrics/session-tracker.d.ts.map +1 -1
- package/dist/metrics/session-tracker.js +58 -17
- package/dist/metrics/session-tracker.js.map +1 -1
- package/dist/metrics/task-detector.d.ts +2 -2
- package/dist/metrics/task-detector.js +2 -2
- package/dist/metrics/token-usage.d.ts +6 -0
- package/dist/metrics/token-usage.d.ts.map +1 -0
- package/dist/metrics/token-usage.js +34 -0
- package/dist/metrics/token-usage.js.map +1 -0
- package/dist/metrics/turn-cost-attributor.d.ts.map +1 -1
- package/dist/metrics/turn-cost-attributor.js +3 -0
- package/dist/metrics/turn-cost-attributor.js.map +1 -1
- package/dist/platforms/copilot-adapter.d.ts.map +1 -1
- package/dist/platforms/copilot-adapter.js +3 -0
- package/dist/platforms/copilot-adapter.js.map +1 -1
- package/dist/platforms/copilot-sdk-adapter.d.ts.map +1 -1
- package/dist/platforms/copilot-sdk-adapter.js +3 -0
- package/dist/platforms/copilot-sdk-adapter.js.map +1 -1
- package/dist/storage/drain-claims.d.ts +82 -0
- package/dist/storage/drain-claims.d.ts.map +1 -0
- package/dist/storage/drain-claims.js +239 -0
- package/dist/storage/drain-claims.js.map +1 -0
- package/dist/storage/hook-buffer.d.ts +7 -0
- package/dist/storage/hook-buffer.d.ts.map +1 -0
- package/dist/storage/hook-buffer.js +132 -0
- package/dist/storage/hook-buffer.js.map +1 -0
- package/dist/storage/local-store.d.ts +38 -16
- package/dist/storage/local-store.d.ts.map +1 -1
- package/dist/storage/local-store.js +228 -72
- package/dist/storage/local-store.js.map +1 -1
- package/dist/storage/retention.d.ts.map +1 -1
- package/dist/storage/retention.js +7 -6
- package/dist/storage/retention.js.map +1 -1
- package/dist/storage/session-store.d.ts +37 -8
- package/dist/storage/session-store.d.ts.map +1 -1
- package/dist/storage/session-store.js +255 -48
- package/dist/storage/session-store.js.map +1 -1
- package/dist/storage/types.d.ts +7 -0
- package/dist/storage/types.d.ts.map +1 -1
- package/dist/storage/types.js +4 -0
- package/dist/storage/types.js.map +1 -1
- package/dist/tools/cost-tools.d.ts.map +1 -1
- package/dist/tools/cost-tools.js +1 -0
- package/dist/tools/cost-tools.js.map +1 -1
- package/dist/web/assets/{index-U9DPZ-Tb.js → index-CZvLy2Qy.js} +2 -2
- package/dist/web/index.html +1 -1
- package/package.json +4 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"copilot-app-usage-watcher.js","sourceRoot":"","sources":["../../src/hooks/copilot-app-usage-watcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"copilot-app-usage-watcher.js","sourceRoot":"","sources":["../../src/hooks/copilot-app-usage-watcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -24,11 +24,11 @@
|
|
|
24
24
|
*
|
|
25
25
|
* Tailing/cursor mechanics mirror `ParentTranscriptWatcher`: durable byte
|
|
26
26
|
* cursor per session (`.copilot-usage-pos-<sessionId>`), bounded reads per
|
|
27
|
-
* poll, bounded partial-line carry,
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* message id.
|
|
27
|
+
* poll, bounded partial-line carry, and each `responseId` claimed in the
|
|
28
|
+
* BilledTurnLedger before it is buffered, so a re-read after a crash or a torn
|
|
29
|
+
* cursor emits nothing already emitted.
|
|
31
30
|
*/
|
|
31
|
+
import { BilledTurnLedger } from './billed-turn-ledger.js';
|
|
32
32
|
import type { LocalStore } from '../storage/local-store.js';
|
|
33
33
|
/**
|
|
34
34
|
* Default VS Code `workspaceStorage` roots per OS (stable + Insiders), plus
|
|
@@ -53,6 +53,8 @@ export interface CopilotUsageWatcherOptions {
|
|
|
53
53
|
readonly parentSessionId?: string;
|
|
54
54
|
/** LocalStore, used to exclude sessions already owned by a live --stdio watcher in unscoped mode. */
|
|
55
55
|
readonly localStore?: LocalStore;
|
|
56
|
+
/** The process's one billed-turn ledger; tests may omit it to get a private one. */
|
|
57
|
+
readonly ledger?: BilledTurnLedger;
|
|
56
58
|
}
|
|
57
59
|
export interface CopilotUsageWatcherHealth {
|
|
58
60
|
readonly filesWatched: number;
|
|
@@ -76,13 +78,17 @@ export declare class CopilotUsageWatcher {
|
|
|
76
78
|
private readonly discoveryHours;
|
|
77
79
|
private readonly parentSessionFilter;
|
|
78
80
|
private readonly localStore;
|
|
81
|
+
private readonly ledger;
|
|
82
|
+
private readonly retryWarning;
|
|
79
83
|
private intervalId;
|
|
84
|
+
private readonly bytePosBySession;
|
|
80
85
|
private readonly partialByPath;
|
|
81
86
|
private readonly decoderByPath;
|
|
82
87
|
private filesWatched;
|
|
83
88
|
private linesRead;
|
|
84
89
|
private bytesRead;
|
|
85
90
|
private parseErrors;
|
|
91
|
+
private warnedElided;
|
|
86
92
|
private debugLoggingLikelyDisabled;
|
|
87
93
|
private warnedDebugLoggingDisabled;
|
|
88
94
|
constructor(options?: CopilotUsageWatcherOptions);
|
|
@@ -95,11 +101,25 @@ export declare class CopilotUsageWatcher {
|
|
|
95
101
|
private tailFile;
|
|
96
102
|
private evictStalePartials;
|
|
97
103
|
/**
|
|
98
|
-
* Parse a debug-log JSONL line
|
|
104
|
+
* Parse a debug-log JSONL line; `parsed` is non-null only for an
|
|
99
105
|
* `llm_request` record carrying a model, responseId, and token counts.
|
|
100
106
|
*/
|
|
101
107
|
private tryParseLine;
|
|
108
|
+
/**
|
|
109
|
+
* Extract usage fields from a line whose middle was elided. Only scalar
|
|
110
|
+
* fields are matched, and an escaped quote inside a string payload never
|
|
111
|
+
* matches the unescaped patterns below. A field that fell in the elided
|
|
112
|
+
* middle yields no record: a guessed zero-token record would misreport cost.
|
|
113
|
+
*/
|
|
114
|
+
private parseElidedLine;
|
|
102
115
|
private cursorPath;
|
|
116
|
+
/**
|
|
117
|
+
* True when a cursor exists and the file has bytes past it. An existing
|
|
118
|
+
* cursor is read from disk once per session and tailFile keeps the cached
|
|
119
|
+
* position current, so the 2s poll does not re-read every old session's
|
|
120
|
+
* cursor file. Absence is not cached: another process may create the cursor.
|
|
121
|
+
*/
|
|
122
|
+
private hasUnreadBytes;
|
|
103
123
|
private readCursor;
|
|
104
124
|
private writeCursor;
|
|
105
125
|
/** Append into the session's hook buffer, same path convention as the collector. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"copilot-usage-watcher.d.ts","sourceRoot":"","sources":["../../src/hooks/copilot-usage-watcher.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"copilot-usage-watcher.d.ts","sourceRoot":"","sources":["../../src/hooks/copilot-usage-watcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAmBH,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AA0C5D;;;;;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;IACjC,oFAAoF;IACpF,QAAQ,CAAC,MAAM,CAAC,EAAE,gBAAgB,CAAC;CACpC;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;AA8BD,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;IACpD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmB;IAC1C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAG3B;IAEF,OAAO,CAAC,UAAU,CAA+C;IACjE,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA6B;IAC9D,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,YAAY,CAAS;IAC7B,OAAO,CAAC,0BAA0B,CAAS;IAC3C,OAAO,CAAC,0BAA0B,CAAS;gBAE/B,OAAO,GAAE,0BAA+B;IAUpD,KAAK,IAAI,IAAI;IAYb,IAAI,IAAI,IAAI;IAOZ,SAAS,IAAI,yBAAyB;IAUtC,8DAA8D;IAC9D,IAAI,IAAI,IAAI;IAiBZ,OAAO,CAAC,aAAa;IA6ErB,OAAO,CAAC,QAAQ;IAiJhB,OAAO,CAAC,kBAAkB;IAW1B;;;OAGG;IACH,OAAO,CAAC,YAAY;IAkCpB;;;;;OAKG;IACH,OAAO,CAAC,eAAe;IAqDvB,OAAO,CAAC,UAAU;IAIlB;;;;;OAKG;IACH,OAAO,CAAC,cAAc;IAWtB,OAAO,CAAC,UAAU;IAiBlB,OAAO,CAAC,WAAW;IAcnB,oFAAoF;IACpF,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,WAAW;CAIpB"}
|
|
@@ -24,16 +24,18 @@
|
|
|
24
24
|
*
|
|
25
25
|
* Tailing/cursor mechanics mirror `ParentTranscriptWatcher`: durable byte
|
|
26
26
|
* cursor per session (`.copilot-usage-pos-<sessionId>`), bounded reads per
|
|
27
|
-
* poll, bounded partial-line carry,
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* message id.
|
|
27
|
+
* poll, bounded partial-line carry, and each `responseId` claimed in the
|
|
28
|
+
* BilledTurnLedger before it is buffered, so a re-read after a crash or a torn
|
|
29
|
+
* cursor emits nothing already emitted.
|
|
31
30
|
*/
|
|
32
|
-
import {
|
|
31
|
+
import { closeSync, existsSync, mkdirSync, openSync, readdirSync, readFileSync, readSync, statSync, writeFileSync, } from 'node:fs';
|
|
33
32
|
import { homedir } from 'node:os';
|
|
34
33
|
import { join } from 'node:path';
|
|
35
34
|
import { StringDecoder } from 'node:string_decoder';
|
|
36
35
|
import { createLogger } from '../shared/index.js';
|
|
36
|
+
import { appendToHookBuffer } from '../storage/hook-buffer.js';
|
|
37
|
+
import { BilledTurnLedger } from './billed-turn-ledger.js';
|
|
38
|
+
import { ThrottledWarning } from './throttled-warning.js';
|
|
37
39
|
const logger = createLogger('copilot-usage-watcher');
|
|
38
40
|
// ---------------------------------------------------------------------------
|
|
39
41
|
// Constants
|
|
@@ -47,6 +49,15 @@ const DEFAULT_DISCOVERY_HOURS = 24;
|
|
|
47
49
|
const MAX_BYTES_PER_POLL = 1024 * 1024;
|
|
48
50
|
/** See SubagentWatcher's identical constant for the OOM-bug rationale this guards against. */
|
|
49
51
|
const MAX_PARTIAL_LINE_BYTES = 4 * 1024 * 1024; // 4 MiB — sized to the larger log lines
|
|
52
|
+
// A line past the cap is not dropped: its head and tail windows are kept and
|
|
53
|
+
// the middle is elided, so memory stays bounded while the usage fields, which
|
|
54
|
+
// sit near the line's ends, are still extracted when the line completes.
|
|
55
|
+
const ELIDE_KEEP_CHARS = 64 * 1024;
|
|
56
|
+
// A raw JSONL line cannot contain a literal NUL, so the mark is unambiguous.
|
|
57
|
+
const ELIDE_MARK = '\u0000ELIDED\u0000';
|
|
58
|
+
// A replaced log is recognised by its first bytes, not its size or inode: a new
|
|
59
|
+
// file can be larger than the old cursor, and ext4 reuses freed inodes.
|
|
60
|
+
const FINGERPRINT_BYTES = 256;
|
|
50
61
|
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
62
|
const COPILOT_LOG_SUBPATH = join('GitHub.copilot-chat', 'debug-logs');
|
|
52
63
|
/**
|
|
@@ -91,13 +102,17 @@ export class CopilotUsageWatcher {
|
|
|
91
102
|
discoveryHours;
|
|
92
103
|
parentSessionFilter;
|
|
93
104
|
localStore;
|
|
105
|
+
ledger;
|
|
106
|
+
retryWarning = new ThrottledWarning(logger, 'CopilotUsageWatcher read deferred to the next poll');
|
|
94
107
|
intervalId = null;
|
|
108
|
+
bytePosBySession = new Map();
|
|
95
109
|
partialByPath = new Map();
|
|
96
110
|
decoderByPath = new Map();
|
|
97
111
|
filesWatched = 0;
|
|
98
112
|
linesRead = 0;
|
|
99
113
|
bytesRead = 0;
|
|
100
114
|
parseErrors = 0;
|
|
115
|
+
warnedElided = false;
|
|
101
116
|
debugLoggingLikelyDisabled = false;
|
|
102
117
|
warnedDebugLoggingDisabled = false;
|
|
103
118
|
constructor(options = {}) {
|
|
@@ -107,6 +122,7 @@ export class CopilotUsageWatcher {
|
|
|
107
122
|
this.discoveryHours = options.discoveryHours ?? DEFAULT_DISCOVERY_HOURS;
|
|
108
123
|
this.parentSessionFilter = options.parentSessionId ?? null;
|
|
109
124
|
this.localStore = options.localStore;
|
|
125
|
+
this.ledger = options.ledger ?? new BilledTurnLedger({ storagePath: this.storagePath });
|
|
110
126
|
}
|
|
111
127
|
start() {
|
|
112
128
|
if (this.intervalId !== null)
|
|
@@ -202,8 +218,11 @@ export class CopilotUsageWatcher {
|
|
|
202
218
|
continue;
|
|
203
219
|
// Scoped mode always tails its own session; unscoped applies the
|
|
204
220
|
// recency window so old logs don't trigger unbounded cold scans.
|
|
205
|
-
if (this.parentSessionFilter === null &&
|
|
221
|
+
if (this.parentSessionFilter === null &&
|
|
222
|
+
st.mtimeMs < cutoffMs &&
|
|
223
|
+
!this.hasUnreadBytes(sessionId, st.size)) {
|
|
206
224
|
continue;
|
|
225
|
+
}
|
|
207
226
|
}
|
|
208
227
|
catch {
|
|
209
228
|
continue;
|
|
@@ -232,9 +251,7 @@ export class CopilotUsageWatcher {
|
|
|
232
251
|
const cursorPath = this.cursorPath(dirSessionId);
|
|
233
252
|
const persisted = this.readCursor(cursorPath);
|
|
234
253
|
const memPartial = this.partialByPath.get(path);
|
|
235
|
-
|
|
236
|
-
? { bytePos: persisted.bytePos, partialLine: memPartial }
|
|
237
|
-
: persisted;
|
|
254
|
+
let startCursor = memPartial !== undefined ? { ...persisted, partialLine: memPartial } : persisted;
|
|
238
255
|
let size;
|
|
239
256
|
try {
|
|
240
257
|
size = statSync(path).size;
|
|
@@ -242,29 +259,51 @@ export class CopilotUsageWatcher {
|
|
|
242
259
|
catch {
|
|
243
260
|
return;
|
|
244
261
|
}
|
|
245
|
-
|
|
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;
|
|
262
|
+
let fd;
|
|
253
263
|
try {
|
|
254
264
|
fd = openSync(path, 'r');
|
|
255
|
-
|
|
265
|
+
}
|
|
266
|
+
catch (err) {
|
|
267
|
+
this.recordError(err);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
let fingerprint;
|
|
271
|
+
let fromPos;
|
|
272
|
+
let buf;
|
|
273
|
+
try {
|
|
274
|
+
const head = readAt(fd, 0, Math.min(size, FINGERPRINT_BYTES));
|
|
275
|
+
// Truncated or replaced file: restart from 0, and drop the carried partial
|
|
276
|
+
// line and decoder state, which belong to the old file's bytes. A cursor
|
|
277
|
+
// without a fingerprint can only detect truncation.
|
|
278
|
+
const knownHead = startCursor.fingerprint === undefined
|
|
279
|
+
? null
|
|
280
|
+
: Buffer.from(startCursor.fingerprint, 'base64');
|
|
281
|
+
const replaced = knownHead !== null && !onePrefixesOther(head, knownHead);
|
|
282
|
+
if (startCursor.bytePos > size || replaced) {
|
|
283
|
+
startCursor = { bytePos: 0, partialLine: '' };
|
|
284
|
+
this.partialByPath.delete(path);
|
|
285
|
+
this.decoderByPath.delete(path);
|
|
286
|
+
}
|
|
287
|
+
fingerprint = (knownHead !== null && !replaced && knownHead.length > head.length ? knownHead : head).toString('base64');
|
|
288
|
+
fromPos = startCursor.bytePos;
|
|
289
|
+
if (fromPos >= size) {
|
|
290
|
+
this.bytePosBySession.set(dirSessionId, fromPos);
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
buf = readAt(fd, fromPos, Math.min(size - fromPos, MAX_BYTES_PER_POLL));
|
|
256
294
|
}
|
|
257
295
|
catch (err) {
|
|
258
296
|
this.recordError(err);
|
|
259
297
|
return;
|
|
260
298
|
}
|
|
261
299
|
finally {
|
|
262
|
-
|
|
263
|
-
closeSync(fd);
|
|
300
|
+
closeSync(fd);
|
|
264
301
|
}
|
|
265
|
-
|
|
302
|
+
const actuallyRead = buf.length;
|
|
303
|
+
if (actuallyRead <= 0) {
|
|
304
|
+
this.bytePosBySession.set(dirSessionId, fromPos);
|
|
266
305
|
return;
|
|
267
|
-
|
|
306
|
+
}
|
|
268
307
|
let decoder = this.decoderByPath.get(path);
|
|
269
308
|
if (!decoder) {
|
|
270
309
|
decoder = new StringDecoder('utf8');
|
|
@@ -279,47 +318,77 @@ export class CopilotUsageWatcher {
|
|
|
279
318
|
lines = combined.slice(0, lastNewline).split('\n');
|
|
280
319
|
newPartial = combined.slice(lastNewline + 1);
|
|
281
320
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
321
|
+
newPartial = elideOversize(newPartial);
|
|
322
|
+
// Health counters wait until the read is committed, so a redone read counts once.
|
|
323
|
+
const recordsBySession = new Map();
|
|
324
|
+
let linesRead = 0;
|
|
325
|
+
let parseErrors = 0;
|
|
286
326
|
for (const line of lines) {
|
|
287
327
|
if (!line)
|
|
288
328
|
continue;
|
|
289
|
-
|
|
290
|
-
const parsed =
|
|
329
|
+
linesRead += 1;
|
|
330
|
+
const { parsed, parseError } = line.includes(ELIDE_MARK)
|
|
331
|
+
? this.parseElidedLine(line)
|
|
332
|
+
: this.tryParseLine(line);
|
|
333
|
+
if (parseError)
|
|
334
|
+
parseErrors += 1;
|
|
291
335
|
if (parsed === null)
|
|
292
336
|
continue;
|
|
293
337
|
const sessionId = parsed.sessionId ?? dirSessionId;
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
sessionId,
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
338
|
+
const group = recordsBySession.get(sessionId);
|
|
339
|
+
if (group)
|
|
340
|
+
group.push(parsed);
|
|
341
|
+
else
|
|
342
|
+
recordsBySession.set(sessionId, [parsed]);
|
|
343
|
+
}
|
|
344
|
+
// If the claim, an append or the cursor write fails, the cursor stays put
|
|
345
|
+
// and the next poll redoes the read: the ledger hands back the records
|
|
346
|
+
// still owed and refuses the ones already appended.
|
|
347
|
+
try {
|
|
348
|
+
for (const [sessionId, records] of recordsBySession) {
|
|
349
|
+
this.ledger.deliver(sessionId, records, (parsed) => {
|
|
350
|
+
this.appendToBuffer(sessionId, {
|
|
351
|
+
mode: 'token',
|
|
352
|
+
tool: 'copilot-usage',
|
|
353
|
+
timestamp: parsed.timestampMs,
|
|
354
|
+
sessionId,
|
|
355
|
+
messageId: parsed.messageId,
|
|
356
|
+
model: parsed.model,
|
|
357
|
+
// VS Code's inputTokens is cache-INCLUSIVE (verified against a real
|
|
358
|
+
// main.jsonl: each request's cachedTokens ≈ the previous request's
|
|
359
|
+
// inputTokens). CostTracker follows the Anthropic convention where
|
|
360
|
+
// input excludes cache reads, so emit only the uncached remainder —
|
|
361
|
+
// otherwise cached tokens are double-billed at full input rate.
|
|
362
|
+
inputTokens: Math.max(0, parsed.inputTokens - parsed.cachedTokens),
|
|
363
|
+
outputTokens: parsed.outputTokens,
|
|
364
|
+
// `cachedTokens` is the only cache figure the debug-log schema exposes,
|
|
365
|
+
// and it is cache-READ (confirmed against a real main.jsonl: it tracks
|
|
366
|
+
// the previous request's input). KNOWN ESTIMATION GAP: VS Code does not
|
|
367
|
+
// surface cache-CREATION (cache-write) tokens separately, so any turn
|
|
368
|
+
// with a fresh cache write has those tokens folded into `inputTokens`
|
|
369
|
+
// above and billed at the base input rate instead of the premium
|
|
370
|
+
// `cacheCreationPerMTok` rate (src/shared/pricing.ts). This causes a
|
|
371
|
+
// small, consistent UNDER-billing on cache-write turns. We report 0
|
|
372
|
+
// rather than guess a split the source doesn't provide; revisit if VS
|
|
373
|
+
// Code adds a cache-creation field to the schema.
|
|
374
|
+
cacheReadTokens: parsed.cachedTokens,
|
|
375
|
+
cacheCreationTokens: 0,
|
|
376
|
+
});
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
this.writeCursor(cursorPath, nextBytePos, newPartial, fingerprint);
|
|
321
380
|
}
|
|
322
|
-
|
|
381
|
+
catch (err) {
|
|
382
|
+
this.retryWarning.warn(err, path);
|
|
383
|
+
this.partialByPath.delete(path);
|
|
384
|
+
this.decoderByPath.delete(path);
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
this.retryWarning.succeeded(path);
|
|
388
|
+
this.bytePosBySession.set(dirSessionId, nextBytePos);
|
|
389
|
+
this.bytesRead += actuallyRead;
|
|
390
|
+
this.linesRead += linesRead;
|
|
391
|
+
this.parseErrors += parseErrors;
|
|
323
392
|
if (newPartial.length > 0)
|
|
324
393
|
this.partialByPath.set(path, newPartial);
|
|
325
394
|
else
|
|
@@ -339,44 +408,102 @@ export class CopilotUsageWatcher {
|
|
|
339
408
|
}
|
|
340
409
|
}
|
|
341
410
|
/**
|
|
342
|
-
* Parse a debug-log JSONL line
|
|
411
|
+
* Parse a debug-log JSONL line; `parsed` is non-null only for an
|
|
343
412
|
* `llm_request` record carrying a model, responseId, and token counts.
|
|
344
413
|
*/
|
|
345
414
|
tryParseLine(line) {
|
|
346
|
-
let
|
|
415
|
+
let json;
|
|
347
416
|
try {
|
|
348
|
-
|
|
417
|
+
json = JSON.parse(line);
|
|
349
418
|
}
|
|
350
419
|
catch {
|
|
351
|
-
|
|
352
|
-
return null;
|
|
420
|
+
return { parsed: null, parseError: true };
|
|
353
421
|
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
422
|
+
const none = { parsed: null, parseError: false };
|
|
423
|
+
if (!json || typeof json !== 'object')
|
|
424
|
+
return none;
|
|
425
|
+
const obj = json;
|
|
357
426
|
if (obj.type !== 'llm_request')
|
|
358
|
-
return
|
|
427
|
+
return none;
|
|
359
428
|
const attrs = obj.attrs;
|
|
360
429
|
if (!attrs || typeof attrs !== 'object')
|
|
361
|
-
return
|
|
430
|
+
return none;
|
|
362
431
|
const a = attrs;
|
|
363
432
|
const model = typeof a.model === 'string' && a.model.length > 0 ? a.model : null;
|
|
364
433
|
if (!model)
|
|
365
|
-
return
|
|
434
|
+
return none;
|
|
366
435
|
const responseId = typeof a.responseId === 'string' && a.responseId.length > 0 ? a.responseId : null;
|
|
367
436
|
if (!responseId)
|
|
368
|
-
return
|
|
369
|
-
const
|
|
437
|
+
return none;
|
|
438
|
+
const ts = typeof obj.ts === 'number' && Number.isFinite(obj.ts) && obj.ts > 0 ? obj.ts : null;
|
|
370
439
|
const sid = typeof obj.sid === 'string' && SESSION_ID_RE.test(obj.sid) ? obj.sid : null;
|
|
371
|
-
|
|
372
|
-
timestampMs,
|
|
440
|
+
const parsed = {
|
|
441
|
+
timestampMs: ts ?? Date.now(),
|
|
442
|
+
undated: ts === null,
|
|
373
443
|
sessionId: sid,
|
|
374
|
-
responseId,
|
|
444
|
+
messageId: responseId,
|
|
375
445
|
model,
|
|
376
446
|
inputTokens: num(a.inputTokens),
|
|
377
447
|
outputTokens: num(a.outputTokens),
|
|
378
448
|
cachedTokens: num(a.cachedTokens),
|
|
379
449
|
};
|
|
450
|
+
return { parsed, parseError: false };
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Extract usage fields from a line whose middle was elided. Only scalar
|
|
454
|
+
* fields are matched, and an escaped quote inside a string payload never
|
|
455
|
+
* matches the unescaped patterns below. A field that fell in the elided
|
|
456
|
+
* middle yields no record: a guessed zero-token record would misreport cost.
|
|
457
|
+
*/
|
|
458
|
+
parseElidedLine(line) {
|
|
459
|
+
const at = line.indexOf(ELIDE_MARK);
|
|
460
|
+
const head = line.slice(0, at);
|
|
461
|
+
const tail = line.slice(at + ELIDE_MARK.length);
|
|
462
|
+
// The same field in both windows with different values is ambiguous, for
|
|
463
|
+
// example a nested object carrying its own `model`. Refuse it instead of
|
|
464
|
+
// guessing which one is the request's.
|
|
465
|
+
let ambiguous = false;
|
|
466
|
+
const find = (re) => {
|
|
467
|
+
const inHead = re.exec(head)?.[1] ?? null;
|
|
468
|
+
const inTail = re.exec(tail)?.[1] ?? null;
|
|
469
|
+
if (inHead !== null && inTail !== null && inHead !== inTail)
|
|
470
|
+
ambiguous = true;
|
|
471
|
+
return inHead ?? inTail;
|
|
472
|
+
};
|
|
473
|
+
if (find(/("type":"llm_request")/) === null)
|
|
474
|
+
return { parsed: null, parseError: false };
|
|
475
|
+
// A number must end at a delimiter, or a value cut by the head boundary
|
|
476
|
+
// would match its leading digits.
|
|
477
|
+
const findNumber = (name) => {
|
|
478
|
+
const raw = find(new RegExp(`"${name}":(\\d+(?:\\.\\d+)?(?:[eE][+-]?\\d+)?)(?=[,}\\s])`));
|
|
479
|
+
return raw === null ? null : num(Number(raw));
|
|
480
|
+
};
|
|
481
|
+
const model = find(/"model":"([^"\\]+)"/);
|
|
482
|
+
const responseId = find(/"responseId":"([^"\\]+)"/);
|
|
483
|
+
const inputTokens = findNumber('inputTokens');
|
|
484
|
+
const outputTokens = findNumber('outputTokens');
|
|
485
|
+
const cachedTokens = findNumber('cachedTokens');
|
|
486
|
+
if (ambiguous || !model || !responseId || inputTokens === null || outputTokens === null) {
|
|
487
|
+
if (!this.warnedElided) {
|
|
488
|
+
this.warnedElided = true;
|
|
489
|
+
logger.warn('Skipped an oversize Copilot log line whose usage fields were not recoverable');
|
|
490
|
+
}
|
|
491
|
+
return { parsed: null, parseError: true };
|
|
492
|
+
}
|
|
493
|
+
const ts = Number(/"ts":(\d+)/.exec(head)?.[1]);
|
|
494
|
+
const dated = Number.isFinite(ts) && ts > 0;
|
|
495
|
+
const sid = /"sid":"([^"\\]+)"/.exec(head)?.[1];
|
|
496
|
+
const parsed = {
|
|
497
|
+
timestampMs: dated ? ts : Date.now(),
|
|
498
|
+
undated: !dated,
|
|
499
|
+
sessionId: sid !== undefined && SESSION_ID_RE.test(sid) ? sid : null,
|
|
500
|
+
messageId: responseId,
|
|
501
|
+
model,
|
|
502
|
+
inputTokens,
|
|
503
|
+
outputTokens,
|
|
504
|
+
cachedTokens: cachedTokens ?? 0,
|
|
505
|
+
};
|
|
506
|
+
return { parsed, parseError: false };
|
|
380
507
|
}
|
|
381
508
|
// -------------------------------------------------------------------------
|
|
382
509
|
// Buffer + cursor I/O
|
|
@@ -384,6 +511,23 @@ export class CopilotUsageWatcher {
|
|
|
384
511
|
cursorPath(sessionId) {
|
|
385
512
|
return join(this.storagePath, `.copilot-usage-pos-${sessionId}`);
|
|
386
513
|
}
|
|
514
|
+
/**
|
|
515
|
+
* True when a cursor exists and the file has bytes past it. An existing
|
|
516
|
+
* cursor is read from disk once per session and tailFile keeps the cached
|
|
517
|
+
* position current, so the 2s poll does not re-read every old session's
|
|
518
|
+
* cursor file. Absence is not cached: another process may create the cursor.
|
|
519
|
+
*/
|
|
520
|
+
hasUnreadBytes(sessionId, size) {
|
|
521
|
+
let bytePos = this.bytePosBySession.get(sessionId);
|
|
522
|
+
if (bytePos === undefined) {
|
|
523
|
+
const cursorPath = this.cursorPath(sessionId);
|
|
524
|
+
if (!existsSync(cursorPath))
|
|
525
|
+
return false;
|
|
526
|
+
bytePos = this.readCursor(cursorPath).bytePos;
|
|
527
|
+
this.bytePosBySession.set(sessionId, bytePos);
|
|
528
|
+
}
|
|
529
|
+
return bytePos < size;
|
|
530
|
+
}
|
|
387
531
|
readCursor(cursorPath) {
|
|
388
532
|
if (!existsSync(cursorPath))
|
|
389
533
|
return { bytePos: 0, partialLine: '' };
|
|
@@ -391,36 +535,27 @@ export class CopilotUsageWatcher {
|
|
|
391
535
|
const parsed = JSON.parse(readFileSync(cursorPath, 'utf-8').trim());
|
|
392
536
|
const bytePos = typeof parsed.bytePos === 'number' && parsed.bytePos >= 0 ? parsed.bytePos : 0;
|
|
393
537
|
const partialLine = typeof parsed.partialLine === 'string' ? parsed.partialLine : '';
|
|
394
|
-
|
|
538
|
+
const fingerprint = typeof parsed.fingerprint === 'string' ? parsed.fingerprint : undefined;
|
|
539
|
+
return { bytePos, partialLine, fingerprint };
|
|
395
540
|
}
|
|
396
541
|
catch {
|
|
397
542
|
return { bytePos: 0, partialLine: '' };
|
|
398
543
|
}
|
|
399
544
|
}
|
|
400
|
-
writeCursor(cursorPath, bytePos, partialLine) {
|
|
401
|
-
|
|
402
|
-
|
|
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);
|
|
545
|
+
writeCursor(cursorPath, bytePos, partialLine, fingerprint) {
|
|
546
|
+
if (!existsSync(this.storagePath)) {
|
|
547
|
+
mkdirSync(this.storagePath, { recursive: true, mode: 0o700 });
|
|
409
548
|
}
|
|
549
|
+
writeFileSync(cursorPath, JSON.stringify({ bytePos, partialLine, fingerprint }), {
|
|
550
|
+
mode: 0o600,
|
|
551
|
+
});
|
|
410
552
|
}
|
|
411
553
|
/** Append into the session's hook buffer, same path convention as the collector. */
|
|
412
554
|
appendToBuffer(sessionId, event) {
|
|
413
|
-
|
|
414
|
-
|
|
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);
|
|
555
|
+
if (!existsSync(this.storagePath)) {
|
|
556
|
+
mkdirSync(this.storagePath, { recursive: true, mode: 0o700 });
|
|
423
557
|
}
|
|
558
|
+
appendToHookBuffer(join(this.storagePath, `buffer-${sessionId}.jsonl`), JSON.stringify(event) + '\n');
|
|
424
559
|
}
|
|
425
560
|
recordError(err) {
|
|
426
561
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -430,6 +565,33 @@ export class CopilotUsageWatcher {
|
|
|
430
565
|
// ---------------------------------------------------------------------------
|
|
431
566
|
// Helpers
|
|
432
567
|
// ---------------------------------------------------------------------------
|
|
568
|
+
/**
|
|
569
|
+
* Bound a carried partial line. Past the cap, keep the first and last
|
|
570
|
+
* ELIDE_KEEP_CHARS and replace the middle with ELIDE_MARK.
|
|
571
|
+
*/
|
|
572
|
+
function elideOversize(partial) {
|
|
573
|
+
const at = partial.indexOf(ELIDE_MARK);
|
|
574
|
+
if (at === -1) {
|
|
575
|
+
if (partial.length <= MAX_PARTIAL_LINE_BYTES)
|
|
576
|
+
return partial;
|
|
577
|
+
return (partial.slice(0, ELIDE_KEEP_CHARS) +
|
|
578
|
+
ELIDE_MARK +
|
|
579
|
+
partial.slice(partial.length - ELIDE_KEEP_CHARS));
|
|
580
|
+
}
|
|
581
|
+
const tailStart = at + ELIDE_MARK.length;
|
|
582
|
+
if (partial.length - tailStart <= 2 * ELIDE_KEEP_CHARS)
|
|
583
|
+
return partial;
|
|
584
|
+
return partial.slice(0, tailStart) + partial.slice(partial.length - ELIDE_KEEP_CHARS);
|
|
585
|
+
}
|
|
586
|
+
function readAt(fd, pos, len) {
|
|
587
|
+
const buf = Buffer.alloc(len);
|
|
588
|
+
const n = len === 0 ? 0 : readSync(fd, buf, 0, len, pos);
|
|
589
|
+
return n === len ? buf : buf.subarray(0, n);
|
|
590
|
+
}
|
|
591
|
+
function onePrefixesOther(a, b) {
|
|
592
|
+
const n = Math.min(a.length, b.length);
|
|
593
|
+
return a.subarray(0, n).equals(b.subarray(0, n));
|
|
594
|
+
}
|
|
433
595
|
function num(v) {
|
|
434
596
|
return typeof v === 'number' && Number.isFinite(v) && v >= 0 ? v : 0;
|
|
435
597
|
}
|