@harness-engineering/core 0.26.0 → 0.26.2
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/architecture/matchers.d.mts +1 -1
- package/dist/architecture/matchers.d.ts +1 -1
- package/dist/index.d.mts +389 -197
- package/dist/index.d.ts +389 -197
- package/dist/index.js +234 -2
- package/dist/index.mjs +230 -2
- package/dist/{matchers-DSibUtbV.d.mts → matchers-XHMrK1kB.d.mts} +134 -134
- package/dist/{matchers-DSibUtbV.d.ts → matchers-XHMrK1kB.d.ts} +134 -134
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -55,6 +55,7 @@ __export(index_exports, {
|
|
|
55
55
|
CINotifier: () => CINotifier,
|
|
56
56
|
COMPLIANCE_DESCRIPTOR: () => COMPLIANCE_DESCRIPTOR,
|
|
57
57
|
CORROBORATED_AGREEMENT: () => CORROBORATED_AGREEMENT,
|
|
58
|
+
CacheMetricsRecorder: () => CacheMetricsRecorder,
|
|
58
59
|
CategoryBaselineSchema: () => CategoryBaselineSchema,
|
|
59
60
|
CategoryForecastSchema: () => CategoryForecastSchema,
|
|
60
61
|
CategoryRegressionSchema: () => CategoryRegressionSchema,
|
|
@@ -119,6 +120,7 @@ __export(index_exports, {
|
|
|
119
120
|
NoOpExecutor: () => NoOpExecutor,
|
|
120
121
|
NoOpSink: () => NoOpSink,
|
|
121
122
|
NoOpTelemetryAdapter: () => NoOpTelemetryAdapter,
|
|
123
|
+
OTLPExporter: () => OTLPExporter,
|
|
122
124
|
OpenAICacheAdapter: () => OpenAICacheAdapter,
|
|
123
125
|
PII_FIELD_DENYLIST: () => PII_FIELD_DENYLIST,
|
|
124
126
|
PII_LINE_RE: () => PII_LINE_RE,
|
|
@@ -157,6 +159,7 @@ __export(index_exports, {
|
|
|
157
159
|
SharableSecurityRulesSchema: () => SharableSecurityRulesSchema,
|
|
158
160
|
SkillEventSchema: () => SkillEventSchema,
|
|
159
161
|
SolutionDocFrontmatterSchema: () => SolutionDocFrontmatterSchema,
|
|
162
|
+
SpanKind: () => SpanKind,
|
|
160
163
|
SpecImpactEstimateSchema: () => SpecImpactEstimateSchema,
|
|
161
164
|
SpecImpactEstimator: () => SpecImpactEstimator,
|
|
162
165
|
SpecImpactSignalsSchema: () => SpecImpactSignalsSchema,
|
|
@@ -325,6 +328,7 @@ __export(index_exports, {
|
|
|
325
328
|
goRules: () => goRules,
|
|
326
329
|
injectionRules: () => injectionRules,
|
|
327
330
|
insecureDefaultsRules: () => insecureDefaultsRules,
|
|
331
|
+
invalidateCheckState: () => invalidateCheckState,
|
|
328
332
|
isBadPort: () => isBadPort,
|
|
329
333
|
isDuplicateFinding: () => isDuplicateFinding,
|
|
330
334
|
isRegression: () => isRegression,
|
|
@@ -5645,7 +5649,10 @@ function findDeadFiles(snapshot, reachability) {
|
|
|
5645
5649
|
return deadFiles;
|
|
5646
5650
|
}
|
|
5647
5651
|
function isIdentifierUsedInAST(ast, identifier, skipImportDeclaration = true) {
|
|
5648
|
-
const astString = JSON.stringify(
|
|
5652
|
+
const astString = JSON.stringify(
|
|
5653
|
+
ast,
|
|
5654
|
+
(_key, value) => typeof value === "bigint" ? value.toString() : value
|
|
5655
|
+
);
|
|
5649
5656
|
const identifierPattern = new RegExp(`"name"\\s*:\\s*"${identifier}"`, "g");
|
|
5650
5657
|
const matches = astString.match(identifierPattern);
|
|
5651
5658
|
if (!matches) return false;
|
|
@@ -20193,6 +20200,12 @@ function shouldRunCheck(state, intervalMs) {
|
|
|
20193
20200
|
if (state === null) return true;
|
|
20194
20201
|
return state.lastCheckTime + intervalMs <= Date.now();
|
|
20195
20202
|
}
|
|
20203
|
+
function invalidateCheckState() {
|
|
20204
|
+
try {
|
|
20205
|
+
fs36.unlinkSync(getStatePath());
|
|
20206
|
+
} catch {
|
|
20207
|
+
}
|
|
20208
|
+
}
|
|
20196
20209
|
function readCheckState() {
|
|
20197
20210
|
try {
|
|
20198
20211
|
const raw = fs36.readFileSync(getStatePath(), "utf-8");
|
|
@@ -21390,7 +21403,7 @@ function resolveConsent(projectRoot, config) {
|
|
|
21390
21403
|
}
|
|
21391
21404
|
|
|
21392
21405
|
// src/version.ts
|
|
21393
|
-
var VERSION = "0.
|
|
21406
|
+
var VERSION = "0.25.0";
|
|
21394
21407
|
|
|
21395
21408
|
// src/telemetry/collector.ts
|
|
21396
21409
|
function mapOutcome(outcome) {
|
|
@@ -21451,6 +21464,221 @@ async function send(events, apiKey) {
|
|
|
21451
21464
|
}
|
|
21452
21465
|
}
|
|
21453
21466
|
|
|
21467
|
+
// src/telemetry/cache-metrics.ts
|
|
21468
|
+
var DEFAULT_CAPACITY = 1e3;
|
|
21469
|
+
var CacheMetricsRecorder = class {
|
|
21470
|
+
capacity;
|
|
21471
|
+
now;
|
|
21472
|
+
buffer = [];
|
|
21473
|
+
windowStartedAt = 0;
|
|
21474
|
+
constructor(opts = {}) {
|
|
21475
|
+
this.capacity = opts.capacity ?? DEFAULT_CAPACITY;
|
|
21476
|
+
this.now = opts.now ?? Date.now;
|
|
21477
|
+
}
|
|
21478
|
+
/**
|
|
21479
|
+
* Append a single cache observation. Hot-path: O(1) amortized, no
|
|
21480
|
+
* allocations beyond the sample object itself.
|
|
21481
|
+
*/
|
|
21482
|
+
record(backendId, hit, tokensCreated, tokensRead) {
|
|
21483
|
+
if (this.windowStartedAt === 0) this.windowStartedAt = this.now();
|
|
21484
|
+
if (this.buffer.length >= this.capacity) this.buffer.shift();
|
|
21485
|
+
this.buffer.push({ backendId, hit, tokensCreated, tokensRead });
|
|
21486
|
+
}
|
|
21487
|
+
/** Aggregate the current window into a `PromptCacheStats` snapshot. */
|
|
21488
|
+
getStats() {
|
|
21489
|
+
let hits = 0;
|
|
21490
|
+
let misses = 0;
|
|
21491
|
+
const byBackend = {};
|
|
21492
|
+
for (const s of this.buffer) {
|
|
21493
|
+
if (s.hit) hits++;
|
|
21494
|
+
else misses++;
|
|
21495
|
+
const slot = byBackend[s.backendId] ?? { hits: 0, misses: 0 };
|
|
21496
|
+
if (s.hit) slot.hits++;
|
|
21497
|
+
else slot.misses++;
|
|
21498
|
+
byBackend[s.backendId] = slot;
|
|
21499
|
+
}
|
|
21500
|
+
const totalRequests = hits + misses;
|
|
21501
|
+
const hitRate = totalRequests === 0 ? 0 : hits / totalRequests;
|
|
21502
|
+
return {
|
|
21503
|
+
totalRequests,
|
|
21504
|
+
hits,
|
|
21505
|
+
misses,
|
|
21506
|
+
hitRate,
|
|
21507
|
+
byBackend,
|
|
21508
|
+
windowStartedAt: this.windowStartedAt
|
|
21509
|
+
};
|
|
21510
|
+
}
|
|
21511
|
+
/** Clear the buffer and reset the window-start marker. */
|
|
21512
|
+
reset() {
|
|
21513
|
+
this.buffer.length = 0;
|
|
21514
|
+
this.windowStartedAt = 0;
|
|
21515
|
+
}
|
|
21516
|
+
};
|
|
21517
|
+
|
|
21518
|
+
// src/telemetry/exporter/otlp-http.ts
|
|
21519
|
+
var RETRY_BACKOFFS_MS = [1e3, 2e3, 4e3];
|
|
21520
|
+
function toUnixNanoString(ns) {
|
|
21521
|
+
return ns.toString(10);
|
|
21522
|
+
}
|
|
21523
|
+
function attributesToOTLP(attrs) {
|
|
21524
|
+
const out = [];
|
|
21525
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
21526
|
+
if (typeof value === "string") {
|
|
21527
|
+
out.push({ key, value: { stringValue: value } });
|
|
21528
|
+
} else if (typeof value === "boolean") {
|
|
21529
|
+
out.push({ key, value: { boolValue: value } });
|
|
21530
|
+
} else if (typeof value === "number") {
|
|
21531
|
+
if (Number.isInteger(value)) {
|
|
21532
|
+
out.push({ key, value: { intValue: String(value) } });
|
|
21533
|
+
} else {
|
|
21534
|
+
out.push({ key, value: { doubleValue: value } });
|
|
21535
|
+
}
|
|
21536
|
+
}
|
|
21537
|
+
}
|
|
21538
|
+
return out;
|
|
21539
|
+
}
|
|
21540
|
+
var OTLPExporter = class {
|
|
21541
|
+
endpoint;
|
|
21542
|
+
enabled;
|
|
21543
|
+
headers;
|
|
21544
|
+
flushIntervalMs;
|
|
21545
|
+
batchSize;
|
|
21546
|
+
fetchImpl;
|
|
21547
|
+
warn;
|
|
21548
|
+
buffer = [];
|
|
21549
|
+
timer = null;
|
|
21550
|
+
inFlightFlushes = /* @__PURE__ */ new Set();
|
|
21551
|
+
constructor(opts) {
|
|
21552
|
+
this.endpoint = opts.endpoint;
|
|
21553
|
+
this.enabled = opts.enabled !== false;
|
|
21554
|
+
this.headers = { "Content-Type": "application/json", ...opts.headers ?? {} };
|
|
21555
|
+
this.flushIntervalMs = opts.flushIntervalMs ?? 2e3;
|
|
21556
|
+
this.batchSize = opts.batchSize ?? 64;
|
|
21557
|
+
this.fetchImpl = opts.fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
21558
|
+
this.warn = opts.warn ?? ((...a) => console.warn(...a));
|
|
21559
|
+
}
|
|
21560
|
+
/**
|
|
21561
|
+
* O(1) buffer push. When `enabled === false` this is a no-op. If the
|
|
21562
|
+
* buffer reaches `batchSize`, a flush is triggered without awaiting.
|
|
21563
|
+
*/
|
|
21564
|
+
push(span) {
|
|
21565
|
+
if (!this.enabled) return;
|
|
21566
|
+
this.buffer.push(span);
|
|
21567
|
+
if (this.buffer.length >= this.batchSize) {
|
|
21568
|
+
void this.flush();
|
|
21569
|
+
}
|
|
21570
|
+
}
|
|
21571
|
+
/** Start the periodic flush timer. Idempotent. */
|
|
21572
|
+
start() {
|
|
21573
|
+
if (!this.enabled || this.timer !== null) return;
|
|
21574
|
+
this.timer = setInterval(() => {
|
|
21575
|
+
void this.flush();
|
|
21576
|
+
}, this.flushIntervalMs);
|
|
21577
|
+
if (typeof this.timer.unref === "function") {
|
|
21578
|
+
this.timer.unref();
|
|
21579
|
+
}
|
|
21580
|
+
}
|
|
21581
|
+
/** Flush any pending spans and stop the timer. Awaits all in-flight flushes. */
|
|
21582
|
+
async stop() {
|
|
21583
|
+
if (this.timer !== null) {
|
|
21584
|
+
clearInterval(this.timer);
|
|
21585
|
+
this.timer = null;
|
|
21586
|
+
}
|
|
21587
|
+
void this.flush();
|
|
21588
|
+
while (this.inFlightFlushes.size > 0) {
|
|
21589
|
+
await Promise.all([...this.inFlightFlushes]);
|
|
21590
|
+
}
|
|
21591
|
+
}
|
|
21592
|
+
/**
|
|
21593
|
+
* Synchronously claim the current buffer and POST it to `/v1/traces`.
|
|
21594
|
+
* Each invocation runs independently — concurrent flushes (e.g. one
|
|
21595
|
+
* from the timer, one from a `batchSize` trip) each drain their own
|
|
21596
|
+
* batch. Retries up to 3 times on transport or 5xx failure, then
|
|
21597
|
+
* drops with a single warning.
|
|
21598
|
+
*/
|
|
21599
|
+
flush() {
|
|
21600
|
+
if (this.buffer.length === 0) return Promise.resolve();
|
|
21601
|
+
const batch = this.buffer;
|
|
21602
|
+
this.buffer = [];
|
|
21603
|
+
const payload = this.spansToOTLPJSON(batch);
|
|
21604
|
+
const work = (async () => {
|
|
21605
|
+
let lastError = null;
|
|
21606
|
+
for (let attempt = 0; attempt < RETRY_BACKOFFS_MS.length; attempt++) {
|
|
21607
|
+
try {
|
|
21608
|
+
const response = await this.fetchImpl(this.endpoint, {
|
|
21609
|
+
method: "POST",
|
|
21610
|
+
headers: this.headers,
|
|
21611
|
+
body: JSON.stringify(payload)
|
|
21612
|
+
});
|
|
21613
|
+
if (response.ok) return;
|
|
21614
|
+
try {
|
|
21615
|
+
await response.text();
|
|
21616
|
+
} catch {
|
|
21617
|
+
}
|
|
21618
|
+
lastError = new Error(`OTLP endpoint returned ${response.status}`);
|
|
21619
|
+
} catch (err) {
|
|
21620
|
+
lastError = err;
|
|
21621
|
+
}
|
|
21622
|
+
const backoff = RETRY_BACKOFFS_MS[attempt] ?? 0;
|
|
21623
|
+
if (attempt < RETRY_BACKOFFS_MS.length - 1) {
|
|
21624
|
+
await new Promise((resolve12) => setTimeout(resolve12, backoff));
|
|
21625
|
+
}
|
|
21626
|
+
}
|
|
21627
|
+
this.warn(
|
|
21628
|
+
`[harness telemetry] dropping ${batch.length} span(s) after 3 failed OTLP attempts:`,
|
|
21629
|
+
lastError
|
|
21630
|
+
);
|
|
21631
|
+
})();
|
|
21632
|
+
this.inFlightFlushes.add(work);
|
|
21633
|
+
void work.finally(() => this.inFlightFlushes.delete(work));
|
|
21634
|
+
return work;
|
|
21635
|
+
}
|
|
21636
|
+
/**
|
|
21637
|
+
* Build the OTLP/HTTP JSON envelope for a batch of spans. Public for
|
|
21638
|
+
* tests; not part of the supported API surface.
|
|
21639
|
+
*/
|
|
21640
|
+
spansToOTLPJSON(spans) {
|
|
21641
|
+
return {
|
|
21642
|
+
resourceSpans: [
|
|
21643
|
+
{
|
|
21644
|
+
resource: {
|
|
21645
|
+
attributes: [{ key: "service.name", value: { stringValue: "harness" } }]
|
|
21646
|
+
},
|
|
21647
|
+
scopeSpans: [
|
|
21648
|
+
{
|
|
21649
|
+
scope: { name: "harness" },
|
|
21650
|
+
spans: spans.map((s) => {
|
|
21651
|
+
const span = {
|
|
21652
|
+
traceId: s.traceId,
|
|
21653
|
+
spanId: s.spanId,
|
|
21654
|
+
name: s.name,
|
|
21655
|
+
kind: s.kind,
|
|
21656
|
+
startTimeUnixNano: toUnixNanoString(s.startTimeNs),
|
|
21657
|
+
endTimeUnixNano: toUnixNanoString(s.endTimeNs),
|
|
21658
|
+
attributes: attributesToOTLP(s.attributes)
|
|
21659
|
+
};
|
|
21660
|
+
if (s.parentSpanId !== void 0) span["parentSpanId"] = s.parentSpanId;
|
|
21661
|
+
if (s.statusCode !== void 0) span["status"] = { code: s.statusCode };
|
|
21662
|
+
return span;
|
|
21663
|
+
})
|
|
21664
|
+
}
|
|
21665
|
+
]
|
|
21666
|
+
}
|
|
21667
|
+
]
|
|
21668
|
+
};
|
|
21669
|
+
}
|
|
21670
|
+
};
|
|
21671
|
+
|
|
21672
|
+
// src/telemetry/exporter/types.ts
|
|
21673
|
+
var SpanKind = /* @__PURE__ */ ((SpanKind2) => {
|
|
21674
|
+
SpanKind2[SpanKind2["INTERNAL"] = 1] = "INTERNAL";
|
|
21675
|
+
SpanKind2[SpanKind2["SERVER"] = 2] = "SERVER";
|
|
21676
|
+
SpanKind2[SpanKind2["CLIENT"] = 3] = "CLIENT";
|
|
21677
|
+
SpanKind2[SpanKind2["PRODUCER"] = 4] = "PRODUCER";
|
|
21678
|
+
SpanKind2[SpanKind2["CONSUMER"] = 5] = "CONSUMER";
|
|
21679
|
+
return SpanKind2;
|
|
21680
|
+
})(SpanKind || {});
|
|
21681
|
+
|
|
21454
21682
|
// src/locks/compound-lock.ts
|
|
21455
21683
|
var fs43 = __toESM(require("fs"));
|
|
21456
21684
|
var path43 = __toESM(require("path"));
|
|
@@ -22210,6 +22438,7 @@ function assembleCandidateReport(input) {
|
|
|
22210
22438
|
CINotifier,
|
|
22211
22439
|
COMPLIANCE_DESCRIPTOR,
|
|
22212
22440
|
CORROBORATED_AGREEMENT,
|
|
22441
|
+
CacheMetricsRecorder,
|
|
22213
22442
|
CategoryBaselineSchema,
|
|
22214
22443
|
CategoryForecastSchema,
|
|
22215
22444
|
CategoryRegressionSchema,
|
|
@@ -22274,6 +22503,7 @@ function assembleCandidateReport(input) {
|
|
|
22274
22503
|
NoOpExecutor,
|
|
22275
22504
|
NoOpSink,
|
|
22276
22505
|
NoOpTelemetryAdapter,
|
|
22506
|
+
OTLPExporter,
|
|
22277
22507
|
OpenAICacheAdapter,
|
|
22278
22508
|
PII_FIELD_DENYLIST,
|
|
22279
22509
|
PII_LINE_RE,
|
|
@@ -22312,6 +22542,7 @@ function assembleCandidateReport(input) {
|
|
|
22312
22542
|
SharableSecurityRulesSchema,
|
|
22313
22543
|
SkillEventSchema,
|
|
22314
22544
|
SolutionDocFrontmatterSchema,
|
|
22545
|
+
SpanKind,
|
|
22315
22546
|
SpecImpactEstimateSchema,
|
|
22316
22547
|
SpecImpactEstimator,
|
|
22317
22548
|
SpecImpactSignalsSchema,
|
|
@@ -22480,6 +22711,7 @@ function assembleCandidateReport(input) {
|
|
|
22480
22711
|
goRules,
|
|
22481
22712
|
injectionRules,
|
|
22482
22713
|
insecureDefaultsRules,
|
|
22714
|
+
invalidateCheckState,
|
|
22483
22715
|
isBadPort,
|
|
22484
22716
|
isDuplicateFinding,
|
|
22485
22717
|
isRegression,
|
package/dist/index.mjs
CHANGED
|
@@ -3988,7 +3988,10 @@ function findDeadFiles(snapshot, reachability) {
|
|
|
3988
3988
|
return deadFiles;
|
|
3989
3989
|
}
|
|
3990
3990
|
function isIdentifierUsedInAST(ast, identifier, skipImportDeclaration = true) {
|
|
3991
|
-
const astString = JSON.stringify(
|
|
3991
|
+
const astString = JSON.stringify(
|
|
3992
|
+
ast,
|
|
3993
|
+
(_key, value) => typeof value === "bigint" ? value.toString() : value
|
|
3994
|
+
);
|
|
3992
3995
|
const identifierPattern = new RegExp(`"name"\\s*:\\s*"${identifier}"`, "g");
|
|
3993
3996
|
const matches = astString.match(identifierPattern);
|
|
3994
3997
|
if (!matches) return false;
|
|
@@ -16920,6 +16923,12 @@ function shouldRunCheck(state, intervalMs) {
|
|
|
16920
16923
|
if (state === null) return true;
|
|
16921
16924
|
return state.lastCheckTime + intervalMs <= Date.now();
|
|
16922
16925
|
}
|
|
16926
|
+
function invalidateCheckState() {
|
|
16927
|
+
try {
|
|
16928
|
+
fs36.unlinkSync(getStatePath());
|
|
16929
|
+
} catch {
|
|
16930
|
+
}
|
|
16931
|
+
}
|
|
16923
16932
|
function readCheckState() {
|
|
16924
16933
|
try {
|
|
16925
16934
|
const raw = fs36.readFileSync(getStatePath(), "utf-8");
|
|
@@ -18117,7 +18126,7 @@ function resolveConsent(projectRoot, config) {
|
|
|
18117
18126
|
}
|
|
18118
18127
|
|
|
18119
18128
|
// src/version.ts
|
|
18120
|
-
var VERSION = "0.
|
|
18129
|
+
var VERSION = "0.25.0";
|
|
18121
18130
|
|
|
18122
18131
|
// src/telemetry/collector.ts
|
|
18123
18132
|
function mapOutcome(outcome) {
|
|
@@ -18178,6 +18187,221 @@ async function send(events, apiKey) {
|
|
|
18178
18187
|
}
|
|
18179
18188
|
}
|
|
18180
18189
|
|
|
18190
|
+
// src/telemetry/cache-metrics.ts
|
|
18191
|
+
var DEFAULT_CAPACITY = 1e3;
|
|
18192
|
+
var CacheMetricsRecorder = class {
|
|
18193
|
+
capacity;
|
|
18194
|
+
now;
|
|
18195
|
+
buffer = [];
|
|
18196
|
+
windowStartedAt = 0;
|
|
18197
|
+
constructor(opts = {}) {
|
|
18198
|
+
this.capacity = opts.capacity ?? DEFAULT_CAPACITY;
|
|
18199
|
+
this.now = opts.now ?? Date.now;
|
|
18200
|
+
}
|
|
18201
|
+
/**
|
|
18202
|
+
* Append a single cache observation. Hot-path: O(1) amortized, no
|
|
18203
|
+
* allocations beyond the sample object itself.
|
|
18204
|
+
*/
|
|
18205
|
+
record(backendId, hit, tokensCreated, tokensRead) {
|
|
18206
|
+
if (this.windowStartedAt === 0) this.windowStartedAt = this.now();
|
|
18207
|
+
if (this.buffer.length >= this.capacity) this.buffer.shift();
|
|
18208
|
+
this.buffer.push({ backendId, hit, tokensCreated, tokensRead });
|
|
18209
|
+
}
|
|
18210
|
+
/** Aggregate the current window into a `PromptCacheStats` snapshot. */
|
|
18211
|
+
getStats() {
|
|
18212
|
+
let hits = 0;
|
|
18213
|
+
let misses = 0;
|
|
18214
|
+
const byBackend = {};
|
|
18215
|
+
for (const s of this.buffer) {
|
|
18216
|
+
if (s.hit) hits++;
|
|
18217
|
+
else misses++;
|
|
18218
|
+
const slot = byBackend[s.backendId] ?? { hits: 0, misses: 0 };
|
|
18219
|
+
if (s.hit) slot.hits++;
|
|
18220
|
+
else slot.misses++;
|
|
18221
|
+
byBackend[s.backendId] = slot;
|
|
18222
|
+
}
|
|
18223
|
+
const totalRequests = hits + misses;
|
|
18224
|
+
const hitRate = totalRequests === 0 ? 0 : hits / totalRequests;
|
|
18225
|
+
return {
|
|
18226
|
+
totalRequests,
|
|
18227
|
+
hits,
|
|
18228
|
+
misses,
|
|
18229
|
+
hitRate,
|
|
18230
|
+
byBackend,
|
|
18231
|
+
windowStartedAt: this.windowStartedAt
|
|
18232
|
+
};
|
|
18233
|
+
}
|
|
18234
|
+
/** Clear the buffer and reset the window-start marker. */
|
|
18235
|
+
reset() {
|
|
18236
|
+
this.buffer.length = 0;
|
|
18237
|
+
this.windowStartedAt = 0;
|
|
18238
|
+
}
|
|
18239
|
+
};
|
|
18240
|
+
|
|
18241
|
+
// src/telemetry/exporter/otlp-http.ts
|
|
18242
|
+
var RETRY_BACKOFFS_MS = [1e3, 2e3, 4e3];
|
|
18243
|
+
function toUnixNanoString(ns) {
|
|
18244
|
+
return ns.toString(10);
|
|
18245
|
+
}
|
|
18246
|
+
function attributesToOTLP(attrs) {
|
|
18247
|
+
const out = [];
|
|
18248
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
18249
|
+
if (typeof value === "string") {
|
|
18250
|
+
out.push({ key, value: { stringValue: value } });
|
|
18251
|
+
} else if (typeof value === "boolean") {
|
|
18252
|
+
out.push({ key, value: { boolValue: value } });
|
|
18253
|
+
} else if (typeof value === "number") {
|
|
18254
|
+
if (Number.isInteger(value)) {
|
|
18255
|
+
out.push({ key, value: { intValue: String(value) } });
|
|
18256
|
+
} else {
|
|
18257
|
+
out.push({ key, value: { doubleValue: value } });
|
|
18258
|
+
}
|
|
18259
|
+
}
|
|
18260
|
+
}
|
|
18261
|
+
return out;
|
|
18262
|
+
}
|
|
18263
|
+
var OTLPExporter = class {
|
|
18264
|
+
endpoint;
|
|
18265
|
+
enabled;
|
|
18266
|
+
headers;
|
|
18267
|
+
flushIntervalMs;
|
|
18268
|
+
batchSize;
|
|
18269
|
+
fetchImpl;
|
|
18270
|
+
warn;
|
|
18271
|
+
buffer = [];
|
|
18272
|
+
timer = null;
|
|
18273
|
+
inFlightFlushes = /* @__PURE__ */ new Set();
|
|
18274
|
+
constructor(opts) {
|
|
18275
|
+
this.endpoint = opts.endpoint;
|
|
18276
|
+
this.enabled = opts.enabled !== false;
|
|
18277
|
+
this.headers = { "Content-Type": "application/json", ...opts.headers ?? {} };
|
|
18278
|
+
this.flushIntervalMs = opts.flushIntervalMs ?? 2e3;
|
|
18279
|
+
this.batchSize = opts.batchSize ?? 64;
|
|
18280
|
+
this.fetchImpl = opts.fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
18281
|
+
this.warn = opts.warn ?? ((...a) => console.warn(...a));
|
|
18282
|
+
}
|
|
18283
|
+
/**
|
|
18284
|
+
* O(1) buffer push. When `enabled === false` this is a no-op. If the
|
|
18285
|
+
* buffer reaches `batchSize`, a flush is triggered without awaiting.
|
|
18286
|
+
*/
|
|
18287
|
+
push(span) {
|
|
18288
|
+
if (!this.enabled) return;
|
|
18289
|
+
this.buffer.push(span);
|
|
18290
|
+
if (this.buffer.length >= this.batchSize) {
|
|
18291
|
+
void this.flush();
|
|
18292
|
+
}
|
|
18293
|
+
}
|
|
18294
|
+
/** Start the periodic flush timer. Idempotent. */
|
|
18295
|
+
start() {
|
|
18296
|
+
if (!this.enabled || this.timer !== null) return;
|
|
18297
|
+
this.timer = setInterval(() => {
|
|
18298
|
+
void this.flush();
|
|
18299
|
+
}, this.flushIntervalMs);
|
|
18300
|
+
if (typeof this.timer.unref === "function") {
|
|
18301
|
+
this.timer.unref();
|
|
18302
|
+
}
|
|
18303
|
+
}
|
|
18304
|
+
/** Flush any pending spans and stop the timer. Awaits all in-flight flushes. */
|
|
18305
|
+
async stop() {
|
|
18306
|
+
if (this.timer !== null) {
|
|
18307
|
+
clearInterval(this.timer);
|
|
18308
|
+
this.timer = null;
|
|
18309
|
+
}
|
|
18310
|
+
void this.flush();
|
|
18311
|
+
while (this.inFlightFlushes.size > 0) {
|
|
18312
|
+
await Promise.all([...this.inFlightFlushes]);
|
|
18313
|
+
}
|
|
18314
|
+
}
|
|
18315
|
+
/**
|
|
18316
|
+
* Synchronously claim the current buffer and POST it to `/v1/traces`.
|
|
18317
|
+
* Each invocation runs independently — concurrent flushes (e.g. one
|
|
18318
|
+
* from the timer, one from a `batchSize` trip) each drain their own
|
|
18319
|
+
* batch. Retries up to 3 times on transport or 5xx failure, then
|
|
18320
|
+
* drops with a single warning.
|
|
18321
|
+
*/
|
|
18322
|
+
flush() {
|
|
18323
|
+
if (this.buffer.length === 0) return Promise.resolve();
|
|
18324
|
+
const batch = this.buffer;
|
|
18325
|
+
this.buffer = [];
|
|
18326
|
+
const payload = this.spansToOTLPJSON(batch);
|
|
18327
|
+
const work = (async () => {
|
|
18328
|
+
let lastError = null;
|
|
18329
|
+
for (let attempt = 0; attempt < RETRY_BACKOFFS_MS.length; attempt++) {
|
|
18330
|
+
try {
|
|
18331
|
+
const response = await this.fetchImpl(this.endpoint, {
|
|
18332
|
+
method: "POST",
|
|
18333
|
+
headers: this.headers,
|
|
18334
|
+
body: JSON.stringify(payload)
|
|
18335
|
+
});
|
|
18336
|
+
if (response.ok) return;
|
|
18337
|
+
try {
|
|
18338
|
+
await response.text();
|
|
18339
|
+
} catch {
|
|
18340
|
+
}
|
|
18341
|
+
lastError = new Error(`OTLP endpoint returned ${response.status}`);
|
|
18342
|
+
} catch (err) {
|
|
18343
|
+
lastError = err;
|
|
18344
|
+
}
|
|
18345
|
+
const backoff = RETRY_BACKOFFS_MS[attempt] ?? 0;
|
|
18346
|
+
if (attempt < RETRY_BACKOFFS_MS.length - 1) {
|
|
18347
|
+
await new Promise((resolve10) => setTimeout(resolve10, backoff));
|
|
18348
|
+
}
|
|
18349
|
+
}
|
|
18350
|
+
this.warn(
|
|
18351
|
+
`[harness telemetry] dropping ${batch.length} span(s) after 3 failed OTLP attempts:`,
|
|
18352
|
+
lastError
|
|
18353
|
+
);
|
|
18354
|
+
})();
|
|
18355
|
+
this.inFlightFlushes.add(work);
|
|
18356
|
+
void work.finally(() => this.inFlightFlushes.delete(work));
|
|
18357
|
+
return work;
|
|
18358
|
+
}
|
|
18359
|
+
/**
|
|
18360
|
+
* Build the OTLP/HTTP JSON envelope for a batch of spans. Public for
|
|
18361
|
+
* tests; not part of the supported API surface.
|
|
18362
|
+
*/
|
|
18363
|
+
spansToOTLPJSON(spans) {
|
|
18364
|
+
return {
|
|
18365
|
+
resourceSpans: [
|
|
18366
|
+
{
|
|
18367
|
+
resource: {
|
|
18368
|
+
attributes: [{ key: "service.name", value: { stringValue: "harness" } }]
|
|
18369
|
+
},
|
|
18370
|
+
scopeSpans: [
|
|
18371
|
+
{
|
|
18372
|
+
scope: { name: "harness" },
|
|
18373
|
+
spans: spans.map((s) => {
|
|
18374
|
+
const span = {
|
|
18375
|
+
traceId: s.traceId,
|
|
18376
|
+
spanId: s.spanId,
|
|
18377
|
+
name: s.name,
|
|
18378
|
+
kind: s.kind,
|
|
18379
|
+
startTimeUnixNano: toUnixNanoString(s.startTimeNs),
|
|
18380
|
+
endTimeUnixNano: toUnixNanoString(s.endTimeNs),
|
|
18381
|
+
attributes: attributesToOTLP(s.attributes)
|
|
18382
|
+
};
|
|
18383
|
+
if (s.parentSpanId !== void 0) span["parentSpanId"] = s.parentSpanId;
|
|
18384
|
+
if (s.statusCode !== void 0) span["status"] = { code: s.statusCode };
|
|
18385
|
+
return span;
|
|
18386
|
+
})
|
|
18387
|
+
}
|
|
18388
|
+
]
|
|
18389
|
+
}
|
|
18390
|
+
]
|
|
18391
|
+
};
|
|
18392
|
+
}
|
|
18393
|
+
};
|
|
18394
|
+
|
|
18395
|
+
// src/telemetry/exporter/types.ts
|
|
18396
|
+
var SpanKind = /* @__PURE__ */ ((SpanKind2) => {
|
|
18397
|
+
SpanKind2[SpanKind2["INTERNAL"] = 1] = "INTERNAL";
|
|
18398
|
+
SpanKind2[SpanKind2["SERVER"] = 2] = "SERVER";
|
|
18399
|
+
SpanKind2[SpanKind2["CLIENT"] = 3] = "CLIENT";
|
|
18400
|
+
SpanKind2[SpanKind2["PRODUCER"] = 4] = "PRODUCER";
|
|
18401
|
+
SpanKind2[SpanKind2["CONSUMER"] = 5] = "CONSUMER";
|
|
18402
|
+
return SpanKind2;
|
|
18403
|
+
})(SpanKind || {});
|
|
18404
|
+
|
|
18181
18405
|
// src/locks/compound-lock.ts
|
|
18182
18406
|
import * as fs43 from "fs";
|
|
18183
18407
|
import * as path43 from "path";
|
|
@@ -18935,6 +19159,7 @@ export {
|
|
|
18935
19159
|
CINotifier,
|
|
18936
19160
|
COMPLIANCE_DESCRIPTOR,
|
|
18937
19161
|
CORROBORATED_AGREEMENT,
|
|
19162
|
+
CacheMetricsRecorder,
|
|
18938
19163
|
CategoryBaselineSchema,
|
|
18939
19164
|
CategoryForecastSchema,
|
|
18940
19165
|
CategoryRegressionSchema,
|
|
@@ -18999,6 +19224,7 @@ export {
|
|
|
18999
19224
|
NoOpExecutor,
|
|
19000
19225
|
NoOpSink,
|
|
19001
19226
|
NoOpTelemetryAdapter,
|
|
19227
|
+
OTLPExporter,
|
|
19002
19228
|
OpenAICacheAdapter,
|
|
19003
19229
|
PII_FIELD_DENYLIST,
|
|
19004
19230
|
PII_LINE_RE,
|
|
@@ -19037,6 +19263,7 @@ export {
|
|
|
19037
19263
|
SharableSecurityRulesSchema,
|
|
19038
19264
|
SkillEventSchema,
|
|
19039
19265
|
SolutionDocFrontmatterSchema,
|
|
19266
|
+
SpanKind,
|
|
19040
19267
|
SpecImpactEstimateSchema,
|
|
19041
19268
|
SpecImpactEstimator,
|
|
19042
19269
|
SpecImpactSignalsSchema,
|
|
@@ -19205,6 +19432,7 @@ export {
|
|
|
19205
19432
|
goRules,
|
|
19206
19433
|
injectionRules,
|
|
19207
19434
|
insecureDefaultsRules,
|
|
19435
|
+
invalidateCheckState,
|
|
19208
19436
|
isBadPort,
|
|
19209
19437
|
isDuplicateFinding,
|
|
19210
19438
|
isRegression,
|