@mondaydotcomorg/atp-providers 0.17.14
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 +430 -0
- package/__tests__/oauth-integration.test.ts +457 -0
- package/__tests__/scope-checkers.test.ts +560 -0
- package/__tests__/token-expiration.test.ts +308 -0
- package/dist/audit/jsonl.d.ts +29 -0
- package/dist/audit/jsonl.d.ts.map +1 -0
- package/dist/audit/jsonl.js +163 -0
- package/dist/audit/jsonl.js.map +1 -0
- package/dist/audit/opentelemetry.d.ts +23 -0
- package/dist/audit/opentelemetry.d.ts.map +1 -0
- package/dist/audit/opentelemetry.js +240 -0
- package/dist/audit/opentelemetry.js.map +1 -0
- package/dist/audit/otel-metrics.d.ts +111 -0
- package/dist/audit/otel-metrics.d.ts.map +1 -0
- package/dist/audit/otel-metrics.js +115 -0
- package/dist/audit/otel-metrics.js.map +1 -0
- package/dist/auth/env.d.ts +21 -0
- package/dist/auth/env.d.ts.map +1 -0
- package/dist/auth/env.js +48 -0
- package/dist/auth/env.js.map +1 -0
- package/dist/cache/file.d.ts +47 -0
- package/dist/cache/file.d.ts.map +1 -0
- package/dist/cache/file.js +262 -0
- package/dist/cache/file.js.map +1 -0
- package/dist/cache/memory.d.ts +30 -0
- package/dist/cache/memory.d.ts.map +1 -0
- package/dist/cache/memory.js +81 -0
- package/dist/cache/memory.js.map +1 -0
- package/dist/cache/redis.d.ts +28 -0
- package/dist/cache/redis.d.ts.map +1 -0
- package/dist/cache/redis.js +124 -0
- package/dist/cache/redis.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/oauth/index.d.ts +2 -0
- package/dist/oauth/index.d.ts.map +1 -0
- package/dist/oauth/index.js +2 -0
- package/dist/oauth/index.js.map +1 -0
- package/dist/oauth/scope-checkers.d.ts +61 -0
- package/dist/oauth/scope-checkers.d.ts.map +1 -0
- package/dist/oauth/scope-checkers.js +166 -0
- package/dist/oauth/scope-checkers.js.map +1 -0
- package/package.json +26 -0
- package/project.json +31 -0
- package/src/audit/jsonl.ts +189 -0
- package/src/audit/opentelemetry.ts +286 -0
- package/src/audit/otel-metrics.ts +122 -0
- package/src/auth/env.ts +65 -0
- package/src/cache/file.ts +330 -0
- package/src/cache/memory.ts +105 -0
- package/src/cache/redis.ts +160 -0
- package/src/index.ts +32 -0
- package/src/oauth/index.ts +1 -0
- package/src/oauth/scope-checkers.ts +196 -0
- package/tsconfig.json +14 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { trace, context, SpanStatusCode, metrics, Span } from '@opentelemetry/api';
|
|
2
|
+
import type { AuditSink, AuditEvent } from '@mondaydotcomorg/atp-protocol';
|
|
3
|
+
import {
|
|
4
|
+
OTelCounter,
|
|
5
|
+
OTelHistogram,
|
|
6
|
+
OTelAttribute,
|
|
7
|
+
METRIC_CONFIGS,
|
|
8
|
+
OTEL_TRACER_NAME,
|
|
9
|
+
OTEL_METER_NAME,
|
|
10
|
+
ATTRIBUTE_PREFIX_TOOL,
|
|
11
|
+
ATTRIBUTE_PREFIX_METADATA,
|
|
12
|
+
} from './otel-metrics.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* OpenTelemetry-based audit sink
|
|
16
|
+
* Provides industry-standard observability with distributed tracing and metrics
|
|
17
|
+
*/
|
|
18
|
+
export class OpenTelemetryAuditSink implements AuditSink {
|
|
19
|
+
name = 'opentelemetry';
|
|
20
|
+
private tracer = trace.getTracer(OTEL_TRACER_NAME);
|
|
21
|
+
private meter = metrics.getMeter(OTEL_METER_NAME);
|
|
22
|
+
|
|
23
|
+
private executionCounter = this.meter.createCounter(
|
|
24
|
+
OTelCounter.EXECUTIONS_TOTAL,
|
|
25
|
+
METRIC_CONFIGS[OTelCounter.EXECUTIONS_TOTAL]
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
private toolCallCounter = this.meter.createCounter(
|
|
29
|
+
OTelCounter.TOOLS_CALLS,
|
|
30
|
+
METRIC_CONFIGS[OTelCounter.TOOLS_CALLS]
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
private llmCallCounter = this.meter.createCounter(
|
|
34
|
+
OTelCounter.LLM_CALLS,
|
|
35
|
+
METRIC_CONFIGS[OTelCounter.LLM_CALLS]
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
private approvalCounter = this.meter.createCounter(
|
|
39
|
+
OTelCounter.APPROVALS_TOTAL,
|
|
40
|
+
METRIC_CONFIGS[OTelCounter.APPROVALS_TOTAL]
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
private executionDuration = this.meter.createHistogram(
|
|
44
|
+
OTelHistogram.EXECUTION_DURATION,
|
|
45
|
+
METRIC_CONFIGS[OTelHistogram.EXECUTION_DURATION]
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
private toolDuration = this.meter.createHistogram(
|
|
49
|
+
OTelHistogram.TOOL_DURATION,
|
|
50
|
+
METRIC_CONFIGS[OTelHistogram.TOOL_DURATION]
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
async write(event: AuditEvent): Promise<void> {
|
|
54
|
+
const span = this.tracer.startSpan(`atp.${event.eventType}.${event.action}`, {
|
|
55
|
+
attributes: this.buildAttributes(event),
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
await context.with(trace.setSpan(context.active(), span), async () => {
|
|
59
|
+
try {
|
|
60
|
+
this.handleEvent(span, event);
|
|
61
|
+
this.recordMetrics(event);
|
|
62
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
63
|
+
} catch (error: any) {
|
|
64
|
+
span.setStatus({
|
|
65
|
+
code: SpanStatusCode.ERROR,
|
|
66
|
+
message: error.message,
|
|
67
|
+
});
|
|
68
|
+
span.recordException(error);
|
|
69
|
+
} finally {
|
|
70
|
+
span.end();
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async writeBatch(events: AuditEvent[]): Promise<void> {
|
|
76
|
+
await Promise.all(events.map((event) => this.write(event)));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
private buildAttributes(event: AuditEvent): Record<string, any> {
|
|
80
|
+
const attrs: Record<string, any> = {
|
|
81
|
+
[OTelAttribute.EVENT_ID]: event.eventId,
|
|
82
|
+
[OTelAttribute.EVENT_TYPE]: event.eventType,
|
|
83
|
+
[OTelAttribute.EVENT_ACTION]: event.action,
|
|
84
|
+
[OTelAttribute.TIMESTAMP]: event.timestamp,
|
|
85
|
+
|
|
86
|
+
[OTelAttribute.CLIENT_ID]: event.clientId,
|
|
87
|
+
[OTelAttribute.STATUS]: event.status,
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
if (event.userId) attrs[OTelAttribute.USER_ID] = event.userId;
|
|
91
|
+
if (event.ipAddress) attrs[OTelAttribute.IP_ADDRESS] = event.ipAddress;
|
|
92
|
+
if (event.userAgent) attrs[OTelAttribute.USER_AGENT] = event.userAgent;
|
|
93
|
+
|
|
94
|
+
if (event.resource) attrs[OTelAttribute.RESOURCE] = event.resource;
|
|
95
|
+
if (event.resourceId) attrs[OTelAttribute.RESOURCE_ID] = event.resourceId;
|
|
96
|
+
|
|
97
|
+
if (event.toolName) attrs[OTelAttribute.TOOL_NAME] = event.toolName;
|
|
98
|
+
if (event.apiGroup) attrs[OTelAttribute.API_GROUP] = event.apiGroup;
|
|
99
|
+
|
|
100
|
+
if (event.duration !== undefined) attrs[OTelAttribute.DURATION_MS] = event.duration;
|
|
101
|
+
if (event.memoryUsed !== undefined) attrs[OTelAttribute.MEMORY_BYTES] = event.memoryUsed;
|
|
102
|
+
if (event.llmCallsCount !== undefined) attrs[OTelAttribute.LLM_CALLS] = event.llmCallsCount;
|
|
103
|
+
if (event.httpCallsCount !== undefined) attrs[OTelAttribute.HTTP_CALLS] = event.httpCallsCount;
|
|
104
|
+
|
|
105
|
+
if (event.riskScore !== undefined) attrs[OTelAttribute.RISK_SCORE] = event.riskScore;
|
|
106
|
+
if (event.securityEvents && event.securityEvents.length > 0) {
|
|
107
|
+
attrs[OTelAttribute.SECURITY_EVENTS] = JSON.stringify(event.securityEvents);
|
|
108
|
+
attrs[OTelAttribute.SECURITY_EVENTS_COUNT] = event.securityEvents.length;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (event.error) {
|
|
112
|
+
attrs[OTelAttribute.ERROR_MESSAGE] = event.error.message;
|
|
113
|
+
if (event.error.code) attrs[OTelAttribute.ERROR_CODE] = event.error.code;
|
|
114
|
+
if (event.error.stack) attrs[OTelAttribute.ERROR_STACK] = event.error.stack;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (event.annotations) {
|
|
118
|
+
Object.assign(attrs, this.flattenObject(event.annotations, ATTRIBUTE_PREFIX_TOOL));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (event.metadata) {
|
|
122
|
+
Object.assign(attrs, this.flattenObject(event.metadata, ATTRIBUTE_PREFIX_METADATA));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return attrs;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private handleEvent(span: Span, event: AuditEvent): void {
|
|
129
|
+
switch (event.eventType) {
|
|
130
|
+
case 'execution':
|
|
131
|
+
if (event.action === 'start') {
|
|
132
|
+
span.addEvent('Execution started', {
|
|
133
|
+
'client.id': event.clientId,
|
|
134
|
+
'resource.id': event.resourceId,
|
|
135
|
+
});
|
|
136
|
+
} else if (event.action === 'complete') {
|
|
137
|
+
span.addEvent('Execution completed', {
|
|
138
|
+
duration_ms: event.duration,
|
|
139
|
+
status: event.status,
|
|
140
|
+
llm_calls: event.llmCallsCount,
|
|
141
|
+
});
|
|
142
|
+
} else if (event.action === 'pause') {
|
|
143
|
+
span.addEvent('Execution paused', {
|
|
144
|
+
status: event.status,
|
|
145
|
+
});
|
|
146
|
+
} else if (event.action === 'resume') {
|
|
147
|
+
span.addEvent('Execution resumed', {
|
|
148
|
+
'resource.id': event.resourceId,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
break;
|
|
152
|
+
|
|
153
|
+
case 'tool_call':
|
|
154
|
+
span.addEvent(`Tool ${event.action}`, {
|
|
155
|
+
'tool.name': event.toolName,
|
|
156
|
+
'api.group': event.apiGroup,
|
|
157
|
+
duration_ms: event.duration,
|
|
158
|
+
});
|
|
159
|
+
if (event.input) {
|
|
160
|
+
span.setAttribute(OTelAttribute.TOOL_INPUT_SIZE, JSON.stringify(event.input).length);
|
|
161
|
+
}
|
|
162
|
+
if (event.output) {
|
|
163
|
+
span.setAttribute(OTelAttribute.TOOL_OUTPUT_SIZE, JSON.stringify(event.output).length);
|
|
164
|
+
}
|
|
165
|
+
break;
|
|
166
|
+
|
|
167
|
+
case 'llm_call':
|
|
168
|
+
span.addEvent('LLM call', {
|
|
169
|
+
duration_ms: event.duration,
|
|
170
|
+
});
|
|
171
|
+
break;
|
|
172
|
+
|
|
173
|
+
case 'approval':
|
|
174
|
+
span.addEvent(`Approval ${event.action}`, {
|
|
175
|
+
'tool.name': event.toolName,
|
|
176
|
+
});
|
|
177
|
+
break;
|
|
178
|
+
|
|
179
|
+
case 'error':
|
|
180
|
+
if (event.error) {
|
|
181
|
+
span.addEvent('Error occurred', {
|
|
182
|
+
'error.message': event.error.message,
|
|
183
|
+
'error.code': event.error.code,
|
|
184
|
+
});
|
|
185
|
+
span.recordException(new Error(event.error.message));
|
|
186
|
+
}
|
|
187
|
+
break;
|
|
188
|
+
|
|
189
|
+
case 'client_init':
|
|
190
|
+
span.addEvent('Client initialized', {
|
|
191
|
+
'client.id': event.clientId,
|
|
192
|
+
});
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (event.securityEvents && event.securityEvents.length > 0) {
|
|
197
|
+
for (const secEvent of event.securityEvents) {
|
|
198
|
+
span.addEvent('Security event', {
|
|
199
|
+
'security.event': secEvent,
|
|
200
|
+
'security.risk_score': event.riskScore,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
private recordMetrics(event: AuditEvent): void {
|
|
207
|
+
const commonAttrs: Record<string, any> = {
|
|
208
|
+
client_id: event.clientId,
|
|
209
|
+
event_type: event.eventType,
|
|
210
|
+
status: event.status,
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
switch (event.eventType) {
|
|
214
|
+
case 'execution':
|
|
215
|
+
this.executionCounter.add(1, {
|
|
216
|
+
...commonAttrs,
|
|
217
|
+
action: event.action,
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
if (event.duration !== undefined) {
|
|
221
|
+
this.executionDuration.record(event.duration, {
|
|
222
|
+
...commonAttrs,
|
|
223
|
+
action: event.action,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
break;
|
|
227
|
+
|
|
228
|
+
case 'tool_call':
|
|
229
|
+
this.toolCallCounter.add(1, {
|
|
230
|
+
...commonAttrs,
|
|
231
|
+
tool_name: event.toolName,
|
|
232
|
+
api_group: event.apiGroup,
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
if (event.duration !== undefined) {
|
|
236
|
+
this.toolDuration.record(event.duration, {
|
|
237
|
+
...commonAttrs,
|
|
238
|
+
tool_name: event.toolName,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
break;
|
|
242
|
+
|
|
243
|
+
case 'llm_call':
|
|
244
|
+
this.llmCallCounter.add(1, commonAttrs);
|
|
245
|
+
break;
|
|
246
|
+
|
|
247
|
+
case 'approval':
|
|
248
|
+
this.approvalCounter.add(1, {
|
|
249
|
+
...commonAttrs,
|
|
250
|
+
action: event.action,
|
|
251
|
+
});
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (event.securityEvents && event.securityEvents.length > 0) {
|
|
256
|
+
const securityEventCounter = this.meter.createCounter(
|
|
257
|
+
OTelCounter.SECURITY_EVENTS,
|
|
258
|
+
METRIC_CONFIGS[OTelCounter.SECURITY_EVENTS]
|
|
259
|
+
);
|
|
260
|
+
securityEventCounter.add(event.securityEvents.length, {
|
|
261
|
+
...commonAttrs,
|
|
262
|
+
risk_score: event.riskScore,
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
private flattenObject(obj: any, prefix: string): Record<string, any> {
|
|
268
|
+
const result: Record<string, any> = {};
|
|
269
|
+
if (!obj || typeof obj !== 'object') return result;
|
|
270
|
+
|
|
271
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
272
|
+
const fullKey = `${prefix}.${key}`;
|
|
273
|
+
if (value === null || value === undefined) {
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
if (typeof value === 'object' && !Array.isArray(value)) {
|
|
277
|
+
Object.assign(result, this.flattenObject(value, fullKey));
|
|
278
|
+
} else if (Array.isArray(value)) {
|
|
279
|
+
result[fullKey] = JSON.stringify(value);
|
|
280
|
+
} else {
|
|
281
|
+
result[fullKey] = value;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return result;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenTelemetry Metrics Definitions
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Counter metric names
|
|
6
|
+
*/
|
|
7
|
+
export enum OTelCounter {
|
|
8
|
+
EXECUTIONS_TOTAL = 'atp.executions.total',
|
|
9
|
+
TOOLS_CALLS = 'atp.tools.calls',
|
|
10
|
+
LLM_CALLS = 'atp.llm.calls',
|
|
11
|
+
APPROVALS_TOTAL = 'atp.approvals.total',
|
|
12
|
+
SECURITY_EVENTS = 'atp.security.events',
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Histogram metric names
|
|
17
|
+
*/
|
|
18
|
+
export enum OTelHistogram {
|
|
19
|
+
EXECUTION_DURATION = 'atp.execution.duration',
|
|
20
|
+
TOOL_DURATION = 'atp.tool.duration',
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Span/trace names
|
|
25
|
+
*/
|
|
26
|
+
export enum OTelSpan {
|
|
27
|
+
EXECUTION_START = 'atp.execution.start',
|
|
28
|
+
EXECUTION_COMPLETE = 'atp.execution.complete',
|
|
29
|
+
EXECUTION_PAUSE = 'atp.execution.pause',
|
|
30
|
+
EXECUTION_RESUME = 'atp.execution.resume',
|
|
31
|
+
EXECUTION_ERROR = 'atp.execution.error',
|
|
32
|
+
TOOL_CALL = 'atp.tool_call',
|
|
33
|
+
LLM_CALL = 'atp.llm_call',
|
|
34
|
+
APPROVAL_REQUEST = 'atp.approval.request',
|
|
35
|
+
APPROVAL_RESPONSE = 'atp.approval.response',
|
|
36
|
+
CLIENT_INIT = 'atp.client_init',
|
|
37
|
+
ERROR = 'atp.error',
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Attribute names (for consistent span/metric attributes)
|
|
42
|
+
*/
|
|
43
|
+
export enum OTelAttribute {
|
|
44
|
+
EVENT_ID = 'atp.event.id',
|
|
45
|
+
EVENT_TYPE = 'atp.event.type',
|
|
46
|
+
EVENT_ACTION = 'atp.event.action',
|
|
47
|
+
TIMESTAMP = 'atp.timestamp',
|
|
48
|
+
|
|
49
|
+
CLIENT_ID = 'atp.client.id',
|
|
50
|
+
USER_ID = 'atp.user.id',
|
|
51
|
+
IP_ADDRESS = 'atp.ip_address',
|
|
52
|
+
USER_AGENT = 'atp.user_agent',
|
|
53
|
+
STATUS = 'atp.status',
|
|
54
|
+
|
|
55
|
+
RESOURCE = 'atp.resource',
|
|
56
|
+
RESOURCE_ID = 'atp.resource.id',
|
|
57
|
+
|
|
58
|
+
TOOL_NAME = 'atp.tool.name',
|
|
59
|
+
TOOL_INPUT_SIZE = 'tool.input_size',
|
|
60
|
+
TOOL_OUTPUT_SIZE = 'tool.output_size',
|
|
61
|
+
API_GROUP = 'atp.api.group',
|
|
62
|
+
|
|
63
|
+
DURATION_MS = 'atp.duration_ms',
|
|
64
|
+
MEMORY_BYTES = 'atp.memory_bytes',
|
|
65
|
+
LLM_CALLS = 'atp.llm_calls',
|
|
66
|
+
HTTP_CALLS = 'atp.http_calls',
|
|
67
|
+
|
|
68
|
+
RISK_SCORE = 'atp.security.risk_score',
|
|
69
|
+
SECURITY_EVENTS = 'atp.security.events',
|
|
70
|
+
SECURITY_EVENTS_COUNT = 'atp.security.events_count',
|
|
71
|
+
|
|
72
|
+
ERROR_MESSAGE = 'atp.error.message',
|
|
73
|
+
ERROR_CODE = 'atp.error.code',
|
|
74
|
+
ERROR_STACK = 'atp.error.stack',
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Metric configurations
|
|
79
|
+
*/
|
|
80
|
+
export const METRIC_CONFIGS = {
|
|
81
|
+
[OTelCounter.EXECUTIONS_TOTAL]: {
|
|
82
|
+
description: 'Total number of executions',
|
|
83
|
+
unit: '1',
|
|
84
|
+
},
|
|
85
|
+
[OTelCounter.TOOLS_CALLS]: {
|
|
86
|
+
description: 'Tool call count',
|
|
87
|
+
unit: '1',
|
|
88
|
+
},
|
|
89
|
+
[OTelCounter.LLM_CALLS]: {
|
|
90
|
+
description: 'LLM call count',
|
|
91
|
+
unit: '1',
|
|
92
|
+
},
|
|
93
|
+
[OTelCounter.APPROVALS_TOTAL]: {
|
|
94
|
+
description: 'Approval request count',
|
|
95
|
+
unit: '1',
|
|
96
|
+
},
|
|
97
|
+
[OTelCounter.SECURITY_EVENTS]: {
|
|
98
|
+
description: 'Security events count',
|
|
99
|
+
unit: '1',
|
|
100
|
+
},
|
|
101
|
+
[OTelHistogram.EXECUTION_DURATION]: {
|
|
102
|
+
description: 'Execution duration in milliseconds',
|
|
103
|
+
unit: 'ms',
|
|
104
|
+
},
|
|
105
|
+
[OTelHistogram.TOOL_DURATION]: {
|
|
106
|
+
description: 'Tool execution duration',
|
|
107
|
+
unit: 'ms',
|
|
108
|
+
},
|
|
109
|
+
} as const;
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* OpenTelemetry tracer and meter names
|
|
113
|
+
*/
|
|
114
|
+
export const OTEL_SERVICE_NAME = 'agent-tool-protocol';
|
|
115
|
+
export const OTEL_TRACER_NAME = 'agent-tool-protocol';
|
|
116
|
+
export const OTEL_METER_NAME = 'agent-tool-protocol';
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Attribute prefixes for custom metadata
|
|
120
|
+
*/
|
|
121
|
+
export const ATTRIBUTE_PREFIX_TOOL = 'atp.tool';
|
|
122
|
+
export const ATTRIBUTE_PREFIX_METADATA = 'atp.metadata';
|
package/src/auth/env.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { AuthProvider } from '@mondaydotcomorg/atp-protocol';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Environment variable based auth provider
|
|
5
|
+
* Simple provider that reads credentials from process.env
|
|
6
|
+
* Good for development and simple deployments
|
|
7
|
+
*/
|
|
8
|
+
export class EnvAuthProvider implements AuthProvider {
|
|
9
|
+
name = 'env';
|
|
10
|
+
private prefix: string;
|
|
11
|
+
private credentials: Map<string, string>;
|
|
12
|
+
|
|
13
|
+
constructor(
|
|
14
|
+
options: {
|
|
15
|
+
prefix?: string;
|
|
16
|
+
credentials?: Record<string, string>;
|
|
17
|
+
} = {}
|
|
18
|
+
) {
|
|
19
|
+
this.prefix = options.prefix || 'ATP_';
|
|
20
|
+
this.credentials = new Map();
|
|
21
|
+
|
|
22
|
+
if (options.credentials) {
|
|
23
|
+
for (const [key, value] of Object.entries(options.credentials)) {
|
|
24
|
+
this.credentials.set(key, value);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async getCredential(key: string): Promise<string | null> {
|
|
30
|
+
if (this.credentials.has(key)) {
|
|
31
|
+
return this.credentials.get(key) || null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const envValue = process.env[key] || process.env[`${this.prefix}${key}`];
|
|
35
|
+
return envValue || null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async setCredential(key: string, value: string, _ttl?: number): Promise<void> {
|
|
39
|
+
this.credentials.set(key, value);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async deleteCredential(key: string): Promise<void> {
|
|
43
|
+
this.credentials.delete(key);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async listCredentials(): Promise<string[]> {
|
|
47
|
+
const keys = new Set<string>();
|
|
48
|
+
|
|
49
|
+
for (const key of this.credentials.keys()) {
|
|
50
|
+
keys.add(key);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
for (const key of Object.keys(process.env)) {
|
|
54
|
+
if (key.startsWith(this.prefix)) {
|
|
55
|
+
keys.add(key.substring(this.prefix.length));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return Array.from(keys);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async disconnect(): Promise<void> {
|
|
63
|
+
this.credentials.clear();
|
|
64
|
+
}
|
|
65
|
+
}
|