@telora/daemon-core 0.2.5
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/activity-tracker.d.ts +69 -0
- package/dist/activity-tracker.d.ts.map +1 -0
- package/dist/activity-tracker.js +155 -0
- package/dist/activity-tracker.js.map +1 -0
- package/dist/api-client.d.ts +94 -0
- package/dist/api-client.d.ts.map +1 -0
- package/dist/api-client.js +145 -0
- package/dist/api-client.js.map +1 -0
- package/dist/config.d.ts +117 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +348 -0
- package/dist/config.js.map +1 -0
- package/dist/engine.d.ts +120 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +18 -0
- package/dist/engine.js.map +1 -0
- package/dist/escalation-types.d.ts +31 -0
- package/dist/escalation-types.d.ts.map +1 -0
- package/dist/escalation-types.js +24 -0
- package/dist/escalation-types.js.map +1 -0
- package/dist/event-logger.d.ts +13 -0
- package/dist/event-logger.d.ts.map +1 -0
- package/dist/event-logger.js +82 -0
- package/dist/event-logger.js.map +1 -0
- package/dist/git.d.ts +39 -0
- package/dist/git.d.ts.map +1 -0
- package/dist/git.js +72 -0
- package/dist/git.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/dist/lifecycle.d.ts +104 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/lifecycle.js +192 -0
- package/dist/lifecycle.js.map +1 -0
- package/dist/log-manager.d.ts +83 -0
- package/dist/log-manager.d.ts.map +1 -0
- package/dist/log-manager.js +217 -0
- package/dist/log-manager.js.map +1 -0
- package/dist/otel-env.d.ts +29 -0
- package/dist/otel-env.d.ts.map +1 -0
- package/dist/otel-env.js +44 -0
- package/dist/otel-env.js.map +1 -0
- package/dist/resilience.d.ts +127 -0
- package/dist/resilience.d.ts.map +1 -0
- package/dist/resilience.js +300 -0
- package/dist/resilience.js.map +1 -0
- package/dist/resource-governor.d.ts +83 -0
- package/dist/resource-governor.d.ts.map +1 -0
- package/dist/resource-governor.js +184 -0
- package/dist/resource-governor.js.map +1 -0
- package/dist/spawn.d.ts +72 -0
- package/dist/spawn.d.ts.map +1 -0
- package/dist/spawn.js +82 -0
- package/dist/spawn.js.map +1 -0
- package/dist/stream-json.d.ts +885 -0
- package/dist/stream-json.d.ts.map +1 -0
- package/dist/stream-json.js +298 -0
- package/dist/stream-json.js.map +1 -0
- package/dist/token-usage.d.ts +67 -0
- package/dist/token-usage.d.ts.map +1 -0
- package/dist/token-usage.js +150 -0
- package/dist/token-usage.js.map +1 -0
- package/dist/transforms.d.ts +64 -0
- package/dist/transforms.d.ts.map +1 -0
- package/dist/transforms.js +78 -0
- package/dist/transforms.js.map +1 -0
- package/dist/unified-config.d.ts +62 -0
- package/dist/unified-config.d.ts.map +1 -0
- package/dist/unified-config.js +155 -0
- package/dist/unified-config.js.map +1 -0
- package/dist/workflow-types.d.ts +202 -0
- package/dist/workflow-types.d.ts.map +1 -0
- package/dist/workflow-types.js +15 -0
- package/dist/workflow-types.js.map +1 -0
- package/dist/worktree.d.ts +92 -0
- package/dist/worktree.d.ts.map +1 -0
- package/dist/worktree.js +221 -0
- package/dist/worktree.js.map +1 -0
- package/package.json +57 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Log manager -- retention policy and pruning for agent log files.
|
|
3
|
+
*
|
|
4
|
+
* Agent logs accumulate with three files per session:
|
|
5
|
+
* agent-{branch}-{ISO-timestamp}.{stdout.log|stderr.log|stream.jsonl}
|
|
6
|
+
*
|
|
7
|
+
* This module provides configurable retention with three thresholds:
|
|
8
|
+
* - Max age (default 7 days)
|
|
9
|
+
* - Max total size (default 1 GB)
|
|
10
|
+
* - Max file count (default 500)
|
|
11
|
+
*
|
|
12
|
+
* Logs referenced by active escalations are preserved regardless of age.
|
|
13
|
+
* Pruning runs on startup and periodically (hourly).
|
|
14
|
+
*
|
|
15
|
+
* Extracted to daemon-core so both daemon and factory engines can share
|
|
16
|
+
* the same log rotation logic with engine-specific config.
|
|
17
|
+
*/
|
|
18
|
+
/** Configuration for log retention policy. */
|
|
19
|
+
export interface LogRetentionConfig {
|
|
20
|
+
/** Directory containing log files. */
|
|
21
|
+
logDir: string;
|
|
22
|
+
/** Max age in days for log files. */
|
|
23
|
+
logMaxAgeDays: number;
|
|
24
|
+
/** Max total bytes for all log files. */
|
|
25
|
+
logMaxTotalBytes: number;
|
|
26
|
+
/** Max number of log files. */
|
|
27
|
+
logMaxFiles: number;
|
|
28
|
+
}
|
|
29
|
+
/** Information about a single log file on disk. */
|
|
30
|
+
export interface LogFileInfo {
|
|
31
|
+
/** Full path to the file. */
|
|
32
|
+
path: string;
|
|
33
|
+
/** File name without directory. */
|
|
34
|
+
name: string;
|
|
35
|
+
/** Size in bytes. */
|
|
36
|
+
size: number;
|
|
37
|
+
/** Last modification time (ms since epoch). */
|
|
38
|
+
mtimeMs: number;
|
|
39
|
+
/** Session group key (shared prefix before the extension). */
|
|
40
|
+
sessionKey: string;
|
|
41
|
+
}
|
|
42
|
+
/** Result of a pruning operation. */
|
|
43
|
+
export interface PruneResult {
|
|
44
|
+
/** Number of files deleted. */
|
|
45
|
+
filesDeleted: number;
|
|
46
|
+
/** Number of session groups deleted (partially or fully). */
|
|
47
|
+
sessionsDeleted: number;
|
|
48
|
+
/** Total bytes reclaimed. */
|
|
49
|
+
bytesReclaimed: number;
|
|
50
|
+
/** Number of files preserved due to escalation references. */
|
|
51
|
+
filesPreserved: number;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Injectable dependencies for testing.
|
|
55
|
+
*/
|
|
56
|
+
export interface LogManagerDeps {
|
|
57
|
+
getActiveEscalationSessionIds: () => Promise<Set<string>>;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Extract the session key from a log filename.
|
|
61
|
+
*
|
|
62
|
+
* Log filenames follow the pattern:
|
|
63
|
+
* agent-{branch}-{ISO-timestamp}.{stdout.log|stderr.log|stream.jsonl}
|
|
64
|
+
*
|
|
65
|
+
* The session key is everything before the log extension.
|
|
66
|
+
*/
|
|
67
|
+
export declare function extractSessionKey(filename: string): string | null;
|
|
68
|
+
/**
|
|
69
|
+
* Scan the log directory and return information about all log files.
|
|
70
|
+
*/
|
|
71
|
+
export declare function scanLogDirectory(logDir: string): LogFileInfo[];
|
|
72
|
+
/**
|
|
73
|
+
* Prune old log files according to the retention policy.
|
|
74
|
+
*
|
|
75
|
+
* Deletion order: oldest session groups first. All three files in a session
|
|
76
|
+
* group are deleted together. Sessions referenced by active escalations are
|
|
77
|
+
* preserved regardless of age.
|
|
78
|
+
*
|
|
79
|
+
* Thresholds are checked in order: age, then file count, then total size.
|
|
80
|
+
* Files that breach any threshold are candidates for deletion.
|
|
81
|
+
*/
|
|
82
|
+
export declare function pruneOldLogs(config: LogRetentionConfig, deps?: LogManagerDeps): Promise<PruneResult>;
|
|
83
|
+
//# sourceMappingURL=log-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log-manager.d.ts","sourceRoot":"","sources":["../src/log-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAYH,8CAA8C;AAC9C,MAAM,WAAW,kBAAkB;IACjC,sCAAsC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,qCAAqC;IACrC,aAAa,EAAE,MAAM,CAAC;IACtB,yCAAyC;IACzC,gBAAgB,EAAE,MAAM,CAAC;IACzB,+BAA+B;IAC/B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,mDAAmD;AACnD,MAAM,WAAW,WAAW;IAC1B,6BAA6B;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,qCAAqC;AACrC,MAAM,WAAW,WAAW;IAC1B,+BAA+B;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,6DAA6D;IAC7D,eAAe,EAAE,MAAM,CAAC;IACxB,6BAA6B;IAC7B,cAAc,EAAE,MAAM,CAAC;IACvB,8DAA8D;IAC9D,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,6BAA6B,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;CAC3D;AAUD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOjE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,EAAE,CAkC9D;AAgBD;;;;;;;;;GASG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,kBAAkB,EAC1B,IAAI,GAAE,cAA4B,GACjC,OAAO,CAAC,WAAW,CAAC,CAyHtB"}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Log manager -- retention policy and pruning for agent log files.
|
|
3
|
+
*
|
|
4
|
+
* Agent logs accumulate with three files per session:
|
|
5
|
+
* agent-{branch}-{ISO-timestamp}.{stdout.log|stderr.log|stream.jsonl}
|
|
6
|
+
*
|
|
7
|
+
* This module provides configurable retention with three thresholds:
|
|
8
|
+
* - Max age (default 7 days)
|
|
9
|
+
* - Max total size (default 1 GB)
|
|
10
|
+
* - Max file count (default 500)
|
|
11
|
+
*
|
|
12
|
+
* Logs referenced by active escalations are preserved regardless of age.
|
|
13
|
+
* Pruning runs on startup and periodically (hourly).
|
|
14
|
+
*
|
|
15
|
+
* Extracted to daemon-core so both daemon and factory engines can share
|
|
16
|
+
* the same log rotation logic with engine-specific config.
|
|
17
|
+
*/
|
|
18
|
+
import { readdirSync, statSync, unlinkSync, existsSync } from 'node:fs';
|
|
19
|
+
import { join } from 'node:path';
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Types
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
/** Extensions that are recognized as agent log files. */
|
|
24
|
+
const LOG_EXTENSIONS = ['.stdout.log', '.stderr.log', '.stream.jsonl'];
|
|
25
|
+
const defaultDeps = {
|
|
26
|
+
getActiveEscalationSessionIds: async () => new Set(),
|
|
27
|
+
};
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// Helpers
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
/**
|
|
32
|
+
* Extract the session key from a log filename.
|
|
33
|
+
*
|
|
34
|
+
* Log filenames follow the pattern:
|
|
35
|
+
* agent-{branch}-{ISO-timestamp}.{stdout.log|stderr.log|stream.jsonl}
|
|
36
|
+
*
|
|
37
|
+
* The session key is everything before the log extension.
|
|
38
|
+
*/
|
|
39
|
+
export function extractSessionKey(filename) {
|
|
40
|
+
for (const ext of LOG_EXTENSIONS) {
|
|
41
|
+
if (filename.endsWith(ext)) {
|
|
42
|
+
return filename.slice(0, -ext.length);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Scan the log directory and return information about all log files.
|
|
49
|
+
*/
|
|
50
|
+
export function scanLogDirectory(logDir) {
|
|
51
|
+
if (!existsSync(logDir))
|
|
52
|
+
return [];
|
|
53
|
+
const files = [];
|
|
54
|
+
let entries;
|
|
55
|
+
try {
|
|
56
|
+
entries = readdirSync(logDir);
|
|
57
|
+
}
|
|
58
|
+
catch (e) {
|
|
59
|
+
console.debug(`[log-manager] Failed to read log directory ${logDir}:`, e.message);
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
for (const name of entries) {
|
|
63
|
+
const sessionKey = extractSessionKey(name);
|
|
64
|
+
if (!sessionKey)
|
|
65
|
+
continue;
|
|
66
|
+
const path = join(logDir, name);
|
|
67
|
+
try {
|
|
68
|
+
const stat = statSync(path);
|
|
69
|
+
if (!stat.isFile())
|
|
70
|
+
continue;
|
|
71
|
+
files.push({
|
|
72
|
+
path,
|
|
73
|
+
name,
|
|
74
|
+
size: stat.size,
|
|
75
|
+
mtimeMs: stat.mtimeMs,
|
|
76
|
+
sessionKey,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
catch (e) {
|
|
80
|
+
console.debug(`[log-manager] Failed to stat log file ${path} (may have been deleted):`, e.message);
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return files;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Check if a session key matches any session ID referenced by active escalations.
|
|
88
|
+
*/
|
|
89
|
+
function isSessionPreserved(sessionKey, preservedSessionKeys) {
|
|
90
|
+
return preservedSessionKeys.has(sessionKey);
|
|
91
|
+
}
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
// Pruning
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
/**
|
|
96
|
+
* Prune old log files according to the retention policy.
|
|
97
|
+
*
|
|
98
|
+
* Deletion order: oldest session groups first. All three files in a session
|
|
99
|
+
* group are deleted together. Sessions referenced by active escalations are
|
|
100
|
+
* preserved regardless of age.
|
|
101
|
+
*
|
|
102
|
+
* Thresholds are checked in order: age, then file count, then total size.
|
|
103
|
+
* Files that breach any threshold are candidates for deletion.
|
|
104
|
+
*/
|
|
105
|
+
export async function pruneOldLogs(config, deps = defaultDeps) {
|
|
106
|
+
const result = {
|
|
107
|
+
filesDeleted: 0,
|
|
108
|
+
sessionsDeleted: 0,
|
|
109
|
+
bytesReclaimed: 0,
|
|
110
|
+
filesPreserved: 0,
|
|
111
|
+
};
|
|
112
|
+
const { logDir, logMaxAgeDays, logMaxTotalBytes, logMaxFiles } = config;
|
|
113
|
+
if (!existsSync(logDir))
|
|
114
|
+
return result;
|
|
115
|
+
// Scan all log files
|
|
116
|
+
const allFiles = scanLogDirectory(logDir);
|
|
117
|
+
if (allFiles.length === 0)
|
|
118
|
+
return result;
|
|
119
|
+
// Get preserved session keys from active escalations
|
|
120
|
+
let preservedSessionKeys;
|
|
121
|
+
try {
|
|
122
|
+
preservedSessionKeys = await deps.getActiveEscalationSessionIds();
|
|
123
|
+
}
|
|
124
|
+
catch (e) {
|
|
125
|
+
console.debug('[log-manager] Failed to query escalation session IDs (preserving none):', e.message);
|
|
126
|
+
preservedSessionKeys = new Set();
|
|
127
|
+
}
|
|
128
|
+
// Group files by session key
|
|
129
|
+
const sessionGroups = new Map();
|
|
130
|
+
for (const file of allFiles) {
|
|
131
|
+
const group = sessionGroups.get(file.sessionKey) ?? [];
|
|
132
|
+
group.push(file);
|
|
133
|
+
sessionGroups.set(file.sessionKey, group);
|
|
134
|
+
}
|
|
135
|
+
// Sort session groups by oldest mtime first (candidates for deletion)
|
|
136
|
+
const sortedSessions = [...sessionGroups.entries()].sort(([, aFiles], [, bFiles]) => {
|
|
137
|
+
const aOldest = Math.min(...aFiles.map(f => f.mtimeMs));
|
|
138
|
+
const bOldest = Math.min(...bFiles.map(f => f.mtimeMs));
|
|
139
|
+
return aOldest - bOldest;
|
|
140
|
+
});
|
|
141
|
+
// Build set of session keys to delete
|
|
142
|
+
const sessionsToDelete = new Set();
|
|
143
|
+
const now = Date.now();
|
|
144
|
+
const maxAgeMs = logMaxAgeDays * 24 * 60 * 60 * 1000;
|
|
145
|
+
// Pass 1: Mark sessions older than max age
|
|
146
|
+
for (const [sessionKey, files] of sortedSessions) {
|
|
147
|
+
if (isSessionPreserved(sessionKey, preservedSessionKeys)) {
|
|
148
|
+
result.filesPreserved += files.length;
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
const oldestMtime = Math.min(...files.map(f => f.mtimeMs));
|
|
152
|
+
if (now - oldestMtime > maxAgeMs) {
|
|
153
|
+
sessionsToDelete.add(sessionKey);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// Calculate remaining stats after age-based deletion
|
|
157
|
+
let remainingFiles = 0;
|
|
158
|
+
let remainingBytes = 0;
|
|
159
|
+
for (const [sessionKey, files] of sortedSessions) {
|
|
160
|
+
if (sessionsToDelete.has(sessionKey))
|
|
161
|
+
continue;
|
|
162
|
+
remainingFiles += files.length;
|
|
163
|
+
remainingBytes += files.reduce((sum, f) => sum + f.size, 0);
|
|
164
|
+
}
|
|
165
|
+
// Pass 2: Delete oldest sessions until under file count limit
|
|
166
|
+
for (const [sessionKey, files] of sortedSessions) {
|
|
167
|
+
if (sessionsToDelete.has(sessionKey))
|
|
168
|
+
continue;
|
|
169
|
+
if (remainingFiles <= logMaxFiles)
|
|
170
|
+
break;
|
|
171
|
+
if (isSessionPreserved(sessionKey, preservedSessionKeys))
|
|
172
|
+
continue;
|
|
173
|
+
sessionsToDelete.add(sessionKey);
|
|
174
|
+
remainingFiles -= files.length;
|
|
175
|
+
remainingBytes -= files.reduce((sum, f) => sum + f.size, 0);
|
|
176
|
+
}
|
|
177
|
+
// Pass 3: Delete oldest sessions until under total size limit
|
|
178
|
+
for (const [sessionKey, files] of sortedSessions) {
|
|
179
|
+
if (sessionsToDelete.has(sessionKey))
|
|
180
|
+
continue;
|
|
181
|
+
if (remainingBytes <= logMaxTotalBytes)
|
|
182
|
+
break;
|
|
183
|
+
if (isSessionPreserved(sessionKey, preservedSessionKeys))
|
|
184
|
+
continue;
|
|
185
|
+
sessionsToDelete.add(sessionKey);
|
|
186
|
+
remainingFiles -= files.length;
|
|
187
|
+
remainingBytes -= files.reduce((sum, f) => sum + f.size, 0);
|
|
188
|
+
}
|
|
189
|
+
// Execute deletions
|
|
190
|
+
for (const sessionKey of sessionsToDelete) {
|
|
191
|
+
const files = sessionGroups.get(sessionKey);
|
|
192
|
+
if (!files)
|
|
193
|
+
continue;
|
|
194
|
+
for (const file of files) {
|
|
195
|
+
try {
|
|
196
|
+
unlinkSync(file.path);
|
|
197
|
+
result.filesDeleted++;
|
|
198
|
+
result.bytesReclaimed += file.size;
|
|
199
|
+
}
|
|
200
|
+
catch (err) {
|
|
201
|
+
console.warn(`[log-manager] Failed to delete ${file.path}: ${err.message}`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
result.sessionsDeleted++;
|
|
205
|
+
}
|
|
206
|
+
// Log summary
|
|
207
|
+
if (result.filesDeleted > 0) {
|
|
208
|
+
const mbReclaimed = (result.bytesReclaimed / (1024 * 1024)).toFixed(1);
|
|
209
|
+
console.log(`[log-manager] Pruned ${result.filesDeleted} log files ` +
|
|
210
|
+
`(${result.sessionsDeleted} sessions), reclaimed ${mbReclaimed} MB`);
|
|
211
|
+
}
|
|
212
|
+
if (result.filesPreserved > 0) {
|
|
213
|
+
console.log(`[log-manager] Preserved ${result.filesPreserved} files referenced by active escalations`);
|
|
214
|
+
}
|
|
215
|
+
return result;
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=log-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log-manager.js","sourceRoot":"","sources":["../src/log-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared OTEL environment variable builder for Telora daemon and factory engines.
|
|
3
|
+
*
|
|
4
|
+
* Constructs the environment variables needed to enable OpenTelemetry
|
|
5
|
+
* telemetry in spawned Claude Code processes. Both the daemon's
|
|
6
|
+
* buildSpawnEnvironment and the factory's buildBuilderEnv call this
|
|
7
|
+
* with their respective resource attribute sets.
|
|
8
|
+
*/
|
|
9
|
+
export interface BuildOtelEnvOptions {
|
|
10
|
+
/** Whether to inject OTEL vars at all. When false, returns empty object. */
|
|
11
|
+
enabled: boolean;
|
|
12
|
+
/** OTLP endpoint port (default 4318). */
|
|
13
|
+
port?: number;
|
|
14
|
+
/** Metric export interval in ms (default 10000). */
|
|
15
|
+
exportInterval?: number;
|
|
16
|
+
/**
|
|
17
|
+
* Arbitrary key-value pairs for OTEL_RESOURCE_ATTRIBUTES.
|
|
18
|
+
* Undefined/null values are silently omitted.
|
|
19
|
+
*/
|
|
20
|
+
resourceAttributes?: Record<string, string | undefined | null>;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Build OTEL environment variables for a spawned Claude Code process.
|
|
24
|
+
*
|
|
25
|
+
* Returns a flat Record of env vars to merge into the spawn environment.
|
|
26
|
+
* When `enabled` is false, returns an empty object (no-op).
|
|
27
|
+
*/
|
|
28
|
+
export declare function buildOtelEnv(options: BuildOtelEnvOptions): Record<string, string>;
|
|
29
|
+
//# sourceMappingURL=otel-env.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"otel-env.d.ts","sourceRoot":"","sources":["../src/otel-env.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,WAAW,mBAAmB;IAClC,4EAA4E;IAC5E,OAAO,EAAE,OAAO,CAAC;IACjB,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC,CAAC;CAChE;AAID;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA8BjF"}
|
package/dist/otel-env.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared OTEL environment variable builder for Telora daemon and factory engines.
|
|
3
|
+
*
|
|
4
|
+
* Constructs the environment variables needed to enable OpenTelemetry
|
|
5
|
+
* telemetry in spawned Claude Code processes. Both the daemon's
|
|
6
|
+
* buildSpawnEnvironment and the factory's buildBuilderEnv call this
|
|
7
|
+
* with their respective resource attribute sets.
|
|
8
|
+
*/
|
|
9
|
+
// -- Builder ------------------------------------------------------------------
|
|
10
|
+
/**
|
|
11
|
+
* Build OTEL environment variables for a spawned Claude Code process.
|
|
12
|
+
*
|
|
13
|
+
* Returns a flat Record of env vars to merge into the spawn environment.
|
|
14
|
+
* When `enabled` is false, returns an empty object (no-op).
|
|
15
|
+
*/
|
|
16
|
+
export function buildOtelEnv(options) {
|
|
17
|
+
if (!options.enabled)
|
|
18
|
+
return {};
|
|
19
|
+
const port = options.port ?? 4318;
|
|
20
|
+
const exportInterval = options.exportInterval ?? 10000;
|
|
21
|
+
const env = {
|
|
22
|
+
CLAUDE_CODE_ENABLE_TELEMETRY: '1',
|
|
23
|
+
OTEL_EXPORTER_OTLP_ENDPOINT: `http://127.0.0.1:${port}`,
|
|
24
|
+
OTEL_EXPORTER_OTLP_PROTOCOL: 'http/json',
|
|
25
|
+
OTEL_METRICS_EXPORTER: 'otlp',
|
|
26
|
+
OTEL_LOGS_EXPORTER: 'otlp',
|
|
27
|
+
OTEL_METRIC_EXPORT_INTERVAL: String(exportInterval),
|
|
28
|
+
OTEL_LOG_TOOL_DETAILS: '1',
|
|
29
|
+
};
|
|
30
|
+
// Build OTEL_RESOURCE_ATTRIBUTES from the provided key-value pairs
|
|
31
|
+
if (options.resourceAttributes) {
|
|
32
|
+
const parts = [];
|
|
33
|
+
for (const [key, val] of Object.entries(options.resourceAttributes)) {
|
|
34
|
+
if (val != null && val !== '') {
|
|
35
|
+
parts.push(`${key}=${val}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (parts.length > 0) {
|
|
39
|
+
env.OTEL_RESOURCE_ATTRIBUTES = parts.join(',');
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return env;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=otel-env.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"otel-env.js","sourceRoot":"","sources":["../src/otel-env.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAkBH,gFAAgF;AAEhF;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,OAA4B;IACvD,IAAI,CAAC,OAAO,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IAEhC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC;IAClC,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,KAAK,CAAC;IAEvD,MAAM,GAAG,GAA2B;QAClC,4BAA4B,EAAE,GAAG;QACjC,2BAA2B,EAAE,oBAAoB,IAAI,EAAE;QACvD,2BAA2B,EAAE,WAAW;QACxC,qBAAqB,EAAE,MAAM;QAC7B,kBAAkB,EAAE,MAAM;QAC1B,2BAA2B,EAAE,MAAM,CAAC,cAAc,CAAC;QACnD,qBAAqB,EAAE,GAAG;KAC3B,CAAC;IAEF,mEAAmE;IACnE,IAAI,OAAO,CAAC,kBAAkB,EAAE,CAAC;QAC/B,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,kBAAkB,CAAC,EAAE,CAAC;YACpE,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,KAAK,EAAE,EAAE,CAAC;gBAC9B,KAAK,CAAC,IAAI,CAAC,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC,CAAC;YAC9B,CAAC;QACH,CAAC;QACD,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrB,GAAG,CAAC,wBAAwB,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACjD,CAAC;IACH,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resilience utilities for Telora daemon and factory.
|
|
3
|
+
*
|
|
4
|
+
* Provides retry with exponential backoff and circuit breaker patterns
|
|
5
|
+
* to make API calls and other operations robust against transient failures.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Custom error class that carries HTTP status information.
|
|
9
|
+
*/
|
|
10
|
+
export declare class ApiError extends Error {
|
|
11
|
+
readonly statusCode?: number | undefined;
|
|
12
|
+
readonly isRetryable: boolean;
|
|
13
|
+
constructor(message: string, statusCode?: number | undefined, isRetryable?: boolean);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Determine if an error is retryable.
|
|
17
|
+
*/
|
|
18
|
+
export declare function isRetryableError(error: unknown): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Classify an HTTP response status code for retry decisions.
|
|
21
|
+
*/
|
|
22
|
+
export declare function classifyHttpStatus(status: number): 'retryable' | 'non-retryable' | 'unknown';
|
|
23
|
+
export interface RetryOptions {
|
|
24
|
+
/** Maximum number of attempts (including the first). Default: 3 */
|
|
25
|
+
maxAttempts?: number;
|
|
26
|
+
/** Base delay in ms for exponential backoff. Default: 1000 */
|
|
27
|
+
baseDelayMs?: number;
|
|
28
|
+
/** Label for log messages. Default: 'operation' */
|
|
29
|
+
label?: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Execute an async function with retry and exponential backoff.
|
|
33
|
+
*
|
|
34
|
+
* Only retries on errors classified as retryable. Non-retryable errors
|
|
35
|
+
* are thrown immediately.
|
|
36
|
+
*/
|
|
37
|
+
export declare function withRetry<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T>;
|
|
38
|
+
export type CircuitState = 'closed' | 'open' | 'half-open';
|
|
39
|
+
export interface CircuitBreakerOptions {
|
|
40
|
+
/** Number of consecutive failures before opening the circuit. Default: 5 */
|
|
41
|
+
failureThreshold?: number;
|
|
42
|
+
/** Base cooldown period in ms before allowing a probe. Default: 60000 */
|
|
43
|
+
cooldownMs?: number;
|
|
44
|
+
/** Maximum cooldown period in ms (caps exponential backoff). Default: 300000 (5 min) */
|
|
45
|
+
maxCooldownMs?: number;
|
|
46
|
+
/** Number of consecutive successes required to close from half-open. Default: 2 */
|
|
47
|
+
halfOpenSuccessThreshold?: number;
|
|
48
|
+
/** Label for log messages. Default: 'circuit' */
|
|
49
|
+
label?: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Get all registered circuit breaker instances.
|
|
53
|
+
*/
|
|
54
|
+
export declare function getCircuitBreakers(): readonly CircuitBreaker[];
|
|
55
|
+
/**
|
|
56
|
+
* Force-close all registered circuit breakers. Used by SIGUSR2 handler.
|
|
57
|
+
*/
|
|
58
|
+
export declare function forceCloseAllCircuitBreakers(): void;
|
|
59
|
+
/**
|
|
60
|
+
* Circuit breaker to prevent hammering a failing endpoint.
|
|
61
|
+
*
|
|
62
|
+
* States:
|
|
63
|
+
* - closed: Normal operation, requests pass through
|
|
64
|
+
* - open: Requests fail immediately without hitting the network
|
|
65
|
+
* - half-open: Probe requests are allowed; N consecutive successes required to close
|
|
66
|
+
*
|
|
67
|
+
* Features:
|
|
68
|
+
* - Graduated recovery: half-open requires N consecutive successes (default 2)
|
|
69
|
+
* - Exponential cooldown backoff on repeated open cycles
|
|
70
|
+
* - State transition logging with endpoint label and reason
|
|
71
|
+
* - Force-close for operator override (SIGUSR2)
|
|
72
|
+
*/
|
|
73
|
+
export declare class CircuitBreaker {
|
|
74
|
+
private state;
|
|
75
|
+
private consecutiveFailures;
|
|
76
|
+
private lastFailureTime;
|
|
77
|
+
private halfOpenSuccesses;
|
|
78
|
+
private consecutiveOpenCycles;
|
|
79
|
+
/** Tracks whether we already logged the open-state transition to suppress repeat logs. */
|
|
80
|
+
private openStateLogged;
|
|
81
|
+
/** Count of suppressed CircuitOpenError throws since the last state-change log. */
|
|
82
|
+
private suppressedCallCount;
|
|
83
|
+
private readonly failureThreshold;
|
|
84
|
+
private readonly baseCooldownMs;
|
|
85
|
+
private readonly maxCooldownMs;
|
|
86
|
+
private readonly halfOpenSuccessThreshold;
|
|
87
|
+
readonly label: string;
|
|
88
|
+
constructor(options?: CircuitBreakerOptions);
|
|
89
|
+
/** Current effective cooldown with exponential backoff. */
|
|
90
|
+
private getEffectiveCooldownMs;
|
|
91
|
+
/** Log a state transition. */
|
|
92
|
+
private logTransition;
|
|
93
|
+
/**
|
|
94
|
+
* Execute a function through the circuit breaker.
|
|
95
|
+
* Throws CircuitOpenError if the circuit is open and cooldown hasn't elapsed.
|
|
96
|
+
*/
|
|
97
|
+
execute<T>(fn: () => Promise<T>): Promise<T>;
|
|
98
|
+
/** Transition from open to half-open, logging suppressed call count. */
|
|
99
|
+
private transitionToHalfOpen;
|
|
100
|
+
private onSuccess;
|
|
101
|
+
private onFailure;
|
|
102
|
+
/** Get current circuit state for diagnostics. */
|
|
103
|
+
getState(): CircuitState;
|
|
104
|
+
/** Get the number of consecutive open cycles (for diagnostics). */
|
|
105
|
+
getConsecutiveOpenCycles(): number;
|
|
106
|
+
/** Reset the circuit breaker to closed state. */
|
|
107
|
+
reset(): void;
|
|
108
|
+
/**
|
|
109
|
+
* Force-close the circuit breaker. Used for operator override.
|
|
110
|
+
* Resets all state including backoff counters.
|
|
111
|
+
*/
|
|
112
|
+
forceClose(): void;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Error thrown when the circuit breaker is open.
|
|
116
|
+
*/
|
|
117
|
+
export declare class CircuitOpenError extends Error {
|
|
118
|
+
constructor(message: string);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Create a setInterval that wraps the callback in try/catch.
|
|
122
|
+
* Prevents unhandled exceptions from crashing the process.
|
|
123
|
+
*
|
|
124
|
+
* @returns The interval handle for clearing with clearInterval()
|
|
125
|
+
*/
|
|
126
|
+
export declare function safeInterval(callback: () => void | Promise<void>, intervalMs: number, label: string): ReturnType<typeof setInterval>;
|
|
127
|
+
//# sourceMappingURL=resilience.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resilience.d.ts","sourceRoot":"","sources":["../src/resilience.ts"],"names":[],"mappings":"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"}
|