@thinkhive/sdk 3.1.0 → 3.3.0
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 +279 -128
- package/dist/api/apiKeys.d.ts +252 -0
- package/dist/api/apiKeys.js +298 -0
- package/dist/api/business-metrics.d.ts +188 -0
- package/dist/api/business-metrics.js +213 -0
- package/dist/api/conversation-eval.d.ts +200 -0
- package/dist/api/conversation-eval.js +235 -0
- package/dist/api/deterministic-graders.d.ts +205 -0
- package/dist/api/deterministic-graders.js +191 -0
- package/dist/api/eval-health.d.ts +250 -0
- package/dist/api/eval-health.js +224 -0
- package/dist/api/human-review.d.ts +275 -0
- package/dist/api/human-review.js +236 -0
- package/dist/api/nondeterminism.d.ts +300 -0
- package/dist/api/nondeterminism.js +250 -0
- package/dist/api/quality-metrics.d.ts +303 -0
- package/dist/api/quality-metrics.js +198 -0
- package/dist/api/roi-analytics.d.ts +263 -0
- package/dist/api/roi-analytics.js +204 -0
- package/dist/api/transcript-patterns.d.ts +204 -0
- package/dist/api/transcript-patterns.js +227 -0
- package/dist/core/client.d.ts +82 -8
- package/dist/core/client.js +223 -32
- package/dist/core/config.d.ts +1 -1
- package/dist/core/config.js +2 -2
- package/dist/core/types.d.ts +27 -2
- package/dist/core/types.js +1 -1
- package/dist/index.d.ts +415 -62
- package/dist/index.js +253 -37
- package/package.json +8 -4
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ThinkHive SDK - API Keys Management
|
|
3
|
+
*
|
|
4
|
+
* Create and manage scoped API keys with:
|
|
5
|
+
* - Permission control (read/write/delete)
|
|
6
|
+
* - Agent-level scoping (allowedAgentIds)
|
|
7
|
+
* - Environment separation (production/staging/development)
|
|
8
|
+
* - IP whitelisting (allowedIps)
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { apiKeys } from 'thinkhive-js';
|
|
13
|
+
*
|
|
14
|
+
* // Create a read-only key for monitoring
|
|
15
|
+
* const readOnlyKey = await apiKeys.create({
|
|
16
|
+
* name: 'Monitoring Key',
|
|
17
|
+
* scopeType: 'readonly'
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* // Create a key scoped to specific agents
|
|
21
|
+
* const agentKey = await apiKeys.create({
|
|
22
|
+
* name: 'Agent A Key',
|
|
23
|
+
* allowedAgentIds: ['agent-a-id', 'agent-b-id'],
|
|
24
|
+
* permissions: { read: true, write: true, delete: false }
|
|
25
|
+
* });
|
|
26
|
+
*
|
|
27
|
+
* // Create a staging environment key
|
|
28
|
+
* const stagingKey = await apiKeys.create({
|
|
29
|
+
* name: 'Staging Key',
|
|
30
|
+
* environment: 'staging',
|
|
31
|
+
* allowedIps: ['10.0.0.1', '10.0.0.2']
|
|
32
|
+
* });
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
/**
|
|
36
|
+
* API key permission configuration
|
|
37
|
+
*/
|
|
38
|
+
export interface ApiKeyPermissions {
|
|
39
|
+
/** Allow read operations (GET requests, listing, etc.) */
|
|
40
|
+
read: boolean;
|
|
41
|
+
/** Allow write operations (POST, PUT requests) */
|
|
42
|
+
write: boolean;
|
|
43
|
+
/** Allow delete operations (DELETE requests) */
|
|
44
|
+
delete: boolean;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* API key scope type
|
|
48
|
+
* - `company`: Full access to all company data
|
|
49
|
+
* - `agent`: Limited to specific agents (requires allowedAgentIds)
|
|
50
|
+
* - `readonly`: Read-only access regardless of other settings
|
|
51
|
+
*/
|
|
52
|
+
export type ScopeType = 'company' | 'agent' | 'readonly';
|
|
53
|
+
/**
|
|
54
|
+
* API key environment
|
|
55
|
+
* - `production`: For production systems
|
|
56
|
+
* - `staging`: For staging/pre-production
|
|
57
|
+
* - `development`: For local development
|
|
58
|
+
*/
|
|
59
|
+
export type Environment = 'production' | 'staging' | 'development';
|
|
60
|
+
/**
|
|
61
|
+
* Options for creating an API key
|
|
62
|
+
*/
|
|
63
|
+
export interface CreateApiKeyOptions {
|
|
64
|
+
/** Display name for the key */
|
|
65
|
+
name: string;
|
|
66
|
+
/** Permission configuration (defaults to read+write) */
|
|
67
|
+
permissions?: Partial<ApiKeyPermissions>;
|
|
68
|
+
/**
|
|
69
|
+
* Restrict key to specific agent IDs
|
|
70
|
+
* When set, key can only access traces for these agents
|
|
71
|
+
*/
|
|
72
|
+
allowedAgentIds?: string[];
|
|
73
|
+
/**
|
|
74
|
+
* Scope type for the key
|
|
75
|
+
* @default 'company' or 'agent' if allowedAgentIds is set
|
|
76
|
+
*/
|
|
77
|
+
scopeType?: ScopeType;
|
|
78
|
+
/**
|
|
79
|
+
* Environment for the key
|
|
80
|
+
* @default 'production'
|
|
81
|
+
*/
|
|
82
|
+
environment?: Environment;
|
|
83
|
+
/**
|
|
84
|
+
* Restrict key to specific IP addresses
|
|
85
|
+
* When set, only requests from these IPs can use this key
|
|
86
|
+
*/
|
|
87
|
+
allowedIps?: string[];
|
|
88
|
+
/** Expiration date for the key */
|
|
89
|
+
expiresAt?: Date;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* API key metadata (returned from server)
|
|
93
|
+
*/
|
|
94
|
+
export interface ApiKey {
|
|
95
|
+
/** Unique key ID */
|
|
96
|
+
id: string;
|
|
97
|
+
/** Display name */
|
|
98
|
+
name: string;
|
|
99
|
+
/** Key prefix (first 8 chars for identification) */
|
|
100
|
+
keyPrefix: string;
|
|
101
|
+
/** Permission configuration */
|
|
102
|
+
permissions: ApiKeyPermissions | string[];
|
|
103
|
+
/** Scope type */
|
|
104
|
+
scopeType: ScopeType;
|
|
105
|
+
/** Environment */
|
|
106
|
+
environment: Environment;
|
|
107
|
+
/** Allowed agent IDs (null = all agents) */
|
|
108
|
+
allowedAgentIds: string[] | null;
|
|
109
|
+
/** Allowed IP addresses (null = all IPs) */
|
|
110
|
+
allowedIps: string[] | null;
|
|
111
|
+
/** Whether key is active */
|
|
112
|
+
isActive: boolean;
|
|
113
|
+
/** Last usage timestamp */
|
|
114
|
+
lastUsedAt: string | null;
|
|
115
|
+
/** Total usage count */
|
|
116
|
+
usageCount: number;
|
|
117
|
+
/** Expiration date */
|
|
118
|
+
expiresAt: string | null;
|
|
119
|
+
/** Creation timestamp */
|
|
120
|
+
createdAt: string;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Result of creating an API key (includes secret)
|
|
124
|
+
*/
|
|
125
|
+
export interface CreateApiKeyResult extends ApiKey {
|
|
126
|
+
/**
|
|
127
|
+
* The full API key value
|
|
128
|
+
* IMPORTANT: This is only returned once and cannot be retrieved later!
|
|
129
|
+
* Store it securely.
|
|
130
|
+
*/
|
|
131
|
+
key: string;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Create a new API key with specified permissions and scoping
|
|
135
|
+
*
|
|
136
|
+
* @param options - API key configuration
|
|
137
|
+
* @returns Created key with secret (only returned once!)
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* ```typescript
|
|
141
|
+
* // Create a production write key
|
|
142
|
+
* const result = await apiKeys.create({
|
|
143
|
+
* name: 'Production SDK Key',
|
|
144
|
+
* permissions: { read: true, write: true, delete: false }
|
|
145
|
+
* });
|
|
146
|
+
*
|
|
147
|
+
* // Save the key securely - it won't be shown again!
|
|
148
|
+
* console.log('Save this key:', result.key);
|
|
149
|
+
* ```
|
|
150
|
+
*/
|
|
151
|
+
export declare function create(options: CreateApiKeyOptions): Promise<CreateApiKeyResult>;
|
|
152
|
+
/**
|
|
153
|
+
* List all API keys for the authenticated company
|
|
154
|
+
*
|
|
155
|
+
* @returns Array of API keys (without secrets)
|
|
156
|
+
*
|
|
157
|
+
* @example
|
|
158
|
+
* ```typescript
|
|
159
|
+
* const keys = await apiKeys.list();
|
|
160
|
+
* console.log(`Found ${keys.length} API keys`);
|
|
161
|
+
*
|
|
162
|
+
* // Filter by environment
|
|
163
|
+
* const prodKeys = keys.filter(k => k.environment === 'production');
|
|
164
|
+
* ```
|
|
165
|
+
*/
|
|
166
|
+
export declare function list(): Promise<ApiKey[]>;
|
|
167
|
+
/**
|
|
168
|
+
* Revoke (deactivate) an API key
|
|
169
|
+
*
|
|
170
|
+
* @param keyId - ID of the key to revoke
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* ```typescript
|
|
174
|
+
* // Revoke a compromised key
|
|
175
|
+
* await apiKeys.revoke('key-id-to-revoke');
|
|
176
|
+
* ```
|
|
177
|
+
*/
|
|
178
|
+
export declare function revoke(keyId: string): Promise<void>;
|
|
179
|
+
/**
|
|
180
|
+
* Rotate an API key (revoke old, create new with same config)
|
|
181
|
+
*
|
|
182
|
+
* @param keyId - ID of the key to rotate
|
|
183
|
+
* @param options - Optional overrides for the new key
|
|
184
|
+
* @returns New key with secret
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* ```typescript
|
|
188
|
+
* // Rotate a key periodically
|
|
189
|
+
* const existingKeys = await apiKeys.list();
|
|
190
|
+
* const oldKey = existingKeys.find(k => k.name === 'Production Key');
|
|
191
|
+
*
|
|
192
|
+
* if (oldKey) {
|
|
193
|
+
* const newKey = await apiKeys.rotate(oldKey.id);
|
|
194
|
+
* // Update your systems with newKey.key
|
|
195
|
+
* console.log('New key:', newKey.key);
|
|
196
|
+
* }
|
|
197
|
+
* ```
|
|
198
|
+
*/
|
|
199
|
+
export declare function rotate(keyId: string, options?: Partial<CreateApiKeyOptions>): Promise<CreateApiKeyResult>;
|
|
200
|
+
/**
|
|
201
|
+
* Test an API key connection
|
|
202
|
+
*
|
|
203
|
+
* @param apiKey - The full API key value to test
|
|
204
|
+
* @param agentId - Optional agent ID to test with
|
|
205
|
+
* @returns Test result
|
|
206
|
+
*
|
|
207
|
+
* @example
|
|
208
|
+
* ```typescript
|
|
209
|
+
* const result = await apiKeys.test('thk_...', 'my-agent-id');
|
|
210
|
+
* if (result.success) {
|
|
211
|
+
* console.log('Key is valid!');
|
|
212
|
+
* }
|
|
213
|
+
* ```
|
|
214
|
+
*/
|
|
215
|
+
export declare function test(apiKey: string, agentId?: string): Promise<{
|
|
216
|
+
success: boolean;
|
|
217
|
+
error?: string;
|
|
218
|
+
testTraceId?: string;
|
|
219
|
+
}>;
|
|
220
|
+
/**
|
|
221
|
+
* Check if an API key has a specific permission
|
|
222
|
+
*/
|
|
223
|
+
export declare function hasPermission(key: ApiKey, permission: 'read' | 'write' | 'delete'): boolean;
|
|
224
|
+
/**
|
|
225
|
+
* Check if an API key is expired
|
|
226
|
+
*/
|
|
227
|
+
export declare function isExpired(key: ApiKey): boolean;
|
|
228
|
+
/**
|
|
229
|
+
* Check if an API key is valid (active and not expired)
|
|
230
|
+
*/
|
|
231
|
+
export declare function isValid(key: ApiKey): boolean;
|
|
232
|
+
/**
|
|
233
|
+
* Get time until key expires (in milliseconds)
|
|
234
|
+
* Returns null if key doesn't expire
|
|
235
|
+
*/
|
|
236
|
+
export declare function getTimeUntilExpiry(key: ApiKey): number | null;
|
|
237
|
+
/**
|
|
238
|
+
* Check if a key can access a specific agent
|
|
239
|
+
*/
|
|
240
|
+
export declare function canAccessAgent(key: ApiKey, agentId: string): boolean;
|
|
241
|
+
export declare const apiKeys: {
|
|
242
|
+
create: typeof create;
|
|
243
|
+
list: typeof list;
|
|
244
|
+
revoke: typeof revoke;
|
|
245
|
+
rotate: typeof rotate;
|
|
246
|
+
test: typeof test;
|
|
247
|
+
hasPermission: typeof hasPermission;
|
|
248
|
+
isExpired: typeof isExpired;
|
|
249
|
+
isValid: typeof isValid;
|
|
250
|
+
getTimeUntilExpiry: typeof getTimeUntilExpiry;
|
|
251
|
+
canAccessAgent: typeof canAccessAgent;
|
|
252
|
+
};
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ThinkHive SDK - API Keys Management
|
|
4
|
+
*
|
|
5
|
+
* Create and manage scoped API keys with:
|
|
6
|
+
* - Permission control (read/write/delete)
|
|
7
|
+
* - Agent-level scoping (allowedAgentIds)
|
|
8
|
+
* - Environment separation (production/staging/development)
|
|
9
|
+
* - IP whitelisting (allowedIps)
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* import { apiKeys } from 'thinkhive-js';
|
|
14
|
+
*
|
|
15
|
+
* // Create a read-only key for monitoring
|
|
16
|
+
* const readOnlyKey = await apiKeys.create({
|
|
17
|
+
* name: 'Monitoring Key',
|
|
18
|
+
* scopeType: 'readonly'
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* // Create a key scoped to specific agents
|
|
22
|
+
* const agentKey = await apiKeys.create({
|
|
23
|
+
* name: 'Agent A Key',
|
|
24
|
+
* allowedAgentIds: ['agent-a-id', 'agent-b-id'],
|
|
25
|
+
* permissions: { read: true, write: true, delete: false }
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* // Create a staging environment key
|
|
29
|
+
* const stagingKey = await apiKeys.create({
|
|
30
|
+
* name: 'Staging Key',
|
|
31
|
+
* environment: 'staging',
|
|
32
|
+
* allowedIps: ['10.0.0.1', '10.0.0.2']
|
|
33
|
+
* });
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
exports.apiKeys = void 0;
|
|
38
|
+
exports.create = create;
|
|
39
|
+
exports.list = list;
|
|
40
|
+
exports.revoke = revoke;
|
|
41
|
+
exports.rotate = rotate;
|
|
42
|
+
exports.test = test;
|
|
43
|
+
exports.hasPermission = hasPermission;
|
|
44
|
+
exports.isExpired = isExpired;
|
|
45
|
+
exports.isValid = isValid;
|
|
46
|
+
exports.getTimeUntilExpiry = getTimeUntilExpiry;
|
|
47
|
+
exports.canAccessAgent = canAccessAgent;
|
|
48
|
+
const client_1 = require("../core/client");
|
|
49
|
+
// ============================================================================
|
|
50
|
+
// API Key Management Functions
|
|
51
|
+
// ============================================================================
|
|
52
|
+
/**
|
|
53
|
+
* Create a new API key with specified permissions and scoping
|
|
54
|
+
*
|
|
55
|
+
* @param options - API key configuration
|
|
56
|
+
* @returns Created key with secret (only returned once!)
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* // Create a production write key
|
|
61
|
+
* const result = await apiKeys.create({
|
|
62
|
+
* name: 'Production SDK Key',
|
|
63
|
+
* permissions: { read: true, write: true, delete: false }
|
|
64
|
+
* });
|
|
65
|
+
*
|
|
66
|
+
* // Save the key securely - it won't be shown again!
|
|
67
|
+
* console.log('Save this key:', result.key);
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
async function create(options) {
|
|
71
|
+
if (!options.name || options.name.trim().length === 0) {
|
|
72
|
+
throw new client_1.ThinkHiveValidationError('API key name is required', 'name');
|
|
73
|
+
}
|
|
74
|
+
// Validate IP addresses if provided
|
|
75
|
+
if (options.allowedIps) {
|
|
76
|
+
// IPv4 pattern
|
|
77
|
+
const ipv4Regex = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
|
|
78
|
+
// IPv6 pattern (supports full and compressed notation like ::1, 2001:db8::1)
|
|
79
|
+
const ipv6Regex = /^(?:(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,7}:|(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:(?::[0-9a-fA-F]{1,4}){1,6}|:(?::[0-9a-fA-F]{1,4}){1,7}|::)$/;
|
|
80
|
+
for (const ip of options.allowedIps) {
|
|
81
|
+
if (!ipv4Regex.test(ip) && !ipv6Regex.test(ip) && ip !== 'localhost') {
|
|
82
|
+
throw new client_1.ThinkHiveValidationError(`Invalid IP address: ${ip}`, 'allowedIps');
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const body = {
|
|
87
|
+
name: options.name.trim(),
|
|
88
|
+
};
|
|
89
|
+
if (options.permissions) {
|
|
90
|
+
body.permissions = {
|
|
91
|
+
read: options.permissions.read !== false,
|
|
92
|
+
write: options.permissions.write === true,
|
|
93
|
+
delete: options.permissions.delete === true,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
if (options.scopeType) {
|
|
97
|
+
body.scopeType = options.scopeType;
|
|
98
|
+
}
|
|
99
|
+
if (options.environment) {
|
|
100
|
+
body.environment = options.environment;
|
|
101
|
+
}
|
|
102
|
+
if (options.allowedAgentIds && options.allowedAgentIds.length > 0) {
|
|
103
|
+
body.allowedAgentIds = options.allowedAgentIds;
|
|
104
|
+
}
|
|
105
|
+
if (options.allowedIps && options.allowedIps.length > 0) {
|
|
106
|
+
body.allowedIps = options.allowedIps;
|
|
107
|
+
}
|
|
108
|
+
if (options.expiresAt) {
|
|
109
|
+
body.expiresAt = options.expiresAt.toISOString();
|
|
110
|
+
}
|
|
111
|
+
return (0, client_1.apiRequest)('/api-keys', {
|
|
112
|
+
method: 'POST',
|
|
113
|
+
body,
|
|
114
|
+
apiVersion: 'v1',
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* List all API keys for the authenticated company
|
|
119
|
+
*
|
|
120
|
+
* @returns Array of API keys (without secrets)
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```typescript
|
|
124
|
+
* const keys = await apiKeys.list();
|
|
125
|
+
* console.log(`Found ${keys.length} API keys`);
|
|
126
|
+
*
|
|
127
|
+
* // Filter by environment
|
|
128
|
+
* const prodKeys = keys.filter(k => k.environment === 'production');
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
async function list() {
|
|
132
|
+
return (0, client_1.apiRequest)('/api-keys', {
|
|
133
|
+
method: 'GET',
|
|
134
|
+
apiVersion: 'v1',
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Revoke (deactivate) an API key
|
|
139
|
+
*
|
|
140
|
+
* @param keyId - ID of the key to revoke
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* ```typescript
|
|
144
|
+
* // Revoke a compromised key
|
|
145
|
+
* await apiKeys.revoke('key-id-to-revoke');
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
async function revoke(keyId) {
|
|
149
|
+
if (!keyId) {
|
|
150
|
+
throw new client_1.ThinkHiveValidationError('Key ID is required', 'keyId');
|
|
151
|
+
}
|
|
152
|
+
await (0, client_1.apiRequest)(`/api-keys/${keyId}`, {
|
|
153
|
+
method: 'DELETE',
|
|
154
|
+
apiVersion: 'v1',
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Rotate an API key (revoke old, create new with same config)
|
|
159
|
+
*
|
|
160
|
+
* @param keyId - ID of the key to rotate
|
|
161
|
+
* @param options - Optional overrides for the new key
|
|
162
|
+
* @returns New key with secret
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```typescript
|
|
166
|
+
* // Rotate a key periodically
|
|
167
|
+
* const existingKeys = await apiKeys.list();
|
|
168
|
+
* const oldKey = existingKeys.find(k => k.name === 'Production Key');
|
|
169
|
+
*
|
|
170
|
+
* if (oldKey) {
|
|
171
|
+
* const newKey = await apiKeys.rotate(oldKey.id);
|
|
172
|
+
* // Update your systems with newKey.key
|
|
173
|
+
* console.log('New key:', newKey.key);
|
|
174
|
+
* }
|
|
175
|
+
* ```
|
|
176
|
+
*/
|
|
177
|
+
async function rotate(keyId, options) {
|
|
178
|
+
// Get existing key metadata
|
|
179
|
+
const keys = await list();
|
|
180
|
+
const existingKey = keys.find(k => k.id === keyId);
|
|
181
|
+
if (!existingKey) {
|
|
182
|
+
throw new client_1.ThinkHiveValidationError('API key not found', 'keyId');
|
|
183
|
+
}
|
|
184
|
+
// Create new key with same (or overridden) config
|
|
185
|
+
const newKeyOptions = {
|
|
186
|
+
name: options?.name || existingKey.name,
|
|
187
|
+
scopeType: options?.scopeType || existingKey.scopeType,
|
|
188
|
+
environment: options?.environment || existingKey.environment,
|
|
189
|
+
allowedAgentIds: options?.allowedAgentIds || existingKey.allowedAgentIds || undefined,
|
|
190
|
+
allowedIps: options?.allowedIps || existingKey.allowedIps || undefined,
|
|
191
|
+
};
|
|
192
|
+
// Handle permissions conversion
|
|
193
|
+
if (options?.permissions) {
|
|
194
|
+
newKeyOptions.permissions = options.permissions;
|
|
195
|
+
}
|
|
196
|
+
else if (existingKey.permissions && typeof existingKey.permissions === 'object' && !Array.isArray(existingKey.permissions)) {
|
|
197
|
+
newKeyOptions.permissions = existingKey.permissions;
|
|
198
|
+
}
|
|
199
|
+
// Create new key first (so we don't lose access if creation fails)
|
|
200
|
+
const newKey = await create(newKeyOptions);
|
|
201
|
+
// Revoke old key
|
|
202
|
+
await revoke(keyId);
|
|
203
|
+
return newKey;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Test an API key connection
|
|
207
|
+
*
|
|
208
|
+
* @param apiKey - The full API key value to test
|
|
209
|
+
* @param agentId - Optional agent ID to test with
|
|
210
|
+
* @returns Test result
|
|
211
|
+
*
|
|
212
|
+
* @example
|
|
213
|
+
* ```typescript
|
|
214
|
+
* const result = await apiKeys.test('thk_...', 'my-agent-id');
|
|
215
|
+
* if (result.success) {
|
|
216
|
+
* console.log('Key is valid!');
|
|
217
|
+
* }
|
|
218
|
+
* ```
|
|
219
|
+
*/
|
|
220
|
+
async function test(apiKey, agentId) {
|
|
221
|
+
if (!apiKey) {
|
|
222
|
+
throw new client_1.ThinkHiveValidationError('API key is required', 'apiKey');
|
|
223
|
+
}
|
|
224
|
+
return (0, client_1.apiRequest)('/api-keys/test', {
|
|
225
|
+
method: 'POST',
|
|
226
|
+
body: { apiKey, agentId, createTestTrace: !!agentId },
|
|
227
|
+
apiVersion: 'v1',
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
// ============================================================================
|
|
231
|
+
// Utility Functions
|
|
232
|
+
// ============================================================================
|
|
233
|
+
/**
|
|
234
|
+
* Check if an API key has a specific permission
|
|
235
|
+
*/
|
|
236
|
+
function hasPermission(key, permission) {
|
|
237
|
+
// Handle object format
|
|
238
|
+
if (typeof key.permissions === 'object' && !Array.isArray(key.permissions)) {
|
|
239
|
+
return key.permissions[permission] === true;
|
|
240
|
+
}
|
|
241
|
+
// Handle legacy array format
|
|
242
|
+
if (Array.isArray(key.permissions)) {
|
|
243
|
+
return key.permissions.some(p => p.endsWith(`:${permission}`));
|
|
244
|
+
}
|
|
245
|
+
// Default: read allowed, write/delete denied
|
|
246
|
+
return permission === 'read';
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Check if an API key is expired
|
|
250
|
+
*/
|
|
251
|
+
function isExpired(key) {
|
|
252
|
+
if (!key.expiresAt)
|
|
253
|
+
return false;
|
|
254
|
+
return new Date(key.expiresAt) < new Date();
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Check if an API key is valid (active and not expired)
|
|
258
|
+
*/
|
|
259
|
+
function isValid(key) {
|
|
260
|
+
return key.isActive && !isExpired(key);
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Get time until key expires (in milliseconds)
|
|
264
|
+
* Returns null if key doesn't expire
|
|
265
|
+
*/
|
|
266
|
+
function getTimeUntilExpiry(key) {
|
|
267
|
+
if (!key.expiresAt)
|
|
268
|
+
return null;
|
|
269
|
+
const expiry = new Date(key.expiresAt).getTime();
|
|
270
|
+
const now = Date.now();
|
|
271
|
+
return Math.max(0, expiry - now);
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Check if a key can access a specific agent
|
|
275
|
+
*/
|
|
276
|
+
function canAccessAgent(key, agentId) {
|
|
277
|
+
// If no agent restrictions, allow all
|
|
278
|
+
if (!key.allowedAgentIds || key.allowedAgentIds.length === 0) {
|
|
279
|
+
return true;
|
|
280
|
+
}
|
|
281
|
+
return key.allowedAgentIds.includes(agentId);
|
|
282
|
+
}
|
|
283
|
+
// ============================================================================
|
|
284
|
+
// Export namespace
|
|
285
|
+
// ============================================================================
|
|
286
|
+
exports.apiKeys = {
|
|
287
|
+
create,
|
|
288
|
+
list,
|
|
289
|
+
revoke,
|
|
290
|
+
rotate,
|
|
291
|
+
test,
|
|
292
|
+
hasPermission,
|
|
293
|
+
isExpired,
|
|
294
|
+
isValid,
|
|
295
|
+
getTimeUntilExpiry,
|
|
296
|
+
canAccessAgent,
|
|
297
|
+
};
|
|
298
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXBpS2V5cy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9hcGkvYXBpS2V5cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQWlDRzs7O0FBNElILHdCQXVEQztBQWdCRCxvQkFLQztBQWFELHdCQVNDO0FBc0JELHdCQW1DQztBQWlCRCxvQkFhQztBQVNELHNDQWdCQztBQUtELDhCQUdDO0FBS0QsMEJBRUM7QUFNRCxnREFLQztBQUtELHdDQU1DO0FBallELDJDQUFzRTtBQW9IdEUsK0VBQStFO0FBQy9FLCtCQUErQjtBQUMvQiwrRUFBK0U7QUFFL0U7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBaUJHO0FBQ0ksS0FBSyxVQUFVLE1BQU0sQ0FBQyxPQUE0QjtJQUN2RCxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksSUFBSSxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUUsQ0FBQztRQUN0RCxNQUFNLElBQUksaUNBQXdCLENBQUMsMEJBQTBCLEVBQUUsTUFBTSxDQUFDLENBQUM7SUFDekUsQ0FBQztJQUVELG9DQUFvQztJQUNwQyxJQUFJLE9BQU8sQ0FBQyxVQUFVLEVBQUUsQ0FBQztRQUN2QixlQUFlO1FBQ2YsTUFBTSxTQUFTLEdBQUcsNkZBQTZGLENBQUM7UUFDaEgsNkVBQTZFO1FBQzdFLE1BQU0sU0FBUyxHQUFHLHVaQUF1WixDQUFDO1FBQzFhLEtBQUssTUFBTSxFQUFFLElBQUksT0FBTyxDQUFDLFVBQVUsRUFBRSxDQUFDO1lBQ3BDLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsSUFBSSxFQUFFLEtBQUssV0FBVyxFQUFFLENBQUM7Z0JBQ3JFLE1BQU0sSUFBSSxpQ0FBd0IsQ0FBQyx1QkFBdUIsRUFBRSxFQUFFLEVB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U0FBUyxDQUFDLEdBQUcsQ0FBQyxDQUFDO0FBQ3pDLENBQUM7QUFFRDs7O0dBR0c7QUFDSCxTQUFnQixrQkFBa0IsQ0FBQyxHQUFXO0lBQzVDLElBQUksQ0FBQyxHQUFHLENBQUMsU0FBUztRQUFFLE9BQU8sSUFBSSxDQUFDO0lBQ2hDLE1BQU0sTUFBTSxHQUFHLElBQUksSUFBSSxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztJQUNqRCxNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7SUFDdkIsT0FBTyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxNQUFNLEdBQUcsR0FBRyxDQUFDLENBQUM7QUFDbkMsQ0FBQztBQUVEOztHQUVHO0FBQ0gsU0FBZ0IsY0FBYyxDQUFDLEdBQVcsRUFBRSxPQUFlO0lBQ3pELHNDQUFzQztJQUN0QyxJQUFJLENBQUMsR0FBRyxDQUFDLGVBQWUsSUFBSSxHQUFHLENBQUMsZUFBZSxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUUsQ0FBQztRQUM3RCxPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFDRCxPQUFPLEdBQUcsQ0FBQyxlQUFlLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxDQUFDO0FBQy9DLENBQUM7QUFFRCwrRUFBK0U7QUFDL0UsbUJBQW1CO0FBQ25CLCtFQUErRTtBQUVsRSxRQUFBLE9BQU8sR0FBRztJQUNyQixNQUFNO0lBQ04sSUFBSTtJQUNKLE1BQU07SUFDTixNQUFNO0lBQ04sSUFBSTtJQUNKLGFBQWE7SUFDYixTQUFTO0lBQ1QsT0FBTztJQUNQLGtCQUFrQjtJQUNsQixjQUFjO0NBQ2YsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogVGhpbmtIaXZlIFNESyAtIEFQSSBLZXlzIE1hbmFnZW1lbnRcbiAqXG4gKiBDcmVhdGUgYW5kIG1hbmFnZSBzY29wZWQgQVBJIGtleXMgd2l0aDpcbiAqIC0gUGVybWlzc2lvbiBjb250cm9sIChyZWFkL3dyaXRlL2RlbGV0ZSlcbiAqIC0gQWdlbnQtbGV2ZWwgc2NvcGluZyAoYWxsb3dlZEFnZW50SWRzKVxuICogLSBFbnZpcm9ubWVudCBzZXBhcmF0aW9uIChwcm9kdWN0aW9uL3N0YWdpbmcvZGV2ZWxvcG1lbnQpXG4gKiAtIElQIHdoaXRlbGlzdGluZyAoYWxsb3dlZElwcylcbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogaW1wb3J0IHsgYXBpS2V5cyB9IGZyb20gJ3RoaW5raGl2ZS1qcyc7XG4gKlxuICogLy8gQ3JlYXRlIGEgcmVhZC1vbmx5IGtleSBmb3IgbW9uaXRvcmluZ1xuICogY29uc3QgcmVhZE9ubHlLZXkgPSBhd2FpdCBhcGlLZXlzLmNyZWF0ZSh7XG4gKiAgIG5hbWU6ICdNb25pdG9yaW5nIEtleScsXG4gKiAgIHNjb3BlVHlwZTogJ3JlYWRvbmx5J1xuICogfSk7XG4gKlxuICogLy8gQ3JlYXRlIGEga2V5IHNjb3BlZCB0byBzcGVjaWZpYyBhZ2VudHNcbiAqIGNvbnN0IGFnZW50S2V5ID0gYXdhaXQgYXBpS2V5cy5jcmVhdGUoe1xuICogICBuYW1lOiAnQWdlbnQgQSBLZXknLFxuICogICBhbGxvd2VkQWdlbnRJZHM6IFsnYWdlbnQtYS1pZCcsICdhZ2VudC1iLWlkJ10sXG4gKiAgIHBlcm1pc3Npb25zOiB7IHJlYWQ6IHRydWUsIHdyaXRlOiB0cnVlLCBkZWxldGU6IGZhbHNlIH1cbiAqIH0pO1xuICpcbiAqIC8vIENyZWF0ZSBhIHN0YWdpbmcgZW52aXJvbm1lbnQga2V5XG4gKiBjb25zdCBzdGFnaW5nS2V5ID0gYXdhaXQgYXBpS2V5cy5jcmVhdGUoe1xuICogICBuYW1lOiAnU3RhZ2luZyBLZXknLFxuICogICBlbnZpcm9ubWVudDogJ3N0YWdpbmcnLFxuICogICBhbGxvd2VkSXBzOiBbJzEwLjAuMC4xJywgJzEwLjAuMC4yJ11cbiAqIH0pO1xuICogYGBgXG4gKi9cblxuaW1wb3J0IHsgYXBpUmVxdWVzdCwgVGhpbmtIaXZlVmFsaWRhdGlvbkVycm9yIH0gZnJvbSAnLi4vY29yZS9jbGllbnQnO1xuXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4vLyBUeXBlc1xuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4vKipcbiAqIEFQSSBrZXkgcGVybWlzc2lvbiBjb25maWd1cmF0aW9uXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQXBpS2V5UGVybWlzc2lvbnMge1xuICAvKiogQWxsb3cgcmVhZCBvcGVyYXRpb25zIChHRVQgcmVxdWVzdHMsIGxpc3RpbmcsIGV0Yy4pICovXG4gIHJlYWQ6IGJvb2xlYW47XG4gIC8qKiBBbGxvdyB3cml0ZSBvcGVyYXRpb25zIChQT1NULCBQVVQgcmVxdWVzdHMpICovXG4gIHdyaXRlOiBib29sZWFuO1xuICAvKiogQWxsb3cgZGVsZXRlIG9wZXJhdGlvbnMgKERFTEVURSByZXF1ZXN0cykgKi9cbiAgZGVsZXRlOiBib29sZWFuO1xufVxuXG4vKipcbiAqIEFQSSBrZXkgc2NvcGUgdHlwZVxuICogLSBgY29tcGFueWA6IEZ1bGwgYWNjZXNzIHRvIGFsbCBjb21wYW55IGRhdGFcbiAqIC0gYGFnZW50YDogTGltaXRlZCB0byBzcGVjaWZpYyBhZ2VudHMgKHJlcXVpcmVzIGFsbG93ZWRBZ2VudElkcylcbiAqIC0gYHJlYWRvbmx5YDogUmVhZC1vbmx5IGFjY2VzcyByZWdhcmRsZXNzIG9mIG90aGVyIHNldHRpbmdzXG4gKi9cbmV4cG9ydCB0eXBlIFNjb3BlVHlwZSA9ICdjb21wYW55JyB8ICdhZ2VudCcgfCAncmVhZG9ubHknO1xuXG4vKipcbiAqIEFQSSBrZXkgZW52aXJvbm1lbnRcbiAqIC0gYHByb2R1Y3Rpb25gOiBGb3IgcHJvZHVjdGlvbiBzeXN0ZW1zXG4gKiAtIGBzdGFnaW5nYDogRm9yIHN0YWdpbmcvcHJlLXByb2R1Y3Rpb25cbiAqIC0gYGRldmVsb3BtZW50YDogRm9yIGxvY2FsIGRldmVsb3BtZW50XG4gKi9cbmV4cG9ydCB0eXBlIEVudmlyb25tZW50ID0gJ3Byb2R1Y3Rpb24nIHwgJ3N0YWdpbmcnIHwgJ2RldmVsb3BtZW50JztcblxuLyoqXG4gKiBPcHRpb25zIGZvciBjcmVhdGluZyBhbiBBUEkga2V5XG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQ3JlYXRlQXBpS2V5T3B0aW9ucyB7XG4gIC8qKiBEaXNwbGF5IG5hbWUgZm9yIHRoZSBrZXkgKi9cbiAgbmFtZTogc3RyaW5nO1xuXG4gIC8qKiBQZXJtaXNzaW9uIGNvbmZpZ3VyYXRpb24gKGRlZmF1bHRzIHRvIHJlYWQrd3JpdGUpICovXG4gIHBlcm1pc3Npb25zPzogUGFydGlhbDxBcGlLZXlQZXJtaXNzaW9ucz47XG5cbiAgLyoqXG4gICAqIFJlc3RyaWN0IGtleSB0byBzcGVjaWZpYyBhZ2VudCBJRHNcbiAgICogV2hlbiBzZXQsIGtleSBjYW4gb25seSBhY2Nlc3MgdHJhY2VzIGZvciB0aGVzZSBhZ2VudHNcbiAgICovXG4gIGFsbG93ZWRBZ2VudElkcz86IHN0cmluZ1tdO1xuXG4gIC8qKlxuICAgKiBTY29wZSB0eXBlIGZvciB0aGUga2V5XG4gICAqIEBkZWZhdWx0ICdjb21wYW55JyBvciAnYWdlbnQnIGlmIGFsbG93ZWRBZ2VudElkcyBpcyBzZXRcbiAgICovXG4gIHNjb3BlVHlwZT86IFNjb3BlVHlwZTtcblxuICAvKipcbiAgICogRW52aXJvbm1lbnQgZm9yIHRoZSBrZXlcbiAgICogQGRlZmF1bHQgJ3Byb2R1Y3Rpb24nXG4gICAqL1xuICBlbnZpcm9ubWVudD86IEVudmlyb25tZW50O1xuXG4gIC8qKlxuICAgKiBSZXN0cmljdCBrZXkgdG8gc3BlY2lmaWMgSVAgYWRkcmVzc2VzXG4gICAqIFdoZW4gc2V0LCBvbmx5IHJlcXVlc3RzIGZyb20gdGhlc2UgSVBzIGNhbiB1c2UgdGhpcyBrZXlcbiAgICovXG4gIGFsbG93ZWRJcHM/OiBzdHJpbmdbXTtcblxuICAvKiogRXhwaXJhdGlvbiBkYXRlIGZvciB0aGUga2V5ICovXG4gIGV4cGlyZXNBdD86IERhdGU7XG59XG5cbi8qKlxuICogQVBJIGtleSBtZXRhZGF0YSAocmV0dXJuZWQgZnJvbSBzZXJ2ZXIpXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQXBpS2V5IHtcbiAgLyoqIFVuaXF1ZSBrZXkgSUQgKi9cbiAgaWQ6IHN0cmluZztcbiAgLyoqIERpc3BsYXkgbmFtZSAqL1xuICBuYW1lOiBzdHJpbmc7XG4gIC8qKiBLZXkgcHJlZml4IChmaXJzdCA4IGNoYXJzIGZvciBpZGVudGlmaWNhdGlvbikgKi9cbiAga2V5UHJlZml4OiBzdHJpbmc7XG4gIC8qKiBQZXJtaXNzaW9uIGNvbmZpZ3VyYXRpb24gKi9cbiAgcGVybWlzc2lvbnM6IEFwaUtleVBlcm1pc3Npb25zIHwgc3RyaW5nW107XG4gIC8qKiBTY29wZSB0eXBlICovXG4gIHNjb3BlVHlwZTogU2NvcGVUeXBlO1xuICAvKiogRW52aXJvbm1lbnQgKi9cbiAgZW52aXJvbm1lbnQ6IEVudmlyb25tZW50O1xuICAvKiogQWxsb3dlZCBhZ2VudCBJRHMgKG51bGwgPSBhbGwgYWdlbnRzKSAqL1xuICBhbGxvd2VkQWdlbnRJZHM6IHN0cmluZ1tdIHwgbnVsbDtcbiAgLyoqIEFsbG93ZWQgSVAgYWRkcmVzc2VzIChudWxsID0gYWxsIElQcykgKi9cbiAgYWxsb3dlZElwczogc3RyaW5nW10gfCBudWxsO1xuICAvKiogV2hldGhlciBrZXkgaXMgYWN0aXZlICovXG4gIGlzQWN0aXZlOiBib29sZWFuO1xuICAvKiogTGFzdCB1c2FnZSB0aW1lc3RhbXAgKi9cbiAgbGFzdFVzZWRBdDogc3RyaW5nIHwgbnVsbDtcbiAgLyoqIFRvdGFsIHVzYWdlIGNvdW50ICovXG4gIHVzYWdlQ291bnQ6IG51bWJlcjtcbiAgLyoqIEV4cGlyYXRpb24gZGF0ZSAqL1xuICBleHBpcmVzQXQ6IHN0cmluZyB8IG51bGw7XG4gIC8qKiBDcmVhdGlvbiB0aW1lc3RhbXAgKi9cbiAgY3JlYXRlZEF0OiBzdHJpbmc7XG59XG5cbi8qKlxuICogUmVzdWx0IG9mIGNyZWF0aW5nIGFuIEFQSSBrZXkgKGluY2x1ZGVzIHNlY3JldClcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBDcmVhdGVBcGlLZXlSZXN1bHQgZXh0ZW5kcyBBcGlLZXkge1xuICAvKipcbiAgICogVGhlIGZ1bGwgQVBJIGtleSB2YWx1ZVxuICAgKiBJTVBPUlRBTlQ6IFRoaXMgaXMgb25seSByZXR1cm5lZCBvbmNlIGFuZCBjYW5ub3QgYmUgcmV0cmlldmVkIGxhdGVyIVxuICAgKiBTdG9yZSBpdCBzZWN1cmVseS5cbiAgICovXG4gIGtleTogc3RyaW5nO1xufVxuXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4vLyBBUEkgS2V5IE1hbmFnZW1lbnQgRnVuY3Rpb25zXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbi8qKlxuICogQ3JlYXRlIGEgbmV3IEFQSSBrZXkgd2l0aCBzcGVjaWZpZWQgcGVybWlzc2lvbnMgYW5kIHNjb3BpbmdcbiAqXG4gKiBAcGFyYW0gb3B0aW9ucyAtIEFQSSBrZXkgY29uZmlndXJhdGlvblxuICogQHJldHVybnMgQ3JlYXRlZCBrZXkgd2l0aCBzZWNyZXQgKG9ubHkgcmV0dXJuZWQgb25jZSEpXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIC8vIENyZWF0ZSBhIHByb2R1Y3Rpb24gd3JpdGUga2V5XG4gKiBjb25zdCByZXN1bHQgPSBhd2FpdCBhcGlLZXlzLmNyZWF0ZSh7XG4gKiAgIG5hbWU6ICdQcm9kdWN0aW9uIFNESyBLZXknLFxuICogICBwZXJtaXNzaW9uczogeyByZWFkOiB0cnVlLCB3cml0ZTogdHJ1ZSwgZGVsZXRlOiBmYWxzZSB9XG4gKiB9KTtcbiAqXG4gKiAvLyBTYXZlIHRoZSBrZXkgc2VjdXJlbHkgLSBpdCB3b24ndCBiZSBzaG93biBhZ2FpbiFcbiAqIGNvbnNvbGUubG9nKCdTYXZlIHRoaXMga2V5OicsIHJlc3VsdC5rZXkpO1xuICogYGBgXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjcmVhdGUob3B0aW9uczogQ3JlYXRlQXBpS2V5T3B0aW9ucyk6IFByb21pc2U8Q3JlYXRlQXBpS2V5UmVzdWx0PiB7XG4gIGlmICghb3B0aW9ucy5uYW1lIHx8IG9wdGlvbnMubmFtZS50cmltKCkubGVuZ3RoID09PSAwKSB7XG4gICAgdGhyb3cgbmV3IFRoaW5rSGl2ZVZhbGlkYXRpb25FcnJvcignQVBJIGtleSBuYW1lIGlzIHJlcXVpcmVkJywgJ25hbWUnKTtcbiAgfVxuXG4gIC8vIFZhbGlkYXRlIElQIGFkZHJlc3NlcyBpZiBwcm92aWRlZFxuICBpZiAob3B0aW9ucy5hbGxvd2VkSXBzKSB7XG4gICAgLy8gSVB2NCBwYXR0ZXJuXG4gICAgY29uc3QgaXB2NFJlZ2V4ID0gL14oPzooPzoyNVswLTVdfDJbMC00XVswLTldfFswMV0/WzAtOV1bMC05XT8pXFwuKXszfSg/OjI1WzAtNV18MlswLTRdWzAtOV18WzAxXT9bMC05XVswLTldPykkLztcbiAgICAvLyBJUHY2IHBhdHRlcm4gKHN1cHBvcnRzIGZ1bGwgYW5kIGNvbXByZXNzZWQgbm90YXRpb24gbGlrZSA6OjEsIDIwMDE6ZGI4OjoxKVxuICAgIGNvbnN0IGlwdjZSZWdleCA9IC9eKD86KD86WzAtOWEtZkEtRl17MSw0fTopezd9WzAtOWEtZkEtRl17MSw0fXwoPzpbMC05YS1mQS1GXXsxLDR9Oil7MSw3fTp8KD86WzAtOWEtZkEtRl17MSw0fTopezEsNn06WzAtOWEtZkEtRl17MSw0fXwoPzpbMC05YS1mQS1GXXsxLDR9Oil7MSw1fSg/OjpbMC05YS1mQS1GXXsxLDR9KXsxLDJ9fCg/OlswLTlhLWZBLUZdezEsNH06KXsxLDR9KD86OlswLTlhLWZBLUZdezEsNH0pezEsM318KD86WzAtOWEtZkEtRl17MSw0fTopezEsM30oPzo6WzAtOWEtZkEtRl17MSw0fSl7MSw0fXwoPzpbMC05YS1mQS1GXXsxLDR9Oil7MSwyfSg/OjpbMC05YS1mQS1GXXsxLDR9KXsxLDV9fFswLTlhLWZBLUZdezEsNH06KD86OlswLTlhLWZBLUZdezEsNH0pezEsNn18Oig/OjpbMC05YS1mQS1GXXsxLDR9KXsxLDd9fDo6KSQvO1xuICAgIGZvciAoY29uc3QgaXAgb2Ygb3B0aW9ucy5hbGxvd2VkSXBzKSB7XG4gICAgICBpZiAoIWlwdjRSZWdleC50ZXN0KGlwKSAmJiAhaXB2NlJlZ2V4LnRlc3QoaXApICYmIGlwICE9PSAnbG9jYWxob3N0Jykge1xuICAgICAgICB0aHJvdyBuZXcgVGhpbmtIaXZlVmFsaWRhdGlvbkVycm9yKGBJbnZhbGlkIElQIGFkZHJlc3M6ICR7aXB9YCwgJ2FsbG93ZWRJcHMnKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBjb25zdCBib2R5OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiA9IHtcbiAgICBuYW1lOiBvcHRpb25zLm5hbWUudHJpbSgpLFxuICB9O1xuXG4gIGlmIChvcHRpb25zLnBlcm1pc3Npb25zKSB7XG4gICAgYm9keS5wZXJtaXNzaW9ucyA9IHtcbiAgICAgIHJlYWQ6IG9wdGlvbnMucGVybWlzc2lvbnMucmVhZCAhPT0gZmFsc2UsXG4gICAgICB3cml0ZTogb3B0aW9ucy5wZXJtaXNzaW9ucy53cml0ZSA9PT0gdHJ1ZSxcbiAgICAgIGRlbGV0ZTogb3B0aW9ucy5wZXJtaXNzaW9ucy5kZWxldGUgPT09IHRydWUsXG4gICAgfTtcbiAgfVxuXG4gIGlmIChvcHRpb25zLnNjb3BlVHlwZSkge1xuICAgIGJvZHkuc2NvcGVUeXBlID0gb3B0aW9ucy5zY29wZVR5cGU7XG4gIH1cblxuICBpZiAob3B0aW9ucy5lbnZpcm9ubWVudCkge1xuICAgIGJvZHkuZW52aXJvbm1lbnQgPSBvcHRpb25zLmVudmlyb25tZW50O1xuICB9XG5cbiAgaWYgKG9wdGlvbnMuYWxsb3dlZEFnZW50SWRzICYmIG9wdGlvbnMuYWxsb3dlZEFnZW50SWRzLmxlbmd0aCA+IDApIHtcbiAgICBib2R5LmFsbG93ZWRBZ2VudElkcyA9IG9wdGlvbnMuYWxsb3dlZEFnZW50SWRzO1xuICB9XG5cbiAgaWYgKG9wdGlvbnMuYWxsb3dlZElwcyAmJiBvcHRpb25zLmFsbG93ZWRJcHMubGVuZ3RoID4gMCkge1xuICAgIGJvZHkuYWxsb3dlZElwcyA9IG9wdGlvbnMuYWxsb3dlZElwcztcbiAgfVxuXG4gIGlmIChvcHRpb25zLmV4cGlyZXNBdCkge1xuICAgIGJvZHkuZXhwaXJlc0F0ID0gb3B0aW9ucy5leHBpcmVzQXQudG9JU09TdHJpbmcoKTtcbiAgfVxuXG4gIHJldHVybiBhcGlSZXF1ZXN0PENyZWF0ZUFwaUtleVJlc3VsdD4oJy9hcGkta2V5cycsIHtcbiAgICBtZXRob2Q6ICdQT1NUJyxcbiAgICBib2R5LFxuICAgIGFwaVZlcnNpb246ICd2MScsXG4gIH0pO1xufVxuXG4vKipcbiAqIExpc3QgYWxsIEFQSSBrZXlzIGZvciB0aGUgYXV0aGVudGljYXRlZCBjb21wYW55XG4gKlxuICogQHJldHVybnMgQXJyYXkgb2YgQVBJIGtleXMgKHdpdGhvdXQgc2VjcmV0cylcbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogY29uc3Qga2V5cyA9IGF3YWl0IGFwaUtleXMubGlzdCgpO1xuICogY29uc29sZS5sb2coYEZvdW5kICR7a2V5cy5sZW5ndGh9IEFQSSBrZXlzYCk7XG4gKlxuICogLy8gRmlsdGVyIGJ5IGVudmlyb25tZW50XG4gKiBjb25zdCBwcm9kS2V5cyA9IGtleXMuZmlsdGVyKGsgPT4gay5lbnZpcm9ubWVudCA9PT0gJ3Byb2R1Y3Rpb24nKTtcbiAqIGBgYFxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gbGlzdCgpOiBQcm9taXNlPEFwaUtleVtdPiB7XG4gIHJldHVybiBhcGlSZXF1ZXN0PEFwaUtleVtdPignL2FwaS1rZXlzJywge1xuICAgIG1ldGhvZDogJ0dFVCcsXG4gICAgYXBpVmVyc2lvbjogJ3YxJyxcbiAgfSk7XG59XG5cbi8qKlxuICogUmV2b2tlIChkZWFjdGl2YXRlKSBhbiBBUEkga2V5XG4gKlxuICogQHBhcmFtIGtleUlkIC0gSUQgb2YgdGhlIGtleSB0byByZXZva2VcbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogLy8gUmV2b2tlIGEgY29tcHJvbWlzZWQga2V5XG4gKiBhd2FpdCBhcGlLZXlzLnJldm9rZSgna2V5LWlkLXRvLXJldm9rZScpO1xuICogYGBgXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiByZXZva2Uoa2V5SWQ6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICBpZiAoIWtleUlkKSB7XG4gICAgdGhyb3cgbmV3IFRoaW5rSGl2ZVZhbGlkYXRpb25FcnJvcignS2V5IElEIGlzIHJlcXVpcmVkJywgJ2tleUlkJyk7XG4gIH1cblxuICBhd2FpdCBhcGlSZXF1ZXN0KGAvYXBpLWtleXMvJHtrZXlJZH1gLCB7XG4gICAgbWV0aG9kOiAnREVMRVRFJyxcbiAgICBhcGlWZXJzaW9uOiAndjEnLFxuICB9KTtcbn1cblxuLyoqXG4gKiBSb3RhdGUgYW4gQVBJIGtleSAocmV2b2tlIG9sZCwgY3JlYXRlIG5ldyB3aXRoIHNhbWUgY29uZmlnKVxuICpcbiAqIEBwYXJhbSBrZXlJZCAtIElEIG9mIHRoZSBrZXkgdG8gcm90YXRlXG4gKiBAcGFyYW0gb3B0aW9ucyAtIE9wdGlvbmFsIG92ZXJyaWRlcyBmb3IgdGhlIG5ldyBrZXlcbiAqIEByZXR1cm5zIE5ldyBrZXkgd2l0aCBzZWNyZXRcbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogLy8gUm90YXRlIGEga2V5IHBlcmlvZGljYWxseVxuICogY29uc3QgZXhpc3RpbmdLZXlzID0gYXdhaXQgYXBpS2V5cy5saXN0KCk7XG4gKiBjb25zdCBvbGRLZXkgPSBleGlzdGluZ0tleXMuZmluZChrID0+IGsubmFtZSA9PT0gJ1Byb2R1Y3Rpb24gS2V5Jyk7XG4gKlxuICogaWYgKG9sZEtleSkge1xuICogICBjb25zdCBuZXdLZXkgPSBhd2FpdCBhcGlLZXlzLnJvdGF0ZShvbGRLZXkuaWQpO1xuICogICAvLyBVcGRhdGUgeW91ciBzeXN0ZW1zIHdpdGggbmV3S2V5LmtleVxuICogICBjb25zb2xlLmxvZygnTmV3IGtleTonLCBuZXdLZXkua2V5KTtcbiAqIH1cbiAqIGBgYFxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcm90YXRlKFxuICBrZXlJZDogc3RyaW5nLFxuICBvcHRpb25zPzogUGFydGlhbDxDcmVhdGVBcGlLZXlPcHRpb25zPlxuKTogUHJvbWlzZTxDcmVhdGVBcGlLZXlSZXN1bHQ+IHtcbiAgLy8gR2V0IGV4aXN0aW5nIGtleSBtZXRhZGF0YVxuICBjb25zdCBrZXlzID0gYXdhaXQgbGlzdCgpO1xuICBjb25zdCBleGlzdGluZ0tleSA9IGtleXMuZmluZChrID0+IGsuaWQgPT09IGtleUlkKTtcblxuICBpZiAoIWV4aXN0aW5nS2V5KSB7XG4gICAgdGhyb3cgbmV3IFRoaW5rSGl2ZVZhbGlkYXRpb25FcnJvcignQVBJIGtleSBub3QgZm91bmQnLCAna2V5SWQnKTtcbiAgfVxuXG4gIC8vIENyZWF0ZSBuZXcga2V5IHdpdGggc2FtZSAob3Igb3ZlcnJpZGRlbikgY29uZmlnXG4gIGNvbnN0IG5ld0tleU9wdGlvbnM6IENyZWF0ZUFwaUtleU9wdGlvbnMgPSB7XG4gICAgbmFtZTogb3B0aW9ucz8ubmFtZSB8fCBleGlzdGluZ0tleS5uYW1lLFxuICAgIHNjb3BlVHlwZTogb3B0aW9ucz8uc2NvcGVUeXBlIHx8IGV4aXN0aW5nS2V5LnNjb3BlVHlwZSxcbiAgICBlbnZpcm9ubWVudDogb3B0aW9ucz8uZW52aXJvbm1lbnQgfHwgZXhpc3RpbmdLZXkuZW52aXJvbm1lbnQsXG4gICAgYWxsb3dlZEFnZW50SWRzOiBvcHRpb25zPy5hbGxvd2VkQWdlbnRJZHMgfHwgZXhpc3RpbmdLZXkuYWxsb3dlZEFnZW50SWRzIHx8IHVuZGVmaW5lZCxcbiAgICBhbGxvd2VkSXBzOiBvcHRpb25zPy5hbGxvd2VkSXBzIHx8IGV4aXN0aW5nS2V5LmFsbG93ZWRJcHMgfHwgdW5kZWZpbmVkLFxuICB9O1xuXG4gIC8vIEhhbmRsZSBwZXJtaXNzaW9ucyBjb252ZXJzaW9uXG4gIGlmIChvcHRpb25zPy5wZXJtaXNzaW9ucykge1xuICAgIG5ld0tleU9wdGlvbnMucGVybWlzc2lvbnMgPSBvcHRpb25zLnBlcm1pc3Npb25zO1xuICB9IGVsc2UgaWYgKGV4aXN0aW5nS2V5LnBlcm1pc3Npb25zICYmIHR5cGVvZiBleGlzdGluZ0tleS5wZXJtaXNzaW9ucyA9PT0gJ29iamVjdCcgJiYgIUFycmF5LmlzQXJyYXkoZXhpc3RpbmdLZXkucGVybWlzc2lvbnMpKSB7XG4gICAgbmV3S2V5T3B0aW9ucy5wZXJtaXNzaW9ucyA9IGV4aXN0aW5nS2V5LnBlcm1pc3Npb25zIGFzIEFwaUtleVBlcm1pc3Npb25zO1xuICB9XG5cbiAgLy8gQ3JlYXRlIG5ldyBrZXkgZmlyc3QgKHNvIHdlIGRvbid0IGxvc2UgYWNjZXNzIGlmIGNyZWF0aW9uIGZhaWxzKVxuICBjb25zdCBuZXdLZXkgPSBhd2FpdCBjcmVhdGUobmV3S2V5T3B0aW9ucyk7XG5cbiAgLy8gUmV2b2tlIG9sZCBrZXlcbiAgYXdhaXQgcmV2b2tlKGtleUlkKTtcblxuICByZXR1cm4gbmV3S2V5O1xufVxuXG4vKipcbiAqIFRlc3QgYW4gQVBJIGtleSBjb25uZWN0aW9uXG4gKlxuICogQHBhcmFtIGFwaUtleSAtIFRoZSBmdWxsIEFQSSBrZXkgdmFsdWUgdG8gdGVzdFxuICogQHBhcmFtIGFnZW50SWQgLSBPcHRpb25hbCBhZ2VudCBJRCB0byB0ZXN0IHdpdGhcbiAqIEByZXR1cm5zIFRlc3QgcmVzdWx0XG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGFwaUtleXMudGVzdCgndGhrXy4uLicsICdteS1hZ2VudC1pZCcpO1xuICogaWYgKHJlc3VsdC5zdWNjZXNzKSB7XG4gKiAgIGNvbnNvbGUubG9nKCdLZXkgaXMgdmFsaWQhJyk7XG4gKiB9XG4gKiBgYGBcbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHRlc3QoXG4gIGFwaUtleTogc3RyaW5nLFxuICBhZ2VudElkPzogc3RyaW5nXG4pOiBQcm9taXNlPHsgc3VjY2VzczogYm9vbGVhbjsgZXJyb3I/OiBzdHJpbmc7IHRlc3RUcmFjZUlkPzogc3RyaW5nIH0+IHtcbiAgaWYgKCFhcGlLZXkpIHtcbiAgICB0aHJvdyBuZXcgVGhpbmtIaXZlVmFsaWRhdGlvbkVycm9yKCdBUEkga2V5IGlzIHJlcXVpcmVkJywgJ2FwaUtleScpO1xuICB9XG5cbiAgcmV0dXJuIGFwaVJlcXVlc3Q8eyBzdWNjZXNzOiBib29sZWFuOyBlcnJvcj86IHN0cmluZzsgdGVzdFRyYWNlSWQ/OiBzdHJpbmcgfT4oJy9hcGkta2V5cy90ZXN0Jywge1xuICAgIG1ldGhvZDogJ1BPU1QnLFxuICAgIGJvZHk6IHsgYXBpS2V5LCBhZ2VudElkLCBjcmVhdGVUZXN0VHJhY2U6ICEhYWdlbnRJZCB9LFxuICAgIGFwaVZlcnNpb246ICd2MScsXG4gIH0pO1xufVxuXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4vLyBVdGlsaXR5IEZ1bmN0aW9uc1xuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4vKipcbiAqIENoZWNrIGlmIGFuIEFQSSBrZXkgaGFzIGEgc3BlY2lmaWMgcGVybWlzc2lvblxuICovXG5leHBvcnQgZnVuY3Rpb24gaGFzUGVybWlzc2lvbihcbiAga2V5OiBBcGlLZXksXG4gIHBlcm1pc3Npb246ICdyZWFkJyB8ICd3cml0ZScgfCAnZGVsZXRlJ1xuKTogYm9vbGVhbiB7XG4gIC8vIEhhbmRsZSBvYmplY3QgZm9ybWF0XG4gIGlmICh0eXBlb2Yga2V5LnBlcm1pc3Npb25zID09PSAnb2JqZWN0JyAmJiAhQXJyYXkuaXNBcnJheShrZXkucGVybWlzc2lvbnMpKSB7XG4gICAgcmV0dXJuIGtleS5wZXJtaXNzaW9uc1twZXJtaXNzaW9uXSA9PT0gdHJ1ZTtcbiAgfVxuXG4gIC8vIEhhbmRsZSBsZWdhY3kgYXJyYXkgZm9ybWF0XG4gIGlmIChBcnJheS5pc0FycmF5KGtleS5wZXJtaXNzaW9ucykpIHtcbiAgICByZXR1cm4ga2V5LnBlcm1pc3Npb25zLnNvbWUocCA9PiBwLmVuZHNXaXRoKGA6JHtwZXJtaXNzaW9ufWApKTtcbiAgfVxuXG4gIC8vIERlZmF1bHQ6IHJlYWQgYWxsb3dlZCwgd3JpdGUvZGVsZXRlIGRlbmllZFxuICByZXR1cm4gcGVybWlzc2lvbiA9PT0gJ3JlYWQnO1xufVxuXG4vKipcbiAqIENoZWNrIGlmIGFuIEFQSSBrZXkgaXMgZXhwaXJlZFxuICovXG5leHBvcnQgZnVuY3Rpb24gaXNFeHBpcmVkKGtleTogQXBpS2V5KTogYm9vbGVhbiB7XG4gIGlmICgha2V5LmV4cGlyZXNBdCkgcmV0dXJuIGZhbHNlO1xuICByZXR1cm4gbmV3IERhdGUoa2V5LmV4cGlyZXNBdCkgPCBuZXcgRGF0ZSgpO1xufVxuXG4vKipcbiAqIENoZWNrIGlmIGFuIEFQSSBrZXkgaXMgdmFsaWQgKGFjdGl2ZSBhbmQgbm90IGV4cGlyZWQpXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpc1ZhbGlkKGtleTogQXBpS2V5KTogYm9vbGVhbiB7XG4gIHJldHVybiBrZXkuaXNBY3RpdmUgJiYgIWlzRXhwaXJlZChrZXkpO1xufVxuXG4vKipcbiAqIEdldCB0aW1lIHVudGlsIGtleSBleHBpcmVzIChpbiBtaWxsaXNlY29uZHMpXG4gKiBSZXR1cm5zIG51bGwgaWYga2V5IGRvZXNuJ3QgZXhwaXJlXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRUaW1lVW50aWxFeHBpcnkoa2V5OiBBcGlLZXkpOiBudW1iZXIgfCBudWxsIHtcbiAgaWYgKCFrZXkuZXhwaXJlc0F0KSByZXR1cm4gbnVsbDtcbiAgY29uc3QgZXhwaXJ5ID0gbmV3IERhdGUoa2V5LmV4cGlyZXNBdCkuZ2V0VGltZSgpO1xuICBjb25zdCBub3cgPSBEYXRlLm5vdygpO1xuICByZXR1cm4gTWF0aC5tYXgoMCwgZXhwaXJ5IC0gbm93KTtcbn1cblxuLyoqXG4gKiBDaGVjayBpZiBhIGtleSBjYW4gYWNjZXNzIGEgc3BlY2lmaWMgYWdlbnRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNhbkFjY2Vzc0FnZW50KGtleTogQXBpS2V5LCBhZ2VudElkOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgLy8gSWYgbm8gYWdlbnQgcmVzdHJpY3Rpb25zLCBhbGxvdyBhbGxcbiAgaWYgKCFrZXkuYWxsb3dlZEFnZW50SWRzIHx8IGtleS5hbGxvd2VkQWdlbnRJZHMubGVuZ3RoID09PSAwKSB7XG4gICAgcmV0dXJuIHRydWU7XG4gIH1cbiAgcmV0dXJuIGtleS5hbGxvd2VkQWdlbnRJZHMuaW5jbHVkZXMoYWdlbnRJZCk7XG59XG5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbi8vIEV4cG9ydCBuYW1lc3BhY2Vcbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuZXhwb3J0IGNvbnN0IGFwaUtleXMgPSB7XG4gIGNyZWF0ZSxcbiAgbGlzdCxcbiAgcmV2b2tlLFxuICByb3RhdGUsXG4gIHRlc3QsXG4gIGhhc1Blcm1pc3Npb24sXG4gIGlzRXhwaXJlZCxcbiAgaXNWYWxpZCxcbiAgZ2V0VGltZVVudGlsRXhwaXJ5LFxuICBjYW5BY2Nlc3NBZ2VudCxcbn07XG4iXX0=
|