@mondaydotcomorg/atp-providers 0.19.7 → 0.19.8
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 +1171 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.js +1151 -8
- package/dist/index.js.map +1 -1
- package/package.json +12 -7
- package/project.json +1 -2
- package/tsup.config.ts +17 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1171 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var atpRuntime = require('@mondaydotcomorg/atp-runtime');
|
|
4
|
+
var fs = require('fs');
|
|
5
|
+
var path = require('path');
|
|
6
|
+
var os = require('os');
|
|
7
|
+
var crypto = require('crypto');
|
|
8
|
+
var promises = require('fs/promises');
|
|
9
|
+
var api = require('@opentelemetry/api');
|
|
10
|
+
|
|
11
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
|
+
|
|
13
|
+
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
14
|
+
var os__default = /*#__PURE__*/_interopDefault(os);
|
|
15
|
+
|
|
16
|
+
var __defProp = Object.defineProperty;
|
|
17
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
18
|
+
|
|
19
|
+
// src/cache/memory.ts
|
|
20
|
+
var MemoryCache = class {
|
|
21
|
+
static {
|
|
22
|
+
__name(this, "MemoryCache");
|
|
23
|
+
}
|
|
24
|
+
name = "memory";
|
|
25
|
+
cache;
|
|
26
|
+
maxKeys;
|
|
27
|
+
defaultTTL;
|
|
28
|
+
constructor(options = {}) {
|
|
29
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
30
|
+
this.maxKeys = options.maxKeys || 1e3;
|
|
31
|
+
this.defaultTTL = options.defaultTTL || 3600;
|
|
32
|
+
}
|
|
33
|
+
async get(key) {
|
|
34
|
+
const entry = this.cache.get(key);
|
|
35
|
+
if (!entry) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
if (entry.expiresAt !== -1 && Date.now() > entry.expiresAt) {
|
|
39
|
+
this.cache.delete(key);
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
this.cache.delete(key);
|
|
43
|
+
this.cache.set(key, entry);
|
|
44
|
+
return entry.value;
|
|
45
|
+
}
|
|
46
|
+
async set(key, value, ttl) {
|
|
47
|
+
if (this.cache.size >= this.maxKeys && !this.cache.has(key)) {
|
|
48
|
+
const firstKey = this.cache.keys().next().value;
|
|
49
|
+
if (firstKey) {
|
|
50
|
+
this.cache.delete(firstKey);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const expiresAt = ttl ? Date.now() + ttl * 1e3 : this.defaultTTL > 0 ? Date.now() + this.defaultTTL * 1e3 : -1;
|
|
54
|
+
this.cache.set(key, {
|
|
55
|
+
value,
|
|
56
|
+
expiresAt
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
async delete(key) {
|
|
60
|
+
this.cache.delete(key);
|
|
61
|
+
}
|
|
62
|
+
async has(key) {
|
|
63
|
+
const value = await this.get(key);
|
|
64
|
+
return value !== null;
|
|
65
|
+
}
|
|
66
|
+
async clear(pattern) {
|
|
67
|
+
if (!pattern) {
|
|
68
|
+
this.cache.clear();
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const regex = new RegExp("^" + pattern.replace(/\*/g, ".*") + "$");
|
|
72
|
+
for (const key of this.cache.keys()) {
|
|
73
|
+
if (regex.test(key)) {
|
|
74
|
+
this.cache.delete(key);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async mget(keys) {
|
|
79
|
+
return Promise.all(keys.map((key) => this.get(key)));
|
|
80
|
+
}
|
|
81
|
+
async mset(entries) {
|
|
82
|
+
for (const [key, value, ttl] of entries) {
|
|
83
|
+
await this.set(key, value, ttl);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
async disconnect() {
|
|
87
|
+
this.cache.clear();
|
|
88
|
+
}
|
|
89
|
+
/** Get cache statistics */
|
|
90
|
+
getStats() {
|
|
91
|
+
return {
|
|
92
|
+
keys: this.cache.size,
|
|
93
|
+
maxKeys: this.maxKeys,
|
|
94
|
+
utilization: this.cache.size / this.maxKeys * 100
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
var RedisCache = class {
|
|
99
|
+
static {
|
|
100
|
+
__name(this, "RedisCache");
|
|
101
|
+
}
|
|
102
|
+
name = "redis";
|
|
103
|
+
redis;
|
|
104
|
+
keyPrefix;
|
|
105
|
+
defaultTTL;
|
|
106
|
+
constructor(options) {
|
|
107
|
+
this.redis = options.redis;
|
|
108
|
+
this.keyPrefix = options.keyPrefix || "atp:cache:";
|
|
109
|
+
this.defaultTTL = options.defaultTTL;
|
|
110
|
+
}
|
|
111
|
+
getFullKey(key) {
|
|
112
|
+
return `${this.keyPrefix}${key}`;
|
|
113
|
+
}
|
|
114
|
+
async get(key) {
|
|
115
|
+
try {
|
|
116
|
+
const value = await this.redis.get(this.getFullKey(key));
|
|
117
|
+
if (!value) return null;
|
|
118
|
+
return JSON.parse(value);
|
|
119
|
+
} catch (error) {
|
|
120
|
+
atpRuntime.log.error("RedisCache failed to get key", {
|
|
121
|
+
key,
|
|
122
|
+
error: error instanceof Error ? error.message : error
|
|
123
|
+
});
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
async set(key, value, ttl) {
|
|
128
|
+
try {
|
|
129
|
+
const serialized = JSON.stringify(value);
|
|
130
|
+
const fullKey = this.getFullKey(key);
|
|
131
|
+
const effectiveTTL = ttl ?? this.defaultTTL;
|
|
132
|
+
if (effectiveTTL) {
|
|
133
|
+
await this.redis.setex(fullKey, effectiveTTL, serialized);
|
|
134
|
+
} else {
|
|
135
|
+
await this.redis.set(fullKey, serialized);
|
|
136
|
+
}
|
|
137
|
+
} catch (error) {
|
|
138
|
+
atpRuntime.log.error("RedisCache failed to set key", {
|
|
139
|
+
key,
|
|
140
|
+
error: error instanceof Error ? error.message : error
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
async delete(key) {
|
|
145
|
+
try {
|
|
146
|
+
await this.redis.del(this.getFullKey(key));
|
|
147
|
+
} catch (error) {
|
|
148
|
+
atpRuntime.log.error("RedisCache failed to delete key", {
|
|
149
|
+
key,
|
|
150
|
+
error: error instanceof Error ? error.message : error
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
async has(key) {
|
|
155
|
+
try {
|
|
156
|
+
const exists = await this.redis.exists(this.getFullKey(key));
|
|
157
|
+
return exists === 1;
|
|
158
|
+
} catch (error) {
|
|
159
|
+
atpRuntime.log.error("RedisCache failed to check key", {
|
|
160
|
+
key,
|
|
161
|
+
error: error instanceof Error ? error.message : error
|
|
162
|
+
});
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
async clear(pattern) {
|
|
167
|
+
try {
|
|
168
|
+
if (pattern) {
|
|
169
|
+
const fullPattern = this.getFullKey(pattern);
|
|
170
|
+
const keys = await this.redis.keys(fullPattern);
|
|
171
|
+
if (keys.length > 0) {
|
|
172
|
+
await this.redis.del(...keys);
|
|
173
|
+
}
|
|
174
|
+
} else {
|
|
175
|
+
const keys = await this.redis.keys(this.getFullKey("*"));
|
|
176
|
+
if (keys.length > 0) {
|
|
177
|
+
await this.redis.del(...keys);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
} catch (error) {
|
|
181
|
+
atpRuntime.log.error("RedisCache failed to clear cache", {
|
|
182
|
+
error: error instanceof Error ? error.message : error
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
async mget(keys) {
|
|
187
|
+
try {
|
|
188
|
+
const fullKeys = keys.map((key) => this.getFullKey(key));
|
|
189
|
+
const values = await this.redis.mget(...fullKeys);
|
|
190
|
+
return values.map((value) => value ? JSON.parse(value) : null);
|
|
191
|
+
} catch (error) {
|
|
192
|
+
atpRuntime.log.error("RedisCache failed to mget keys", {
|
|
193
|
+
error: error instanceof Error ? error.message : error
|
|
194
|
+
});
|
|
195
|
+
return keys.map(() => null);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
async mset(entries) {
|
|
199
|
+
try {
|
|
200
|
+
const pipeline = this.redis.pipeline();
|
|
201
|
+
for (const [key, value, ttl] of entries) {
|
|
202
|
+
const serialized = JSON.stringify(value);
|
|
203
|
+
const fullKey = this.getFullKey(key);
|
|
204
|
+
const effectiveTTL = ttl ?? this.defaultTTL;
|
|
205
|
+
if (effectiveTTL) {
|
|
206
|
+
pipeline.setex(fullKey, effectiveTTL, serialized);
|
|
207
|
+
} else {
|
|
208
|
+
pipeline.set(fullKey, serialized);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
await pipeline.exec();
|
|
212
|
+
} catch (error) {
|
|
213
|
+
atpRuntime.log.error("RedisCache failed to mset entries", {
|
|
214
|
+
error: error instanceof Error ? error.message : error
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
async disconnect() {
|
|
219
|
+
try {
|
|
220
|
+
await this.redis.quit();
|
|
221
|
+
} catch (error) {
|
|
222
|
+
atpRuntime.log.error("RedisCache failed to disconnect", {
|
|
223
|
+
error: error instanceof Error ? error.message : error
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
var FileCache = class {
|
|
229
|
+
static {
|
|
230
|
+
__name(this, "FileCache");
|
|
231
|
+
}
|
|
232
|
+
name = "file";
|
|
233
|
+
cacheDir;
|
|
234
|
+
maxKeys;
|
|
235
|
+
defaultTTL;
|
|
236
|
+
cleanupInterval;
|
|
237
|
+
cleanupTimer;
|
|
238
|
+
initPromise;
|
|
239
|
+
constructor(options = {}) {
|
|
240
|
+
this.cacheDir = options.cacheDir || path__default.default.join(os__default.default.tmpdir(), "atp-cache");
|
|
241
|
+
this.maxKeys = options.maxKeys || 1e3;
|
|
242
|
+
this.defaultTTL = options.defaultTTL || 3600;
|
|
243
|
+
this.cleanupInterval = options.cleanupInterval || 300;
|
|
244
|
+
this.initPromise = this.initialize();
|
|
245
|
+
}
|
|
246
|
+
async initialize() {
|
|
247
|
+
try {
|
|
248
|
+
await fs.promises.mkdir(this.cacheDir, {
|
|
249
|
+
recursive: true
|
|
250
|
+
});
|
|
251
|
+
this.startCleanup();
|
|
252
|
+
} catch (error) {
|
|
253
|
+
atpRuntime.log.error("FileCache failed to initialize cache directory", {
|
|
254
|
+
error: error instanceof Error ? error.message : error
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
async ensureInitialized() {
|
|
259
|
+
if (this.initPromise) {
|
|
260
|
+
await this.initPromise;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
getFilePath(key) {
|
|
264
|
+
const sanitizedKey = key.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
265
|
+
return path__default.default.join(this.cacheDir, `${sanitizedKey}.json`);
|
|
266
|
+
}
|
|
267
|
+
startCleanup() {
|
|
268
|
+
if (this.cleanupInterval > 0) {
|
|
269
|
+
this.cleanupTimer = setInterval(() => {
|
|
270
|
+
this.cleanExpired().catch((error) => {
|
|
271
|
+
atpRuntime.log.error("FileCache cleanup error", {
|
|
272
|
+
error
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
}, this.cleanupInterval * 1e3);
|
|
276
|
+
if (this.cleanupTimer.unref) {
|
|
277
|
+
this.cleanupTimer.unref();
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
async cleanExpired() {
|
|
282
|
+
try {
|
|
283
|
+
await this.ensureInitialized();
|
|
284
|
+
const files = await fs.promises.readdir(this.cacheDir);
|
|
285
|
+
const now = Date.now();
|
|
286
|
+
for (const file of files) {
|
|
287
|
+
if (!file.endsWith(".json")) continue;
|
|
288
|
+
const filePath = path__default.default.join(this.cacheDir, file);
|
|
289
|
+
try {
|
|
290
|
+
const content = await fs.promises.readFile(filePath, "utf-8");
|
|
291
|
+
const entry = JSON.parse(content);
|
|
292
|
+
if (entry.expiresAt !== -1 && now > entry.expiresAt) {
|
|
293
|
+
await fs.promises.unlink(filePath);
|
|
294
|
+
}
|
|
295
|
+
} catch {
|
|
296
|
+
try {
|
|
297
|
+
await fs.promises.unlink(filePath);
|
|
298
|
+
} catch {
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
await this.enforceMaxKeys();
|
|
303
|
+
} catch (error) {
|
|
304
|
+
atpRuntime.log.error("FileCache failed to clean expired entries", {
|
|
305
|
+
error: error instanceof Error ? error.message : error
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
async enforceMaxKeys() {
|
|
310
|
+
try {
|
|
311
|
+
const files = await fs.promises.readdir(this.cacheDir);
|
|
312
|
+
const jsonFiles = files.filter((f) => f.endsWith(".json"));
|
|
313
|
+
if (jsonFiles.length > this.maxKeys) {
|
|
314
|
+
const fileStats = await Promise.all(jsonFiles.map(async (file) => {
|
|
315
|
+
const filePath = path__default.default.join(this.cacheDir, file);
|
|
316
|
+
const stats = await fs.promises.stat(filePath);
|
|
317
|
+
return {
|
|
318
|
+
file,
|
|
319
|
+
mtime: stats.mtime.getTime()
|
|
320
|
+
};
|
|
321
|
+
}));
|
|
322
|
+
fileStats.sort((a, b) => a.mtime - b.mtime);
|
|
323
|
+
const toDelete = fileStats.slice(0, jsonFiles.length - this.maxKeys);
|
|
324
|
+
await Promise.all(toDelete.map((item) => {
|
|
325
|
+
const filePath = path__default.default.join(this.cacheDir, item.file);
|
|
326
|
+
return fs.promises.unlink(filePath).catch(() => {
|
|
327
|
+
});
|
|
328
|
+
}));
|
|
329
|
+
}
|
|
330
|
+
} catch (error) {
|
|
331
|
+
atpRuntime.log.error("FileCache failed to enforce max keys", {
|
|
332
|
+
error: error instanceof Error ? error.message : error
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
async get(key) {
|
|
337
|
+
try {
|
|
338
|
+
await this.ensureInitialized();
|
|
339
|
+
const filePath = this.getFilePath(key);
|
|
340
|
+
const content = await fs.promises.readFile(filePath, "utf-8");
|
|
341
|
+
const entry = JSON.parse(content);
|
|
342
|
+
if (entry.expiresAt !== -1 && Date.now() > entry.expiresAt) {
|
|
343
|
+
await this.delete(key);
|
|
344
|
+
return null;
|
|
345
|
+
}
|
|
346
|
+
return entry.value;
|
|
347
|
+
} catch (error) {
|
|
348
|
+
if (error.code === "ENOENT") {
|
|
349
|
+
return null;
|
|
350
|
+
}
|
|
351
|
+
atpRuntime.log.error("FileCache failed to get key", {
|
|
352
|
+
key,
|
|
353
|
+
error: error instanceof Error ? error.message : error
|
|
354
|
+
});
|
|
355
|
+
return null;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
async set(key, value, ttl) {
|
|
359
|
+
try {
|
|
360
|
+
await this.ensureInitialized();
|
|
361
|
+
await this.enforceMaxKeys();
|
|
362
|
+
const expiresAt = ttl ? Date.now() + ttl * 1e3 : this.defaultTTL > 0 ? Date.now() + this.defaultTTL * 1e3 : -1;
|
|
363
|
+
const entry = {
|
|
364
|
+
value,
|
|
365
|
+
expiresAt
|
|
366
|
+
};
|
|
367
|
+
const filePath = this.getFilePath(key);
|
|
368
|
+
await fs.promises.writeFile(filePath, JSON.stringify(entry), "utf-8");
|
|
369
|
+
} catch (error) {
|
|
370
|
+
atpRuntime.log.error("FileCache failed to set key", {
|
|
371
|
+
key,
|
|
372
|
+
error: error instanceof Error ? error.message : error
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
async delete(key) {
|
|
377
|
+
try {
|
|
378
|
+
await this.ensureInitialized();
|
|
379
|
+
const filePath = this.getFilePath(key);
|
|
380
|
+
await fs.promises.unlink(filePath);
|
|
381
|
+
} catch (error) {
|
|
382
|
+
if (error.code !== "ENOENT") {
|
|
383
|
+
atpRuntime.log.error("FileCache failed to delete key", {
|
|
384
|
+
key,
|
|
385
|
+
error: error instanceof Error ? error.message : error
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
async has(key) {
|
|
391
|
+
const value = await this.get(key);
|
|
392
|
+
return value !== null;
|
|
393
|
+
}
|
|
394
|
+
async clear(pattern) {
|
|
395
|
+
try {
|
|
396
|
+
await this.ensureInitialized();
|
|
397
|
+
const files = await fs.promises.readdir(this.cacheDir);
|
|
398
|
+
if (!pattern) {
|
|
399
|
+
await Promise.all(files.filter((f) => f.endsWith(".json")).map((file) => {
|
|
400
|
+
const filePath = path__default.default.join(this.cacheDir, file);
|
|
401
|
+
return fs.promises.unlink(filePath).catch(() => {
|
|
402
|
+
});
|
|
403
|
+
}));
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
const regex = new RegExp("^" + pattern.replace(/\*/g, ".*") + "$");
|
|
407
|
+
for (const file of files) {
|
|
408
|
+
if (!file.endsWith(".json")) continue;
|
|
409
|
+
const keyBase = file.replace(".json", "");
|
|
410
|
+
if (regex.test(keyBase)) {
|
|
411
|
+
const filePath = path__default.default.join(this.cacheDir, file);
|
|
412
|
+
await fs.promises.unlink(filePath).catch(() => {
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
} catch (error) {
|
|
417
|
+
atpRuntime.log.error("FileCache failed to clear cache", {
|
|
418
|
+
error: error instanceof Error ? error.message : error
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
async mget(keys) {
|
|
423
|
+
return Promise.all(keys.map((key) => this.get(key)));
|
|
424
|
+
}
|
|
425
|
+
async mset(entries) {
|
|
426
|
+
await Promise.all(entries.map(([key, value, ttl]) => this.set(key, value, ttl)));
|
|
427
|
+
}
|
|
428
|
+
async disconnect() {
|
|
429
|
+
if (this.cleanupTimer) {
|
|
430
|
+
clearInterval(this.cleanupTimer);
|
|
431
|
+
this.cleanupTimer = void 0;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
/** Get cache statistics */
|
|
435
|
+
async getStats() {
|
|
436
|
+
try {
|
|
437
|
+
await this.ensureInitialized();
|
|
438
|
+
const files = await fs.promises.readdir(this.cacheDir);
|
|
439
|
+
const jsonFiles = files.filter((f) => f.endsWith(".json"));
|
|
440
|
+
let totalSize = 0;
|
|
441
|
+
for (const file of jsonFiles) {
|
|
442
|
+
const filePath = path__default.default.join(this.cacheDir, file);
|
|
443
|
+
try {
|
|
444
|
+
const stats = await fs.promises.stat(filePath);
|
|
445
|
+
totalSize += stats.size;
|
|
446
|
+
} catch {
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
return {
|
|
450
|
+
keys: jsonFiles.length,
|
|
451
|
+
maxKeys: this.maxKeys,
|
|
452
|
+
utilization: jsonFiles.length / this.maxKeys * 100,
|
|
453
|
+
sizeBytes: totalSize,
|
|
454
|
+
cacheDir: this.cacheDir
|
|
455
|
+
};
|
|
456
|
+
} catch (error) {
|
|
457
|
+
atpRuntime.log.error("[FileCache] Failed to get stats:", error);
|
|
458
|
+
return {
|
|
459
|
+
keys: 0,
|
|
460
|
+
maxKeys: this.maxKeys,
|
|
461
|
+
utilization: 0,
|
|
462
|
+
sizeBytes: 0,
|
|
463
|
+
cacheDir: this.cacheDir
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
/** Manually trigger cleanup of expired entries */
|
|
468
|
+
async cleanup() {
|
|
469
|
+
await this.cleanExpired();
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
// src/auth/env.ts
|
|
474
|
+
var EnvAuthProvider = class {
|
|
475
|
+
static {
|
|
476
|
+
__name(this, "EnvAuthProvider");
|
|
477
|
+
}
|
|
478
|
+
name = "env";
|
|
479
|
+
prefix;
|
|
480
|
+
credentials;
|
|
481
|
+
constructor(options = {}) {
|
|
482
|
+
this.prefix = options.prefix || "ATP_";
|
|
483
|
+
this.credentials = /* @__PURE__ */ new Map();
|
|
484
|
+
if (options.credentials) {
|
|
485
|
+
for (const [key, value] of Object.entries(options.credentials)) {
|
|
486
|
+
this.credentials.set(key, value);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
async getCredential(key) {
|
|
491
|
+
if (this.credentials.has(key)) {
|
|
492
|
+
return this.credentials.get(key) || null;
|
|
493
|
+
}
|
|
494
|
+
const envValue = process.env[key] || process.env[`${this.prefix}${key}`];
|
|
495
|
+
return envValue || null;
|
|
496
|
+
}
|
|
497
|
+
async setCredential(key, value, _ttl) {
|
|
498
|
+
this.credentials.set(key, value);
|
|
499
|
+
}
|
|
500
|
+
async deleteCredential(key) {
|
|
501
|
+
this.credentials.delete(key);
|
|
502
|
+
}
|
|
503
|
+
async listCredentials() {
|
|
504
|
+
const keys = /* @__PURE__ */ new Set();
|
|
505
|
+
for (const key of this.credentials.keys()) {
|
|
506
|
+
keys.add(key);
|
|
507
|
+
}
|
|
508
|
+
for (const key of Object.keys(process.env)) {
|
|
509
|
+
if (key.startsWith(this.prefix)) {
|
|
510
|
+
keys.add(key.substring(this.prefix.length));
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
return Array.from(keys);
|
|
514
|
+
}
|
|
515
|
+
async disconnect() {
|
|
516
|
+
this.credentials.clear();
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
var ScopeCheckerRegistry = class {
|
|
520
|
+
static {
|
|
521
|
+
__name(this, "ScopeCheckerRegistry");
|
|
522
|
+
}
|
|
523
|
+
checkers = /* @__PURE__ */ new Map();
|
|
524
|
+
scopeCache = /* @__PURE__ */ new Map();
|
|
525
|
+
cleanupInterval;
|
|
526
|
+
maxCacheSize = 1e4;
|
|
527
|
+
pendingChecks = /* @__PURE__ */ new Map();
|
|
528
|
+
constructor() {
|
|
529
|
+
this.startCleanup();
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Start periodic cache cleanup
|
|
533
|
+
*/
|
|
534
|
+
startCleanup() {
|
|
535
|
+
this.cleanupInterval = setInterval(() => {
|
|
536
|
+
this.cleanupExpiredCache();
|
|
537
|
+
}, 5 * 60 * 1e3);
|
|
538
|
+
if (this.cleanupInterval.unref) {
|
|
539
|
+
this.cleanupInterval.unref();
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Stop periodic cache cleanup (for testing or shutdown)
|
|
544
|
+
*/
|
|
545
|
+
stopCleanup() {
|
|
546
|
+
if (this.cleanupInterval) {
|
|
547
|
+
clearInterval(this.cleanupInterval);
|
|
548
|
+
this.cleanupInterval = void 0;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Remove expired entries from cache
|
|
553
|
+
*/
|
|
554
|
+
cleanupExpiredCache() {
|
|
555
|
+
const now = Date.now();
|
|
556
|
+
let cleaned = 0;
|
|
557
|
+
for (const [key, value] of this.scopeCache.entries()) {
|
|
558
|
+
if (value.expiresAt <= now) {
|
|
559
|
+
this.scopeCache.delete(key);
|
|
560
|
+
cleaned++;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
if (this.scopeCache.size > this.maxCacheSize) {
|
|
564
|
+
const entriesToRemove = this.scopeCache.size - this.maxCacheSize;
|
|
565
|
+
const entries = Array.from(this.scopeCache.entries());
|
|
566
|
+
entries.sort((a, b) => a[1].expiresAt - b[1].expiresAt);
|
|
567
|
+
for (let i = 0; i < entriesToRemove; i++) {
|
|
568
|
+
const entry = entries[i];
|
|
569
|
+
if (entry) {
|
|
570
|
+
this.scopeCache.delete(entry[0]);
|
|
571
|
+
cleaned++;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
if (cleaned > 0) {
|
|
576
|
+
atpRuntime.log.debug(`Cleaned ${cleaned} expired/old scope cache entries`);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Register a custom scope checker
|
|
581
|
+
*/
|
|
582
|
+
register(checker) {
|
|
583
|
+
this.checkers.set(checker.provider, checker);
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* Check if a scope checker is available for a provider
|
|
587
|
+
*/
|
|
588
|
+
hasChecker(provider) {
|
|
589
|
+
return this.checkers.has(provider);
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* Get scope checker for a provider
|
|
593
|
+
*/
|
|
594
|
+
getChecker(provider) {
|
|
595
|
+
return this.checkers.get(provider);
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* Check what scopes a token has (with caching and deduplication)
|
|
599
|
+
* @param provider - Provider name
|
|
600
|
+
* @param token - Access token
|
|
601
|
+
* @param cacheTTL - Cache TTL in seconds (default: 3600 = 1 hour)
|
|
602
|
+
*/
|
|
603
|
+
async checkScopes(provider, token, cacheTTL = 3600) {
|
|
604
|
+
const cacheKey = `${provider}:${this.hashToken(token)}`;
|
|
605
|
+
const cached = this.scopeCache.get(cacheKey);
|
|
606
|
+
if (cached && cached.expiresAt > Date.now()) {
|
|
607
|
+
return cached.scopes;
|
|
608
|
+
}
|
|
609
|
+
const pending = this.pendingChecks.get(cacheKey);
|
|
610
|
+
if (pending) {
|
|
611
|
+
return pending;
|
|
612
|
+
}
|
|
613
|
+
const checker = this.checkers.get(provider);
|
|
614
|
+
if (!checker) {
|
|
615
|
+
throw new Error(`No scope checker registered for provider: ${provider}`);
|
|
616
|
+
}
|
|
617
|
+
const checkPromise = (async () => {
|
|
618
|
+
try {
|
|
619
|
+
const scopes = await checker.check(token);
|
|
620
|
+
this.scopeCache.set(cacheKey, {
|
|
621
|
+
scopes,
|
|
622
|
+
expiresAt: Date.now() + cacheTTL * 1e3
|
|
623
|
+
});
|
|
624
|
+
return scopes;
|
|
625
|
+
} finally {
|
|
626
|
+
this.pendingChecks.delete(cacheKey);
|
|
627
|
+
}
|
|
628
|
+
})();
|
|
629
|
+
this.pendingChecks.set(cacheKey, checkPromise);
|
|
630
|
+
return checkPromise;
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* Validate if a token is still valid
|
|
634
|
+
*/
|
|
635
|
+
async validateToken(provider, token) {
|
|
636
|
+
const checker = this.checkers.get(provider);
|
|
637
|
+
if (!checker || !checker.validate) {
|
|
638
|
+
return true;
|
|
639
|
+
}
|
|
640
|
+
return await checker.validate(token);
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Get complete token information
|
|
644
|
+
*/
|
|
645
|
+
async getTokenInfo(provider, token) {
|
|
646
|
+
const checker = this.checkers.get(provider);
|
|
647
|
+
if (!checker) {
|
|
648
|
+
throw new Error(`No scope checker registered for provider: ${provider}`);
|
|
649
|
+
}
|
|
650
|
+
const [scopes, valid] = await Promise.all([
|
|
651
|
+
checker.check(token),
|
|
652
|
+
checker.validate ? checker.validate(token) : Promise.resolve(true)
|
|
653
|
+
]);
|
|
654
|
+
return {
|
|
655
|
+
valid,
|
|
656
|
+
scopes
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* Clear cached scopes
|
|
661
|
+
*/
|
|
662
|
+
clearCache(provider) {
|
|
663
|
+
if (provider) {
|
|
664
|
+
for (const key of this.scopeCache.keys()) {
|
|
665
|
+
if (key.startsWith(`${provider}:`)) {
|
|
666
|
+
this.scopeCache.delete(key);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
} else {
|
|
670
|
+
this.scopeCache.clear();
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* Hash token for cache key (SHA-256, first 16 chars)
|
|
675
|
+
*/
|
|
676
|
+
hashToken(token) {
|
|
677
|
+
return crypto.createHash("sha256").update(token).digest("hex").substring(0, 16);
|
|
678
|
+
}
|
|
679
|
+
};
|
|
680
|
+
var JSONLAuditSink = class {
|
|
681
|
+
static {
|
|
682
|
+
__name(this, "JSONLAuditSink");
|
|
683
|
+
}
|
|
684
|
+
name = "jsonl";
|
|
685
|
+
filePath;
|
|
686
|
+
sanitizeSecrets;
|
|
687
|
+
buffer = [];
|
|
688
|
+
flushInterval = null;
|
|
689
|
+
batchSize;
|
|
690
|
+
constructor(options) {
|
|
691
|
+
this.filePath = options.filePath;
|
|
692
|
+
this.sanitizeSecrets = options.sanitizeSecrets ?? true;
|
|
693
|
+
this.batchSize = options.batchSize || 10;
|
|
694
|
+
const dir = path.dirname(this.filePath);
|
|
695
|
+
if (!fs.existsSync(dir)) {
|
|
696
|
+
promises.mkdir(dir, {
|
|
697
|
+
recursive: true
|
|
698
|
+
}).catch((err) => {
|
|
699
|
+
atpRuntime.log.error("Failed to create audit directory", {
|
|
700
|
+
error: err.message
|
|
701
|
+
});
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
if (options.flushIntervalMs) {
|
|
705
|
+
this.flushInterval = setInterval(() => {
|
|
706
|
+
if (this.buffer.length > 0) {
|
|
707
|
+
this.flush().catch((err) => {
|
|
708
|
+
atpRuntime.log.error("Failed to flush audit buffer", {
|
|
709
|
+
error: err.message
|
|
710
|
+
});
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
}, options.flushIntervalMs);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
async write(event) {
|
|
717
|
+
const sanitized = this.sanitizeSecrets ? this.sanitizeEvent(event) : event;
|
|
718
|
+
const line = JSON.stringify(sanitized) + "\n";
|
|
719
|
+
try {
|
|
720
|
+
await promises.appendFile(this.filePath, line, "utf8");
|
|
721
|
+
} catch (error) {
|
|
722
|
+
atpRuntime.log.error("Failed to write audit event", {
|
|
723
|
+
error: error.message
|
|
724
|
+
});
|
|
725
|
+
throw error;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
async writeBatch(events) {
|
|
729
|
+
const sanitized = this.sanitizeSecrets ? events.map((e) => this.sanitizeEvent(e)) : events;
|
|
730
|
+
const lines = sanitized.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
731
|
+
try {
|
|
732
|
+
await promises.appendFile(this.filePath, lines, "utf8");
|
|
733
|
+
} catch (error) {
|
|
734
|
+
atpRuntime.log.error("Failed to write audit batch", {
|
|
735
|
+
error: error.message
|
|
736
|
+
});
|
|
737
|
+
throw error;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
async query(filter) {
|
|
741
|
+
try {
|
|
742
|
+
const content = await promises.readFile(this.filePath, "utf8");
|
|
743
|
+
const lines = content.split("\n").filter((line) => line.trim());
|
|
744
|
+
const events = lines.map((line) => JSON.parse(line));
|
|
745
|
+
return events.filter((event) => {
|
|
746
|
+
if (filter.clientId && event.clientId !== filter.clientId) return false;
|
|
747
|
+
if (filter.userId && event.userId !== filter.userId) return false;
|
|
748
|
+
if (filter.from && event.timestamp < filter.from) return false;
|
|
749
|
+
if (filter.to && event.timestamp > filter.to) return false;
|
|
750
|
+
if (filter.resource && event.resource !== filter.resource) return false;
|
|
751
|
+
if (filter.eventType) {
|
|
752
|
+
const types = Array.isArray(filter.eventType) ? filter.eventType : [
|
|
753
|
+
filter.eventType
|
|
754
|
+
];
|
|
755
|
+
if (!types.includes(event.eventType)) return false;
|
|
756
|
+
}
|
|
757
|
+
if (filter.status) {
|
|
758
|
+
const statuses = Array.isArray(filter.status) ? filter.status : [
|
|
759
|
+
filter.status
|
|
760
|
+
];
|
|
761
|
+
if (!statuses.includes(event.status)) return false;
|
|
762
|
+
}
|
|
763
|
+
if (filter.minRiskScore && (event.riskScore || 0) < filter.minRiskScore) return false;
|
|
764
|
+
return true;
|
|
765
|
+
}).slice(filter.offset || 0, (filter.offset || 0) + (filter.limit || 100));
|
|
766
|
+
} catch (error) {
|
|
767
|
+
if (error.code === "ENOENT") {
|
|
768
|
+
return [];
|
|
769
|
+
}
|
|
770
|
+
throw error;
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
async disconnect() {
|
|
774
|
+
if (this.flushInterval) {
|
|
775
|
+
clearInterval(this.flushInterval);
|
|
776
|
+
}
|
|
777
|
+
if (this.buffer.length > 0) {
|
|
778
|
+
await this.flush();
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
async flush() {
|
|
782
|
+
if (this.buffer.length === 0) return;
|
|
783
|
+
await this.writeBatch([
|
|
784
|
+
...this.buffer
|
|
785
|
+
]);
|
|
786
|
+
this.buffer = [];
|
|
787
|
+
}
|
|
788
|
+
sanitizeEvent(event) {
|
|
789
|
+
const sanitized = {
|
|
790
|
+
...event
|
|
791
|
+
};
|
|
792
|
+
if (sanitized.code) {
|
|
793
|
+
sanitized.code = this.sanitizeString(sanitized.code);
|
|
794
|
+
}
|
|
795
|
+
if (sanitized.input) {
|
|
796
|
+
sanitized.input = this.sanitizeObject(sanitized.input);
|
|
797
|
+
}
|
|
798
|
+
if (sanitized.output) {
|
|
799
|
+
sanitized.output = this.sanitizeObject(sanitized.output);
|
|
800
|
+
}
|
|
801
|
+
return sanitized;
|
|
802
|
+
}
|
|
803
|
+
sanitizeString(str) {
|
|
804
|
+
const patterns = [
|
|
805
|
+
/api[_-]?key/gi,
|
|
806
|
+
/secret/gi,
|
|
807
|
+
/token/gi,
|
|
808
|
+
/password/gi,
|
|
809
|
+
/bearer/gi,
|
|
810
|
+
/authorization/gi
|
|
811
|
+
];
|
|
812
|
+
for (const pattern of patterns) {
|
|
813
|
+
str = str.replace(new RegExp(`(${pattern.source})\\s*[:=]\\s*['"]?([^'"\\s]+)`, "gi"), "$1: [REDACTED]");
|
|
814
|
+
}
|
|
815
|
+
return str;
|
|
816
|
+
}
|
|
817
|
+
sanitizeObject(obj) {
|
|
818
|
+
if (typeof obj !== "object" || obj === null) {
|
|
819
|
+
return obj;
|
|
820
|
+
}
|
|
821
|
+
if (Array.isArray(obj)) {
|
|
822
|
+
return obj.map((item) => this.sanitizeObject(item));
|
|
823
|
+
}
|
|
824
|
+
const sanitized = {};
|
|
825
|
+
const secretPatterns = [
|
|
826
|
+
"key",
|
|
827
|
+
"secret",
|
|
828
|
+
"token",
|
|
829
|
+
"password",
|
|
830
|
+
"bearer",
|
|
831
|
+
"auth"
|
|
832
|
+
];
|
|
833
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
834
|
+
const lowerKey = key.toLowerCase();
|
|
835
|
+
if (secretPatterns.some((pattern) => lowerKey.includes(pattern))) {
|
|
836
|
+
sanitized[key] = "[REDACTED]";
|
|
837
|
+
} else if (typeof value === "object") {
|
|
838
|
+
sanitized[key] = this.sanitizeObject(value);
|
|
839
|
+
} else {
|
|
840
|
+
sanitized[key] = value;
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
return sanitized;
|
|
844
|
+
}
|
|
845
|
+
};
|
|
846
|
+
|
|
847
|
+
// src/audit/otel-metrics.ts
|
|
848
|
+
exports.OTelCounter = void 0;
|
|
849
|
+
(function(OTelCounter2) {
|
|
850
|
+
OTelCounter2["EXECUTIONS_TOTAL"] = "atp.executions.total";
|
|
851
|
+
OTelCounter2["TOOLS_CALLS"] = "atp.tools.calls";
|
|
852
|
+
OTelCounter2["LLM_CALLS"] = "atp.llm.calls";
|
|
853
|
+
OTelCounter2["APPROVALS_TOTAL"] = "atp.approvals.total";
|
|
854
|
+
OTelCounter2["SECURITY_EVENTS"] = "atp.security.events";
|
|
855
|
+
})(exports.OTelCounter || (exports.OTelCounter = {}));
|
|
856
|
+
exports.OTelHistogram = void 0;
|
|
857
|
+
(function(OTelHistogram2) {
|
|
858
|
+
OTelHistogram2["EXECUTION_DURATION"] = "atp.execution.duration";
|
|
859
|
+
OTelHistogram2["TOOL_DURATION"] = "atp.tool.duration";
|
|
860
|
+
})(exports.OTelHistogram || (exports.OTelHistogram = {}));
|
|
861
|
+
exports.OTelSpan = void 0;
|
|
862
|
+
(function(OTelSpan2) {
|
|
863
|
+
OTelSpan2["EXECUTION_START"] = "atp.execution.start";
|
|
864
|
+
OTelSpan2["EXECUTION_COMPLETE"] = "atp.execution.complete";
|
|
865
|
+
OTelSpan2["EXECUTION_PAUSE"] = "atp.execution.pause";
|
|
866
|
+
OTelSpan2["EXECUTION_RESUME"] = "atp.execution.resume";
|
|
867
|
+
OTelSpan2["EXECUTION_ERROR"] = "atp.execution.error";
|
|
868
|
+
OTelSpan2["TOOL_CALL"] = "atp.tool_call";
|
|
869
|
+
OTelSpan2["LLM_CALL"] = "atp.llm_call";
|
|
870
|
+
OTelSpan2["APPROVAL_REQUEST"] = "atp.approval.request";
|
|
871
|
+
OTelSpan2["APPROVAL_RESPONSE"] = "atp.approval.response";
|
|
872
|
+
OTelSpan2["CLIENT_INIT"] = "atp.client_init";
|
|
873
|
+
OTelSpan2["ERROR"] = "atp.error";
|
|
874
|
+
})(exports.OTelSpan || (exports.OTelSpan = {}));
|
|
875
|
+
exports.OTelAttribute = void 0;
|
|
876
|
+
(function(OTelAttribute2) {
|
|
877
|
+
OTelAttribute2["EVENT_ID"] = "atp.event.id";
|
|
878
|
+
OTelAttribute2["EVENT_TYPE"] = "atp.event.type";
|
|
879
|
+
OTelAttribute2["EVENT_ACTION"] = "atp.event.action";
|
|
880
|
+
OTelAttribute2["TIMESTAMP"] = "atp.timestamp";
|
|
881
|
+
OTelAttribute2["CLIENT_ID"] = "atp.client.id";
|
|
882
|
+
OTelAttribute2["USER_ID"] = "atp.user.id";
|
|
883
|
+
OTelAttribute2["IP_ADDRESS"] = "atp.ip_address";
|
|
884
|
+
OTelAttribute2["USER_AGENT"] = "atp.user_agent";
|
|
885
|
+
OTelAttribute2["STATUS"] = "atp.status";
|
|
886
|
+
OTelAttribute2["RESOURCE"] = "atp.resource";
|
|
887
|
+
OTelAttribute2["RESOURCE_ID"] = "atp.resource.id";
|
|
888
|
+
OTelAttribute2["TOOL_NAME"] = "atp.tool.name";
|
|
889
|
+
OTelAttribute2["TOOL_INPUT_SIZE"] = "tool.input_size";
|
|
890
|
+
OTelAttribute2["TOOL_OUTPUT_SIZE"] = "tool.output_size";
|
|
891
|
+
OTelAttribute2["API_GROUP"] = "atp.api.group";
|
|
892
|
+
OTelAttribute2["DURATION_MS"] = "atp.duration_ms";
|
|
893
|
+
OTelAttribute2["MEMORY_BYTES"] = "atp.memory_bytes";
|
|
894
|
+
OTelAttribute2["LLM_CALLS"] = "atp.llm_calls";
|
|
895
|
+
OTelAttribute2["HTTP_CALLS"] = "atp.http_calls";
|
|
896
|
+
OTelAttribute2["RISK_SCORE"] = "atp.security.risk_score";
|
|
897
|
+
OTelAttribute2["SECURITY_EVENTS"] = "atp.security.events";
|
|
898
|
+
OTelAttribute2["SECURITY_EVENTS_COUNT"] = "atp.security.events_count";
|
|
899
|
+
OTelAttribute2["ERROR_MESSAGE"] = "atp.error.message";
|
|
900
|
+
OTelAttribute2["ERROR_CODE"] = "atp.error.code";
|
|
901
|
+
OTelAttribute2["ERROR_STACK"] = "atp.error.stack";
|
|
902
|
+
})(exports.OTelAttribute || (exports.OTelAttribute = {}));
|
|
903
|
+
var METRIC_CONFIGS = {
|
|
904
|
+
["atp.executions.total"]: {
|
|
905
|
+
description: "Total number of executions",
|
|
906
|
+
unit: "1"
|
|
907
|
+
},
|
|
908
|
+
["atp.tools.calls"]: {
|
|
909
|
+
description: "Tool call count",
|
|
910
|
+
unit: "1"
|
|
911
|
+
},
|
|
912
|
+
["atp.llm.calls"]: {
|
|
913
|
+
description: "LLM call count",
|
|
914
|
+
unit: "1"
|
|
915
|
+
},
|
|
916
|
+
["atp.approvals.total"]: {
|
|
917
|
+
description: "Approval request count",
|
|
918
|
+
unit: "1"
|
|
919
|
+
},
|
|
920
|
+
["atp.security.events"]: {
|
|
921
|
+
description: "Security events count",
|
|
922
|
+
unit: "1"
|
|
923
|
+
},
|
|
924
|
+
["atp.execution.duration"]: {
|
|
925
|
+
description: "Execution duration in milliseconds",
|
|
926
|
+
unit: "ms"
|
|
927
|
+
},
|
|
928
|
+
["atp.tool.duration"]: {
|
|
929
|
+
description: "Tool execution duration",
|
|
930
|
+
unit: "ms"
|
|
931
|
+
}
|
|
932
|
+
};
|
|
933
|
+
var OTEL_SERVICE_NAME = "agent-tool-protocol";
|
|
934
|
+
var OTEL_TRACER_NAME = "agent-tool-protocol";
|
|
935
|
+
var OTEL_METER_NAME = "agent-tool-protocol";
|
|
936
|
+
var ATTRIBUTE_PREFIX_TOOL = "atp.tool";
|
|
937
|
+
var ATTRIBUTE_PREFIX_METADATA = "atp.metadata";
|
|
938
|
+
|
|
939
|
+
// src/audit/opentelemetry.ts
|
|
940
|
+
var OpenTelemetryAuditSink = class {
|
|
941
|
+
static {
|
|
942
|
+
__name(this, "OpenTelemetryAuditSink");
|
|
943
|
+
}
|
|
944
|
+
name = "opentelemetry";
|
|
945
|
+
tracer = api.trace.getTracer(OTEL_TRACER_NAME);
|
|
946
|
+
meter = api.metrics.getMeter(OTEL_METER_NAME);
|
|
947
|
+
executionCounter = this.meter.createCounter(exports.OTelCounter.EXECUTIONS_TOTAL, METRIC_CONFIGS[exports.OTelCounter.EXECUTIONS_TOTAL]);
|
|
948
|
+
toolCallCounter = this.meter.createCounter(exports.OTelCounter.TOOLS_CALLS, METRIC_CONFIGS[exports.OTelCounter.TOOLS_CALLS]);
|
|
949
|
+
llmCallCounter = this.meter.createCounter(exports.OTelCounter.LLM_CALLS, METRIC_CONFIGS[exports.OTelCounter.LLM_CALLS]);
|
|
950
|
+
approvalCounter = this.meter.createCounter(exports.OTelCounter.APPROVALS_TOTAL, METRIC_CONFIGS[exports.OTelCounter.APPROVALS_TOTAL]);
|
|
951
|
+
executionDuration = this.meter.createHistogram(exports.OTelHistogram.EXECUTION_DURATION, METRIC_CONFIGS[exports.OTelHistogram.EXECUTION_DURATION]);
|
|
952
|
+
toolDuration = this.meter.createHistogram(exports.OTelHistogram.TOOL_DURATION, METRIC_CONFIGS[exports.OTelHistogram.TOOL_DURATION]);
|
|
953
|
+
async write(event) {
|
|
954
|
+
const span = this.tracer.startSpan(`atp.${event.eventType}.${event.action}`, {
|
|
955
|
+
attributes: this.buildAttributes(event)
|
|
956
|
+
});
|
|
957
|
+
await api.context.with(api.trace.setSpan(api.context.active(), span), async () => {
|
|
958
|
+
try {
|
|
959
|
+
this.handleEvent(span, event);
|
|
960
|
+
this.recordMetrics(event);
|
|
961
|
+
span.setStatus({
|
|
962
|
+
code: api.SpanStatusCode.OK
|
|
963
|
+
});
|
|
964
|
+
} catch (error) {
|
|
965
|
+
span.setStatus({
|
|
966
|
+
code: api.SpanStatusCode.ERROR,
|
|
967
|
+
message: error.message
|
|
968
|
+
});
|
|
969
|
+
span.recordException(error);
|
|
970
|
+
} finally {
|
|
971
|
+
span.end();
|
|
972
|
+
}
|
|
973
|
+
});
|
|
974
|
+
}
|
|
975
|
+
async writeBatch(events) {
|
|
976
|
+
await Promise.all(events.map((event) => this.write(event)));
|
|
977
|
+
}
|
|
978
|
+
buildAttributes(event) {
|
|
979
|
+
const attrs = {
|
|
980
|
+
[exports.OTelAttribute.EVENT_ID]: event.eventId,
|
|
981
|
+
[exports.OTelAttribute.EVENT_TYPE]: event.eventType,
|
|
982
|
+
[exports.OTelAttribute.EVENT_ACTION]: event.action,
|
|
983
|
+
[exports.OTelAttribute.TIMESTAMP]: event.timestamp,
|
|
984
|
+
[exports.OTelAttribute.CLIENT_ID]: event.clientId,
|
|
985
|
+
[exports.OTelAttribute.STATUS]: event.status
|
|
986
|
+
};
|
|
987
|
+
if (event.userId) attrs[exports.OTelAttribute.USER_ID] = event.userId;
|
|
988
|
+
if (event.ipAddress) attrs[exports.OTelAttribute.IP_ADDRESS] = event.ipAddress;
|
|
989
|
+
if (event.userAgent) attrs[exports.OTelAttribute.USER_AGENT] = event.userAgent;
|
|
990
|
+
if (event.resource) attrs[exports.OTelAttribute.RESOURCE] = event.resource;
|
|
991
|
+
if (event.resourceId) attrs[exports.OTelAttribute.RESOURCE_ID] = event.resourceId;
|
|
992
|
+
if (event.toolName) attrs[exports.OTelAttribute.TOOL_NAME] = event.toolName;
|
|
993
|
+
if (event.apiGroup) attrs[exports.OTelAttribute.API_GROUP] = event.apiGroup;
|
|
994
|
+
if (event.duration !== void 0) attrs[exports.OTelAttribute.DURATION_MS] = event.duration;
|
|
995
|
+
if (event.memoryUsed !== void 0) attrs[exports.OTelAttribute.MEMORY_BYTES] = event.memoryUsed;
|
|
996
|
+
if (event.llmCallsCount !== void 0) attrs[exports.OTelAttribute.LLM_CALLS] = event.llmCallsCount;
|
|
997
|
+
if (event.httpCallsCount !== void 0) attrs[exports.OTelAttribute.HTTP_CALLS] = event.httpCallsCount;
|
|
998
|
+
if (event.riskScore !== void 0) attrs[exports.OTelAttribute.RISK_SCORE] = event.riskScore;
|
|
999
|
+
if (event.securityEvents && event.securityEvents.length > 0) {
|
|
1000
|
+
attrs[exports.OTelAttribute.SECURITY_EVENTS] = JSON.stringify(event.securityEvents);
|
|
1001
|
+
attrs[exports.OTelAttribute.SECURITY_EVENTS_COUNT] = event.securityEvents.length;
|
|
1002
|
+
}
|
|
1003
|
+
if (event.error) {
|
|
1004
|
+
attrs[exports.OTelAttribute.ERROR_MESSAGE] = event.error.message;
|
|
1005
|
+
if (event.error.code) attrs[exports.OTelAttribute.ERROR_CODE] = event.error.code;
|
|
1006
|
+
if (event.error.stack) attrs[exports.OTelAttribute.ERROR_STACK] = event.error.stack;
|
|
1007
|
+
}
|
|
1008
|
+
if (event.annotations) {
|
|
1009
|
+
Object.assign(attrs, this.flattenObject(event.annotations, ATTRIBUTE_PREFIX_TOOL));
|
|
1010
|
+
}
|
|
1011
|
+
if (event.metadata) {
|
|
1012
|
+
Object.assign(attrs, this.flattenObject(event.metadata, ATTRIBUTE_PREFIX_METADATA));
|
|
1013
|
+
}
|
|
1014
|
+
return attrs;
|
|
1015
|
+
}
|
|
1016
|
+
handleEvent(span, event) {
|
|
1017
|
+
switch (event.eventType) {
|
|
1018
|
+
case "execution":
|
|
1019
|
+
if (event.action === "start") {
|
|
1020
|
+
span.addEvent("Execution started", {
|
|
1021
|
+
"client.id": event.clientId,
|
|
1022
|
+
"resource.id": event.resourceId
|
|
1023
|
+
});
|
|
1024
|
+
} else if (event.action === "complete") {
|
|
1025
|
+
span.addEvent("Execution completed", {
|
|
1026
|
+
duration_ms: event.duration,
|
|
1027
|
+
status: event.status,
|
|
1028
|
+
llm_calls: event.llmCallsCount
|
|
1029
|
+
});
|
|
1030
|
+
} else if (event.action === "pause") {
|
|
1031
|
+
span.addEvent("Execution paused", {
|
|
1032
|
+
status: event.status
|
|
1033
|
+
});
|
|
1034
|
+
} else if (event.action === "resume") {
|
|
1035
|
+
span.addEvent("Execution resumed", {
|
|
1036
|
+
"resource.id": event.resourceId
|
|
1037
|
+
});
|
|
1038
|
+
}
|
|
1039
|
+
break;
|
|
1040
|
+
case "tool_call":
|
|
1041
|
+
span.addEvent(`Tool ${event.action}`, {
|
|
1042
|
+
"tool.name": event.toolName,
|
|
1043
|
+
"api.group": event.apiGroup,
|
|
1044
|
+
duration_ms: event.duration
|
|
1045
|
+
});
|
|
1046
|
+
if (event.input) {
|
|
1047
|
+
span.setAttribute(exports.OTelAttribute.TOOL_INPUT_SIZE, JSON.stringify(event.input).length);
|
|
1048
|
+
}
|
|
1049
|
+
if (event.output) {
|
|
1050
|
+
span.setAttribute(exports.OTelAttribute.TOOL_OUTPUT_SIZE, JSON.stringify(event.output).length);
|
|
1051
|
+
}
|
|
1052
|
+
break;
|
|
1053
|
+
case "llm_call":
|
|
1054
|
+
span.addEvent("LLM call", {
|
|
1055
|
+
duration_ms: event.duration
|
|
1056
|
+
});
|
|
1057
|
+
break;
|
|
1058
|
+
case "approval":
|
|
1059
|
+
span.addEvent(`Approval ${event.action}`, {
|
|
1060
|
+
"tool.name": event.toolName
|
|
1061
|
+
});
|
|
1062
|
+
break;
|
|
1063
|
+
case "error":
|
|
1064
|
+
if (event.error) {
|
|
1065
|
+
span.addEvent("Error occurred", {
|
|
1066
|
+
"error.message": event.error.message,
|
|
1067
|
+
"error.code": event.error.code
|
|
1068
|
+
});
|
|
1069
|
+
span.recordException(new Error(event.error.message));
|
|
1070
|
+
}
|
|
1071
|
+
break;
|
|
1072
|
+
case "client_init":
|
|
1073
|
+
span.addEvent("Client initialized", {
|
|
1074
|
+
"client.id": event.clientId
|
|
1075
|
+
});
|
|
1076
|
+
break;
|
|
1077
|
+
}
|
|
1078
|
+
if (event.securityEvents && event.securityEvents.length > 0) {
|
|
1079
|
+
for (const secEvent of event.securityEvents) {
|
|
1080
|
+
span.addEvent("Security event", {
|
|
1081
|
+
"security.event": secEvent,
|
|
1082
|
+
"security.risk_score": event.riskScore
|
|
1083
|
+
});
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
recordMetrics(event) {
|
|
1088
|
+
const commonAttrs = {
|
|
1089
|
+
client_id: event.clientId,
|
|
1090
|
+
event_type: event.eventType,
|
|
1091
|
+
status: event.status
|
|
1092
|
+
};
|
|
1093
|
+
switch (event.eventType) {
|
|
1094
|
+
case "execution":
|
|
1095
|
+
this.executionCounter.add(1, {
|
|
1096
|
+
...commonAttrs,
|
|
1097
|
+
action: event.action
|
|
1098
|
+
});
|
|
1099
|
+
if (event.duration !== void 0) {
|
|
1100
|
+
this.executionDuration.record(event.duration, {
|
|
1101
|
+
...commonAttrs,
|
|
1102
|
+
action: event.action
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
break;
|
|
1106
|
+
case "tool_call":
|
|
1107
|
+
this.toolCallCounter.add(1, {
|
|
1108
|
+
...commonAttrs,
|
|
1109
|
+
tool_name: event.toolName,
|
|
1110
|
+
api_group: event.apiGroup
|
|
1111
|
+
});
|
|
1112
|
+
if (event.duration !== void 0) {
|
|
1113
|
+
this.toolDuration.record(event.duration, {
|
|
1114
|
+
...commonAttrs,
|
|
1115
|
+
tool_name: event.toolName
|
|
1116
|
+
});
|
|
1117
|
+
}
|
|
1118
|
+
break;
|
|
1119
|
+
case "llm_call":
|
|
1120
|
+
this.llmCallCounter.add(1, commonAttrs);
|
|
1121
|
+
break;
|
|
1122
|
+
case "approval":
|
|
1123
|
+
this.approvalCounter.add(1, {
|
|
1124
|
+
...commonAttrs,
|
|
1125
|
+
action: event.action
|
|
1126
|
+
});
|
|
1127
|
+
break;
|
|
1128
|
+
}
|
|
1129
|
+
if (event.securityEvents && event.securityEvents.length > 0) {
|
|
1130
|
+
const securityEventCounter = this.meter.createCounter(exports.OTelCounter.SECURITY_EVENTS, METRIC_CONFIGS[exports.OTelCounter.SECURITY_EVENTS]);
|
|
1131
|
+
securityEventCounter.add(event.securityEvents.length, {
|
|
1132
|
+
...commonAttrs,
|
|
1133
|
+
risk_score: event.riskScore
|
|
1134
|
+
});
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
flattenObject(obj, prefix) {
|
|
1138
|
+
const result = {};
|
|
1139
|
+
if (!obj || typeof obj !== "object") return result;
|
|
1140
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
1141
|
+
const fullKey = `${prefix}.${key}`;
|
|
1142
|
+
if (value === null || value === void 0) {
|
|
1143
|
+
continue;
|
|
1144
|
+
}
|
|
1145
|
+
if (typeof value === "object" && !Array.isArray(value)) {
|
|
1146
|
+
Object.assign(result, this.flattenObject(value, fullKey));
|
|
1147
|
+
} else if (Array.isArray(value)) {
|
|
1148
|
+
result[fullKey] = JSON.stringify(value);
|
|
1149
|
+
} else {
|
|
1150
|
+
result[fullKey] = value;
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
return result;
|
|
1154
|
+
}
|
|
1155
|
+
};
|
|
1156
|
+
|
|
1157
|
+
exports.ATTRIBUTE_PREFIX_METADATA = ATTRIBUTE_PREFIX_METADATA;
|
|
1158
|
+
exports.ATTRIBUTE_PREFIX_TOOL = ATTRIBUTE_PREFIX_TOOL;
|
|
1159
|
+
exports.EnvAuthProvider = EnvAuthProvider;
|
|
1160
|
+
exports.FileCache = FileCache;
|
|
1161
|
+
exports.JSONLAuditSink = JSONLAuditSink;
|
|
1162
|
+
exports.METRIC_CONFIGS = METRIC_CONFIGS;
|
|
1163
|
+
exports.MemoryCache = MemoryCache;
|
|
1164
|
+
exports.OTEL_METER_NAME = OTEL_METER_NAME;
|
|
1165
|
+
exports.OTEL_SERVICE_NAME = OTEL_SERVICE_NAME;
|
|
1166
|
+
exports.OTEL_TRACER_NAME = OTEL_TRACER_NAME;
|
|
1167
|
+
exports.OpenTelemetryAuditSink = OpenTelemetryAuditSink;
|
|
1168
|
+
exports.RedisCache = RedisCache;
|
|
1169
|
+
exports.ScopeCheckerRegistry = ScopeCheckerRegistry;
|
|
1170
|
+
//# sourceMappingURL=index.cjs.map
|
|
1171
|
+
//# sourceMappingURL=index.cjs.map
|