@dotsetlabs/tollgate 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/LICENSE +21 -0
- package/README.md +885 -0
- package/dist/analyzers/filesystem.d.ts +26 -0
- package/dist/analyzers/filesystem.d.ts.map +1 -0
- package/dist/analyzers/filesystem.js +284 -0
- package/dist/analyzers/filesystem.js.map +1 -0
- package/dist/analyzers/http.d.ts +90 -0
- package/dist/analyzers/http.d.ts.map +1 -0
- package/dist/analyzers/http.js +433 -0
- package/dist/analyzers/http.js.map +1 -0
- package/dist/analyzers/index.d.ts +101 -0
- package/dist/analyzers/index.d.ts.map +1 -0
- package/dist/analyzers/index.js +342 -0
- package/dist/analyzers/index.js.map +1 -0
- package/dist/analyzers/loader.d.ts +114 -0
- package/dist/analyzers/loader.d.ts.map +1 -0
- package/dist/analyzers/loader.js +184 -0
- package/dist/analyzers/loader.js.map +1 -0
- package/dist/analyzers/prompt-injection.d.ts +95 -0
- package/dist/analyzers/prompt-injection.d.ts.map +1 -0
- package/dist/analyzers/prompt-injection.js +725 -0
- package/dist/analyzers/prompt-injection.js.map +1 -0
- package/dist/analyzers/sdk.d.ts +230 -0
- package/dist/analyzers/sdk.d.ts.map +1 -0
- package/dist/analyzers/sdk.js +283 -0
- package/dist/analyzers/sdk.js.map +1 -0
- package/dist/analyzers/shell.d.ts +20 -0
- package/dist/analyzers/shell.d.ts.map +1 -0
- package/dist/analyzers/shell.js +297 -0
- package/dist/analyzers/shell.js.map +1 -0
- package/dist/analyzers/sql.d.ts +37 -0
- package/dist/analyzers/sql.d.ts.map +1 -0
- package/dist/analyzers/sql.js +455 -0
- package/dist/analyzers/sql.js.map +1 -0
- package/dist/analyzers/types.d.ts +117 -0
- package/dist/analyzers/types.d.ts.map +1 -0
- package/dist/analyzers/types.js +46 -0
- package/dist/analyzers/types.js.map +1 -0
- package/dist/approval/interactive.d.ts +72 -0
- package/dist/approval/interactive.d.ts.map +1 -0
- package/dist/approval/interactive.js +550 -0
- package/dist/approval/interactive.js.map +1 -0
- package/dist/approval/terminal.d.ts +59 -0
- package/dist/approval/terminal.d.ts.map +1 -0
- package/dist/approval/terminal.js +238 -0
- package/dist/approval/terminal.js.map +1 -0
- package/dist/approval/types.d.ts +66 -0
- package/dist/approval/types.d.ts.map +1 -0
- package/dist/approval/types.js +2 -0
- package/dist/approval/types.js.map +1 -0
- package/dist/audit/exporter.d.ts +138 -0
- package/dist/audit/exporter.d.ts.map +1 -0
- package/dist/audit/exporter.js +366 -0
- package/dist/audit/exporter.js.map +1 -0
- package/dist/audit/logger.d.ts +156 -0
- package/dist/audit/logger.d.ts.map +1 -0
- package/dist/audit/logger.js +406 -0
- package/dist/audit/logger.js.map +1 -0
- package/dist/audit/redaction.d.ts +110 -0
- package/dist/audit/redaction.d.ts.map +1 -0
- package/dist/audit/redaction.js +307 -0
- package/dist/audit/redaction.js.map +1 -0
- package/dist/audit/schema.d.ts +76 -0
- package/dist/audit/schema.d.ts.map +1 -0
- package/dist/audit/schema.js +122 -0
- package/dist/audit/schema.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +34 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -0
- package/dist/cli/commands/doctor.js +431 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/export.d.ts +18 -0
- package/dist/cli/commands/export.d.ts.map +1 -0
- package/dist/cli/commands/export.js +63 -0
- package/dist/cli/commands/export.js.map +1 -0
- package/dist/cli/commands/init.d.ts +12 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +102 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/logs.d.ts +11 -0
- package/dist/cli/commands/logs.d.ts.map +1 -0
- package/dist/cli/commands/logs.js +60 -0
- package/dist/cli/commands/logs.js.map +1 -0
- package/dist/cli/commands/scan.d.ts +29 -0
- package/dist/cli/commands/scan.d.ts.map +1 -0
- package/dist/cli/commands/scan.js +251 -0
- package/dist/cli/commands/scan.js.map +1 -0
- package/dist/cli/commands/serve.d.ts +26 -0
- package/dist/cli/commands/serve.d.ts.map +1 -0
- package/dist/cli/commands/serve.js +424 -0
- package/dist/cli/commands/serve.js.map +1 -0
- package/dist/cli/commands/start.d.ts +20 -0
- package/dist/cli/commands/start.d.ts.map +1 -0
- package/dist/cli/commands/start.js +82 -0
- package/dist/cli/commands/start.js.map +1 -0
- package/dist/cli/commands/stats.d.ts +10 -0
- package/dist/cli/commands/stats.d.ts.map +1 -0
- package/dist/cli/commands/stats.js +42 -0
- package/dist/cli/commands/stats.js.map +1 -0
- package/dist/cli/commands/templates.d.ts +26 -0
- package/dist/cli/commands/templates.d.ts.map +1 -0
- package/dist/cli/commands/templates.js +221 -0
- package/dist/cli/commands/templates.js.map +1 -0
- package/dist/cli/commands/validate.d.ts +12 -0
- package/dist/cli/commands/validate.d.ts.map +1 -0
- package/dist/cli/commands/validate.js +107 -0
- package/dist/cli/commands/validate.js.map +1 -0
- package/dist/cli/commands/wrap.d.ts +19 -0
- package/dist/cli/commands/wrap.d.ts.map +1 -0
- package/dist/cli/commands/wrap.js +59 -0
- package/dist/cli/commands/wrap.js.map +1 -0
- package/dist/cli/index.d.ts +17 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +202 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/ui.d.ts +139 -0
- package/dist/cli/ui.d.ts.map +1 -0
- package/dist/cli/ui.js +271 -0
- package/dist/cli/ui.js.map +1 -0
- package/dist/constants.d.ts +33 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +54 -0
- package/dist/constants.js.map +1 -0
- package/dist/errors.d.ts +28 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +37 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +49 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +82 -0
- package/dist/index.js.map +1 -0
- package/dist/orchestrator/index.d.ts +11 -0
- package/dist/orchestrator/index.d.ts.map +1 -0
- package/dist/orchestrator/index.js +10 -0
- package/dist/orchestrator/index.js.map +1 -0
- package/dist/orchestrator/manager.d.ts +127 -0
- package/dist/orchestrator/manager.d.ts.map +1 -0
- package/dist/orchestrator/manager.js +498 -0
- package/dist/orchestrator/manager.js.map +1 -0
- package/dist/orchestrator/types.d.ts +141 -0
- package/dist/orchestrator/types.d.ts.map +1 -0
- package/dist/orchestrator/types.js +9 -0
- package/dist/orchestrator/types.js.map +1 -0
- package/dist/policy/engine.d.ts +55 -0
- package/dist/policy/engine.d.ts.map +1 -0
- package/dist/policy/engine.js +288 -0
- package/dist/policy/engine.js.map +1 -0
- package/dist/policy/natural-language.d.ts +141 -0
- package/dist/policy/natural-language.d.ts.map +1 -0
- package/dist/policy/natural-language.js +552 -0
- package/dist/policy/natural-language.js.map +1 -0
- package/dist/policy/parser.d.ts +141 -0
- package/dist/policy/parser.d.ts.map +1 -0
- package/dist/policy/parser.js +314 -0
- package/dist/policy/parser.js.map +1 -0
- package/dist/policy/types.d.ts +428 -0
- package/dist/policy/types.d.ts.map +1 -0
- package/dist/policy/types.js +32 -0
- package/dist/policy/types.js.map +1 -0
- package/dist/policy/validator.d.ts +72 -0
- package/dist/policy/validator.d.ts.map +1 -0
- package/dist/policy/validator.js +453 -0
- package/dist/policy/validator.js.map +1 -0
- package/dist/proxy/bridge.d.ts +84 -0
- package/dist/proxy/bridge.d.ts.map +1 -0
- package/dist/proxy/bridge.js +217 -0
- package/dist/proxy/bridge.js.map +1 -0
- package/dist/proxy/client.d.ts +130 -0
- package/dist/proxy/client.d.ts.map +1 -0
- package/dist/proxy/client.js +290 -0
- package/dist/proxy/client.js.map +1 -0
- package/dist/proxy/server.d.ts +111 -0
- package/dist/proxy/server.d.ts.map +1 -0
- package/dist/proxy/server.js +444 -0
- package/dist/proxy/server.js.map +1 -0
- package/dist/scanner.d.ts +91 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +373 -0
- package/dist/scanner.js.map +1 -0
- package/dist/session/index.d.ts +32 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +31 -0
- package/dist/session/index.js.map +1 -0
- package/dist/session/manager.d.ts +166 -0
- package/dist/session/manager.d.ts.map +1 -0
- package/dist/session/manager.js +454 -0
- package/dist/session/manager.js.map +1 -0
- package/dist/session/sqlite-store.d.ts +54 -0
- package/dist/session/sqlite-store.d.ts.map +1 -0
- package/dist/session/sqlite-store.js +209 -0
- package/dist/session/sqlite-store.js.map +1 -0
- package/dist/session/types.d.ts +179 -0
- package/dist/session/types.d.ts.map +1 -0
- package/dist/session/types.js +38 -0
- package/dist/session/types.js.map +1 -0
- package/dist/templates.d.ts +64 -0
- package/dist/templates.d.ts.map +1 -0
- package/dist/templates.js +451 -0
- package/dist/templates.js.map +1 -0
- package/dist/utils/config.d.ts +57 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +104 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/errors.d.ts +18 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +35 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/logger.d.ts +144 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +300 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/wizard.d.ts +68 -0
- package/dist/wizard.d.ts.map +1 -0
- package/dist/wizard.js +395 -0
- package/dist/wizard.js.map +1 -0
- package/package.json +99 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive approval handler for Tollgate
|
|
3
|
+
*
|
|
4
|
+
* Serves a web UI for approving tool calls, enabling approval from
|
|
5
|
+
* any browser rather than just the terminal.
|
|
6
|
+
*/
|
|
7
|
+
import type { ApprovalHandler, ApprovalRequest, ApprovalResponse } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Interactive approval handler that serves a web UI.
|
|
10
|
+
*
|
|
11
|
+
* When an approval is needed, the handler:
|
|
12
|
+
* 1. Adds the request to a pending queue
|
|
13
|
+
* 2. Notifies connected browsers via Server-Sent Events
|
|
14
|
+
* 3. Waits for a response from the web UI
|
|
15
|
+
* 4. Returns the approval decision
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* const handler = new InteractiveApprovalHandler({ port: 9847, timeoutMs: 60000 });
|
|
20
|
+
* await handler.start();
|
|
21
|
+
*
|
|
22
|
+
* // Open http://localhost:9847 to see pending approvals
|
|
23
|
+
* const response = await handler.prompt(request);
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare class InteractiveApprovalHandler implements ApprovalHandler {
|
|
27
|
+
private server;
|
|
28
|
+
private pendingRequests;
|
|
29
|
+
private sseClients;
|
|
30
|
+
private port;
|
|
31
|
+
private timeoutMs;
|
|
32
|
+
private isStarted;
|
|
33
|
+
constructor(options?: {
|
|
34
|
+
port?: number;
|
|
35
|
+
timeoutMs?: number;
|
|
36
|
+
});
|
|
37
|
+
/**
|
|
38
|
+
* Starts the HTTP server for the web UI.
|
|
39
|
+
*/
|
|
40
|
+
start(): Promise<void>;
|
|
41
|
+
prompt(request: ApprovalRequest): Promise<ApprovalResponse>;
|
|
42
|
+
close(): void;
|
|
43
|
+
/**
|
|
44
|
+
* Handles incoming HTTP requests.
|
|
45
|
+
*/
|
|
46
|
+
private handleRequest;
|
|
47
|
+
/**
|
|
48
|
+
* Serves the main approval UI.
|
|
49
|
+
*/
|
|
50
|
+
private serveUI;
|
|
51
|
+
/**
|
|
52
|
+
* Returns current pending requests as JSON.
|
|
53
|
+
*/
|
|
54
|
+
private serveRequests;
|
|
55
|
+
/**
|
|
56
|
+
* Handles Server-Sent Events for real-time updates.
|
|
57
|
+
*/
|
|
58
|
+
private handleSSE;
|
|
59
|
+
/**
|
|
60
|
+
* Broadcasts updates to all SSE clients.
|
|
61
|
+
*/
|
|
62
|
+
private broadcastUpdate;
|
|
63
|
+
/**
|
|
64
|
+
* Handles approval/denial responses from the UI.
|
|
65
|
+
*/
|
|
66
|
+
private handleResponse;
|
|
67
|
+
/**
|
|
68
|
+
* Generates the HTML for the approval UI.
|
|
69
|
+
*/
|
|
70
|
+
private generateHTML;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=interactive.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interactive.d.ts","sourceRoot":"","sources":["../../src/approval/interactive.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,gBAAgB,EAGjB,MAAM,YAAY,CAAC;AAgBpB;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,0BAA2B,YAAW,eAAe;IAChE,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,eAAe,CAAqC;IAC5D,OAAO,CAAC,UAAU,CAAkC;IACpD,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,SAAS,CAAS;gBAEd,OAAO,GAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO;IAK/D;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAsBtB,MAAM,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA8CjE,KAAK,IAAI,IAAI;IA0Bb;;OAEG;IACH,OAAO,CAAC,aAAa;IA6BrB;;OAEG;IACH,OAAO,CAAC,OAAO;IAKf;;OAEG;IACH,OAAO,CAAC,aAAa;IAkBrB;;OAEG;IACH,OAAO,CAAC,SAAS;IAkBjB;;OAEG;IACH,OAAO,CAAC,eAAe;IAOvB;;OAEG;IACH,OAAO,CAAC,cAAc;IAkEtB;;OAEG;IACH,OAAO,CAAC,YAAY;CA0RrB"}
|
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive approval handler for Tollgate
|
|
3
|
+
*
|
|
4
|
+
* Serves a web UI for approving tool calls, enabling approval from
|
|
5
|
+
* any browser rather than just the terminal.
|
|
6
|
+
*/
|
|
7
|
+
import * as http from 'node:http';
|
|
8
|
+
import { approvalLogger as logger } from '../utils/logger.js';
|
|
9
|
+
import { DEFAULT_APPROVAL_TIMEOUT_MS, DEFAULT_APPROVAL_PORT, MAX_APPROVAL_REQUEST_BODY_BYTES, } from '../constants.js';
|
|
10
|
+
/**
|
|
11
|
+
* Interactive approval handler that serves a web UI.
|
|
12
|
+
*
|
|
13
|
+
* When an approval is needed, the handler:
|
|
14
|
+
* 1. Adds the request to a pending queue
|
|
15
|
+
* 2. Notifies connected browsers via Server-Sent Events
|
|
16
|
+
* 3. Waits for a response from the web UI
|
|
17
|
+
* 4. Returns the approval decision
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* const handler = new InteractiveApprovalHandler({ port: 9847, timeoutMs: 60000 });
|
|
22
|
+
* await handler.start();
|
|
23
|
+
*
|
|
24
|
+
* // Open http://localhost:9847 to see pending approvals
|
|
25
|
+
* const response = await handler.prompt(request);
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export class InteractiveApprovalHandler {
|
|
29
|
+
server = null;
|
|
30
|
+
pendingRequests = new Map();
|
|
31
|
+
sseClients = new Set();
|
|
32
|
+
port;
|
|
33
|
+
timeoutMs;
|
|
34
|
+
isStarted = false;
|
|
35
|
+
constructor(options = {}) {
|
|
36
|
+
this.port = options.port ?? DEFAULT_APPROVAL_PORT;
|
|
37
|
+
this.timeoutMs = options.timeoutMs ?? DEFAULT_APPROVAL_TIMEOUT_MS;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Starts the HTTP server for the web UI.
|
|
41
|
+
*/
|
|
42
|
+
async start() {
|
|
43
|
+
if (this.isStarted)
|
|
44
|
+
return;
|
|
45
|
+
return new Promise((resolve, reject) => {
|
|
46
|
+
this.server = http.createServer((req, res) => this.handleRequest(req, res));
|
|
47
|
+
this.server.on('error', (err) => {
|
|
48
|
+
if (err.code === 'EADDRINUSE') {
|
|
49
|
+
reject(new Error(`Port ${this.port} is already in use. Choose a different port for the approval UI.`));
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
reject(err);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
this.server.listen(this.port, () => {
|
|
56
|
+
this.isStarted = true;
|
|
57
|
+
logger.info('Approval UI available', { url: `http://localhost:${this.port}` });
|
|
58
|
+
resolve();
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
async prompt(request) {
|
|
63
|
+
// Auto-start if not started
|
|
64
|
+
if (!this.isStarted) {
|
|
65
|
+
await this.start();
|
|
66
|
+
}
|
|
67
|
+
const startTime = Date.now();
|
|
68
|
+
return new Promise((resolve) => {
|
|
69
|
+
// Set up timeout
|
|
70
|
+
const timeout = setTimeout(() => {
|
|
71
|
+
this.pendingRequests.delete(request.id);
|
|
72
|
+
this.broadcastUpdate();
|
|
73
|
+
logger.warn('Approval request timed out', { requestId: request.id });
|
|
74
|
+
resolve({
|
|
75
|
+
result: 'timeout',
|
|
76
|
+
respondedAt: new Date(),
|
|
77
|
+
durationMs: Date.now() - startTime,
|
|
78
|
+
});
|
|
79
|
+
}, this.timeoutMs);
|
|
80
|
+
// Store pending request
|
|
81
|
+
this.pendingRequests.set(request.id, {
|
|
82
|
+
request,
|
|
83
|
+
resolve: (response) => {
|
|
84
|
+
clearTimeout(timeout);
|
|
85
|
+
this.pendingRequests.delete(request.id);
|
|
86
|
+
this.broadcastUpdate();
|
|
87
|
+
resolve(response);
|
|
88
|
+
},
|
|
89
|
+
startTime,
|
|
90
|
+
timeout,
|
|
91
|
+
});
|
|
92
|
+
// Notify UI of new request
|
|
93
|
+
this.broadcastUpdate();
|
|
94
|
+
// Also log to terminal as a fallback notification
|
|
95
|
+
logger.info('Approval required - open web UI to respond', {
|
|
96
|
+
url: `http://localhost:${this.port}`,
|
|
97
|
+
server: request.context.server,
|
|
98
|
+
tool: request.context.tool,
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
close() {
|
|
103
|
+
// Resolve all pending requests as denied
|
|
104
|
+
for (const pending of this.pendingRequests.values()) {
|
|
105
|
+
clearTimeout(pending.timeout);
|
|
106
|
+
pending.resolve({
|
|
107
|
+
result: 'denied',
|
|
108
|
+
respondedAt: new Date(),
|
|
109
|
+
durationMs: Date.now() - pending.startTime,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
this.pendingRequests.clear();
|
|
113
|
+
// Close SSE connections
|
|
114
|
+
for (const client of this.sseClients) {
|
|
115
|
+
client.end();
|
|
116
|
+
}
|
|
117
|
+
this.sseClients.clear();
|
|
118
|
+
// Close server
|
|
119
|
+
if (this.server) {
|
|
120
|
+
this.server.close();
|
|
121
|
+
this.server = null;
|
|
122
|
+
this.isStarted = false;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Handles incoming HTTP requests.
|
|
127
|
+
*/
|
|
128
|
+
handleRequest(req, res) {
|
|
129
|
+
const url = new URL(req.url ?? '/', `http://localhost:${this.port}`);
|
|
130
|
+
// Enable CORS for local development
|
|
131
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
132
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
133
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
134
|
+
if (req.method === 'OPTIONS') {
|
|
135
|
+
res.writeHead(204);
|
|
136
|
+
res.end();
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
// Route requests
|
|
140
|
+
if (url.pathname === '/' && req.method === 'GET') {
|
|
141
|
+
this.serveUI(res);
|
|
142
|
+
}
|
|
143
|
+
else if (url.pathname === '/api/requests' && req.method === 'GET') {
|
|
144
|
+
this.serveRequests(res);
|
|
145
|
+
}
|
|
146
|
+
else if (url.pathname === '/api/events' && req.method === 'GET') {
|
|
147
|
+
this.handleSSE(res);
|
|
148
|
+
}
|
|
149
|
+
else if (url.pathname === '/api/respond' && req.method === 'POST') {
|
|
150
|
+
this.handleResponse(req, res);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
154
|
+
res.end(JSON.stringify({ error: 'Not found' }));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Serves the main approval UI.
|
|
159
|
+
*/
|
|
160
|
+
serveUI(res) {
|
|
161
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
162
|
+
res.end(this.generateHTML());
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Returns current pending requests as JSON.
|
|
166
|
+
*/
|
|
167
|
+
serveRequests(res) {
|
|
168
|
+
const requests = Array.from(this.pendingRequests.values()).map((p) => ({
|
|
169
|
+
id: p.request.id,
|
|
170
|
+
server: p.request.context.server,
|
|
171
|
+
tool: p.request.context.tool,
|
|
172
|
+
args: p.request.context.args,
|
|
173
|
+
risk: p.request.decision.analysis?.risk ?? 'unknown',
|
|
174
|
+
rule: p.request.decision.matchedRule ?? 'default',
|
|
175
|
+
message: p.request.decision.message,
|
|
176
|
+
timestamp: p.request.timestamp.toISOString(),
|
|
177
|
+
elapsedMs: Date.now() - p.startTime,
|
|
178
|
+
timeoutMs: this.timeoutMs,
|
|
179
|
+
}));
|
|
180
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
181
|
+
res.end(JSON.stringify({ requests }));
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Handles Server-Sent Events for real-time updates.
|
|
185
|
+
*/
|
|
186
|
+
handleSSE(res) {
|
|
187
|
+
res.writeHead(200, {
|
|
188
|
+
'Content-Type': 'text/event-stream',
|
|
189
|
+
'Cache-Control': 'no-cache',
|
|
190
|
+
'Connection': 'keep-alive',
|
|
191
|
+
});
|
|
192
|
+
this.sseClients.add(res);
|
|
193
|
+
// Send initial data
|
|
194
|
+
res.write(`data: ${JSON.stringify({ type: 'connected' })}\n\n`);
|
|
195
|
+
// Handle client disconnect
|
|
196
|
+
res.on('close', () => {
|
|
197
|
+
this.sseClients.delete(res);
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Broadcasts updates to all SSE clients.
|
|
202
|
+
*/
|
|
203
|
+
broadcastUpdate() {
|
|
204
|
+
const message = `data: ${JSON.stringify({ type: 'update' })}\n\n`;
|
|
205
|
+
for (const client of this.sseClients) {
|
|
206
|
+
client.write(message);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Handles approval/denial responses from the UI.
|
|
211
|
+
*/
|
|
212
|
+
handleResponse(req, res) {
|
|
213
|
+
let body = '';
|
|
214
|
+
let bodySize = 0;
|
|
215
|
+
req.on('data', (chunk) => {
|
|
216
|
+
const chunkSize = Buffer.isBuffer(chunk) ? chunk.length : Buffer.byteLength(chunk);
|
|
217
|
+
bodySize += chunkSize;
|
|
218
|
+
// Reject oversized requests to prevent DoS
|
|
219
|
+
if (bodySize > MAX_APPROVAL_REQUEST_BODY_BYTES) {
|
|
220
|
+
res.writeHead(413, { 'Content-Type': 'application/json' });
|
|
221
|
+
res.end(JSON.stringify({ error: 'Request body too large' }));
|
|
222
|
+
req.destroy();
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
body += chunk;
|
|
226
|
+
});
|
|
227
|
+
req.on('end', () => {
|
|
228
|
+
try {
|
|
229
|
+
const data = JSON.parse(body);
|
|
230
|
+
const pending = this.pendingRequests.get(data.requestId);
|
|
231
|
+
if (!pending) {
|
|
232
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
233
|
+
res.end(JSON.stringify({ error: 'Request not found or already responded' }));
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
// Build session grant if duration provided
|
|
237
|
+
let sessionGrant;
|
|
238
|
+
if (data.result === 'approved' && data.duration && data.duration !== 'once') {
|
|
239
|
+
sessionGrant = {
|
|
240
|
+
scope: data.scope ?? 'tool',
|
|
241
|
+
duration: data.duration,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
// Resolve the pending request
|
|
245
|
+
pending.resolve({
|
|
246
|
+
result: data.result,
|
|
247
|
+
respondedAt: new Date(),
|
|
248
|
+
durationMs: Date.now() - pending.startTime,
|
|
249
|
+
sessionGrant,
|
|
250
|
+
});
|
|
251
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
252
|
+
res.end(JSON.stringify({ success: true }));
|
|
253
|
+
logger.info('Request resolved via web UI', {
|
|
254
|
+
requestId: data.requestId,
|
|
255
|
+
result: data.result,
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
catch (_err) {
|
|
259
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
260
|
+
res.end(JSON.stringify({ error: 'Invalid request body' }));
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Generates the HTML for the approval UI.
|
|
266
|
+
*/
|
|
267
|
+
generateHTML() {
|
|
268
|
+
return `<!DOCTYPE html>
|
|
269
|
+
<html lang="en">
|
|
270
|
+
<head>
|
|
271
|
+
<meta charset="UTF-8">
|
|
272
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
273
|
+
<title>Tollgate Approval</title>
|
|
274
|
+
<style>
|
|
275
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
276
|
+
body {
|
|
277
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
278
|
+
background: #0f1419;
|
|
279
|
+
color: #e7e9ea;
|
|
280
|
+
min-height: 100vh;
|
|
281
|
+
padding: 2rem;
|
|
282
|
+
}
|
|
283
|
+
.container { max-width: 800px; margin: 0 auto; }
|
|
284
|
+
h1 {
|
|
285
|
+
font-size: 1.5rem;
|
|
286
|
+
margin-bottom: 0.5rem;
|
|
287
|
+
display: flex;
|
|
288
|
+
align-items: center;
|
|
289
|
+
gap: 0.5rem;
|
|
290
|
+
}
|
|
291
|
+
.subtitle { color: #71767b; margin-bottom: 2rem; }
|
|
292
|
+
.badge {
|
|
293
|
+
font-size: 0.75rem;
|
|
294
|
+
padding: 0.25rem 0.5rem;
|
|
295
|
+
border-radius: 9999px;
|
|
296
|
+
background: #22c55e20;
|
|
297
|
+
color: #22c55e;
|
|
298
|
+
}
|
|
299
|
+
.no-requests {
|
|
300
|
+
text-align: center;
|
|
301
|
+
padding: 4rem 2rem;
|
|
302
|
+
background: #16181c;
|
|
303
|
+
border-radius: 1rem;
|
|
304
|
+
border: 1px dashed #2f3336;
|
|
305
|
+
}
|
|
306
|
+
.no-requests h2 { color: #71767b; margin-bottom: 0.5rem; }
|
|
307
|
+
.request-card {
|
|
308
|
+
background: #16181c;
|
|
309
|
+
border: 1px solid #2f3336;
|
|
310
|
+
border-radius: 1rem;
|
|
311
|
+
padding: 1.5rem;
|
|
312
|
+
margin-bottom: 1rem;
|
|
313
|
+
}
|
|
314
|
+
.request-header {
|
|
315
|
+
display: flex;
|
|
316
|
+
justify-content: space-between;
|
|
317
|
+
align-items: flex-start;
|
|
318
|
+
margin-bottom: 1rem;
|
|
319
|
+
}
|
|
320
|
+
.request-title {
|
|
321
|
+
font-size: 1.125rem;
|
|
322
|
+
font-weight: 600;
|
|
323
|
+
}
|
|
324
|
+
.request-meta { font-size: 0.875rem; color: #71767b; }
|
|
325
|
+
.risk-badge {
|
|
326
|
+
font-size: 0.75rem;
|
|
327
|
+
padding: 0.25rem 0.75rem;
|
|
328
|
+
border-radius: 9999px;
|
|
329
|
+
font-weight: 600;
|
|
330
|
+
}
|
|
331
|
+
.risk-safe { background: #22c55e20; color: #22c55e; }
|
|
332
|
+
.risk-read { background: #3b82f620; color: #3b82f6; }
|
|
333
|
+
.risk-write { background: #f59e0b20; color: #f59e0b; }
|
|
334
|
+
.risk-destructive { background: #f97316; color: white; }
|
|
335
|
+
.risk-dangerous { background: #ef4444; color: white; }
|
|
336
|
+
.risk-unknown { background: #71767b20; color: #71767b; }
|
|
337
|
+
.args-section {
|
|
338
|
+
background: #0f1419;
|
|
339
|
+
border-radius: 0.5rem;
|
|
340
|
+
padding: 1rem;
|
|
341
|
+
margin: 1rem 0;
|
|
342
|
+
font-family: 'Monaco', 'Menlo', monospace;
|
|
343
|
+
font-size: 0.8125rem;
|
|
344
|
+
overflow-x: auto;
|
|
345
|
+
white-space: pre-wrap;
|
|
346
|
+
word-break: break-all;
|
|
347
|
+
}
|
|
348
|
+
.message {
|
|
349
|
+
padding: 0.75rem 1rem;
|
|
350
|
+
background: #f59e0b15;
|
|
351
|
+
border-left: 3px solid #f59e0b;
|
|
352
|
+
border-radius: 0 0.5rem 0.5rem 0;
|
|
353
|
+
margin: 1rem 0;
|
|
354
|
+
font-size: 0.875rem;
|
|
355
|
+
}
|
|
356
|
+
.actions {
|
|
357
|
+
display: flex;
|
|
358
|
+
gap: 0.5rem;
|
|
359
|
+
flex-wrap: wrap;
|
|
360
|
+
margin-top: 1rem;
|
|
361
|
+
}
|
|
362
|
+
button {
|
|
363
|
+
padding: 0.625rem 1rem;
|
|
364
|
+
border-radius: 0.5rem;
|
|
365
|
+
font-weight: 600;
|
|
366
|
+
font-size: 0.875rem;
|
|
367
|
+
cursor: pointer;
|
|
368
|
+
border: none;
|
|
369
|
+
transition: all 0.15s;
|
|
370
|
+
}
|
|
371
|
+
button:hover { filter: brightness(1.1); }
|
|
372
|
+
button:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
373
|
+
.btn-approve { background: #22c55e; color: white; }
|
|
374
|
+
.btn-approve-session { background: #3b82f6; color: white; }
|
|
375
|
+
.btn-deny { background: #ef4444; color: white; }
|
|
376
|
+
.timer {
|
|
377
|
+
font-size: 0.75rem;
|
|
378
|
+
color: #71767b;
|
|
379
|
+
margin-top: 0.5rem;
|
|
380
|
+
}
|
|
381
|
+
.timer.urgent { color: #f59e0b; }
|
|
382
|
+
.timer.critical { color: #ef4444; }
|
|
383
|
+
.connected-status {
|
|
384
|
+
position: fixed;
|
|
385
|
+
bottom: 1rem;
|
|
386
|
+
right: 1rem;
|
|
387
|
+
padding: 0.5rem 1rem;
|
|
388
|
+
border-radius: 9999px;
|
|
389
|
+
font-size: 0.75rem;
|
|
390
|
+
background: #16181c;
|
|
391
|
+
border: 1px solid #2f3336;
|
|
392
|
+
}
|
|
393
|
+
.connected-status.online { border-color: #22c55e; color: #22c55e; }
|
|
394
|
+
.connected-status.offline { border-color: #ef4444; color: #ef4444; }
|
|
395
|
+
</style>
|
|
396
|
+
</head>
|
|
397
|
+
<body>
|
|
398
|
+
<div class="container">
|
|
399
|
+
<h1>
|
|
400
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
401
|
+
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
|
|
402
|
+
</svg>
|
|
403
|
+
Tollgate Approval
|
|
404
|
+
<span class="badge" id="count">0 pending</span>
|
|
405
|
+
</h1>
|
|
406
|
+
<p class="subtitle">Review and approve AI agent actions</p>
|
|
407
|
+
|
|
408
|
+
<div id="requests"></div>
|
|
409
|
+
</div>
|
|
410
|
+
|
|
411
|
+
<div class="connected-status" id="status">Connecting...</div>
|
|
412
|
+
|
|
413
|
+
<script>
|
|
414
|
+
let requests = [];
|
|
415
|
+
let eventSource = null;
|
|
416
|
+
|
|
417
|
+
function formatArgs(args) {
|
|
418
|
+
return JSON.stringify(args, null, 2);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function getRiskClass(risk) {
|
|
422
|
+
const classes = {
|
|
423
|
+
safe: 'risk-safe',
|
|
424
|
+
read: 'risk-read',
|
|
425
|
+
write: 'risk-write',
|
|
426
|
+
destructive: 'risk-destructive',
|
|
427
|
+
dangerous: 'risk-dangerous',
|
|
428
|
+
};
|
|
429
|
+
return classes[risk] || 'risk-unknown';
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function renderRequests() {
|
|
433
|
+
const container = document.getElementById('requests');
|
|
434
|
+
const countEl = document.getElementById('count');
|
|
435
|
+
|
|
436
|
+
countEl.textContent = requests.length + ' pending';
|
|
437
|
+
|
|
438
|
+
if (requests.length === 0) {
|
|
439
|
+
container.innerHTML = \`
|
|
440
|
+
<div class="no-requests">
|
|
441
|
+
<h2>No pending approvals</h2>
|
|
442
|
+
<p>When an AI agent needs approval, it will appear here.</p>
|
|
443
|
+
</div>
|
|
444
|
+
\`;
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
container.innerHTML = requests.map(req => {
|
|
449
|
+
const remaining = Math.max(0, req.timeoutMs - req.elapsedMs);
|
|
450
|
+
const seconds = Math.ceil(remaining / 1000);
|
|
451
|
+
const timerClass = seconds < 10 ? 'critical' : seconds < 30 ? 'urgent' : '';
|
|
452
|
+
|
|
453
|
+
return \`
|
|
454
|
+
<div class="request-card" data-id="\${req.id}">
|
|
455
|
+
<div class="request-header">
|
|
456
|
+
<div>
|
|
457
|
+
<div class="request-title">\${req.server} → \${req.tool}</div>
|
|
458
|
+
<div class="request-meta">Rule: \${req.rule}</div>
|
|
459
|
+
</div>
|
|
460
|
+
<span class="risk-badge \${getRiskClass(req.risk)}">\${req.risk}</span>
|
|
461
|
+
</div>
|
|
462
|
+
|
|
463
|
+
\${req.message ? \`<div class="message">\${req.message}</div>\` : ''}
|
|
464
|
+
|
|
465
|
+
<div class="args-section">\${formatArgs(req.args)}</div>
|
|
466
|
+
|
|
467
|
+
<div class="actions">
|
|
468
|
+
<button class="btn-deny" onclick="respond('\${req.id}', 'denied')">Deny</button>
|
|
469
|
+
<button class="btn-approve" onclick="respond('\${req.id}', 'approved', null, 'once')">Allow Once</button>
|
|
470
|
+
<button class="btn-approve-session" onclick="respond('\${req.id}', 'approved', 'tool', '5min')">Allow 5 min</button>
|
|
471
|
+
<button class="btn-approve-session" onclick="respond('\${req.id}', 'approved', 'tool', '15min')">Allow 15 min</button>
|
|
472
|
+
<button class="btn-approve-session" onclick="respond('\${req.id}', 'approved', 'tool', 'session')">Allow Session</button>
|
|
473
|
+
</div>
|
|
474
|
+
|
|
475
|
+
<div class="timer \${timerClass}">Auto-deny in \${seconds}s</div>
|
|
476
|
+
</div>
|
|
477
|
+
\`;
|
|
478
|
+
}).join('');
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
async function fetchRequests() {
|
|
482
|
+
try {
|
|
483
|
+
const res = await fetch('/api/requests');
|
|
484
|
+
const data = await res.json();
|
|
485
|
+
requests = data.requests;
|
|
486
|
+
renderRequests();
|
|
487
|
+
} catch (err) {
|
|
488
|
+
console.error('Failed to fetch requests:', err);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
async function respond(requestId, result, scope, duration) {
|
|
493
|
+
const buttons = document.querySelectorAll(\`[data-id="\${requestId}"] button\`);
|
|
494
|
+
buttons.forEach(btn => btn.disabled = true);
|
|
495
|
+
|
|
496
|
+
try {
|
|
497
|
+
await fetch('/api/respond', {
|
|
498
|
+
method: 'POST',
|
|
499
|
+
headers: { 'Content-Type': 'application/json' },
|
|
500
|
+
body: JSON.stringify({ requestId, result, scope, duration }),
|
|
501
|
+
});
|
|
502
|
+
} catch (err) {
|
|
503
|
+
console.error('Failed to respond:', err);
|
|
504
|
+
buttons.forEach(btn => btn.disabled = false);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
function connectSSE() {
|
|
509
|
+
const statusEl = document.getElementById('status');
|
|
510
|
+
|
|
511
|
+
eventSource = new EventSource('/api/events');
|
|
512
|
+
|
|
513
|
+
eventSource.onopen = () => {
|
|
514
|
+
statusEl.textContent = 'Connected';
|
|
515
|
+
statusEl.className = 'connected-status online';
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
eventSource.onmessage = (event) => {
|
|
519
|
+
const data = JSON.parse(event.data);
|
|
520
|
+
if (data.type === 'update') {
|
|
521
|
+
fetchRequests();
|
|
522
|
+
}
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
eventSource.onerror = () => {
|
|
526
|
+
statusEl.textContent = 'Disconnected';
|
|
527
|
+
statusEl.className = 'connected-status offline';
|
|
528
|
+
eventSource.close();
|
|
529
|
+
setTimeout(connectSSE, 2000);
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// Update timers every second
|
|
534
|
+
setInterval(() => {
|
|
535
|
+
requests = requests.map(req => ({
|
|
536
|
+
...req,
|
|
537
|
+
elapsedMs: req.elapsedMs + 1000,
|
|
538
|
+
}));
|
|
539
|
+
renderRequests();
|
|
540
|
+
}, 1000);
|
|
541
|
+
|
|
542
|
+
// Initial load
|
|
543
|
+
fetchRequests();
|
|
544
|
+
connectSSE();
|
|
545
|
+
</script>
|
|
546
|
+
</body>
|
|
547
|
+
</html>`;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
//# sourceMappingURL=interactive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interactive.js","sourceRoot":"","sources":["../../src/approval/interactive.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AASlC,OAAO,EAAE,cAAc,IAAI,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EACL,2BAA2B,EAC3B,qBAAqB,EACrB,+BAA+B,GAChC,MAAM,iBAAiB,CAAC;AASzB;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,OAAO,0BAA0B;IAC7B,MAAM,GAAuB,IAAI,CAAC;IAClC,eAAe,GAAG,IAAI,GAAG,EAA0B,CAAC;IACpD,UAAU,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC5C,IAAI,CAAS;IACb,SAAS,CAAS;IAClB,SAAS,GAAG,KAAK,CAAC;IAE1B,YAAY,UAAiD,EAAE;QAC7D,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,qBAAqB,CAAC;QAClD,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,2BAA2B,CAAC;IACpE,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO;QAE3B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;YAE5E,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAA0B,EAAE,EAAE;gBACrD,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;oBAC9B,MAAM,CAAC,IAAI,KAAK,CAAC,QAAQ,IAAI,CAAC,IAAI,kEAAkE,CAAC,CAAC,CAAC;gBACzG,CAAC;qBAAM,CAAC;oBACN,MAAM,CAAC,GAAG,CAAC,CAAC;gBACd,CAAC;YACH,CAAC,CAAC,CAAC;YAEH,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE;gBACjC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;gBACtB,MAAM,CAAC,IAAI,CAAC,uBAAuB,EAAE,EAAE,GAAG,EAAE,oBAAoB,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBAC/E,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,OAAwB;QACnC,4BAA4B;QAC5B,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;QACrB,CAAC;QAED,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAE7B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC7B,iBAAiB;YACjB,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC9B,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;gBACxC,IAAI,CAAC,eAAe,EAAE,CAAC;gBACvB,MAAM,CAAC,IAAI,CAAC,4BAA4B,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;gBACrE,OAAO,CAAC;oBACN,MAAM,EAAE,SAAS;oBACjB,WAAW,EAAE,IAAI,IAAI,EAAE;oBACvB,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;iBACnC,CAAC,CAAC;YACL,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;YAEnB,wBAAwB;YACxB,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE;gBACnC,OAAO;gBACP,OAAO,EAAE,CAAC,QAAQ,EAAE,EAAE;oBACpB,YAAY,CAAC,OAAO,CAAC,CAAC;oBACtB,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;oBACxC,IAAI,CAAC,eAAe,EAAE,CAAC;oBACvB,OAAO,CAAC,QAAQ,CAAC,CAAC;gBACpB,CAAC;gBACD,SAAS;gBACT,OAAO;aACR,CAAC,CAAC;YAEH,2BAA2B;YAC3B,IAAI,CAAC,eAAe,EAAE,CAAC;YAEvB,kDAAkD;YAClD,MAAM,CAAC,IAAI,CAAC,4CAA4C,EAAE;gBACxD,GAAG,EAAE,oBAAoB,IAAI,CAAC,IAAI,EAAE;gBACpC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM;gBAC9B,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,IAAI;aAC3B,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK;QACH,yCAAyC;QACzC,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,EAAE,CAAC;YACpD,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YAC9B,OAAO,CAAC,OAAO,CAAC;gBACd,MAAM,EAAE,QAAQ;gBAChB,WAAW,EAAE,IAAI,IAAI,EAAE;gBACvB,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,SAAS;aAC3C,CAAC,CAAC;QACL,CAAC;QACD,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;QAE7B,wBAAwB;QACxB,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACrC,MAAM,CAAC,GAAG,EAAE,CAAC;QACf,CAAC;QACD,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;QAExB,eAAe;QACf,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YACpB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;YACnB,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;QACzB,CAAC;IACH,CAAC;IAED;;OAEG;IACK,aAAa,CAAC,GAAyB,EAAE,GAAwB;QACvE,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,oBAAoB,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAErE,oCAAoC;QACpC,GAAG,CAAC,SAAS,CAAC,6BAA6B,EAAE,GAAG,CAAC,CAAC;QAClD,GAAG,CAAC,SAAS,CAAC,8BAA8B,EAAE,oBAAoB,CAAC,CAAC;QACpE,GAAG,CAAC,SAAS,CAAC,8BAA8B,EAAE,cAAc,CAAC,CAAC;QAE9D,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC7B,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YACnB,GAAG,CAAC,GAAG,EAAE,CAAC;YACV,OAAO;QACT,CAAC;QAED,iBAAiB;QACjB,IAAI,GAAG,CAAC,QAAQ,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;YACjD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACpB,CAAC;aAAM,IAAI,GAAG,CAAC,QAAQ,KAAK,eAAe,IAAI,GAAG,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;YACpE,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;QAC1B,CAAC;aAAM,IAAI,GAAG,CAAC,QAAQ,KAAK,aAAa,IAAI,GAAG,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;YAClE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACtB,CAAC;aAAM,IAAI,GAAG,CAAC,QAAQ,KAAK,cAAc,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;YACpE,IAAI,CAAC,cAAc,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAChC,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;YAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC;QAClD,CAAC;IACH,CAAC;IAED;;OAEG;IACK,OAAO,CAAC,GAAwB;QACtC,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,0BAA0B,EAAE,CAAC,CAAC;QACnE,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC;IAC/B,CAAC;IAED;;OAEG;IACK,aAAa,CAAC,GAAwB;QAC5C,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACrE,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE;YAChB,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM;YAChC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI;YAC5B,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI;YAC5B,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,EAAE,IAAI,IAAI,SAAS;YACpD,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,WAAW,IAAI,SAAS;YACjD,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO;YACnC,SAAS,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,WAAW,EAAE;YAC5C,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,SAAS;YACnC,SAAS,EAAE,IAAI,CAAC,SAAS;SAC1B,CAAC,CAAC,CAAC;QAEJ,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;QAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;IACxC,CAAC;IAED;;OAEG;IACK,SAAS,CAAC,GAAwB;QACxC,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE;YACjB,cAAc,EAAE,mBAAmB;YACnC,eAAe,EAAE,UAAU;YAC3B,YAAY,EAAE,YAAY;SAC3B,CAAC,CAAC;QAEH,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAEzB,oBAAoB;QACpB,GAAG,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,MAAM,CAAC,CAAC;QAEhE,2BAA2B;QAC3B,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACnB,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC9B,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACK,eAAe;QACrB,MAAM,OAAO,GAAG,SAAS,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,MAAM,CAAC;QAClE,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACrC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;IAED;;OAEG;IACK,cAAc,CAAC,GAAyB,EAAE,GAAwB;QACxE,IAAI,IAAI,GAAG,EAAE,CAAC;QACd,IAAI,QAAQ,GAAG,CAAC,CAAC;QAEjB,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAsB,EAAE,EAAE;YACxC,MAAM,SAAS,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;YACnF,QAAQ,IAAI,SAAS,CAAC;YAEtB,2CAA2C;YAC3C,IAAI,QAAQ,GAAG,+BAA+B,EAAE,CAAC;gBAC/C,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;gBAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,wBAAwB,EAAE,CAAC,CAAC,CAAC;gBAC7D,GAAG,CAAC,OAAO,EAAE,CAAC;gBACd,OAAO;YACT,CAAC;YAED,IAAI,IAAI,KAAK,CAAC;QAChB,CAAC,CAAC,CAAC;QAEH,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;YACjB,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAK3B,CAAC;gBAEF,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;gBACzD,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;oBAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,wCAAwC,EAAE,CAAC,CAAC,CAAC;oBAC7E,OAAO;gBACT,CAAC;gBAED,2CAA2C;gBAC3C,IAAI,YAA0C,CAAC;gBAC/C,IAAI,IAAI,CAAC,MAAM,KAAK,UAAU,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;oBAC5E,YAAY,GAAG;wBACb,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,MAAM;wBAC3B,QAAQ,EAAE,IAAI,CAAC,QAAQ;qBACxB,CAAC;gBACJ,CAAC;gBAED,8BAA8B;gBAC9B,OAAO,CAAC,OAAO,CAAC;oBACd,MAAM,EAAE,IAAI,CAAC,MAAM;oBACnB,WAAW,EAAE,IAAI,IAAI,EAAE;oBACvB,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,SAAS;oBAC1C,YAAY;iBACb,CAAC,CAAC;gBAEH,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;gBAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;gBAE3C,MAAM,CAAC,IAAI,CAAC,6BAA6B,EAAE;oBACzC,SAAS,EAAE,IAAI,CAAC,SAAS;oBACzB,MAAM,EAAE,IAAI,CAAC,MAAM;iBACpB,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,IAAI,EAAE,CAAC;gBACd,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;gBAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,sBAAsB,EAAE,CAAC,CAAC,CAAC;YAC7D,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACK,YAAY;QAClB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAuRH,CAAC;IACP,CAAC;CACF"}
|