@a5c-ai/agent-runtime 5.0.1-staging.04ca6ab00d21
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 +23 -0
- package/dist/apiResult.d.ts +19 -0
- package/dist/apiResult.d.ts.map +1 -0
- package/dist/apiResult.js +16 -0
- package/dist/background/state.d.ts +14 -0
- package/dist/background/state.d.ts.map +1 -0
- package/dist/background/state.js +25 -0
- package/dist/backgroundProcessRegistry.d.ts +66 -0
- package/dist/backgroundProcessRegistry.d.ts.map +1 -0
- package/dist/backgroundProcessRegistry.js +202 -0
- package/dist/cost/claudeCodeParser.d.ts +81 -0
- package/dist/cost/claudeCodeParser.d.ts.map +1 -0
- package/dist/cost/claudeCodeParser.js +232 -0
- package/dist/cost/collector.d.ts +42 -0
- package/dist/cost/collector.d.ts.map +1 -0
- package/dist/cost/collector.js +105 -0
- package/dist/cost/effectCost.d.ts +23 -0
- package/dist/cost/effectCost.d.ts.map +1 -0
- package/dist/cost/effectCost.js +26 -0
- package/dist/cost/index.d.ts +19 -0
- package/dist/cost/index.d.ts.map +1 -0
- package/dist/cost/index.js +39 -0
- package/dist/cost/journal.d.ts +40 -0
- package/dist/cost/journal.d.ts.map +1 -0
- package/dist/cost/journal.js +137 -0
- package/dist/cost/types.d.ts +164 -0
- package/dist/cost/types.d.ts.map +1 -0
- package/dist/cost/types.js +228 -0
- package/dist/daemon/automationExecutor.d.ts +16 -0
- package/dist/daemon/automationExecutor.d.ts.map +1 -0
- package/dist/daemon/automationExecutor.js +222 -0
- package/dist/daemon/config.d.ts +8 -0
- package/dist/daemon/config.d.ts.map +1 -0
- package/dist/daemon/config.js +209 -0
- package/dist/daemon/daemonLog.d.ts +13 -0
- package/dist/daemon/daemonLog.d.ts.map +1 -0
- package/dist/daemon/daemonLog.js +64 -0
- package/dist/daemon/fileWatcher.d.ts +9 -0
- package/dist/daemon/fileWatcher.d.ts.map +1 -0
- package/dist/daemon/fileWatcher.js +141 -0
- package/dist/daemon/index.d.ts +13 -0
- package/dist/daemon/index.d.ts.map +1 -0
- package/dist/daemon/index.js +22 -0
- package/dist/daemon/lifecycle.d.ts +12 -0
- package/dist/daemon/lifecycle.d.ts.map +1 -0
- package/dist/daemon/lifecycle.js +257 -0
- package/dist/daemon/loop.d.ts +21 -0
- package/dist/daemon/loop.d.ts.map +1 -0
- package/dist/daemon/loop.js +196 -0
- package/dist/daemon/timerScheduler.d.ts +13 -0
- package/dist/daemon/timerScheduler.d.ts.map +1 -0
- package/dist/daemon/timerScheduler.js +122 -0
- package/dist/daemon/types.d.ts +93 -0
- package/dist/daemon/types.d.ts.map +1 -0
- package/dist/daemon/types.js +25 -0
- package/dist/daemon/webhookListener.d.ts +6 -0
- package/dist/daemon/webhookListener.d.ts.map +1 -0
- package/dist/daemon/webhookListener.js +110 -0
- package/dist/execution/index.d.ts +8 -0
- package/dist/execution/index.d.ts.map +1 -0
- package/dist/execution/index.js +12 -0
- package/dist/execution/modes/docker.d.ts +21 -0
- package/dist/execution/modes/docker.d.ts.map +1 -0
- package/dist/execution/modes/docker.js +125 -0
- package/dist/execution/modes/index.d.ts +10 -0
- package/dist/execution/modes/index.d.ts.map +1 -0
- package/dist/execution/modes/index.js +14 -0
- package/dist/execution/modes/kubernetes.d.ts +29 -0
- package/dist/execution/modes/kubernetes.d.ts.map +1 -0
- package/dist/execution/modes/kubernetes.js +121 -0
- package/dist/execution/modes/local.d.ts +23 -0
- package/dist/execution/modes/local.d.ts.map +1 -0
- package/dist/execution/modes/local.js +102 -0
- package/dist/execution/modes/ssh.d.ts +23 -0
- package/dist/execution/modes/ssh.d.ts.map +1 -0
- package/dist/execution/modes/ssh.js +134 -0
- package/dist/execution/provider.d.ts +32 -0
- package/dist/execution/provider.d.ts.map +1 -0
- package/dist/execution/provider.js +90 -0
- package/dist/execution/types.d.ts +105 -0
- package/dist/execution/types.d.ts.map +1 -0
- package/dist/execution/types.js +9 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +42 -0
- package/dist/observability/health.d.ts +19 -0
- package/dist/observability/health.d.ts.map +1 -0
- package/dist/observability/health.js +129 -0
- package/dist/observability/index.d.ts +6 -0
- package/dist/observability/index.d.ts.map +1 -0
- package/dist/observability/index.js +20 -0
- package/dist/observability/runStatus.d.ts +44 -0
- package/dist/observability/runStatus.d.ts.map +1 -0
- package/dist/observability/runStatus.js +169 -0
- package/dist/observability/timeline.d.ts +11 -0
- package/dist/observability/timeline.d.ts.map +1 -0
- package/dist/observability/timeline.js +176 -0
- package/dist/observability/types.d.ts +62 -0
- package/dist/observability/types.d.ts.map +1 -0
- package/dist/observability/types.js +8 -0
- package/dist/observability/webhooks.d.ts +68 -0
- package/dist/observability/webhooks.d.ts.map +1 -0
- package/dist/observability/webhooks.js +132 -0
- package/dist/resources/budget-tracker.d.ts +56 -0
- package/dist/resources/budget-tracker.d.ts.map +1 -0
- package/dist/resources/budget-tracker.js +131 -0
- package/dist/resources/concurrency-guard.d.ts +55 -0
- package/dist/resources/concurrency-guard.d.ts.map +1 -0
- package/dist/resources/concurrency-guard.js +132 -0
- package/dist/resources/index.d.ts +12 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +20 -0
- package/dist/resources/manager.d.ts +49 -0
- package/dist/resources/manager.d.ts.map +1 -0
- package/dist/resources/manager.js +111 -0
- package/dist/resources/timeout-cascade.d.ts +56 -0
- package/dist/resources/timeout-cascade.d.ts.map +1 -0
- package/dist/resources/timeout-cascade.js +145 -0
- package/dist/resources/types.d.ts +108 -0
- package/dist/resources/types.d.ts.map +1 -0
- package/dist/resources/types.js +9 -0
- package/dist/session/context.d.ts +22 -0
- package/dist/session/context.d.ts.map +1 -0
- package/dist/session/context.js +113 -0
- package/dist/session/continuityState.d.ts +39 -0
- package/dist/session/continuityState.d.ts.map +1 -0
- package/dist/session/continuityState.js +164 -0
- package/dist/session/cost.d.ts +63 -0
- package/dist/session/cost.d.ts.map +1 -0
- package/dist/session/cost.js +194 -0
- package/dist/session/discovery.d.ts +22 -0
- package/dist/session/discovery.d.ts.map +1 -0
- package/dist/session/discovery.js +35 -0
- package/dist/session/history.d.ts +30 -0
- package/dist/session/history.d.ts.map +1 -0
- package/dist/session/history.js +143 -0
- package/dist/session/index.d.ts +20 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +78 -0
- package/dist/session/memoryExtraction.d.ts +65 -0
- package/dist/session/memoryExtraction.d.ts.map +1 -0
- package/dist/session/memoryExtraction.js +201 -0
- package/dist/session/parse.d.ts +45 -0
- package/dist/session/parse.d.ts.map +1 -0
- package/dist/session/parse.js +170 -0
- package/dist/session/persistence.d.ts +46 -0
- package/dist/session/persistence.d.ts.map +1 -0
- package/dist/session/persistence.js +180 -0
- package/dist/session/types.d.ts +267 -0
- package/dist/session/types.d.ts.map +1 -0
- package/dist/session/types.js +45 -0
- package/dist/session/write.d.ts +61 -0
- package/dist/session/write.d.ts.map +1 -0
- package/dist/session/write.js +213 -0
- package/dist/telemetry/audit-log.d.ts +56 -0
- package/dist/telemetry/audit-log.d.ts.map +1 -0
- package/dist/telemetry/audit-log.js +59 -0
- package/dist/telemetry/index.d.ts +9 -0
- package/dist/telemetry/index.d.ts.map +1 -0
- package/dist/telemetry/index.js +15 -0
- package/dist/telemetry/provider.d.ts +39 -0
- package/dist/telemetry/provider.d.ts.map +1 -0
- package/dist/telemetry/provider.js +91 -0
- package/dist/telemetry/span-tree.d.ts +46 -0
- package/dist/telemetry/span-tree.d.ts.map +1 -0
- package/dist/telemetry/span-tree.js +93 -0
- package/dist/telemetry/types.d.ts +85 -0
- package/dist/telemetry/types.d.ts.map +1 -0
- package/dist/telemetry/types.js +21 -0
- package/package.json +90 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Session state file writing utilities.
|
|
4
|
+
* Provides atomic writes for session state files.
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.serializeSessionState = serializeSessionState;
|
|
41
|
+
exports.createSessionFileContent = createSessionFileContent;
|
|
42
|
+
exports.writeSessionFile = writeSessionFile;
|
|
43
|
+
exports.updateSessionState = updateSessionState;
|
|
44
|
+
exports.addRunToSession = addRunToSession;
|
|
45
|
+
exports.getSessionRuns = getSessionRuns;
|
|
46
|
+
exports.getCurrentTimestamp = getCurrentTimestamp;
|
|
47
|
+
exports.isoToEpochSeconds = isoToEpochSeconds;
|
|
48
|
+
exports.updateIterationTimes = updateIterationTimes;
|
|
49
|
+
const node_fs_1 = require("node:fs");
|
|
50
|
+
const path = __importStar(require("node:path"));
|
|
51
|
+
const types_1 = require("./types");
|
|
52
|
+
/**
|
|
53
|
+
* Serialize session state to YAML frontmatter format.
|
|
54
|
+
*/
|
|
55
|
+
function serializeSessionState(state) {
|
|
56
|
+
const lines = [];
|
|
57
|
+
lines.push(`active: ${state.active}`);
|
|
58
|
+
lines.push(`iteration: ${state.iteration}`);
|
|
59
|
+
lines.push(`max_iterations: ${state.maxIterations}`);
|
|
60
|
+
lines.push(`run_id: "${state.runId}"`);
|
|
61
|
+
lines.push(`run_ids: ${state.runIds.join(',')}`);
|
|
62
|
+
lines.push(`started_at: "${state.startedAt}"`);
|
|
63
|
+
lines.push(`last_iteration_at: "${state.lastIterationAt}"`);
|
|
64
|
+
lines.push(`iteration_times: ${state.iterationTimes.join(',')}`);
|
|
65
|
+
for (const [key, value] of Object.entries(state.metadata ?? {})) {
|
|
66
|
+
lines.push(`metadata_${key}: "${String(value).replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`);
|
|
67
|
+
}
|
|
68
|
+
return lines.join('\n');
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Create full session file content with YAML frontmatter and prompt.
|
|
72
|
+
*/
|
|
73
|
+
function createSessionFileContent(state, prompt) {
|
|
74
|
+
const frontmatter = serializeSessionState(state);
|
|
75
|
+
return `---\n${frontmatter}\n---\n\n${prompt}\n`;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Write session state file atomically.
|
|
79
|
+
* Uses temp file + rename pattern to ensure atomic writes.
|
|
80
|
+
*/
|
|
81
|
+
async function writeSessionFile(filePath, state, prompt) {
|
|
82
|
+
const content = createSessionFileContent(state, prompt);
|
|
83
|
+
const dir = path.dirname(filePath);
|
|
84
|
+
const tempPath = `${filePath}.tmp.${process.pid}`;
|
|
85
|
+
try {
|
|
86
|
+
// Ensure directory exists
|
|
87
|
+
await node_fs_1.promises.mkdir(dir, { recursive: true });
|
|
88
|
+
// Write to temp file
|
|
89
|
+
await node_fs_1.promises.writeFile(tempPath, content, 'utf8');
|
|
90
|
+
// Atomic rename
|
|
91
|
+
await node_fs_1.promises.rename(tempPath, filePath);
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
// Clean up temp file on error
|
|
95
|
+
try {
|
|
96
|
+
await node_fs_1.promises.unlink(tempPath);
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// Ignore cleanup errors
|
|
100
|
+
}
|
|
101
|
+
const err = error;
|
|
102
|
+
throw new types_1.SessionError(`Failed to write session state file: ${err.message}`, types_1.SessionErrorCode.FS_ERROR, { filePath, originalError: err.message });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Update specific fields in an existing session state file.
|
|
107
|
+
* Reads, modifies, and atomically writes the file.
|
|
108
|
+
*/
|
|
109
|
+
async function updateSessionState(filePath, updates, existingContent) {
|
|
110
|
+
let state;
|
|
111
|
+
let prompt;
|
|
112
|
+
if (existingContent) {
|
|
113
|
+
state = existingContent.state;
|
|
114
|
+
prompt = existingContent.prompt;
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
// Read existing file
|
|
118
|
+
const { readSessionFile } = await Promise.resolve().then(() => __importStar(require('./parse')));
|
|
119
|
+
const file = await readSessionFile(filePath);
|
|
120
|
+
state = file.state;
|
|
121
|
+
prompt = file.prompt;
|
|
122
|
+
}
|
|
123
|
+
// Apply updates
|
|
124
|
+
const updatedState = {
|
|
125
|
+
...state,
|
|
126
|
+
...updates,
|
|
127
|
+
};
|
|
128
|
+
// Write updated file
|
|
129
|
+
await writeSessionFile(filePath, updatedState, prompt);
|
|
130
|
+
return updatedState;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Bind a new run to the session, retiring the previous active run to history.
|
|
134
|
+
*
|
|
135
|
+
* Invariant: only one run is active at a time (`runId`).
|
|
136
|
+
* The previous `runId` (if any) is pushed into `runIds` as audit history.
|
|
137
|
+
* Idempotent: re-binding the same runId is a no-op.
|
|
138
|
+
*
|
|
139
|
+
* Throws if the caller tries to bind a new run while `runId` is still set
|
|
140
|
+
* and `retirePrevious` is not explicitly true — this forces callers to
|
|
141
|
+
* acknowledge that the prior run is done.
|
|
142
|
+
*/
|
|
143
|
+
function addRunToSession(state, runId, options) {
|
|
144
|
+
// Idempotent: already bound to this run
|
|
145
|
+
if (state.runId === runId)
|
|
146
|
+
return state;
|
|
147
|
+
// Guard: refuse to silently overwrite an active run
|
|
148
|
+
if (state.runId && !options?.retirePrevious) {
|
|
149
|
+
throw new types_1.SessionError(`Session already bound to run ${state.runId}. ` +
|
|
150
|
+
`Pass { retirePrevious: true } to retire it and bind ${runId}.`, types_1.SessionErrorCode.RUN_ALREADY_ASSOCIATED, { currentRunId: state.runId, requestedRunId: runId });
|
|
151
|
+
}
|
|
152
|
+
// Retire the old runId into history (if not already there)
|
|
153
|
+
const runIds = [...state.runIds];
|
|
154
|
+
if (state.runId && !runIds.includes(state.runId)) {
|
|
155
|
+
runIds.push(state.runId);
|
|
156
|
+
}
|
|
157
|
+
// Add the new one to the audit trail too
|
|
158
|
+
if (!runIds.includes(runId)) {
|
|
159
|
+
runIds.push(runId);
|
|
160
|
+
}
|
|
161
|
+
return { ...state, runId, runIds };
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Get the historical audit trail of all run IDs for this session (GAP-SESSION-001).
|
|
165
|
+
* Falls back to [runId] when runIds is empty for backward compatibility
|
|
166
|
+
* with sessions created before the runIds field existed.
|
|
167
|
+
*/
|
|
168
|
+
function getSessionRuns(state) {
|
|
169
|
+
if (state.runIds.length > 0)
|
|
170
|
+
return state.runIds;
|
|
171
|
+
return state.runId ? [state.runId] : [];
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Get current ISO timestamp.
|
|
175
|
+
*/
|
|
176
|
+
function getCurrentTimestamp() {
|
|
177
|
+
return new Date().toISOString().replace(/\.\d{3}Z$/, 'Z');
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Convert ISO timestamp to epoch seconds.
|
|
181
|
+
* Returns null if conversion fails.
|
|
182
|
+
*/
|
|
183
|
+
function isoToEpochSeconds(isoTimestamp) {
|
|
184
|
+
if (!isoTimestamp)
|
|
185
|
+
return null;
|
|
186
|
+
try {
|
|
187
|
+
const date = new Date(isoTimestamp);
|
|
188
|
+
if (Number.isNaN(date.getTime()))
|
|
189
|
+
return null;
|
|
190
|
+
return Math.floor(date.getTime() / 1000);
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Calculate iteration duration and update times array.
|
|
198
|
+
* Keeps only the last 10 durations for diagnostics.
|
|
199
|
+
*/
|
|
200
|
+
function updateIterationTimes(existingTimes, lastIterationAt, currentTime) {
|
|
201
|
+
const lastEpoch = isoToEpochSeconds(lastIterationAt);
|
|
202
|
+
const currentEpoch = isoToEpochSeconds(currentTime);
|
|
203
|
+
if (lastEpoch === null || currentEpoch === null) {
|
|
204
|
+
return existingTimes;
|
|
205
|
+
}
|
|
206
|
+
const duration = currentEpoch - lastEpoch;
|
|
207
|
+
if (duration <= 0) {
|
|
208
|
+
return existingTimes;
|
|
209
|
+
}
|
|
210
|
+
const newTimes = [...existingTimes, duration];
|
|
211
|
+
// Keep only last 10
|
|
212
|
+
return newTimes.slice(-10);
|
|
213
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AuditLog — append-only structured log for security-relevant and
|
|
3
|
+
* compliance-relevant actions within a babysitter agent runtime session.
|
|
4
|
+
*
|
|
5
|
+
* Each entry captures *who* did *what* to *which target*, with an optional
|
|
6
|
+
* telemetry span correlation. Entries are queryable by actor, action, and
|
|
7
|
+
* time range.
|
|
8
|
+
*/
|
|
9
|
+
/** A single audit log entry. */
|
|
10
|
+
export interface AuditEntry {
|
|
11
|
+
/** Who performed the action (user id, agent name, system, etc.). */
|
|
12
|
+
readonly actor: string;
|
|
13
|
+
/** What action was performed. */
|
|
14
|
+
readonly action: string;
|
|
15
|
+
/** What the action was performed on (resource, span, task, etc.). */
|
|
16
|
+
readonly target: string;
|
|
17
|
+
/** Optional human-readable detail or serialized context. */
|
|
18
|
+
readonly detail?: string;
|
|
19
|
+
/** Optional telemetry span ID to correlate with the span tree. */
|
|
20
|
+
readonly spanId?: string;
|
|
21
|
+
/** ISO-8601 timestamp of when the entry was recorded. */
|
|
22
|
+
readonly timestamp: string;
|
|
23
|
+
}
|
|
24
|
+
/** Filter criteria for querying the audit log. */
|
|
25
|
+
export interface AuditFilter {
|
|
26
|
+
/** Match entries from this actor. */
|
|
27
|
+
actor?: string;
|
|
28
|
+
/** Match entries with this action. */
|
|
29
|
+
action?: string;
|
|
30
|
+
/** Match entries recorded at or after this ISO-8601 timestamp. */
|
|
31
|
+
from?: string;
|
|
32
|
+
/** Match entries recorded at or before this ISO-8601 timestamp. */
|
|
33
|
+
to?: string;
|
|
34
|
+
}
|
|
35
|
+
/** Append-only in-memory audit log. */
|
|
36
|
+
export declare class AuditLog {
|
|
37
|
+
private readonly entries;
|
|
38
|
+
/**
|
|
39
|
+
* Record a new audit entry.
|
|
40
|
+
*
|
|
41
|
+
* The timestamp is auto-populated to the current time.
|
|
42
|
+
*/
|
|
43
|
+
record(entry: Omit<AuditEntry, "timestamp">): AuditEntry;
|
|
44
|
+
/**
|
|
45
|
+
* Query log entries with optional filters.
|
|
46
|
+
*
|
|
47
|
+
* All supplied filter fields are AND-ed together. Omitted fields match
|
|
48
|
+
* everything.
|
|
49
|
+
*/
|
|
50
|
+
getEntries(filter?: AuditFilter): AuditEntry[];
|
|
51
|
+
/** Total number of entries in the log. */
|
|
52
|
+
get size(): number;
|
|
53
|
+
/** Clear all entries (useful in tests or session resets). */
|
|
54
|
+
clear(): void;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=audit-log.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-log.d.ts","sourceRoot":"","sources":["../../src/telemetry/audit-log.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,gCAAgC;AAChC,MAAM,WAAW,UAAU;IACzB,oEAAoE;IACpE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,iCAAiC;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,qEAAqE;IACrE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,4DAA4D;IAC5D,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,kEAAkE;IAClE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,yDAAyD;IACzD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,kDAAkD;AAClD,MAAM,WAAW,WAAW;IAC1B,qCAAqC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kEAAkE;IAClE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAMD,uCAAuC;AACvC,qBAAa,QAAQ;IACnB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;IAE5C;;;;OAIG;IACH,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,GAAG,UAAU;IAMxD;;;;;OAKG;IACH,UAAU,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,UAAU,EAAE;IAc9C,0CAA0C;IAC1C,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,6DAA6D;IAC7D,KAAK,IAAI,IAAI;CAGd"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* AuditLog — append-only structured log for security-relevant and
|
|
4
|
+
* compliance-relevant actions within a babysitter agent runtime session.
|
|
5
|
+
*
|
|
6
|
+
* Each entry captures *who* did *what* to *which target*, with an optional
|
|
7
|
+
* telemetry span correlation. Entries are queryable by actor, action, and
|
|
8
|
+
* time range.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.AuditLog = void 0;
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Implementation
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
/** Append-only in-memory audit log. */
|
|
16
|
+
class AuditLog {
|
|
17
|
+
entries = [];
|
|
18
|
+
/**
|
|
19
|
+
* Record a new audit entry.
|
|
20
|
+
*
|
|
21
|
+
* The timestamp is auto-populated to the current time.
|
|
22
|
+
*/
|
|
23
|
+
record(entry) {
|
|
24
|
+
const full = { ...entry, timestamp: new Date().toISOString() };
|
|
25
|
+
this.entries.push(full);
|
|
26
|
+
return full;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Query log entries with optional filters.
|
|
30
|
+
*
|
|
31
|
+
* All supplied filter fields are AND-ed together. Omitted fields match
|
|
32
|
+
* everything.
|
|
33
|
+
*/
|
|
34
|
+
getEntries(filter) {
|
|
35
|
+
if (!filter) {
|
|
36
|
+
return [...this.entries];
|
|
37
|
+
}
|
|
38
|
+
return this.entries.filter((e) => {
|
|
39
|
+
if (filter.actor && e.actor !== filter.actor)
|
|
40
|
+
return false;
|
|
41
|
+
if (filter.action && e.action !== filter.action)
|
|
42
|
+
return false;
|
|
43
|
+
if (filter.from && e.timestamp < filter.from)
|
|
44
|
+
return false;
|
|
45
|
+
if (filter.to && e.timestamp > filter.to)
|
|
46
|
+
return false;
|
|
47
|
+
return true;
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/** Total number of entries in the log. */
|
|
51
|
+
get size() {
|
|
52
|
+
return this.entries.length;
|
|
53
|
+
}
|
|
54
|
+
/** Clear all entries (useful in tests or session resets). */
|
|
55
|
+
clear() {
|
|
56
|
+
this.entries.length = 0;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
exports.AuditLog = AuditLog;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telemetry module — OpenTelemetry-aligned structured telemetry interfaces
|
|
3
|
+
* and in-memory implementations.
|
|
4
|
+
*/
|
|
5
|
+
export { TelemetrySpanStatus, type TelemetryEvent, type TelemetrySpan, type TelemetryProvider, type TelemetryConfig, } from "./types";
|
|
6
|
+
export { InMemoryTelemetryProvider } from "./provider";
|
|
7
|
+
export { AuditLog, type AuditEntry, type AuditFilter, } from "./audit-log";
|
|
8
|
+
export { SpanTree, type SpanTreeNode, type SerializedSpanTreeNode, } from "./span-tree";
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/telemetry/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EACL,mBAAmB,EACnB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,eAAe,GACrB,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAEvD,OAAO,EACL,QAAQ,EACR,KAAK,UAAU,EACf,KAAK,WAAW,GACjB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,QAAQ,EACR,KAAK,YAAY,EACjB,KAAK,sBAAsB,GAC5B,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SpanTree = exports.AuditLog = exports.InMemoryTelemetryProvider = exports.TelemetrySpanStatus = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Telemetry module — OpenTelemetry-aligned structured telemetry interfaces
|
|
6
|
+
* and in-memory implementations.
|
|
7
|
+
*/
|
|
8
|
+
var types_1 = require("./types");
|
|
9
|
+
Object.defineProperty(exports, "TelemetrySpanStatus", { enumerable: true, get: function () { return types_1.TelemetrySpanStatus; } });
|
|
10
|
+
var provider_1 = require("./provider");
|
|
11
|
+
Object.defineProperty(exports, "InMemoryTelemetryProvider", { enumerable: true, get: function () { return provider_1.InMemoryTelemetryProvider; } });
|
|
12
|
+
var audit_log_1 = require("./audit-log");
|
|
13
|
+
Object.defineProperty(exports, "AuditLog", { enumerable: true, get: function () { return audit_log_1.AuditLog; } });
|
|
14
|
+
var span_tree_1 = require("./span-tree");
|
|
15
|
+
Object.defineProperty(exports, "SpanTree", { enumerable: true, get: function () { return span_tree_1.SpanTree; } });
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* InMemoryTelemetryProvider — in-process telemetry backend for development,
|
|
3
|
+
* testing, and single-process agent runtimes.
|
|
4
|
+
*
|
|
5
|
+
* Stores spans in memory and supports flush/drain for export pipelines.
|
|
6
|
+
*/
|
|
7
|
+
import type { TelemetryProvider, TelemetrySpan, TelemetryEvent } from "./types";
|
|
8
|
+
import { TelemetrySpanStatus } from "./types";
|
|
9
|
+
/**
|
|
10
|
+
* In-memory telemetry provider that satisfies the TelemetryProvider interface.
|
|
11
|
+
*
|
|
12
|
+
* - `startSpan` creates a new span and tracks it as active.
|
|
13
|
+
* - `endSpan` marks a span as completed and moves it to the completed set.
|
|
14
|
+
* - `recordEvent` appends a discrete event to an active span.
|
|
15
|
+
* - `flush` returns all completed spans and clears the completed buffer.
|
|
16
|
+
* - `getActiveSpans` returns all currently open (un-ended) spans.
|
|
17
|
+
*/
|
|
18
|
+
export declare class InMemoryTelemetryProvider implements TelemetryProvider {
|
|
19
|
+
/** Active (in-flight) spans keyed by spanId. */
|
|
20
|
+
private readonly active;
|
|
21
|
+
/** Completed spans awaiting flush. */
|
|
22
|
+
private readonly completed;
|
|
23
|
+
/** Maps spanId -> traceId so child spans inherit the parent's trace. */
|
|
24
|
+
private readonly traceIndex;
|
|
25
|
+
startSpan(name: string, parentSpanId?: string): Promise<TelemetrySpan>;
|
|
26
|
+
endSpan(spanId: string, status?: TelemetrySpanStatus): Promise<void>;
|
|
27
|
+
recordEvent(spanId: string, event: TelemetryEvent): Promise<void>;
|
|
28
|
+
flush(): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Drain completed spans — returns and clears the completed buffer.
|
|
31
|
+
*
|
|
32
|
+
* Unlike `flush()` (which satisfies the TelemetryProvider interface and
|
|
33
|
+
* returns void), this method hands the span data back to the caller.
|
|
34
|
+
*/
|
|
35
|
+
drain(): Promise<TelemetrySpan[]>;
|
|
36
|
+
/** Return a snapshot of all currently open (un-ended) spans. */
|
|
37
|
+
getActiveSpans(): TelemetrySpan[];
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/telemetry/provider.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EACV,iBAAiB,EACjB,aAAa,EACb,cAAc,EACf,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAwB9C;;;;;;;;GAQG;AACH,qBAAa,yBAA0B,YAAW,iBAAiB;IACjE,gDAAgD;IAChD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkC;IACzD,sCAAsC;IACtC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqB;IAC/C,wEAAwE;IACxE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA6B;IAIlD,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IA4BtE,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAapE,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IASjE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B;;;;;OAKG;IACG,KAAK,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;IAMvC,gEAAgE;IAChE,cAAc,IAAI,aAAa,EAAE;CAGlC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* InMemoryTelemetryProvider — in-process telemetry backend for development,
|
|
4
|
+
* testing, and single-process agent runtimes.
|
|
5
|
+
*
|
|
6
|
+
* Stores spans in memory and supports flush/drain for export pipelines.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.InMemoryTelemetryProvider = void 0;
|
|
10
|
+
const node_crypto_1 = require("node:crypto");
|
|
11
|
+
const types_1 = require("./types");
|
|
12
|
+
function toReadonly(span) {
|
|
13
|
+
return { ...span, attributes: { ...span.attributes }, events: [...span.events] };
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* In-memory telemetry provider that satisfies the TelemetryProvider interface.
|
|
17
|
+
*
|
|
18
|
+
* - `startSpan` creates a new span and tracks it as active.
|
|
19
|
+
* - `endSpan` marks a span as completed and moves it to the completed set.
|
|
20
|
+
* - `recordEvent` appends a discrete event to an active span.
|
|
21
|
+
* - `flush` returns all completed spans and clears the completed buffer.
|
|
22
|
+
* - `getActiveSpans` returns all currently open (un-ended) spans.
|
|
23
|
+
*/
|
|
24
|
+
class InMemoryTelemetryProvider {
|
|
25
|
+
/** Active (in-flight) spans keyed by spanId. */
|
|
26
|
+
active = new Map();
|
|
27
|
+
/** Completed spans awaiting flush. */
|
|
28
|
+
completed = [];
|
|
29
|
+
/** Maps spanId -> traceId so child spans inherit the parent's trace. */
|
|
30
|
+
traceIndex = new Map();
|
|
31
|
+
// ---------- TelemetryProvider interface ----------
|
|
32
|
+
async startSpan(name, parentSpanId) {
|
|
33
|
+
const spanId = (0, node_crypto_1.randomUUID)();
|
|
34
|
+
let traceId;
|
|
35
|
+
if (parentSpanId) {
|
|
36
|
+
// Inherit traceId from the parent span.
|
|
37
|
+
traceId = this.traceIndex.get(parentSpanId) ?? (0, node_crypto_1.randomUUID)();
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
traceId = (0, node_crypto_1.randomUUID)();
|
|
41
|
+
}
|
|
42
|
+
const span = {
|
|
43
|
+
name,
|
|
44
|
+
traceId,
|
|
45
|
+
spanId,
|
|
46
|
+
parentSpanId,
|
|
47
|
+
startTime: new Date().toISOString(),
|
|
48
|
+
status: types_1.TelemetrySpanStatus.Unset,
|
|
49
|
+
attributes: {},
|
|
50
|
+
events: [],
|
|
51
|
+
};
|
|
52
|
+
this.active.set(spanId, span);
|
|
53
|
+
this.traceIndex.set(spanId, traceId);
|
|
54
|
+
return toReadonly(span);
|
|
55
|
+
}
|
|
56
|
+
async endSpan(spanId, status) {
|
|
57
|
+
const span = this.active.get(spanId);
|
|
58
|
+
if (!span) {
|
|
59
|
+
return; // Silently ignore unknown/already-ended spans.
|
|
60
|
+
}
|
|
61
|
+
span.endTime = new Date().toISOString();
|
|
62
|
+
span.status = status ?? types_1.TelemetrySpanStatus.Ok;
|
|
63
|
+
this.active.delete(spanId);
|
|
64
|
+
this.completed.push(span);
|
|
65
|
+
}
|
|
66
|
+
async recordEvent(spanId, event) {
|
|
67
|
+
const span = this.active.get(spanId);
|
|
68
|
+
if (!span) {
|
|
69
|
+
return; // Silently ignore events on unknown/ended spans.
|
|
70
|
+
}
|
|
71
|
+
span.events.push(event);
|
|
72
|
+
}
|
|
73
|
+
async flush() {
|
|
74
|
+
this.completed.length = 0;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Drain completed spans — returns and clears the completed buffer.
|
|
78
|
+
*
|
|
79
|
+
* Unlike `flush()` (which satisfies the TelemetryProvider interface and
|
|
80
|
+
* returns void), this method hands the span data back to the caller.
|
|
81
|
+
*/
|
|
82
|
+
async drain() {
|
|
83
|
+
return this.completed.splice(0).map(toReadonly);
|
|
84
|
+
}
|
|
85
|
+
// ---------- Extra helpers (not on interface) ----------
|
|
86
|
+
/** Return a snapshot of all currently open (un-ended) spans. */
|
|
87
|
+
getActiveSpans() {
|
|
88
|
+
return [...this.active.values()].map(toReadonly);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
exports.InMemoryTelemetryProvider = InMemoryTelemetryProvider;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SpanTree — builds a parent/child tree from a flat list of TelemetrySpans.
|
|
3
|
+
*
|
|
4
|
+
* Useful for rendering trace waterfalls, computing subtree durations, and
|
|
5
|
+
* serializing trace hierarchies to JSON for the observer UI.
|
|
6
|
+
*/
|
|
7
|
+
import type { TelemetrySpan } from "./types";
|
|
8
|
+
/** A tree node wrapping a span with its children. */
|
|
9
|
+
export interface SpanTreeNode {
|
|
10
|
+
readonly span: TelemetrySpan;
|
|
11
|
+
readonly children: SpanTreeNode[];
|
|
12
|
+
}
|
|
13
|
+
/** Serializable representation of a SpanTreeNode. */
|
|
14
|
+
export interface SerializedSpanTreeNode {
|
|
15
|
+
readonly spanId: string;
|
|
16
|
+
readonly name: string;
|
|
17
|
+
readonly traceId: string;
|
|
18
|
+
readonly parentSpanId?: string;
|
|
19
|
+
readonly startTime: string;
|
|
20
|
+
readonly endTime?: string;
|
|
21
|
+
readonly status: string;
|
|
22
|
+
readonly attributes: Record<string, string | number | boolean>;
|
|
23
|
+
readonly events: Array<{
|
|
24
|
+
readonly name: string;
|
|
25
|
+
readonly timestamp: string;
|
|
26
|
+
readonly attributes?: Record<string, string | number | boolean>;
|
|
27
|
+
}>;
|
|
28
|
+
readonly children: SerializedSpanTreeNode[];
|
|
29
|
+
}
|
|
30
|
+
/** Tree built from a flat span list. */
|
|
31
|
+
export declare class SpanTree {
|
|
32
|
+
/** All nodes keyed by spanId for O(1) lookup. */
|
|
33
|
+
private readonly nodes;
|
|
34
|
+
/** Root node ids (spans with no parent or whose parent is unknown). */
|
|
35
|
+
private readonly rootIds;
|
|
36
|
+
/** Insert a span into the tree, linking it to its parent if present. */
|
|
37
|
+
addSpan(span: TelemetrySpan): void;
|
|
38
|
+
/** Get direct children of a span. */
|
|
39
|
+
getChildren(spanId: string): TelemetrySpan[];
|
|
40
|
+
/** Get top-level spans (those with no parent). */
|
|
41
|
+
getRoots(): TelemetrySpan[];
|
|
42
|
+
/** Serialize the full tree to a JSON-safe structure. */
|
|
43
|
+
toJSON(): SerializedSpanTreeNode[];
|
|
44
|
+
private serializeNode;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=span-tree.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"span-tree.d.ts","sourceRoot":"","sources":["../../src/telemetry/span-tree.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAM7C,qDAAqD;AACrD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,YAAY,EAAE,CAAC;CACnC;AAED,qDAAqD;AACrD,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;IAC/D,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC;QACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;QAC3B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;KACjE,CAAC,CAAC;IACH,QAAQ,CAAC,QAAQ,EAAE,sBAAsB,EAAE,CAAC;CAC7C;AAMD,wCAAwC;AACxC,qBAAa,QAAQ;IACnB,iDAAiD;IACjD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAmC;IACzD,uEAAuE;IACvE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IAExC,wEAAwE;IACxE,OAAO,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI;IAgClC,qCAAqC;IACrC,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,EAAE;IAM5C,kDAAkD;IAClD,QAAQ,IAAI,aAAa,EAAE;IAO3B,wDAAwD;IACxD,MAAM,IAAI,sBAAsB,EAAE;IAOlC,OAAO,CAAC,aAAa;CAmBtB"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* SpanTree — builds a parent/child tree from a flat list of TelemetrySpans.
|
|
4
|
+
*
|
|
5
|
+
* Useful for rendering trace waterfalls, computing subtree durations, and
|
|
6
|
+
* serializing trace hierarchies to JSON for the observer UI.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.SpanTree = void 0;
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Implementation
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
/** Tree built from a flat span list. */
|
|
14
|
+
class SpanTree {
|
|
15
|
+
/** All nodes keyed by spanId for O(1) lookup. */
|
|
16
|
+
nodes = new Map();
|
|
17
|
+
/** Root node ids (spans with no parent or whose parent is unknown). */
|
|
18
|
+
rootIds = [];
|
|
19
|
+
/** Insert a span into the tree, linking it to its parent if present. */
|
|
20
|
+
addSpan(span) {
|
|
21
|
+
// Create or retrieve the node for this span.
|
|
22
|
+
let node = this.nodes.get(span.spanId);
|
|
23
|
+
if (node) {
|
|
24
|
+
// Node was pre-created as a placeholder by a child — update the span.
|
|
25
|
+
node.span = span;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
node = { span, children: [] };
|
|
29
|
+
this.nodes.set(span.spanId, node);
|
|
30
|
+
}
|
|
31
|
+
if (span.parentSpanId) {
|
|
32
|
+
let parentNode = this.nodes.get(span.parentSpanId);
|
|
33
|
+
if (!parentNode) {
|
|
34
|
+
// Parent hasn't been added yet — create a placeholder.
|
|
35
|
+
parentNode = {
|
|
36
|
+
span: undefined,
|
|
37
|
+
children: [],
|
|
38
|
+
};
|
|
39
|
+
this.nodes.set(span.parentSpanId, parentNode);
|
|
40
|
+
}
|
|
41
|
+
// Avoid duplicate children on re-add.
|
|
42
|
+
if (!parentNode.children.some((c) => c.span?.spanId === span.spanId)) {
|
|
43
|
+
parentNode.children.push(node);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
if (!this.rootIds.includes(span.spanId)) {
|
|
48
|
+
this.rootIds.push(span.spanId);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/** Get direct children of a span. */
|
|
53
|
+
getChildren(spanId) {
|
|
54
|
+
const node = this.nodes.get(spanId);
|
|
55
|
+
if (!node)
|
|
56
|
+
return [];
|
|
57
|
+
return node.children.map((c) => c.span);
|
|
58
|
+
}
|
|
59
|
+
/** Get top-level spans (those with no parent). */
|
|
60
|
+
getRoots() {
|
|
61
|
+
return this.rootIds
|
|
62
|
+
.map((id) => this.nodes.get(id))
|
|
63
|
+
.filter((n) => n != null && n.span != null)
|
|
64
|
+
.map((n) => n.span);
|
|
65
|
+
}
|
|
66
|
+
/** Serialize the full tree to a JSON-safe structure. */
|
|
67
|
+
toJSON() {
|
|
68
|
+
return this.getRoots()
|
|
69
|
+
.map((root) => this.nodes.get(root.spanId))
|
|
70
|
+
.filter((n) => n != null)
|
|
71
|
+
.map((n) => this.serializeNode(n));
|
|
72
|
+
}
|
|
73
|
+
serializeNode(node) {
|
|
74
|
+
const { span } = node;
|
|
75
|
+
return {
|
|
76
|
+
spanId: span.spanId,
|
|
77
|
+
name: span.name,
|
|
78
|
+
traceId: span.traceId,
|
|
79
|
+
parentSpanId: span.parentSpanId,
|
|
80
|
+
startTime: span.startTime,
|
|
81
|
+
endTime: span.endTime,
|
|
82
|
+
status: span.status,
|
|
83
|
+
attributes: { ...span.attributes },
|
|
84
|
+
events: span.events.map((e) => ({
|
|
85
|
+
name: e.name,
|
|
86
|
+
timestamp: e.timestamp,
|
|
87
|
+
...(e.attributes ? { attributes: { ...e.attributes } } : {}),
|
|
88
|
+
})),
|
|
89
|
+
children: node.children.map((c) => this.serializeNode(c)),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
exports.SpanTree = SpanTree;
|