@stackbone/sdk 0.1.0-alpha.2 → 0.1.0-alpha.4

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.
@@ -0,0 +1,610 @@
1
+ 'use strict';
2
+
3
+ var promises = require('fs/promises');
4
+ var os = require('os');
5
+ var path = require('path');
6
+ var async_hooks = require('async_hooks');
7
+ var crypto = require('crypto');
8
+
9
+ var __defProp = Object.defineProperty;
10
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
11
+
12
+ // package.json
13
+ var version = "0.1.0-alpha.2";
14
+ var storage = new async_hooks.AsyncLocalStorage();
15
+ function getInvocationContext() {
16
+ return storage.getStore();
17
+ }
18
+ __name(getInvocationContext, "getInvocationContext");
19
+
20
+ // src/observability/logger.ts
21
+ var LOG_LEVELS = {
22
+ trace: 10,
23
+ debug: 20,
24
+ info: 30,
25
+ warn: 40,
26
+ error: 50,
27
+ fatal: 60
28
+ };
29
+ var DEFAULT_BATCH_SIZE = 50;
30
+ var DEFAULT_INTERVAL_MS = 1e3;
31
+ var DEFAULT_HTTP_TIMEOUT_MS = 5e3;
32
+ function createPlatformLogger(options) {
33
+ const otelEndpoint = options.otelEndpoint ?? void 0;
34
+ const mode = options.mode ?? (otelEndpoint ? "cloud" : "local");
35
+ const baseBindings = {
36
+ run_id: options.runId
37
+ };
38
+ if (options.installationId !== void 0) baseBindings["installation_id"] = options.installationId;
39
+ if (options.agentId !== void 0) baseBindings["agent_id"] = options.agentId;
40
+ const destination = mode === "cloud" ? new OtlpHttpDestination({
41
+ endpoint: otelEndpoint ?? "",
42
+ resourceAttributes: options.resourceAttributes ?? {},
43
+ httpTimeoutMs: options.httpTimeoutMs ?? DEFAULT_HTTP_TIMEOUT_MS,
44
+ flushBatchSize: options.flushBatchSize ?? DEFAULT_BATCH_SIZE,
45
+ flushIntervalMs: options.flushIntervalMs ?? DEFAULT_INTERVAL_MS,
46
+ ...options.fetchImpl !== void 0 ? {
47
+ fetchImpl: options.fetchImpl
48
+ } : {}
49
+ }) : new JsonlFileDestination({
50
+ runId: options.runId,
51
+ runsDir: options.runsDir ?? defaultRunsDir(),
52
+ ...options.appendFileImpl !== void 0 ? {
53
+ appendFileImpl: options.appendFileImpl
54
+ } : {},
55
+ ...options.mkdirImpl !== void 0 ? {
56
+ mkdirImpl: options.mkdirImpl
57
+ } : {}
58
+ });
59
+ const now = options.now ?? Date.now;
60
+ return buildLogger({
61
+ destination,
62
+ baseBindings,
63
+ mode,
64
+ now
65
+ });
66
+ }
67
+ __name(createPlatformLogger, "createPlatformLogger");
68
+ function buildLogger(opts) {
69
+ const writeAt = /* @__PURE__ */ __name((level, msgOrObj, msg) => {
70
+ const time = opts.now();
71
+ const levelNumber = LOG_LEVELS[level];
72
+ const record = {
73
+ level: levelNumber,
74
+ time,
75
+ msg: "",
76
+ run_id: String(opts.baseBindings["run_id"] ?? ""),
77
+ ...opts.baseBindings
78
+ };
79
+ if (typeof msgOrObj === "string") {
80
+ record.msg = msgOrObj;
81
+ } else if (msgOrObj && typeof msgOrObj === "object") {
82
+ Object.assign(record, msgOrObj);
83
+ if (typeof msg === "string") record.msg = msg;
84
+ else if (typeof record.msg !== "string") record.msg = "";
85
+ }
86
+ const ctx = getInvocationContext();
87
+ if (ctx) {
88
+ if (record.trace_id === void 0) record.trace_id = ctx.invocationId;
89
+ if (!record.run_id) record.run_id = ctx.runId;
90
+ if (record["handler"] === void 0 && ctx.handler !== void 0) {
91
+ record["handler"] = ctx.handler;
92
+ }
93
+ }
94
+ opts.destination.write(record);
95
+ }, "writeAt");
96
+ const child = /* @__PURE__ */ __name((bindings) => buildLogger({
97
+ ...opts,
98
+ baseBindings: {
99
+ ...opts.baseBindings,
100
+ ...bindings
101
+ }
102
+ }), "child");
103
+ return {
104
+ mode: opts.mode,
105
+ trace: /* @__PURE__ */ __name((msgOrObj, msg) => writeAt("trace", msgOrObj, msg), "trace"),
106
+ debug: /* @__PURE__ */ __name((msgOrObj, msg) => writeAt("debug", msgOrObj, msg), "debug"),
107
+ info: /* @__PURE__ */ __name((msgOrObj, msg) => writeAt("info", msgOrObj, msg), "info"),
108
+ warn: /* @__PURE__ */ __name((msgOrObj, msg) => writeAt("warn", msgOrObj, msg), "warn"),
109
+ error: /* @__PURE__ */ __name((msgOrObj, msg) => writeAt("error", msgOrObj, msg), "error"),
110
+ fatal: /* @__PURE__ */ __name((msgOrObj, msg) => writeAt("fatal", msgOrObj, msg), "fatal"),
111
+ child,
112
+ flush: /* @__PURE__ */ __name(() => opts.destination.flush(), "flush"),
113
+ close: /* @__PURE__ */ __name(() => opts.destination.close(), "close")
114
+ };
115
+ }
116
+ __name(buildLogger, "buildLogger");
117
+ var JsonlFileDestination = class JsonlFileDestination2 {
118
+ static {
119
+ __name(this, "JsonlFileDestination");
120
+ }
121
+ path;
122
+ appendFile;
123
+ mkdir;
124
+ inFlight = Promise.resolve();
125
+ dirReady = false;
126
+ constructor(opts) {
127
+ this.path = path.resolve(opts.runsDir, `${opts.runId}.jsonl`);
128
+ this.appendFile = opts.appendFileImpl ?? ((p, d) => promises.appendFile(p, d, "utf8"));
129
+ this.mkdir = opts.mkdirImpl ?? (async (p) => {
130
+ await promises.mkdir(p, {
131
+ recursive: true
132
+ });
133
+ });
134
+ }
135
+ write(record) {
136
+ const line = `${JSON.stringify(record)}
137
+ `;
138
+ this.inFlight = this.inFlight.then(async () => {
139
+ if (!this.dirReady) {
140
+ await this.mkdir(path.dirname(this.path));
141
+ this.dirReady = true;
142
+ }
143
+ try {
144
+ await this.appendFile(this.path, line);
145
+ } catch (error) {
146
+ process.stderr.write(`[stackbone/sdk] PlatformLogger JSONL write failed: ${error instanceof Error ? error.message : String(error)}
147
+ `);
148
+ }
149
+ });
150
+ }
151
+ async flush() {
152
+ await this.inFlight;
153
+ }
154
+ async close() {
155
+ await this.flush();
156
+ }
157
+ };
158
+ var OtlpHttpDestination = class OtlpHttpDestination2 {
159
+ static {
160
+ __name(this, "OtlpHttpDestination");
161
+ }
162
+ url;
163
+ resourceAttributes;
164
+ httpTimeoutMs;
165
+ flushBatchSize;
166
+ flushIntervalMs;
167
+ fetchImpl;
168
+ buffer = [];
169
+ timer = null;
170
+ inFlight = Promise.resolve();
171
+ closed = false;
172
+ constructor(opts) {
173
+ this.url = `${opts.endpoint.replace(/\/+$/, "")}/v1/logs`;
174
+ this.resourceAttributes = opts.resourceAttributes;
175
+ this.httpTimeoutMs = opts.httpTimeoutMs;
176
+ this.flushBatchSize = opts.flushBatchSize;
177
+ this.flushIntervalMs = opts.flushIntervalMs;
178
+ this.fetchImpl = opts.fetchImpl ?? fetch;
179
+ }
180
+ write(record) {
181
+ if (this.closed) return;
182
+ this.buffer.push(record);
183
+ if (this.buffer.length >= this.flushBatchSize) {
184
+ void this.flush();
185
+ } else {
186
+ this.armTimer();
187
+ }
188
+ }
189
+ async flush() {
190
+ await this.inFlight;
191
+ if (this.buffer.length === 0) return;
192
+ const drained = this.buffer.splice(0, this.buffer.length);
193
+ this.disarmTimer();
194
+ this.inFlight = this.post(drained).catch((error) => {
195
+ process.stderr.write(`[stackbone/sdk] PlatformLogger OTLP POST failed: ${error instanceof Error ? error.message : String(error)}
196
+ `);
197
+ });
198
+ await this.inFlight;
199
+ }
200
+ async close() {
201
+ this.closed = true;
202
+ await this.flush();
203
+ }
204
+ armTimer() {
205
+ if (this.timer) return;
206
+ this.timer = setTimeout(() => {
207
+ this.timer = null;
208
+ void this.flush();
209
+ }, this.flushIntervalMs);
210
+ this.timer.unref?.();
211
+ }
212
+ disarmTimer() {
213
+ if (this.timer) {
214
+ clearTimeout(this.timer);
215
+ this.timer = null;
216
+ }
217
+ }
218
+ async post(records) {
219
+ const body = JSON.stringify(buildOtlpLogsBody(records, this.resourceAttributes));
220
+ const controller = new AbortController();
221
+ const timeout = setTimeout(() => controller.abort(), this.httpTimeoutMs);
222
+ try {
223
+ const resp = await this.fetchImpl(this.url, {
224
+ method: "POST",
225
+ headers: {
226
+ "content-type": "application/json"
227
+ },
228
+ body,
229
+ signal: controller.signal
230
+ });
231
+ if (!resp.ok) {
232
+ const text = await resp.text().catch(() => "");
233
+ throw new Error(`HTTP ${resp.status}${text ? `: ${text.slice(0, 200)}` : ""}`);
234
+ }
235
+ } finally {
236
+ clearTimeout(timeout);
237
+ }
238
+ }
239
+ };
240
+ var SDK_SCOPE = {
241
+ name: "@stackbone/sdk",
242
+ version
243
+ };
244
+ function buildOtlpLogsBody(records, resourceAttributes) {
245
+ return {
246
+ resourceLogs: [
247
+ {
248
+ resource: {
249
+ attributes: toOtlpAttributes(resourceAttributes)
250
+ },
251
+ scopeLogs: [
252
+ {
253
+ scope: SDK_SCOPE,
254
+ logRecords: records.map(toOtlpLogRecord)
255
+ }
256
+ ]
257
+ }
258
+ ]
259
+ };
260
+ }
261
+ __name(buildOtlpLogsBody, "buildOtlpLogsBody");
262
+ function toOtlpLogRecord(record) {
263
+ const { level, time, msg, ...rest } = record;
264
+ return {
265
+ timeUnixNano: `${time * 1e6}`,
266
+ severityNumber: level,
267
+ severityText: severityText(level),
268
+ body: {
269
+ stringValue: msg
270
+ },
271
+ attributes: toOtlpAttributesFromUnknown(rest)
272
+ };
273
+ }
274
+ __name(toOtlpLogRecord, "toOtlpLogRecord");
275
+ var SEVERITY_TEXT_BY_LEVEL = Object.fromEntries(Object.entries(LOG_LEVELS).map(([name, value]) => [
276
+ value,
277
+ name.toUpperCase()
278
+ ]));
279
+ function severityText(level) {
280
+ return SEVERITY_TEXT_BY_LEVEL[level] ?? "INFO";
281
+ }
282
+ __name(severityText, "severityText");
283
+ function toOtlpAttributes(attrs) {
284
+ return Object.entries(attrs).map(([key, value]) => ({
285
+ key,
286
+ value: {
287
+ stringValue: value
288
+ }
289
+ }));
290
+ }
291
+ __name(toOtlpAttributes, "toOtlpAttributes");
292
+ function toOtlpAttributesFromUnknown(attrs) {
293
+ const out = [];
294
+ for (const [key, value] of Object.entries(attrs)) {
295
+ if (value === null || value === void 0) continue;
296
+ if (typeof value === "string") {
297
+ out.push({
298
+ key,
299
+ value: {
300
+ stringValue: value
301
+ }
302
+ });
303
+ } else if (typeof value === "number") {
304
+ if (Number.isInteger(value)) out.push({
305
+ key,
306
+ value: {
307
+ intValue: String(value)
308
+ }
309
+ });
310
+ else out.push({
311
+ key,
312
+ value: {
313
+ doubleValue: value
314
+ }
315
+ });
316
+ } else if (typeof value === "boolean") {
317
+ out.push({
318
+ key,
319
+ value: {
320
+ boolValue: value
321
+ }
322
+ });
323
+ } else {
324
+ out.push({
325
+ key,
326
+ value: {
327
+ stringValue: JSON.stringify(value)
328
+ }
329
+ });
330
+ }
331
+ }
332
+ return out;
333
+ }
334
+ __name(toOtlpAttributesFromUnknown, "toOtlpAttributesFromUnknown");
335
+ function defaultRunsDir() {
336
+ return path.resolve(os.homedir(), ".stackbone", "dev", "runs");
337
+ }
338
+ __name(defaultRunsDir, "defaultRunsDir");
339
+ var RUN_STEP_TYPES = [
340
+ "agent",
341
+ "llm_call",
342
+ "db_query",
343
+ "http_fetch",
344
+ "queue_publish",
345
+ "hitl_pause",
346
+ "tool_call",
347
+ "rag_query",
348
+ "storage_op"
349
+ ];
350
+ var STEP_TYPE_ATTRIBUTE = "stackbone.step.type";
351
+ var RUN_ID_ATTRIBUTE = "stackbone.run.id";
352
+ var NAME_PREFIX_MAP = [
353
+ [
354
+ /^stackbone\.ai\./,
355
+ "llm_call"
356
+ ],
357
+ [
358
+ /^stackbone\.db\./,
359
+ "db_query"
360
+ ],
361
+ [
362
+ /^stackbone\.http\./,
363
+ "http_fetch"
364
+ ],
365
+ [
366
+ /^stackbone\.queues\./,
367
+ "queue_publish"
368
+ ],
369
+ [
370
+ /^stackbone\.approval\./,
371
+ "hitl_pause"
372
+ ],
373
+ [
374
+ /^stackbone\.tool\./,
375
+ "tool_call"
376
+ ],
377
+ [
378
+ /^stackbone\.rag\./,
379
+ "rag_query"
380
+ ],
381
+ [
382
+ /^stackbone\.storage\./,
383
+ "storage_op"
384
+ ]
385
+ ];
386
+ var DEFAULT_BATCH_SIZE2 = 50;
387
+ var DEFAULT_INTERVAL_MS2 = 500;
388
+ var RunStepsSpanProcessor = class {
389
+ static {
390
+ __name(this, "RunStepsSpanProcessor");
391
+ }
392
+ buffer = [];
393
+ stepIdBySpanId = /* @__PURE__ */ new Map();
394
+ batchSize;
395
+ intervalMs;
396
+ resolveStepType;
397
+ newId;
398
+ connectionString;
399
+ ownsSql;
400
+ sql;
401
+ timer = null;
402
+ inFlight = null;
403
+ constructor(options = {}) {
404
+ this.batchSize = options.flushBatchSize ?? DEFAULT_BATCH_SIZE2;
405
+ this.intervalMs = options.flushIntervalMs ?? DEFAULT_INTERVAL_MS2;
406
+ this.resolveStepType = options.resolveStepType ?? defaultStepTypeResolver;
407
+ this.newId = options.newId ?? crypto.randomUUID;
408
+ this.connectionString = options.connectionString;
409
+ if (options.sql) {
410
+ this.sql = options.sql;
411
+ this.ownsSql = false;
412
+ } else {
413
+ this.sql = null;
414
+ this.ownsSql = true;
415
+ }
416
+ }
417
+ // OTel calls onStart for the parent before any child, and onEnd for
418
+ // children before the parent. Reserving the UUID up-front lets the child's
419
+ // onEnd resolve `parent_step_id` synchronously — the alternative (looking
420
+ // up the parent at write time) would race the buffer flush.
421
+ onStart(span) {
422
+ const spanId = span.spanContext().spanId;
423
+ if (!this.stepIdBySpanId.has(spanId)) {
424
+ this.stepIdBySpanId.set(spanId, this.newId());
425
+ }
426
+ }
427
+ onEnd(span) {
428
+ const row = this.buildRow(span);
429
+ if (!row) return;
430
+ this.buffer.push(row);
431
+ if (this.buffer.length >= this.batchSize) {
432
+ void this.flush();
433
+ } else {
434
+ this.armTimer();
435
+ }
436
+ }
437
+ async forceFlush() {
438
+ await this.flush();
439
+ }
440
+ async shutdown() {
441
+ this.disarmTimer();
442
+ await this.flush();
443
+ if (this.ownsSql && this.sql?.end) {
444
+ await this.sql.end().catch(() => void 0);
445
+ }
446
+ }
447
+ buildRow(span) {
448
+ const spanId = span.spanContext().spanId;
449
+ const stepId = this.stepIdBySpanId.get(spanId) ?? this.newId();
450
+ this.stepIdBySpanId.delete(spanId);
451
+ const runId = readString(span.attributes[RUN_ID_ATTRIBUTE]);
452
+ if (!runId) return null;
453
+ const parentSpanId = span.parentSpanContext?.spanId ?? span.parentSpanId ?? null;
454
+ const parentStepId = parentSpanId ? this.stepIdBySpanId.get(parentSpanId) ?? null : null;
455
+ const startedAt = formatHrTime(span.startTime);
456
+ const finishedAt = formatHrTime(span.endTime ?? span.startTime);
457
+ const durationMs = hrTimeToMs(span.duration ?? diffHrTime(span.endTime, span.startTime));
458
+ const isError = span.status?.code === 2;
459
+ return {
460
+ id: stepId,
461
+ run_id: runId,
462
+ parent_step_id: parentStepId,
463
+ type: this.resolveStepType(span),
464
+ name: span.name,
465
+ status: isError ? "error" : "ok",
466
+ payload: serialisablePayload(span.attributes),
467
+ error: isError ? {
468
+ message: span.status?.message ?? "span ended with ERROR status"
469
+ } : null,
470
+ started_at: startedAt,
471
+ finished_at: finishedAt,
472
+ duration_ms: durationMs
473
+ };
474
+ }
475
+ armTimer() {
476
+ if (this.timer) return;
477
+ this.timer = setTimeout(() => {
478
+ this.timer = null;
479
+ void this.flush();
480
+ }, this.intervalMs);
481
+ this.timer.unref?.();
482
+ }
483
+ disarmTimer() {
484
+ if (this.timer) {
485
+ clearTimeout(this.timer);
486
+ this.timer = null;
487
+ }
488
+ }
489
+ async flush() {
490
+ if (this.inFlight) {
491
+ await this.inFlight;
492
+ }
493
+ if (this.buffer.length === 0) return;
494
+ const drained = this.buffer.splice(0, this.buffer.length);
495
+ this.disarmTimer();
496
+ this.inFlight = this.writeBatch(drained).finally(() => {
497
+ this.inFlight = null;
498
+ });
499
+ await this.inFlight;
500
+ }
501
+ async ensureSql() {
502
+ if (this.sql) return this.sql;
503
+ if (!this.connectionString) return null;
504
+ const mod = await import('postgres');
505
+ this.sql = mod.default(this.connectionString);
506
+ return this.sql;
507
+ }
508
+ async writeBatch(rows) {
509
+ try {
510
+ const sql = await this.ensureSql();
511
+ if (!sql) {
512
+ process.stderr.write("[stackbone/sdk] RunStepsSpanProcessor dropped batch: no DATABASE_URL configured.\n");
513
+ return;
514
+ }
515
+ const placeholders = [];
516
+ const params = [];
517
+ for (const row of rows) {
518
+ const base = params.length;
519
+ placeholders.push(`($${base + 1}::uuid, $${base + 2}::uuid, $${base + 3}::uuid, $${base + 4}, $${base + 5}, $${base + 6}, $${base + 7}::jsonb, $${base + 8}::jsonb, $${base + 9}::timestamptz, $${base + 10}::timestamptz, $${base + 11}::int)`);
520
+ params.push(row.id, row.run_id, row.parent_step_id, row.type, row.name, row.status, JSON.stringify(row.payload), row.error ? JSON.stringify(row.error) : null, row.started_at, row.finished_at, row.duration_ms);
521
+ }
522
+ const text = `INSERT INTO stackbone_platform.run_steps (
523
+ id, run_id, parent_step_id, type, name, status, payload, error,
524
+ started_at, finished_at, duration_ms
525
+ ) VALUES ${placeholders.join(", ")}`;
526
+ await sql.unsafe(text, params);
527
+ } catch (error) {
528
+ process.stderr.write(`[stackbone/sdk] RunStepsSpanProcessor write failed: ${error instanceof Error ? error.message : String(error)}
529
+ `);
530
+ }
531
+ }
532
+ };
533
+ var defaultStepTypeResolver = /* @__PURE__ */ __name((span) => {
534
+ const explicit = readString(span.attributes[STEP_TYPE_ATTRIBUTE]);
535
+ if (explicit && RUN_STEP_TYPES.includes(explicit)) {
536
+ return explicit;
537
+ }
538
+ for (const [pattern, type] of NAME_PREFIX_MAP) {
539
+ if (pattern.test(span.name)) return type;
540
+ }
541
+ return "agent";
542
+ }, "defaultStepTypeResolver");
543
+ var readString = /* @__PURE__ */ __name((value) => typeof value === "string" && value.length > 0 ? value : void 0, "readString");
544
+ var formatHrTime = /* @__PURE__ */ __name((hr) => new Date(hr ? hrTimeToMs(hr) : Date.now()).toISOString(), "formatHrTime");
545
+ var hrTimeToMs = /* @__PURE__ */ __name((hr) => {
546
+ if (!hr) return 0;
547
+ const [seconds, nanos] = hr;
548
+ return seconds * 1e3 + Math.floor(nanos / 1e6);
549
+ }, "hrTimeToMs");
550
+ var diffHrTime = /* @__PURE__ */ __name((end, start) => {
551
+ if (!end || !start) return void 0;
552
+ const seconds = end[0] - start[0];
553
+ const nanos = end[1] - start[1];
554
+ return [
555
+ seconds,
556
+ nanos
557
+ ];
558
+ }, "diffHrTime");
559
+ var serialisablePayload = /* @__PURE__ */ __name((attributes) => {
560
+ const out = {};
561
+ for (const [key, value] of Object.entries(attributes)) {
562
+ if (key === RUN_ID_ATTRIBUTE) continue;
563
+ if (key === STEP_TYPE_ATTRIBUTE) continue;
564
+ out[key] = value;
565
+ }
566
+ return out;
567
+ }, "serialisablePayload");
568
+
569
+ // src/observability/aggregate-run-cost.ts
570
+ var SQL = `WITH llm_costs AS (
571
+ SELECT COALESCE(SUM((payload->>'cost_usd')::numeric), 0) AS total
572
+ FROM stackbone_platform.run_steps
573
+ WHERE run_id = $1::uuid
574
+ AND type = 'llm_call'
575
+ )
576
+ UPDATE stackbone_platform.runs
577
+ SET cost_estimated_usd = (SELECT total FROM llm_costs)
578
+ WHERE id = $1::uuid
579
+ RETURNING cost_estimated_usd`;
580
+ async function aggregateRunCost(runId, options) {
581
+ const result = await options.sql.unsafe(SQL, [
582
+ runId
583
+ ]);
584
+ const row = result[0];
585
+ if (!row) {
586
+ return {
587
+ costEstimatedUsd: 0
588
+ };
589
+ }
590
+ const raw = row.cost_estimated_usd;
591
+ if (raw === null || raw === void 0) return {
592
+ costEstimatedUsd: 0
593
+ };
594
+ const num = typeof raw === "string" ? Number(raw) : raw;
595
+ return {
596
+ costEstimatedUsd: Number.isFinite(num) ? num : 0
597
+ };
598
+ }
599
+ __name(aggregateRunCost, "aggregateRunCost");
600
+
601
+ exports.LOG_LEVELS = LOG_LEVELS;
602
+ exports.RUN_ID_ATTRIBUTE = RUN_ID_ATTRIBUTE;
603
+ exports.RUN_STEP_TYPES = RUN_STEP_TYPES;
604
+ exports.RunStepsSpanProcessor = RunStepsSpanProcessor;
605
+ exports.STEP_TYPE_ATTRIBUTE = STEP_TYPE_ATTRIBUTE;
606
+ exports.aggregateRunCost = aggregateRunCost;
607
+ exports.createPlatformLogger = createPlatformLogger;
608
+ exports.defaultRunsDir = defaultRunsDir;
609
+ //# sourceMappingURL=index.cjs.map
610
+ //# sourceMappingURL=index.cjs.map