lavs-runtime 0.1.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 +75 -0
- package/dist/function-executor.d.ts +26 -0
- package/dist/function-executor.d.ts.map +1 -0
- package/dist/function-executor.js +116 -0
- package/dist/function-executor.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/loader.d.ts +37 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +187 -0
- package/dist/loader.js.map +1 -0
- package/dist/permission-checker.d.ts +86 -0
- package/dist/permission-checker.d.ts.map +1 -0
- package/dist/permission-checker.js +172 -0
- package/dist/permission-checker.js.map +1 -0
- package/dist/rate-limiter.d.ts +57 -0
- package/dist/rate-limiter.d.ts.map +1 -0
- package/dist/rate-limiter.js +84 -0
- package/dist/rate-limiter.js.map +1 -0
- package/dist/script-executor.d.ts +70 -0
- package/dist/script-executor.d.ts.map +1 -0
- package/dist/script-executor.js +314 -0
- package/dist/script-executor.js.map +1 -0
- package/dist/subscription-manager.d.ts +106 -0
- package/dist/subscription-manager.d.ts.map +1 -0
- package/dist/subscription-manager.js +257 -0
- package/dist/subscription-manager.js.map +1 -0
- package/dist/tool-generator.d.ts +51 -0
- package/dist/tool-generator.d.ts.map +1 -0
- package/dist/tool-generator.js +147 -0
- package/dist/tool-generator.js.map +1 -0
- package/dist/types.d.ts +171 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +38 -0
- package/dist/types.js.map +1 -0
- package/dist/validator.d.ts +94 -0
- package/dist/validator.d.ts.map +1 -0
- package/dist/validator.js +187 -0
- package/dist/validator.js.map +1 -0
- package/package.json +51 -0
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* LAVS Script Executor
|
|
4
|
+
*
|
|
5
|
+
* Executes script handlers with proper input/output handling and security.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.ScriptExecutor = void 0;
|
|
9
|
+
const child_process_1 = require("child_process");
|
|
10
|
+
const types_1 = require("./types");
|
|
11
|
+
class ScriptExecutor {
|
|
12
|
+
/**
|
|
13
|
+
* Execute a script handler
|
|
14
|
+
* @param handler - Script handler configuration
|
|
15
|
+
* @param input - Input data to pass to script
|
|
16
|
+
* @param context - Execution context with permissions
|
|
17
|
+
* @returns Script output (parsed as JSON)
|
|
18
|
+
*/
|
|
19
|
+
async execute(handler, input, context) {
|
|
20
|
+
const startTime = Date.now();
|
|
21
|
+
console.log(`[LAVS] Executing script for ${context.endpointId}`, {
|
|
22
|
+
command: handler.command,
|
|
23
|
+
args: handler.args,
|
|
24
|
+
input: handler.input,
|
|
25
|
+
});
|
|
26
|
+
try {
|
|
27
|
+
// 1. Prepare command and arguments
|
|
28
|
+
const { command, args = [] } = handler;
|
|
29
|
+
const resolvedArgs = this.resolveArgs(args, input);
|
|
30
|
+
// 2. Prepare environment variables
|
|
31
|
+
const processEnv = this.buildEnvironment(handler, input, context);
|
|
32
|
+
// 3. Determine timeout
|
|
33
|
+
const timeout = handler.timeout || context.timeout || 30000;
|
|
34
|
+
// 4. Spawn process
|
|
35
|
+
const proc = (0, child_process_1.spawn)(command, resolvedArgs, {
|
|
36
|
+
cwd: handler.cwd || context.workdir,
|
|
37
|
+
env: processEnv,
|
|
38
|
+
timeout,
|
|
39
|
+
stdio: ['pipe', 'pipe', 'pipe'], // stdin, stdout, stderr
|
|
40
|
+
});
|
|
41
|
+
// 5. Send input to stdin if needed
|
|
42
|
+
if (handler.input === 'stdin' && input) {
|
|
43
|
+
try {
|
|
44
|
+
proc.stdin.write(JSON.stringify(input));
|
|
45
|
+
proc.stdin.end();
|
|
46
|
+
}
|
|
47
|
+
catch (e) {
|
|
48
|
+
console.error(`[LAVS] Failed to write to stdin:`, e);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// 6. Capture output and wait for completion
|
|
52
|
+
const result = await this.captureOutput(proc, timeout, context.endpointId);
|
|
53
|
+
const duration = Date.now() - startTime;
|
|
54
|
+
console.log(`[LAVS] Script completed in ${duration}ms`, {
|
|
55
|
+
endpointId: context.endpointId,
|
|
56
|
+
exitCode: result.exitCode,
|
|
57
|
+
});
|
|
58
|
+
// 7. Handle non-zero exit code
|
|
59
|
+
if (result.exitCode !== 0) {
|
|
60
|
+
throw new types_1.LAVSError(types_1.LAVSErrorCode.HandlerError, `Script exited with code ${result.exitCode}`, {
|
|
61
|
+
exitCode: result.exitCode,
|
|
62
|
+
stderr: result.stderr,
|
|
63
|
+
stdout: result.stdout,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
// 8. Parse output as JSON
|
|
67
|
+
return this.parseOutput(result.stdout, result.stderr);
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
if (error instanceof types_1.LAVSError) {
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
// Handle timeout
|
|
74
|
+
if (error.code === 'ETIMEDOUT' || error.killed) {
|
|
75
|
+
throw new types_1.LAVSError(types_1.LAVSErrorCode.Timeout, `Script execution timeout after ${handler.timeout || context.timeout || 30000}ms`);
|
|
76
|
+
}
|
|
77
|
+
// Handle spawn errors
|
|
78
|
+
throw new types_1.LAVSError(types_1.LAVSErrorCode.HandlerError, `Script execution failed: ${error.message}`, { cause: error });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Resolve argument templates with input values
|
|
83
|
+
* Replaces {{path.to.value}} with actual values from input
|
|
84
|
+
*/
|
|
85
|
+
resolveArgs(args, input) {
|
|
86
|
+
if (!input)
|
|
87
|
+
return args;
|
|
88
|
+
return args.map((arg) => {
|
|
89
|
+
return arg.replace(/\{\{([^}]+)\}\}/g, (_, path) => {
|
|
90
|
+
const value = this.getValueByPath(input, path);
|
|
91
|
+
return value != null ? String(value) : '';
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Get value from nested object by dot path
|
|
97
|
+
* e.g., "user.name" from { user: { name: "Alice" } } => "Alice"
|
|
98
|
+
* Protects against prototype pollution by blocking __proto__, constructor, prototype keys.
|
|
99
|
+
*/
|
|
100
|
+
getValueByPath(obj, path) {
|
|
101
|
+
// Block prototype pollution attacks
|
|
102
|
+
const BLOCKED_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
|
|
103
|
+
const keys = path.split('.');
|
|
104
|
+
let current = obj;
|
|
105
|
+
for (const key of keys) {
|
|
106
|
+
if (current == null)
|
|
107
|
+
return undefined;
|
|
108
|
+
if (BLOCKED_KEYS.has(key))
|
|
109
|
+
return undefined;
|
|
110
|
+
if (typeof current !== 'object')
|
|
111
|
+
return undefined;
|
|
112
|
+
current = current[key];
|
|
113
|
+
}
|
|
114
|
+
return current;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Build environment variables for script execution
|
|
118
|
+
*/
|
|
119
|
+
buildEnvironment(handler, input, context) {
|
|
120
|
+
const env = {
|
|
121
|
+
// Inherit base environment (filtered for security)
|
|
122
|
+
...this.getBaseEnvironment(),
|
|
123
|
+
// LAVS context variables
|
|
124
|
+
LAVS_AGENT_ID: context.agentId,
|
|
125
|
+
LAVS_ENDPOINT_ID: context.endpointId,
|
|
126
|
+
// Handler-specific env vars (declared by manifest author)
|
|
127
|
+
...(handler.env || {}),
|
|
128
|
+
// Context env vars (e.g. LAVS_PROJECT_PATH)
|
|
129
|
+
...(context.env || {}),
|
|
130
|
+
};
|
|
131
|
+
// If input mode is 'env', flatten input object to env vars
|
|
132
|
+
if (handler.input === 'env' && input) {
|
|
133
|
+
Object.assign(env, this.inputToEnv(input));
|
|
134
|
+
}
|
|
135
|
+
// Final safety pass: remove any sensitive vars that might have leaked through
|
|
136
|
+
return this.filterSensitiveVars(env);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Sensitive environment variable patterns (case-insensitive match).
|
|
140
|
+
* These keywords in variable names indicate potentially sensitive data.
|
|
141
|
+
*/
|
|
142
|
+
static SENSITIVE_PATTERNS = [
|
|
143
|
+
'SECRET',
|
|
144
|
+
'TOKEN',
|
|
145
|
+
'PASSWORD',
|
|
146
|
+
'PASSWD',
|
|
147
|
+
'CREDENTIAL',
|
|
148
|
+
'PRIVATE_KEY',
|
|
149
|
+
'API_KEY',
|
|
150
|
+
'APIKEY',
|
|
151
|
+
'ACCESS_KEY',
|
|
152
|
+
'AUTH',
|
|
153
|
+
];
|
|
154
|
+
/**
|
|
155
|
+
* Whitelist of variable names that are safe even if they match sensitive patterns.
|
|
156
|
+
* For example, LAVS variables or NODE_ENV.
|
|
157
|
+
*/
|
|
158
|
+
static SAFE_OVERRIDES = new Set([
|
|
159
|
+
'LAVS_AGENT_ID',
|
|
160
|
+
'LAVS_ENDPOINT_ID',
|
|
161
|
+
'LAVS_PROJECT_PATH',
|
|
162
|
+
'NODE_ENV',
|
|
163
|
+
]);
|
|
164
|
+
/**
|
|
165
|
+
* Filter out environment variables that match sensitive patterns.
|
|
166
|
+
* Variables explicitly declared in handler.env are preserved (they are
|
|
167
|
+
* intentionally set by the manifest author), but inherited vars from
|
|
168
|
+
* process.env that match sensitive patterns are removed.
|
|
169
|
+
*
|
|
170
|
+
* @param env - Environment variables to filter
|
|
171
|
+
* @returns Filtered environment variables
|
|
172
|
+
*/
|
|
173
|
+
filterSensitiveVars(env) {
|
|
174
|
+
const filtered = {};
|
|
175
|
+
for (const [key, value] of Object.entries(env)) {
|
|
176
|
+
// Always allow safe overrides
|
|
177
|
+
if (ScriptExecutor.SAFE_OVERRIDES.has(key)) {
|
|
178
|
+
filtered[key] = value;
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
// Check if variable name matches any sensitive pattern
|
|
182
|
+
const upperKey = key.toUpperCase();
|
|
183
|
+
const isSensitive = ScriptExecutor.SENSITIVE_PATTERNS.some((pattern) => upperKey.includes(pattern));
|
|
184
|
+
if (!isSensitive) {
|
|
185
|
+
filtered[key] = value;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return filtered;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Get base environment variables (filtered for security)
|
|
192
|
+
* Only include safe variables, exclude sensitive ones
|
|
193
|
+
*/
|
|
194
|
+
getBaseEnvironment() {
|
|
195
|
+
const env = {};
|
|
196
|
+
// Whitelist of safe env vars to inherit
|
|
197
|
+
const safeVars = [
|
|
198
|
+
'PATH',
|
|
199
|
+
'HOME',
|
|
200
|
+
'USER',
|
|
201
|
+
'LANG',
|
|
202
|
+
'LC_ALL',
|
|
203
|
+
'TZ',
|
|
204
|
+
'NODE_ENV',
|
|
205
|
+
'SHELL',
|
|
206
|
+
'TMPDIR',
|
|
207
|
+
'TERM',
|
|
208
|
+
];
|
|
209
|
+
for (const key of safeVars) {
|
|
210
|
+
if (process.env[key]) {
|
|
211
|
+
env[key] = process.env[key];
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return env;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Convert input object to environment variables
|
|
218
|
+
* Flattens nested objects with underscore notation
|
|
219
|
+
*/
|
|
220
|
+
inputToEnv(input, prefix = '') {
|
|
221
|
+
const env = {};
|
|
222
|
+
for (const [key, value] of Object.entries(input)) {
|
|
223
|
+
const envKey = prefix ? `${prefix}_${key}` : key;
|
|
224
|
+
if (value == null) {
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
else if (typeof value === 'object' && !Array.isArray(value)) {
|
|
228
|
+
// Recursively flatten nested objects
|
|
229
|
+
Object.assign(env, this.inputToEnv(value, envKey.toUpperCase()));
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
// Convert to string
|
|
233
|
+
env[envKey.toUpperCase()] = String(value);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return env;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Capture stdout/stderr from process and wait for completion
|
|
240
|
+
*/
|
|
241
|
+
async captureOutput(proc, timeout, endpointId) {
|
|
242
|
+
let stdout = '';
|
|
243
|
+
let stderr = '';
|
|
244
|
+
// Collect stdout
|
|
245
|
+
proc.stdout?.on('data', (data) => {
|
|
246
|
+
stdout += data.toString();
|
|
247
|
+
});
|
|
248
|
+
// Collect stderr (and log it)
|
|
249
|
+
proc.stderr?.on('data', (data) => {
|
|
250
|
+
const text = data.toString();
|
|
251
|
+
stderr += text;
|
|
252
|
+
console.log(`[LAVS:${endpointId}] stderr:`, text);
|
|
253
|
+
});
|
|
254
|
+
// Wait for process to exit
|
|
255
|
+
return new Promise((resolve, reject) => {
|
|
256
|
+
let timeoutHandle = null;
|
|
257
|
+
// Set timeout
|
|
258
|
+
timeoutHandle = setTimeout(() => {
|
|
259
|
+
proc.kill('SIGTERM');
|
|
260
|
+
// Give it 5 seconds to terminate gracefully, then force kill
|
|
261
|
+
setTimeout(() => proc.kill('SIGKILL'), 5000);
|
|
262
|
+
reject(new types_1.LAVSError(types_1.LAVSErrorCode.Timeout, `Script execution timeout after ${timeout}ms`));
|
|
263
|
+
}, timeout);
|
|
264
|
+
proc.on('exit', (code) => {
|
|
265
|
+
if (timeoutHandle)
|
|
266
|
+
clearTimeout(timeoutHandle);
|
|
267
|
+
resolve({
|
|
268
|
+
stdout,
|
|
269
|
+
stderr,
|
|
270
|
+
exitCode: code || 0,
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
proc.on('error', (error) => {
|
|
274
|
+
if (timeoutHandle)
|
|
275
|
+
clearTimeout(timeoutHandle);
|
|
276
|
+
reject(error);
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Parse script output as JSON
|
|
282
|
+
*/
|
|
283
|
+
parseOutput(stdout, stderr) {
|
|
284
|
+
const trimmed = stdout.trim();
|
|
285
|
+
// Handle empty output
|
|
286
|
+
if (!trimmed) {
|
|
287
|
+
return null;
|
|
288
|
+
}
|
|
289
|
+
// Try to parse as JSON
|
|
290
|
+
try {
|
|
291
|
+
return JSON.parse(trimmed);
|
|
292
|
+
}
|
|
293
|
+
catch (e) {
|
|
294
|
+
// If not valid JSON, try to extract JSON from output
|
|
295
|
+
// Look for first { or [ and last } or ]
|
|
296
|
+
const jsonMatch = trimmed.match(/(\{[\s\S]*\}|\[[\s\S]*\])/);
|
|
297
|
+
if (jsonMatch) {
|
|
298
|
+
try {
|
|
299
|
+
return JSON.parse(jsonMatch[1]);
|
|
300
|
+
}
|
|
301
|
+
catch (e2) {
|
|
302
|
+
// Fall through to error
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
throw new types_1.LAVSError(types_1.LAVSErrorCode.HandlerError, 'Script output is not valid JSON', {
|
|
306
|
+
stdout: trimmed,
|
|
307
|
+
stderr,
|
|
308
|
+
parseError: e.message,
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
exports.ScriptExecutor = ScriptExecutor;
|
|
314
|
+
//# sourceMappingURL=script-executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"script-executor.js","sourceRoot":"","sources":["../src/script-executor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LAVS Subscription Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages real-time subscriptions for LAVS endpoints.
|
|
5
|
+
* Uses Server-Sent Events (SSE) as the transport layer.
|
|
6
|
+
*
|
|
7
|
+
* Protocol flow:
|
|
8
|
+
* 1. Client opens SSE connection: GET /api/agents/:agentId/lavs/:endpoint/subscribe
|
|
9
|
+
* 2. Server sends events when data changes
|
|
10
|
+
* 3. Client or server can close the connection
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Minimal writable response interface for SSE.
|
|
14
|
+
* Compatible with express.Response, http.ServerResponse, etc.
|
|
15
|
+
*/
|
|
16
|
+
export interface SSEResponse {
|
|
17
|
+
writeHead(statusCode: number, headers: Record<string, string>): void;
|
|
18
|
+
write(chunk: string): boolean;
|
|
19
|
+
end(): void;
|
|
20
|
+
on(event: string, listener: (...args: any[]) => void): void;
|
|
21
|
+
writableEnded: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Event data to push to subscribers
|
|
25
|
+
*/
|
|
26
|
+
export interface SubscriptionEvent {
|
|
27
|
+
type: string;
|
|
28
|
+
data?: unknown;
|
|
29
|
+
timestamp?: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Configuration for the subscription manager
|
|
33
|
+
*/
|
|
34
|
+
interface SubscriptionManagerOptions {
|
|
35
|
+
/** Maximum number of concurrent subscriptions (default: 100) */
|
|
36
|
+
maxSubscriptions?: number;
|
|
37
|
+
/** Heartbeat interval in ms to keep connections alive (default: 30000) */
|
|
38
|
+
heartbeatIntervalMs?: number;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* LAVS Subscription Manager - manages SSE-based subscriptions
|
|
42
|
+
*/
|
|
43
|
+
export declare class SubscriptionManager {
|
|
44
|
+
private subscriptions;
|
|
45
|
+
private readonly maxSubscriptions;
|
|
46
|
+
private heartbeatTimer;
|
|
47
|
+
constructor(options?: SubscriptionManagerOptions);
|
|
48
|
+
/**
|
|
49
|
+
* Send heartbeat to all subscriptions. Removes stale connections.
|
|
50
|
+
*/
|
|
51
|
+
private sendHeartbeats;
|
|
52
|
+
/**
|
|
53
|
+
* Stop the heartbeat timer (for graceful shutdown / tests)
|
|
54
|
+
*/
|
|
55
|
+
destroy(): void;
|
|
56
|
+
/**
|
|
57
|
+
* Create a new subscription.
|
|
58
|
+
*
|
|
59
|
+
* @param agentId - Agent identifier
|
|
60
|
+
* @param endpointId - Endpoint identifier (must be a subscription endpoint)
|
|
61
|
+
* @param res - Express response object (will be held open for SSE)
|
|
62
|
+
* @returns Subscription ID
|
|
63
|
+
* @throws Error if max subscriptions limit is reached
|
|
64
|
+
*/
|
|
65
|
+
subscribe(agentId: string, endpointId: string, res: SSEResponse): string;
|
|
66
|
+
/**
|
|
67
|
+
* Remove a subscription
|
|
68
|
+
*/
|
|
69
|
+
unsubscribe(subscriptionId: string): boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Push an event to all subscribers of a specific agent+endpoint.
|
|
72
|
+
*
|
|
73
|
+
* @param agentId - Agent identifier
|
|
74
|
+
* @param endpointId - Endpoint identifier
|
|
75
|
+
* @param event - Event data to push
|
|
76
|
+
* @returns Number of subscribers notified
|
|
77
|
+
*/
|
|
78
|
+
publish(agentId: string, endpointId: string, event: SubscriptionEvent): number;
|
|
79
|
+
/**
|
|
80
|
+
* Push an event to all subscribers of a specific agent (any endpoint).
|
|
81
|
+
*
|
|
82
|
+
* @param agentId - Agent identifier
|
|
83
|
+
* @param event - Event data to push
|
|
84
|
+
* @returns Number of subscribers notified
|
|
85
|
+
*/
|
|
86
|
+
publishToAgent(agentId: string, event: SubscriptionEvent): number;
|
|
87
|
+
/**
|
|
88
|
+
* Get count of active subscriptions
|
|
89
|
+
*/
|
|
90
|
+
getActiveCount(): number;
|
|
91
|
+
/**
|
|
92
|
+
* Get active subscriptions for an agent
|
|
93
|
+
*/
|
|
94
|
+
getSubscriptionsForAgent(agentId: string): {
|
|
95
|
+
id: string;
|
|
96
|
+
endpointId: string;
|
|
97
|
+
createdAt: number;
|
|
98
|
+
}[];
|
|
99
|
+
/**
|
|
100
|
+
* Send an SSE event to a response stream
|
|
101
|
+
*/
|
|
102
|
+
private sendSSE;
|
|
103
|
+
}
|
|
104
|
+
export declare const subscriptionManager: SubscriptionManager;
|
|
105
|
+
export {};
|
|
106
|
+
//# sourceMappingURL=subscription-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscription-manager.d.ts","sourceRoot":"","sources":["../src/subscription-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IACrE,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IAC9B,GAAG,IAAI,IAAI,CAAC;IACZ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,GAAG,IAAI,CAAC;IAC5D,aAAa,EAAE,OAAO,CAAC;CACxB;AAaD;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,UAAU,0BAA0B;IAClC,gEAAgE;IAChE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,0EAA0E;IAC1E,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;GAEG;AACH,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,aAAa,CAAwC;IAC7D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,cAAc,CAA+C;gBAEzD,OAAO,GAAE,0BAA+B;IAYpD;;OAEG;IACH,OAAO,CAAC,cAAc;IAetB;;OAEG;IACH,OAAO,IAAI,IAAI;IAWf;;;;;;;;OAQG;IACH,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,GAAG,MAAM;IAwDxE;;OAEG;IACH,WAAW,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO;IAgC5C;;;;;;;OAOG;IACH,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,GAAG,MAAM;IA2B9E;;;;;;OAMG;IACH,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,GAAG,MAAM;IA0BjE;;OAEG;IACH,cAAc,IAAI,MAAM;IAIxB;;OAEG;IACH,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,EAAE;IAclG;;OAEG;IACH,OAAO,CAAC,OAAO;CAYhB;AAGD,eAAO,MAAM,mBAAmB,qBAA4B,CAAC"}
|