@replayci/replay 0.1.2 → 0.1.3
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/index.cjs +623 -35
- package/dist/index.d.cts +72 -8
- package/dist/index.d.ts +72 -8
- package/dist/index.js +629 -31
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -22,6 +22,7 @@ var CaptureBuffer = class {
|
|
|
22
22
|
apiKey;
|
|
23
23
|
endpoint;
|
|
24
24
|
diagnostics;
|
|
25
|
+
onStateChange;
|
|
25
26
|
fetchImpl;
|
|
26
27
|
now;
|
|
27
28
|
queue = [];
|
|
@@ -31,6 +32,11 @@ var CaptureBuffer = class {
|
|
|
31
32
|
circuitOpenUntil = 0;
|
|
32
33
|
remoteDisabled = false;
|
|
33
34
|
closed = false;
|
|
35
|
+
droppedOverflowTotal = 0;
|
|
36
|
+
lastFlushAttemptMs = 0;
|
|
37
|
+
lastFlushSuccessMs = 0;
|
|
38
|
+
lastFlushErrorMs = 0;
|
|
39
|
+
lastFlushErrorMsg = null;
|
|
34
40
|
constructor(opts) {
|
|
35
41
|
this.apiKey = opts.apiKey;
|
|
36
42
|
this.endpoint = normalizeEndpoint(opts.endpoint);
|
|
@@ -41,6 +47,7 @@ var CaptureBuffer = class {
|
|
|
41
47
|
MAX_SEND_TIMEOUT
|
|
42
48
|
);
|
|
43
49
|
this.diagnostics = opts.diagnostics;
|
|
50
|
+
this.onStateChange = opts.onStateChange;
|
|
44
51
|
this.fetchImpl = opts.fetchImpl ?? fetch;
|
|
45
52
|
this.now = opts.now ?? Date.now;
|
|
46
53
|
this.scheduleNextDrain();
|
|
@@ -57,6 +64,21 @@ var CaptureBuffer = class {
|
|
|
57
64
|
get isRemoteDisabled() {
|
|
58
65
|
return this.remoteDisabled;
|
|
59
66
|
}
|
|
67
|
+
get droppedOverflow() {
|
|
68
|
+
return this.droppedOverflowTotal;
|
|
69
|
+
}
|
|
70
|
+
get lastFlushAttemptAt() {
|
|
71
|
+
return this.lastFlushAttemptMs;
|
|
72
|
+
}
|
|
73
|
+
get lastFlushSuccessAt() {
|
|
74
|
+
return this.lastFlushSuccessMs;
|
|
75
|
+
}
|
|
76
|
+
get lastFlushErrorAt() {
|
|
77
|
+
return this.lastFlushErrorMs;
|
|
78
|
+
}
|
|
79
|
+
get lastFlushError() {
|
|
80
|
+
return this.lastFlushErrorMsg;
|
|
81
|
+
}
|
|
60
82
|
push(item) {
|
|
61
83
|
if (this.closed || this.remoteDisabled) {
|
|
62
84
|
return;
|
|
@@ -66,10 +88,12 @@ var CaptureBuffer = class {
|
|
|
66
88
|
}
|
|
67
89
|
if (this.queue.length >= this.maxBuffer) {
|
|
68
90
|
this.queue.shift();
|
|
91
|
+
this.droppedOverflowTotal += 1;
|
|
69
92
|
emitDiagnostics(this.diagnostics, {
|
|
70
93
|
type: "buffer_overflow",
|
|
71
94
|
dropped: 1
|
|
72
95
|
});
|
|
96
|
+
emitStateChange(this.onStateChange, { type: "buffer_overflow", dropped: 1 });
|
|
73
97
|
}
|
|
74
98
|
this.queue.push(item);
|
|
75
99
|
}
|
|
@@ -127,11 +151,13 @@ var CaptureBuffer = class {
|
|
|
127
151
|
if (batch.length === 0) {
|
|
128
152
|
return;
|
|
129
153
|
}
|
|
154
|
+
this.lastFlushAttemptMs = this.now();
|
|
155
|
+
emitStateChange(this.onStateChange, { type: "flush_attempt" });
|
|
130
156
|
let payload = "";
|
|
131
157
|
try {
|
|
132
158
|
payload = JSON.stringify({ captures: batch });
|
|
133
159
|
} catch {
|
|
134
|
-
this.handleFailure();
|
|
160
|
+
this.handleFailure("JSON serialization failed");
|
|
135
161
|
return;
|
|
136
162
|
}
|
|
137
163
|
const controller = new AbortController();
|
|
@@ -153,22 +179,35 @@ var CaptureBuffer = class {
|
|
|
153
179
|
this.clearTimer();
|
|
154
180
|
this.failureCount = 0;
|
|
155
181
|
this.circuitOpenUntil = Number.MAX_SAFE_INTEGER;
|
|
182
|
+
emitDiagnostics(this.diagnostics, { type: "remote_disabled" });
|
|
183
|
+
emitStateChange(this.onStateChange, { type: "remote_disabled" });
|
|
156
184
|
return;
|
|
157
185
|
}
|
|
158
186
|
if (!response.ok) {
|
|
159
|
-
this.handleFailure();
|
|
187
|
+
this.handleFailure(`HTTP ${response.status}`);
|
|
160
188
|
return;
|
|
161
189
|
}
|
|
162
190
|
this.failureCount = 0;
|
|
163
191
|
this.circuitOpenUntil = 0;
|
|
164
|
-
|
|
165
|
-
this.
|
|
192
|
+
this.lastFlushSuccessMs = this.now();
|
|
193
|
+
this.lastFlushErrorMsg = null;
|
|
194
|
+
emitStateChange(this.onStateChange, { type: "flush_success", batch_size: batch.length });
|
|
195
|
+
} catch (err) {
|
|
196
|
+
this.handleFailure(err instanceof Error ? err.message : String(err));
|
|
166
197
|
} finally {
|
|
167
198
|
clearTimeout(timeout);
|
|
168
199
|
}
|
|
169
200
|
}
|
|
170
|
-
handleFailure() {
|
|
201
|
+
handleFailure(errorMsg) {
|
|
171
202
|
this.failureCount += 1;
|
|
203
|
+
const errorStr = errorMsg ?? "unknown error";
|
|
204
|
+
this.lastFlushErrorMs = this.now();
|
|
205
|
+
this.lastFlushErrorMsg = errorStr;
|
|
206
|
+
emitDiagnostics(this.diagnostics, {
|
|
207
|
+
type: "flush_error",
|
|
208
|
+
error: errorStr
|
|
209
|
+
});
|
|
210
|
+
emitStateChange(this.onStateChange, { type: "flush_error", error: errorStr });
|
|
172
211
|
if (this.failureCount >= CIRCUIT_BREAKER_FAILURE_LIMIT) {
|
|
173
212
|
this.circuitOpenUntil = this.now() + CIRCUIT_BREAKER_MS;
|
|
174
213
|
emitDiagnostics(this.diagnostics, {
|
|
@@ -176,6 +215,12 @@ var CaptureBuffer = class {
|
|
|
176
215
|
failures: this.failureCount,
|
|
177
216
|
backoffMs: CIRCUIT_BREAKER_MS
|
|
178
217
|
});
|
|
218
|
+
emitStateChange(this.onStateChange, {
|
|
219
|
+
type: "circuit_open",
|
|
220
|
+
failures: this.failureCount,
|
|
221
|
+
backoffMs: CIRCUIT_BREAKER_MS,
|
|
222
|
+
openUntil: this.circuitOpenUntil
|
|
223
|
+
});
|
|
179
224
|
try {
|
|
180
225
|
console.warn(
|
|
181
226
|
`[replayci] Capture buffer circuit breaker open after ${this.failureCount} consecutive failures. Captures will be dropped for ${CIRCUIT_BREAKER_MS / 6e4} minutes.`
|
|
@@ -255,6 +300,12 @@ function emitDiagnostics(diagnostics, event) {
|
|
|
255
300
|
} catch {
|
|
256
301
|
}
|
|
257
302
|
}
|
|
303
|
+
function emitStateChange(listener, event) {
|
|
304
|
+
try {
|
|
305
|
+
listener?.(event);
|
|
306
|
+
} catch {
|
|
307
|
+
}
|
|
308
|
+
}
|
|
258
309
|
function isRemoteDisable(response) {
|
|
259
310
|
return response.headers.get("x-replayci-disable")?.toLowerCase() === "true";
|
|
260
311
|
}
|
|
@@ -905,7 +956,8 @@ function parseNodeMajorVersion(nodeVersion) {
|
|
|
905
956
|
|
|
906
957
|
// src/captureSchema.ts
|
|
907
958
|
var CAPTURE_SCHEMA_VERSION_LEGACY = "2026-03-04";
|
|
908
|
-
var
|
|
959
|
+
var CAPTURE_SCHEMA_VERSION_V2 = "2026-03-06";
|
|
960
|
+
var CAPTURE_SCHEMA_VERSION_CURRENT = "2026-03-09";
|
|
909
961
|
function isRecord(value) {
|
|
910
962
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
911
963
|
}
|
|
@@ -1069,9 +1121,19 @@ function validateUsage(value, path) {
|
|
|
1069
1121
|
total_tokens: requireNonNegativeInt(usage.total_tokens, `${path}.total_tokens`)
|
|
1070
1122
|
};
|
|
1071
1123
|
}
|
|
1124
|
+
function optionalString(value, path) {
|
|
1125
|
+
if (value === void 0 || value === null) {
|
|
1126
|
+
return void 0;
|
|
1127
|
+
}
|
|
1128
|
+
if (typeof value !== "string") {
|
|
1129
|
+
throw new Error(`${path} must be a string when provided`);
|
|
1130
|
+
}
|
|
1131
|
+
return value;
|
|
1132
|
+
}
|
|
1072
1133
|
function parseCommonCapture(capture, index, modelId, schemaVersion) {
|
|
1073
1134
|
const toolNames = requireStringArray(capture.tool_names, `captures[${index}].tool_names`);
|
|
1074
1135
|
const primaryToolName = capture.primary_tool_name === void 0 ? toolNames[0] ?? null : nullableString(capture.primary_tool_name, `captures[${index}].primary_tool_name`);
|
|
1136
|
+
const sdkSessionId = optionalString(capture.sdk_session_id, `captures[${index}].sdk_session_id`);
|
|
1075
1137
|
return {
|
|
1076
1138
|
schema_version: schemaVersion,
|
|
1077
1139
|
agent: requireString(capture.agent, `captures[${index}].agent`),
|
|
@@ -1084,7 +1146,8 @@ function parseCommonCapture(capture, index, modelId, schemaVersion) {
|
|
|
1084
1146
|
response: validateResponse(capture.response, `captures[${index}].response`),
|
|
1085
1147
|
...capture.validation !== void 0 ? { validation: validateValidation(capture.validation, `captures[${index}].validation`) } : {},
|
|
1086
1148
|
...capture.usage !== void 0 ? { usage: validateUsage(capture.usage, `captures[${index}].usage`) } : {},
|
|
1087
|
-
latency_ms: requireNonNegativeInt(capture.latency_ms, `captures[${index}].latency_ms`)
|
|
1149
|
+
latency_ms: requireNonNegativeInt(capture.latency_ms, `captures[${index}].latency_ms`),
|
|
1150
|
+
...sdkSessionId !== void 0 ? { sdk_session_id: sdkSessionId } : {}
|
|
1088
1151
|
};
|
|
1089
1152
|
}
|
|
1090
1153
|
function parseLegacyCapturedCall(capture, index) {
|
|
@@ -1095,6 +1158,14 @@ function parseLegacyCapturedCall(capture, index) {
|
|
|
1095
1158
|
CAPTURE_SCHEMA_VERSION_LEGACY
|
|
1096
1159
|
);
|
|
1097
1160
|
}
|
|
1161
|
+
function parseV2CapturedCall(capture, index) {
|
|
1162
|
+
return parseCommonCapture(
|
|
1163
|
+
capture,
|
|
1164
|
+
index,
|
|
1165
|
+
requireString(capture.model_id, `captures[${index}].model_id`),
|
|
1166
|
+
CAPTURE_SCHEMA_VERSION_V2
|
|
1167
|
+
);
|
|
1168
|
+
}
|
|
1098
1169
|
function parseCurrentCapturedCall(capture, index) {
|
|
1099
1170
|
return parseCommonCapture(
|
|
1100
1171
|
capture,
|
|
@@ -1105,25 +1176,264 @@ function parseCurrentCapturedCall(capture, index) {
|
|
|
1105
1176
|
}
|
|
1106
1177
|
var CAPTURE_SCHEMA_PARSERS = {
|
|
1107
1178
|
[CAPTURE_SCHEMA_VERSION_LEGACY]: parseLegacyCapturedCall,
|
|
1179
|
+
[CAPTURE_SCHEMA_VERSION_V2]: parseV2CapturedCall,
|
|
1108
1180
|
[CAPTURE_SCHEMA_VERSION_CURRENT]: parseCurrentCapturedCall
|
|
1109
1181
|
};
|
|
1110
1182
|
|
|
1183
|
+
// src/healthStore.ts
|
|
1184
|
+
import { randomBytes } from "crypto";
|
|
1185
|
+
import {
|
|
1186
|
+
closeSync,
|
|
1187
|
+
existsSync,
|
|
1188
|
+
fsyncSync,
|
|
1189
|
+
mkdirSync,
|
|
1190
|
+
openSync,
|
|
1191
|
+
readFileSync,
|
|
1192
|
+
readdirSync,
|
|
1193
|
+
renameSync,
|
|
1194
|
+
unlinkSync,
|
|
1195
|
+
writeFileSync
|
|
1196
|
+
} from "fs";
|
|
1197
|
+
import { readFile } from "fs/promises";
|
|
1198
|
+
import { join } from "path";
|
|
1199
|
+
var SESSIONS_DIR = "observe-sessions";
|
|
1200
|
+
var SCHEMA_VERSION = "1.0";
|
|
1201
|
+
var JANITOR_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
1202
|
+
function generateSessionId() {
|
|
1203
|
+
return `obs_${randomBytes(12).toString("hex")}`;
|
|
1204
|
+
}
|
|
1205
|
+
function resolveStateDir(opts) {
|
|
1206
|
+
let explicit;
|
|
1207
|
+
let workDir;
|
|
1208
|
+
if (typeof opts === "string") {
|
|
1209
|
+
workDir = opts;
|
|
1210
|
+
} else if (opts != null) {
|
|
1211
|
+
explicit = opts.stateDir;
|
|
1212
|
+
workDir = opts.cwd;
|
|
1213
|
+
}
|
|
1214
|
+
if (typeof explicit === "string" && explicit.length > 0) {
|
|
1215
|
+
return explicit;
|
|
1216
|
+
}
|
|
1217
|
+
const envValue = typeof process !== "undefined" ? process.env.REPLAYCI_STATE_DIR : void 0;
|
|
1218
|
+
if (typeof envValue === "string" && envValue.length > 0) {
|
|
1219
|
+
return envValue;
|
|
1220
|
+
}
|
|
1221
|
+
const base = workDir ?? process.cwd();
|
|
1222
|
+
return join(base, ".replayci", "runtime");
|
|
1223
|
+
}
|
|
1224
|
+
function resolveSessionsDir(stateDir) {
|
|
1225
|
+
return join(stateDir, SESSIONS_DIR);
|
|
1226
|
+
}
|
|
1227
|
+
function sessionFilePath(sessionsDir, sessionId) {
|
|
1228
|
+
return join(sessionsDir, `${sessionId}.json`);
|
|
1229
|
+
}
|
|
1230
|
+
function writeHealthSnapshot(sessionsDir, sessionId, snapshot) {
|
|
1231
|
+
ensureDir(sessionsDir);
|
|
1232
|
+
const destPath = sessionFilePath(sessionsDir, sessionId);
|
|
1233
|
+
const tmpPath = join(sessionsDir, `.tmp_${sessionId}_${randomBytes(4).toString("hex")}.json`);
|
|
1234
|
+
const content = JSON.stringify(snapshot, null, 2);
|
|
1235
|
+
const fd = openSync(tmpPath, "w", 384);
|
|
1236
|
+
try {
|
|
1237
|
+
writeFileSync(fd, content);
|
|
1238
|
+
fsyncSync(fd);
|
|
1239
|
+
} finally {
|
|
1240
|
+
closeSync(fd);
|
|
1241
|
+
}
|
|
1242
|
+
renameSync(tmpPath, destPath);
|
|
1243
|
+
}
|
|
1244
|
+
function parseHealthFile(raw) {
|
|
1245
|
+
let parsed;
|
|
1246
|
+
try {
|
|
1247
|
+
parsed = JSON.parse(raw);
|
|
1248
|
+
} catch {
|
|
1249
|
+
return null;
|
|
1250
|
+
}
|
|
1251
|
+
if (!isValidHealthSnapshot(parsed)) {
|
|
1252
|
+
return null;
|
|
1253
|
+
}
|
|
1254
|
+
return parsed;
|
|
1255
|
+
}
|
|
1256
|
+
function isValidHealthSnapshot(value) {
|
|
1257
|
+
if (value === null || typeof value !== "object") {
|
|
1258
|
+
return false;
|
|
1259
|
+
}
|
|
1260
|
+
const obj = value;
|
|
1261
|
+
if (obj.schema_version !== SCHEMA_VERSION) {
|
|
1262
|
+
return false;
|
|
1263
|
+
}
|
|
1264
|
+
if (typeof obj.session_id !== "string" || obj.session_id.length === 0) {
|
|
1265
|
+
return false;
|
|
1266
|
+
}
|
|
1267
|
+
if (typeof obj.agent !== "string") {
|
|
1268
|
+
return false;
|
|
1269
|
+
}
|
|
1270
|
+
const validProviders = ["openai", "anthropic", null];
|
|
1271
|
+
if (!validProviders.includes(obj.provider)) {
|
|
1272
|
+
return false;
|
|
1273
|
+
}
|
|
1274
|
+
const validStates = ["active", "inactive", "stopped", "stale"];
|
|
1275
|
+
if (typeof obj.state !== "string" || !validStates.includes(obj.state)) {
|
|
1276
|
+
return false;
|
|
1277
|
+
}
|
|
1278
|
+
if (!isValidActivation(obj.activation)) {
|
|
1279
|
+
return false;
|
|
1280
|
+
}
|
|
1281
|
+
if (!isValidProcess(obj.process)) {
|
|
1282
|
+
return false;
|
|
1283
|
+
}
|
|
1284
|
+
if (!isValidRuntime(obj.runtime)) {
|
|
1285
|
+
return false;
|
|
1286
|
+
}
|
|
1287
|
+
if (typeof obj.last_heartbeat_at !== "string") {
|
|
1288
|
+
return false;
|
|
1289
|
+
}
|
|
1290
|
+
return true;
|
|
1291
|
+
}
|
|
1292
|
+
function isValidActivation(value) {
|
|
1293
|
+
if (value === null || typeof value !== "object") {
|
|
1294
|
+
return false;
|
|
1295
|
+
}
|
|
1296
|
+
const act = value;
|
|
1297
|
+
if (typeof act.active !== "boolean") {
|
|
1298
|
+
return false;
|
|
1299
|
+
}
|
|
1300
|
+
const validReasons = [
|
|
1301
|
+
"active",
|
|
1302
|
+
"disabled",
|
|
1303
|
+
"missing_api_key",
|
|
1304
|
+
"unsupported_client",
|
|
1305
|
+
"double_wrap",
|
|
1306
|
+
"patch_target_unwritable",
|
|
1307
|
+
"internal_error"
|
|
1308
|
+
];
|
|
1309
|
+
if (typeof act.reason_code !== "string" || !validReasons.includes(act.reason_code)) {
|
|
1310
|
+
return false;
|
|
1311
|
+
}
|
|
1312
|
+
if (typeof act.activated_at !== "string") {
|
|
1313
|
+
return false;
|
|
1314
|
+
}
|
|
1315
|
+
return true;
|
|
1316
|
+
}
|
|
1317
|
+
function isValidProcess(value) {
|
|
1318
|
+
if (value === null || typeof value !== "object") {
|
|
1319
|
+
return false;
|
|
1320
|
+
}
|
|
1321
|
+
const proc = value;
|
|
1322
|
+
if (typeof proc.cwd !== "string") {
|
|
1323
|
+
return false;
|
|
1324
|
+
}
|
|
1325
|
+
if (typeof proc.node_version !== "string") {
|
|
1326
|
+
return false;
|
|
1327
|
+
}
|
|
1328
|
+
if (typeof proc.sdk_version !== "string") {
|
|
1329
|
+
return false;
|
|
1330
|
+
}
|
|
1331
|
+
return true;
|
|
1332
|
+
}
|
|
1333
|
+
function isValidRuntime(value) {
|
|
1334
|
+
if (value === null || typeof value !== "object") {
|
|
1335
|
+
return false;
|
|
1336
|
+
}
|
|
1337
|
+
const rt = value;
|
|
1338
|
+
if (typeof rt.captures_seen !== "number") {
|
|
1339
|
+
return false;
|
|
1340
|
+
}
|
|
1341
|
+
if (typeof rt.dropped_overflow !== "number") {
|
|
1342
|
+
return false;
|
|
1343
|
+
}
|
|
1344
|
+
if (typeof rt.queue_size !== "number") {
|
|
1345
|
+
return false;
|
|
1346
|
+
}
|
|
1347
|
+
if (typeof rt.consecutive_failures !== "number") {
|
|
1348
|
+
return false;
|
|
1349
|
+
}
|
|
1350
|
+
if (typeof rt.remote_disabled !== "boolean") {
|
|
1351
|
+
return false;
|
|
1352
|
+
}
|
|
1353
|
+
return true;
|
|
1354
|
+
}
|
|
1355
|
+
function runJanitor(sessionsDir, currentSessionId, now) {
|
|
1356
|
+
try {
|
|
1357
|
+
if (!existsSync(sessionsDir)) {
|
|
1358
|
+
return;
|
|
1359
|
+
}
|
|
1360
|
+
const nowMs = now ?? Date.now();
|
|
1361
|
+
const entries = readdirSync(sessionsDir);
|
|
1362
|
+
for (const entry of entries) {
|
|
1363
|
+
if (!entry.endsWith(".json") || entry.startsWith(".tmp_")) {
|
|
1364
|
+
continue;
|
|
1365
|
+
}
|
|
1366
|
+
const sessionId = entry.replace(/\.json$/, "");
|
|
1367
|
+
if (sessionId === currentSessionId) {
|
|
1368
|
+
continue;
|
|
1369
|
+
}
|
|
1370
|
+
const filePath = join(sessionsDir, entry);
|
|
1371
|
+
let snapshot = null;
|
|
1372
|
+
try {
|
|
1373
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
1374
|
+
snapshot = parseHealthFile(raw);
|
|
1375
|
+
} catch {
|
|
1376
|
+
continue;
|
|
1377
|
+
}
|
|
1378
|
+
if (!snapshot) {
|
|
1379
|
+
continue;
|
|
1380
|
+
}
|
|
1381
|
+
const isTerminal = snapshot.state === "stopped" || snapshot.state === "stale";
|
|
1382
|
+
if (!isTerminal) {
|
|
1383
|
+
continue;
|
|
1384
|
+
}
|
|
1385
|
+
const heartbeatAge = nowMs - new Date(snapshot.last_heartbeat_at).getTime();
|
|
1386
|
+
if (heartbeatAge < JANITOR_TTL_MS) {
|
|
1387
|
+
continue;
|
|
1388
|
+
}
|
|
1389
|
+
try {
|
|
1390
|
+
unlinkSync(filePath);
|
|
1391
|
+
} catch {
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
} catch {
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
function getSdkVersion() {
|
|
1398
|
+
try {
|
|
1399
|
+
const url = new URL("../package.json", import.meta.url);
|
|
1400
|
+
const raw = readFileSync(url, "utf-8");
|
|
1401
|
+
const pkg = JSON.parse(raw);
|
|
1402
|
+
return typeof pkg.version === "string" ? pkg.version : "0.0.0";
|
|
1403
|
+
} catch {
|
|
1404
|
+
return "0.0.0";
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
function ensureDir(dir) {
|
|
1408
|
+
try {
|
|
1409
|
+
mkdirSync(dir, { recursive: true });
|
|
1410
|
+
} catch (err) {
|
|
1411
|
+
if (err.code !== "EEXIST") {
|
|
1412
|
+
throw err;
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1111
1417
|
// src/observe.ts
|
|
1112
1418
|
var REPLAY_WRAPPED = /* @__PURE__ */ Symbol.for("replayci.wrapped");
|
|
1113
1419
|
var DEFAULT_AGENT = "default";
|
|
1420
|
+
var IDLE_HEARTBEAT_MS = 3e4;
|
|
1114
1421
|
function observe(client, opts = {}) {
|
|
1115
1422
|
assertSupportedNodeRuntime();
|
|
1423
|
+
const sessionId = generateSessionId();
|
|
1424
|
+
const agent = typeof opts.agent === "string" && opts.agent.length > 0 ? opts.agent : DEFAULT_AGENT;
|
|
1425
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1116
1426
|
try {
|
|
1117
1427
|
if (isDisabled(opts)) {
|
|
1118
|
-
return
|
|
1428
|
+
return createInactiveHandle(client, sessionId, agent, "disabled", void 0, now, opts.diagnostics, opts.stateDir);
|
|
1119
1429
|
}
|
|
1120
1430
|
const apiKey = resolveApiKey(opts);
|
|
1121
1431
|
if (!apiKey) {
|
|
1122
|
-
return
|
|
1432
|
+
return createInactiveHandle(client, sessionId, agent, "missing_api_key", void 0, now, opts.diagnostics, opts.stateDir);
|
|
1123
1433
|
}
|
|
1124
1434
|
const provider = detectProviderSafely(client, opts.diagnostics);
|
|
1125
1435
|
if (!provider) {
|
|
1126
|
-
return
|
|
1436
|
+
return createInactiveHandle(client, sessionId, agent, "unsupported_client", "Could not detect provider.", now, opts.diagnostics, opts.stateDir);
|
|
1127
1437
|
}
|
|
1128
1438
|
const patchTarget = resolvePatchTarget(client, provider);
|
|
1129
1439
|
if (!patchTarget) {
|
|
@@ -1132,14 +1442,14 @@ function observe(client, opts = {}) {
|
|
|
1132
1442
|
mode: "observe",
|
|
1133
1443
|
detail: `Unsupported ${provider} client shape.`
|
|
1134
1444
|
});
|
|
1135
|
-
return
|
|
1445
|
+
return createInactiveHandle(client, sessionId, agent, "unsupported_client", `Unsupported ${provider} client shape.`, now, opts.diagnostics, opts.stateDir);
|
|
1136
1446
|
}
|
|
1137
1447
|
if (isWrapped(client, patchTarget.target)) {
|
|
1138
1448
|
emitDiagnostic(opts.diagnostics, {
|
|
1139
1449
|
type: "double_wrap",
|
|
1140
1450
|
mode: "observe"
|
|
1141
1451
|
});
|
|
1142
|
-
return
|
|
1452
|
+
return createInactiveHandle(client, sessionId, agent, "double_wrap", void 0, now, opts.diagnostics, opts.stateDir);
|
|
1143
1453
|
}
|
|
1144
1454
|
const patchabilityError = getPatchabilityError(patchTarget.target, patchTarget.methodName);
|
|
1145
1455
|
if (patchabilityError) {
|
|
@@ -1148,19 +1458,171 @@ function observe(client, opts = {}) {
|
|
|
1148
1458
|
mode: "observe",
|
|
1149
1459
|
detail: patchabilityError
|
|
1150
1460
|
});
|
|
1151
|
-
return
|
|
1461
|
+
return createInactiveHandle(client, sessionId, agent, "patch_target_unwritable", patchabilityError, now, opts.diagnostics, opts.stateDir);
|
|
1152
1462
|
}
|
|
1153
1463
|
const captureLevel = normalizeCaptureLevel(opts.captureLevel);
|
|
1154
|
-
const
|
|
1464
|
+
const patchTargetName = `${provider}.${provider === "openai" ? "chat.completions.create" : "messages.create"}`;
|
|
1465
|
+
const runtimeState = {
|
|
1466
|
+
captures_seen: 0,
|
|
1467
|
+
dropped_overflow: 0,
|
|
1468
|
+
last_capture_at: null,
|
|
1469
|
+
last_flush_attempt_at: null,
|
|
1470
|
+
last_flush_success_at: null,
|
|
1471
|
+
last_flush_error_at: null,
|
|
1472
|
+
last_flush_error: null,
|
|
1473
|
+
queue_size: 0,
|
|
1474
|
+
consecutive_failures: 0,
|
|
1475
|
+
circuit_open_until: null,
|
|
1476
|
+
remote_disabled: false
|
|
1477
|
+
};
|
|
1478
|
+
let lastHealthStoreErrorAt = null;
|
|
1479
|
+
let lastHealthStoreError = null;
|
|
1480
|
+
const sessionsDir = resolveSessionsDir(
|
|
1481
|
+
resolveStateDir(opts.stateDir ? { stateDir: opts.stateDir } : void 0)
|
|
1482
|
+
);
|
|
1483
|
+
const buildCurrentSnapshot = () => ({
|
|
1484
|
+
schema_version: "1.0",
|
|
1485
|
+
session_id: sessionId,
|
|
1486
|
+
agent,
|
|
1487
|
+
provider,
|
|
1488
|
+
patch_target: patchTargetName,
|
|
1489
|
+
state: "active",
|
|
1490
|
+
activation: {
|
|
1491
|
+
active: true,
|
|
1492
|
+
reason_code: "active",
|
|
1493
|
+
activated_at: now
|
|
1494
|
+
},
|
|
1495
|
+
process: {
|
|
1496
|
+
pid: typeof process !== "undefined" ? process.pid : null,
|
|
1497
|
+
cwd: typeof process !== "undefined" ? process.cwd() : "",
|
|
1498
|
+
node_version: typeof process !== "undefined" ? process.versions?.node ?? "" : "",
|
|
1499
|
+
sdk_version: getSdkVersion()
|
|
1500
|
+
},
|
|
1501
|
+
runtime: { ...runtimeState },
|
|
1502
|
+
stopped_at: null,
|
|
1503
|
+
last_heartbeat_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1504
|
+
last_health_store_error_at: lastHealthStoreErrorAt,
|
|
1505
|
+
last_health_store_error: lastHealthStoreError
|
|
1506
|
+
});
|
|
1507
|
+
const persistHealth = (snapshot) => {
|
|
1508
|
+
const s = snapshot ?? buildCurrentSnapshot();
|
|
1509
|
+
try {
|
|
1510
|
+
writeHealthSnapshot(sessionsDir, sessionId, s);
|
|
1511
|
+
if (lastHealthStoreErrorAt !== null) {
|
|
1512
|
+
lastHealthStoreErrorAt = null;
|
|
1513
|
+
lastHealthStoreError = null;
|
|
1514
|
+
}
|
|
1515
|
+
} catch (err) {
|
|
1516
|
+
const detail = err instanceof Error ? err.message : "Failed to write health snapshot";
|
|
1517
|
+
lastHealthStoreErrorAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1518
|
+
lastHealthStoreError = detail;
|
|
1519
|
+
emitDiagnostic(opts.diagnostics, {
|
|
1520
|
+
type: "health_store_error",
|
|
1521
|
+
detail
|
|
1522
|
+
});
|
|
1523
|
+
}
|
|
1524
|
+
};
|
|
1525
|
+
let lastEventWriteAt = Date.now();
|
|
1526
|
+
let heartbeatTimer;
|
|
1527
|
+
const persistHealthEvent = () => {
|
|
1528
|
+
lastEventWriteAt = Date.now();
|
|
1529
|
+
persistHealth();
|
|
1530
|
+
};
|
|
1531
|
+
const startHeartbeat = () => {
|
|
1532
|
+
heartbeatTimer = setInterval(() => {
|
|
1533
|
+
const sinceLast = Date.now() - lastEventWriteAt;
|
|
1534
|
+
if (sinceLast >= IDLE_HEARTBEAT_MS) {
|
|
1535
|
+
persistHealth();
|
|
1536
|
+
}
|
|
1537
|
+
}, IDLE_HEARTBEAT_MS);
|
|
1538
|
+
unrefTimerHandle(heartbeatTimer);
|
|
1539
|
+
};
|
|
1540
|
+
const stopHeartbeat = () => {
|
|
1541
|
+
if (heartbeatTimer !== void 0) {
|
|
1542
|
+
clearInterval(heartbeatTimer);
|
|
1543
|
+
heartbeatTimer = void 0;
|
|
1544
|
+
}
|
|
1545
|
+
};
|
|
1546
|
+
const onBufferStateChange = (event) => {
|
|
1547
|
+
if (restored) return;
|
|
1548
|
+
switch (event.type) {
|
|
1549
|
+
case "flush_attempt":
|
|
1550
|
+
runtimeState.last_flush_attempt_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
1551
|
+
persistHealthEvent();
|
|
1552
|
+
break;
|
|
1553
|
+
case "flush_success":
|
|
1554
|
+
runtimeState.last_flush_success_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
1555
|
+
runtimeState.last_flush_error = null;
|
|
1556
|
+
runtimeState.consecutive_failures = 0;
|
|
1557
|
+
runtimeState.circuit_open_until = null;
|
|
1558
|
+
runtimeState.queue_size = buffer.size;
|
|
1559
|
+
emitDiagnostic(opts.diagnostics, {
|
|
1560
|
+
type: "flush_succeeded",
|
|
1561
|
+
batch_size: event.batch_size
|
|
1562
|
+
});
|
|
1563
|
+
persistHealthEvent();
|
|
1564
|
+
break;
|
|
1565
|
+
case "flush_error":
|
|
1566
|
+
runtimeState.last_flush_error_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
1567
|
+
runtimeState.last_flush_error = event.error;
|
|
1568
|
+
runtimeState.consecutive_failures = buffer.consecutiveFailures;
|
|
1569
|
+
runtimeState.queue_size = buffer.size;
|
|
1570
|
+
persistHealthEvent();
|
|
1571
|
+
break;
|
|
1572
|
+
case "buffer_overflow":
|
|
1573
|
+
runtimeState.dropped_overflow = buffer.droppedOverflow;
|
|
1574
|
+
runtimeState.queue_size = buffer.size;
|
|
1575
|
+
persistHealthEvent();
|
|
1576
|
+
break;
|
|
1577
|
+
case "circuit_open":
|
|
1578
|
+
runtimeState.circuit_open_until = new Date(event.openUntil).toISOString();
|
|
1579
|
+
runtimeState.consecutive_failures = event.failures;
|
|
1580
|
+
persistHealthEvent();
|
|
1581
|
+
break;
|
|
1582
|
+
case "remote_disabled":
|
|
1583
|
+
runtimeState.remote_disabled = true;
|
|
1584
|
+
runtimeState.queue_size = 0;
|
|
1585
|
+
persistHealthEvent();
|
|
1586
|
+
break;
|
|
1587
|
+
}
|
|
1588
|
+
};
|
|
1155
1589
|
const buffer = new CaptureBuffer({
|
|
1156
1590
|
apiKey,
|
|
1157
1591
|
endpoint: opts.endpoint,
|
|
1158
1592
|
maxBuffer: opts.maxBuffer,
|
|
1159
1593
|
flushMs: opts.flushMs,
|
|
1160
1594
|
timeoutMs: opts.timeoutMs,
|
|
1161
|
-
diagnostics: opts.diagnostics
|
|
1595
|
+
diagnostics: opts.diagnostics,
|
|
1596
|
+
onStateChange: onBufferStateChange
|
|
1162
1597
|
});
|
|
1163
1598
|
registerBeforeExit(buffer);
|
|
1599
|
+
let exitSnapshotWritten = false;
|
|
1600
|
+
const beforeExitHandler = () => {
|
|
1601
|
+
if (exitSnapshotWritten || restored) return;
|
|
1602
|
+
exitSnapshotWritten = true;
|
|
1603
|
+
try {
|
|
1604
|
+
stopHeartbeat();
|
|
1605
|
+
const stoppedSnapshot = buildCurrentSnapshot();
|
|
1606
|
+
stoppedSnapshot.state = "stopped";
|
|
1607
|
+
stoppedSnapshot.stopped_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
1608
|
+
stoppedSnapshot.last_heartbeat_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
1609
|
+
persistHealth(stoppedSnapshot);
|
|
1610
|
+
} catch {
|
|
1611
|
+
}
|
|
1612
|
+
};
|
|
1613
|
+
if (typeof process !== "undefined" && typeof process.on === "function") {
|
|
1614
|
+
process.on("beforeExit", beforeExitHandler);
|
|
1615
|
+
}
|
|
1616
|
+
persistHealthEvent();
|
|
1617
|
+
safeRunJanitor(sessionsDir, sessionId);
|
|
1618
|
+
startHeartbeat();
|
|
1619
|
+
emitDiagnostic(opts.diagnostics, {
|
|
1620
|
+
type: "observe_activated",
|
|
1621
|
+
session_id: sessionId,
|
|
1622
|
+
provider,
|
|
1623
|
+
agent,
|
|
1624
|
+
patch_target: patchTargetName
|
|
1625
|
+
});
|
|
1164
1626
|
const wrappedCreate = function observeWrappedCreate(...args) {
|
|
1165
1627
|
const requestSnapshot = snapshotRequest(args[0], captureLevel);
|
|
1166
1628
|
const startedAt = Date.now();
|
|
@@ -1173,7 +1635,11 @@ function observe(client, opts = {}) {
|
|
|
1173
1635
|
provider,
|
|
1174
1636
|
requestSnapshot,
|
|
1175
1637
|
response,
|
|
1176
|
-
startedAt
|
|
1638
|
+
startedAt,
|
|
1639
|
+
sessionId,
|
|
1640
|
+
runtimeState,
|
|
1641
|
+
persistHealthEvent,
|
|
1642
|
+
diagnostics: opts.diagnostics
|
|
1177
1643
|
});
|
|
1178
1644
|
return response;
|
|
1179
1645
|
});
|
|
@@ -1183,11 +1649,18 @@ function observe(client, opts = {}) {
|
|
|
1183
1649
|
let restored = false;
|
|
1184
1650
|
return {
|
|
1185
1651
|
client,
|
|
1652
|
+
flush() {
|
|
1653
|
+
return buffer.flush();
|
|
1654
|
+
},
|
|
1186
1655
|
restore() {
|
|
1187
1656
|
if (restored) {
|
|
1188
1657
|
return;
|
|
1189
1658
|
}
|
|
1190
1659
|
restored = true;
|
|
1660
|
+
stopHeartbeat();
|
|
1661
|
+
if (typeof process !== "undefined" && typeof process.removeListener === "function") {
|
|
1662
|
+
process.removeListener("beforeExit", beforeExitHandler);
|
|
1663
|
+
}
|
|
1191
1664
|
if (patchTarget.target[patchTarget.methodName] === wrappedCreate) {
|
|
1192
1665
|
if (patchTarget.hadOwnMethod) {
|
|
1193
1666
|
patchTarget.target[patchTarget.methodName] = patchTarget.originalCreate;
|
|
@@ -1196,11 +1669,124 @@ function observe(client, opts = {}) {
|
|
|
1196
1669
|
}
|
|
1197
1670
|
}
|
|
1198
1671
|
clearWrapped(client, patchTarget.target);
|
|
1672
|
+
const stoppedSnapshot = buildCurrentSnapshot();
|
|
1673
|
+
stoppedSnapshot.state = "stopped";
|
|
1674
|
+
stoppedSnapshot.stopped_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
1675
|
+
stoppedSnapshot.last_heartbeat_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
1676
|
+
persistHealth(stoppedSnapshot);
|
|
1199
1677
|
buffer.close();
|
|
1678
|
+
},
|
|
1679
|
+
getHealth() {
|
|
1680
|
+
if (restored) {
|
|
1681
|
+
const snapshot = buildCurrentSnapshot();
|
|
1682
|
+
snapshot.state = "stopped";
|
|
1683
|
+
snapshot.stopped_at = snapshot.stopped_at ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
1684
|
+
return snapshot;
|
|
1685
|
+
}
|
|
1686
|
+
runtimeState.queue_size = buffer.size;
|
|
1687
|
+
return buildCurrentSnapshot();
|
|
1200
1688
|
}
|
|
1201
1689
|
};
|
|
1690
|
+
} catch (err) {
|
|
1691
|
+
const detail = err instanceof Error ? err.message : "Unknown internal error";
|
|
1692
|
+
return createInactiveHandle(client, sessionId, agent, "internal_error", detail, now, opts.diagnostics, opts.stateDir);
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
function createInactiveHandle(client, sessionId, agent, reasonCode, detail, activatedAt, diagnostics, stateDir) {
|
|
1696
|
+
const healthSnapshot = buildHealthSnapshot({
|
|
1697
|
+
sessionId,
|
|
1698
|
+
agent,
|
|
1699
|
+
provider: null,
|
|
1700
|
+
patchTarget: null,
|
|
1701
|
+
state: "inactive",
|
|
1702
|
+
reasonCode,
|
|
1703
|
+
active: false,
|
|
1704
|
+
activatedAt,
|
|
1705
|
+
detail
|
|
1706
|
+
});
|
|
1707
|
+
const sessionsDir = resolveSessionsDir(
|
|
1708
|
+
resolveStateDir(stateDir ? { stateDir } : void 0)
|
|
1709
|
+
);
|
|
1710
|
+
safeWriteHealth(sessionsDir, sessionId, healthSnapshot, diagnostics);
|
|
1711
|
+
emitDiagnostic(diagnostics, {
|
|
1712
|
+
type: "observe_inactive",
|
|
1713
|
+
reason_code: reasonCode,
|
|
1714
|
+
...detail ? { detail } : {}
|
|
1715
|
+
});
|
|
1716
|
+
return {
|
|
1717
|
+
client,
|
|
1718
|
+
flush() {
|
|
1719
|
+
return Promise.resolve();
|
|
1720
|
+
},
|
|
1721
|
+
restore() {
|
|
1722
|
+
},
|
|
1723
|
+
getHealth() {
|
|
1724
|
+
return { ...healthSnapshot };
|
|
1725
|
+
}
|
|
1726
|
+
};
|
|
1727
|
+
}
|
|
1728
|
+
function unrefTimerHandle(timer) {
|
|
1729
|
+
if (typeof timer === "object" && timer !== null && "unref" in timer) {
|
|
1730
|
+
try {
|
|
1731
|
+
timer.unref?.call(timer);
|
|
1732
|
+
} catch {
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
function buildHealthSnapshot(input) {
|
|
1737
|
+
const snapshot = {
|
|
1738
|
+
schema_version: "1.0",
|
|
1739
|
+
session_id: input.sessionId,
|
|
1740
|
+
agent: input.agent,
|
|
1741
|
+
provider: input.provider,
|
|
1742
|
+
patch_target: input.patchTarget,
|
|
1743
|
+
state: input.state,
|
|
1744
|
+
activation: {
|
|
1745
|
+
active: input.active,
|
|
1746
|
+
reason_code: input.reasonCode,
|
|
1747
|
+
activated_at: input.activatedAt,
|
|
1748
|
+
...input.detail ? { detail: input.detail } : {}
|
|
1749
|
+
},
|
|
1750
|
+
process: {
|
|
1751
|
+
pid: typeof process !== "undefined" ? process.pid : null,
|
|
1752
|
+
cwd: typeof process !== "undefined" ? process.cwd() : "",
|
|
1753
|
+
node_version: typeof process !== "undefined" ? process.versions?.node ?? "" : "",
|
|
1754
|
+
sdk_version: getSdkVersion()
|
|
1755
|
+
},
|
|
1756
|
+
runtime: {
|
|
1757
|
+
captures_seen: 0,
|
|
1758
|
+
dropped_overflow: 0,
|
|
1759
|
+
last_capture_at: null,
|
|
1760
|
+
last_flush_attempt_at: null,
|
|
1761
|
+
last_flush_success_at: null,
|
|
1762
|
+
last_flush_error_at: null,
|
|
1763
|
+
last_flush_error: null,
|
|
1764
|
+
queue_size: 0,
|
|
1765
|
+
consecutive_failures: 0,
|
|
1766
|
+
circuit_open_until: null,
|
|
1767
|
+
remote_disabled: false
|
|
1768
|
+
},
|
|
1769
|
+
stopped_at: null,
|
|
1770
|
+
last_heartbeat_at: input.activatedAt,
|
|
1771
|
+
last_health_store_error_at: null,
|
|
1772
|
+
last_health_store_error: null
|
|
1773
|
+
};
|
|
1774
|
+
return snapshot;
|
|
1775
|
+
}
|
|
1776
|
+
function safeWriteHealth(sessionsDir, sessionId, snapshot, diagnostics) {
|
|
1777
|
+
try {
|
|
1778
|
+
writeHealthSnapshot(sessionsDir, sessionId, snapshot);
|
|
1779
|
+
} catch (err) {
|
|
1780
|
+
emitDiagnostic(diagnostics, {
|
|
1781
|
+
type: "health_store_error",
|
|
1782
|
+
detail: err instanceof Error ? err.message : "Failed to write health snapshot"
|
|
1783
|
+
});
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
function safeRunJanitor(sessionsDir, currentSessionId) {
|
|
1787
|
+
try {
|
|
1788
|
+
runJanitor(sessionsDir, currentSessionId);
|
|
1202
1789
|
} catch {
|
|
1203
|
-
return createNoopHandle(client);
|
|
1204
1790
|
}
|
|
1205
1791
|
}
|
|
1206
1792
|
function safelyCaptureResponse(input) {
|
|
@@ -1230,10 +1816,19 @@ function safelyCaptureResponse(input) {
|
|
|
1230
1816
|
usage: extractUsage(input.response, input.provider)
|
|
1231
1817
|
},
|
|
1232
1818
|
endedAt: Date.now(),
|
|
1233
|
-
startedAt: input.startedAt
|
|
1819
|
+
startedAt: input.startedAt,
|
|
1820
|
+
sessionId: input.sessionId
|
|
1234
1821
|
});
|
|
1235
1822
|
if (capture) {
|
|
1236
1823
|
input.buffer.push(capture);
|
|
1824
|
+
input.runtimeState.captures_seen += 1;
|
|
1825
|
+
input.runtimeState.last_capture_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
1826
|
+
input.runtimeState.queue_size = input.buffer.size;
|
|
1827
|
+
emitDiagnostic(input.diagnostics, {
|
|
1828
|
+
type: "capture_seen",
|
|
1829
|
+
session_id: input.sessionId
|
|
1830
|
+
});
|
|
1831
|
+
input.persistHealthEvent();
|
|
1237
1832
|
}
|
|
1238
1833
|
} catch {
|
|
1239
1834
|
}
|
|
@@ -1250,10 +1845,19 @@ function safelyPushStreamCapture(input) {
|
|
|
1250
1845
|
requestSnapshot: input.requestSnapshot,
|
|
1251
1846
|
responseData: input.summary,
|
|
1252
1847
|
startedAt: input.startedAt,
|
|
1253
|
-
endedAt: Date.now()
|
|
1848
|
+
endedAt: Date.now(),
|
|
1849
|
+
sessionId: input.sessionId
|
|
1254
1850
|
});
|
|
1255
1851
|
if (capture) {
|
|
1256
1852
|
input.buffer.push(capture);
|
|
1853
|
+
input.runtimeState.captures_seen += 1;
|
|
1854
|
+
input.runtimeState.last_capture_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
1855
|
+
input.runtimeState.queue_size = input.buffer.size;
|
|
1856
|
+
emitDiagnostic(input.diagnostics, {
|
|
1857
|
+
type: "capture_seen",
|
|
1858
|
+
session_id: input.sessionId
|
|
1859
|
+
});
|
|
1860
|
+
input.persistHealthEvent();
|
|
1257
1861
|
}
|
|
1258
1862
|
} catch {
|
|
1259
1863
|
}
|
|
@@ -1279,7 +1883,8 @@ function buildCapturedCall(input) {
|
|
|
1279
1883
|
...input.captureLevel === "full" && input.responseData.textBlocks && input.responseData.textBlocks.length > 0 ? { text_blocks: input.responseData.textBlocks } : {}
|
|
1280
1884
|
},
|
|
1281
1885
|
...input.responseData.usage ? { usage: input.responseData.usage } : {},
|
|
1282
|
-
latency_ms: Math.max(0, input.endedAt - input.startedAt)
|
|
1886
|
+
latency_ms: Math.max(0, input.endedAt - input.startedAt),
|
|
1887
|
+
...input.sessionId ? { sdk_session_id: input.sessionId } : {}
|
|
1283
1888
|
};
|
|
1284
1889
|
} catch {
|
|
1285
1890
|
return null;
|
|
@@ -1458,13 +2063,6 @@ function clearWrapped(client, target) {
|
|
|
1458
2063
|
} catch {
|
|
1459
2064
|
}
|
|
1460
2065
|
}
|
|
1461
|
-
function createNoopHandle(client) {
|
|
1462
|
-
return {
|
|
1463
|
-
client,
|
|
1464
|
-
restore() {
|
|
1465
|
-
}
|
|
1466
|
-
};
|
|
1467
|
-
}
|
|
1468
2066
|
function resolveApiKey(opts) {
|
|
1469
2067
|
return typeof opts.apiKey === "string" && opts.apiKey.length > 0 ? opts.apiKey : toNonEmptyString(process.env.REPLAYCI_API_KEY);
|
|
1470
2068
|
}
|
|
@@ -1517,12 +2115,12 @@ import {
|
|
|
1517
2115
|
normalizeToolArray as normalizeToolArray2
|
|
1518
2116
|
} from "@replayci/contracts-core";
|
|
1519
2117
|
import {
|
|
1520
|
-
readdirSync,
|
|
2118
|
+
readdirSync as readdirSync2,
|
|
1521
2119
|
statSync
|
|
1522
2120
|
} from "fs";
|
|
1523
2121
|
import {
|
|
1524
2122
|
extname,
|
|
1525
|
-
join,
|
|
2123
|
+
join as join2,
|
|
1526
2124
|
relative,
|
|
1527
2125
|
resolve
|
|
1528
2126
|
} from "path";
|
|
@@ -1613,8 +2211,8 @@ function collectContractFiles(inputPath) {
|
|
|
1613
2211
|
if (!stat.isDirectory()) {
|
|
1614
2212
|
return [];
|
|
1615
2213
|
}
|
|
1616
|
-
return
|
|
1617
|
-
const fullPath =
|
|
2214
|
+
return readdirSync2(inputPath, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name)).flatMap((entry) => {
|
|
2215
|
+
const fullPath = join2(inputPath, entry.name);
|
|
1618
2216
|
if (entry.isDirectory()) {
|
|
1619
2217
|
return collectContractFiles(fullPath);
|
|
1620
2218
|
}
|