safari-pilot 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.
Files changed (143) hide show
  1. package/.claude-plugin/plugin.json +35 -0
  2. package/.mcp.json +11 -0
  3. package/LICENSE +21 -0
  4. package/README.md +324 -0
  5. package/bin/.gitkeep +0 -0
  6. package/bin/Safari Pilot.app/Contents/CodeResources +0 -0
  7. package/bin/Safari Pilot.app/Contents/Info.plist +58 -0
  8. package/bin/Safari Pilot.app/Contents/MacOS/Safari Pilot +0 -0
  9. package/bin/Safari Pilot.app/Contents/PkgInfo +1 -0
  10. package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Info.plist +55 -0
  11. package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/MacOS/Safari Pilot Extension +0 -0
  12. package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Resources/background.js +294 -0
  13. package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Resources/content-isolated.js +80 -0
  14. package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Resources/content-main.js +310 -0
  15. package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Resources/icons/icon-128.png +0 -0
  16. package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Resources/icons/icon-48.png +0 -0
  17. package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Resources/icons/icon-96.png +0 -0
  18. package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/Resources/manifest.json +39 -0
  19. package/bin/Safari Pilot.app/Contents/PlugIns/Safari Pilot Extension.appex/Contents/_CodeSignature/CodeResources +194 -0
  20. package/bin/Safari Pilot.app/Contents/Resources/AppIcon.icns +0 -0
  21. package/bin/Safari Pilot.app/Contents/Resources/Assets.car +0 -0
  22. package/bin/Safari Pilot.app/Contents/Resources/Base.lproj/Main.html +19 -0
  23. package/bin/Safari Pilot.app/Contents/Resources/Base.lproj/Main.storyboardc/Info.plist +0 -0
  24. package/bin/Safari Pilot.app/Contents/Resources/Base.lproj/Main.storyboardc/MainMenu.nib +0 -0
  25. package/bin/Safari Pilot.app/Contents/Resources/Base.lproj/Main.storyboardc/NSWindowController-B8D-0N-5wS.nib +0 -0
  26. package/bin/Safari Pilot.app/Contents/Resources/Base.lproj/Main.storyboardc/XfG-lQ-9wD-view-m2S-Jp-Qdl.nib +0 -0
  27. package/bin/Safari Pilot.app/Contents/Resources/Icon.png +0 -0
  28. package/bin/Safari Pilot.app/Contents/Resources/Script.js +22 -0
  29. package/bin/Safari Pilot.app/Contents/Resources/Style.css +45 -0
  30. package/bin/Safari Pilot.app/Contents/_CodeSignature/CodeResources +236 -0
  31. package/bin/Safari Pilot.zip +0 -0
  32. package/bin/SafariPilotd +0 -0
  33. package/dist/engine-selector.d.ts +10 -0
  34. package/dist/engine-selector.js +55 -0
  35. package/dist/engine-selector.js.map +1 -0
  36. package/dist/engines/applescript.d.ts +53 -0
  37. package/dist/engines/applescript.js +290 -0
  38. package/dist/engines/applescript.js.map +1 -0
  39. package/dist/engines/daemon.d.ts +19 -0
  40. package/dist/engines/daemon.js +187 -0
  41. package/dist/engines/daemon.js.map +1 -0
  42. package/dist/engines/engine.d.ts +15 -0
  43. package/dist/engines/engine.js +42 -0
  44. package/dist/engines/engine.js.map +1 -0
  45. package/dist/engines/extension.d.ts +34 -0
  46. package/dist/engines/extension.js +66 -0
  47. package/dist/engines/extension.js.map +1 -0
  48. package/dist/errors.d.ts +128 -0
  49. package/dist/errors.js +250 -0
  50. package/dist/errors.js.map +1 -0
  51. package/dist/index.d.ts +2 -0
  52. package/dist/index.js +11 -0
  53. package/dist/index.js.map +1 -0
  54. package/dist/security/audit-log.d.ts +23 -0
  55. package/dist/security/audit-log.js +68 -0
  56. package/dist/security/audit-log.js.map +1 -0
  57. package/dist/security/circuit-breaker.d.ts +29 -0
  58. package/dist/security/circuit-breaker.js +114 -0
  59. package/dist/security/circuit-breaker.js.map +1 -0
  60. package/dist/security/domain-policy.d.ts +29 -0
  61. package/dist/security/domain-policy.js +96 -0
  62. package/dist/security/domain-policy.js.map +1 -0
  63. package/dist/security/human-approval.d.ts +20 -0
  64. package/dist/security/human-approval.js +150 -0
  65. package/dist/security/human-approval.js.map +1 -0
  66. package/dist/security/idpi-scanner.d.ts +20 -0
  67. package/dist/security/idpi-scanner.js +102 -0
  68. package/dist/security/idpi-scanner.js.map +1 -0
  69. package/dist/security/kill-switch.d.ts +51 -0
  70. package/dist/security/kill-switch.js +103 -0
  71. package/dist/security/kill-switch.js.map +1 -0
  72. package/dist/security/rate-limiter.d.ts +30 -0
  73. package/dist/security/rate-limiter.js +70 -0
  74. package/dist/security/rate-limiter.js.map +1 -0
  75. package/dist/security/screenshot-redaction.d.ts +42 -0
  76. package/dist/security/screenshot-redaction.js +134 -0
  77. package/dist/security/screenshot-redaction.js.map +1 -0
  78. package/dist/security/tab-ownership.d.ts +46 -0
  79. package/dist/security/tab-ownership.js +85 -0
  80. package/dist/security/tab-ownership.js.map +1 -0
  81. package/dist/server.d.ts +53 -0
  82. package/dist/server.js +347 -0
  83. package/dist/server.js.map +1 -0
  84. package/dist/tools/clipboard.d.ts +15 -0
  85. package/dist/tools/clipboard.js +128 -0
  86. package/dist/tools/clipboard.js.map +1 -0
  87. package/dist/tools/compound.d.ts +68 -0
  88. package/dist/tools/compound.js +491 -0
  89. package/dist/tools/compound.js.map +1 -0
  90. package/dist/tools/extraction.d.ts +26 -0
  91. package/dist/tools/extraction.js +414 -0
  92. package/dist/tools/extraction.js.map +1 -0
  93. package/dist/tools/frames.d.ts +22 -0
  94. package/dist/tools/frames.js +165 -0
  95. package/dist/tools/frames.js.map +1 -0
  96. package/dist/tools/interaction.d.ts +30 -0
  97. package/dist/tools/interaction.js +651 -0
  98. package/dist/tools/interaction.js.map +1 -0
  99. package/dist/tools/navigation.d.ts +41 -0
  100. package/dist/tools/navigation.js +316 -0
  101. package/dist/tools/navigation.js.map +1 -0
  102. package/dist/tools/network.d.ts +27 -0
  103. package/dist/tools/network.js +721 -0
  104. package/dist/tools/network.js.map +1 -0
  105. package/dist/tools/performance.d.ts +16 -0
  106. package/dist/tools/performance.js +240 -0
  107. package/dist/tools/performance.js.map +1 -0
  108. package/dist/tools/permissions.d.ts +25 -0
  109. package/dist/tools/permissions.js +308 -0
  110. package/dist/tools/permissions.js.map +1 -0
  111. package/dist/tools/service-workers.d.ts +15 -0
  112. package/dist/tools/service-workers.js +136 -0
  113. package/dist/tools/service-workers.js.map +1 -0
  114. package/dist/tools/shadow.d.ts +21 -0
  115. package/dist/tools/shadow.js +126 -0
  116. package/dist/tools/shadow.js.map +1 -0
  117. package/dist/tools/storage.d.ts +30 -0
  118. package/dist/tools/storage.js +679 -0
  119. package/dist/tools/storage.js.map +1 -0
  120. package/dist/tools/structured-extraction.d.ts +22 -0
  121. package/dist/tools/structured-extraction.js +433 -0
  122. package/dist/tools/structured-extraction.js.map +1 -0
  123. package/dist/tools/wait.d.ts +18 -0
  124. package/dist/tools/wait.js +182 -0
  125. package/dist/tools/wait.js.map +1 -0
  126. package/dist/types.d.ts +85 -0
  127. package/dist/types.js +2 -0
  128. package/dist/types.js.map +1 -0
  129. package/extension/background.js +294 -0
  130. package/extension/content-isolated.js +80 -0
  131. package/extension/content-main.js +310 -0
  132. package/extension/icons/icon-128.png +0 -0
  133. package/extension/icons/icon-48.png +0 -0
  134. package/extension/icons/icon-96.png +0 -0
  135. package/extension/manifest.json +39 -0
  136. package/hooks/session-end.sh +67 -0
  137. package/hooks/session-start.sh +66 -0
  138. package/package.json +46 -0
  139. package/scripts/build-extension.sh +135 -0
  140. package/scripts/postinstall.sh +91 -0
  141. package/scripts/preuninstall.sh +25 -0
  142. package/scripts/update-daemon.sh +62 -0
  143. package/skills/safari-pilot/SKILL.md +157 -0
@@ -0,0 +1,53 @@
1
+ import { BaseEngine } from './engine.js';
2
+ import type { Engine, EngineError, EngineResult } from '../types.js';
3
+ export declare class AppleScriptEngine extends BaseEngine {
4
+ readonly name: Engine;
5
+ isAvailable(): Promise<boolean>;
6
+ execute(script: string, timeout?: number): Promise<EngineResult>;
7
+ /** Execute a raw AppleScript string directly (for testing/internal use). */
8
+ executeRaw(script: string, timeout?: number): Promise<string>;
9
+ /**
10
+ * Build an AppleScript that targets a tab by URL and executes JS inside it.
11
+ */
12
+ buildTabScript(url: string, jsCode: string): string;
13
+ /**
14
+ * Build an AppleScript that navigates to a URL (opens new document if needed).
15
+ */
16
+ buildNavigateScript(url: string): string;
17
+ /**
18
+ * Build an AppleScript that opens a new tab (optionally in a private window).
19
+ */
20
+ buildNewTabScript(url: string, privateWindow?: boolean): string;
21
+ /**
22
+ * Build an AppleScript that closes the tab matching a given URL.
23
+ */
24
+ buildCloseTabScript(url: string): string;
25
+ /**
26
+ * Build an AppleScript that lists all open tabs across all windows.
27
+ */
28
+ buildListTabsScript(): string;
29
+ /**
30
+ * Execute JavaScript in a tab identified by its current URL.
31
+ * Wraps the JS in a try/catch serialization harness so `return` works
32
+ * and results are properly JSON-serialized. Parses the wrapper on the
33
+ * way back so callers get the inner value directly.
34
+ */
35
+ executeJsInTab(tabUrl: string, jsCode: string, timeout?: number): Promise<EngineResult>;
36
+ /**
37
+ * Wrap a JS snippet in a try/catch serialization harness.
38
+ * Returns a single-line string safe for embedding in AppleScript do JavaScript.
39
+ */
40
+ wrapJavaScript(jsCode: string): string;
41
+ /**
42
+ * Parse the raw string returned by do JavaScript / osascript stdout.
43
+ * Handles JSON envelope, CSP/ShadowDOM signals, and bare string results.
44
+ */
45
+ parseJsResult(raw: string): EngineResult;
46
+ /**
47
+ * Parse an AppleScript error from stderr or error message text.
48
+ * Extracts OSA error codes via regex.
49
+ */
50
+ parseAppleScriptError(stderr: string): EngineError;
51
+ private classifyError;
52
+ private mapJsErrorName;
53
+ }
@@ -0,0 +1,290 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+ import { BaseEngine } from './engine.js';
4
+ const execFileAsync = promisify(execFile);
5
+ const MAX_BUFFER = 10 * 1024 * 1024; // 10 MB
6
+ const DEFAULT_TIMEOUT_MS = 30_000;
7
+ // AppleScript error codes from OSA and Safari
8
+ const AS_ERROR_CODES = {
9
+ [-600]: { code: 'SAFARI_NOT_RUNNING', retryable: true },
10
+ [-609]: { code: 'SAFARI_CRASHED', retryable: true },
11
+ [-1743]: { code: 'PERMISSION_DENIED', retryable: false },
12
+ [-1728]: { code: 'ELEMENT_NOT_FOUND', retryable: true },
13
+ };
14
+ export class AppleScriptEngine extends BaseEngine {
15
+ name = 'applescript';
16
+ async isAvailable() {
17
+ try {
18
+ await execFileAsync('osascript', ['-e', 'tell application "Safari" to return name'], {
19
+ timeout: 5000,
20
+ maxBuffer: MAX_BUFFER,
21
+ });
22
+ return true;
23
+ }
24
+ catch {
25
+ return false;
26
+ }
27
+ }
28
+ async execute(script, timeout = DEFAULT_TIMEOUT_MS) {
29
+ const start = Date.now();
30
+ try {
31
+ const { stdout } = await execFileAsync('osascript', ['-e', script], {
32
+ timeout,
33
+ maxBuffer: MAX_BUFFER,
34
+ });
35
+ const raw = stdout.trim();
36
+ const result = this.parseJsResult(raw);
37
+ return { ...result, elapsed_ms: Date.now() - start };
38
+ }
39
+ catch (err) {
40
+ const elapsed = Date.now() - start;
41
+ const engineError = this.classifyError(err);
42
+ return { ok: false, error: engineError, elapsed_ms: elapsed };
43
+ }
44
+ }
45
+ /** Execute a raw AppleScript string directly (for testing/internal use). */
46
+ async executeRaw(script, timeout = DEFAULT_TIMEOUT_MS) {
47
+ const { stdout } = await execFileAsync('osascript', ['-e', script], {
48
+ timeout,
49
+ maxBuffer: MAX_BUFFER,
50
+ });
51
+ return stdout.trim();
52
+ }
53
+ // ── Script builders ─────────────────────────────────────────────────────────
54
+ /**
55
+ * Build an AppleScript that targets a tab by URL and executes JS inside it.
56
+ */
57
+ buildTabScript(url, jsCode) {
58
+ const escapedUrl = url.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
59
+ const escapedJs = jsCode.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
60
+ return `tell application "Safari"
61
+ set _result to ""
62
+ repeat with _window in every window
63
+ repeat with _tab in every tab of _window
64
+ if URL of _tab is "${escapedUrl}" then
65
+ set _result to do JavaScript "${escapedJs}" in _tab
66
+ return _result
67
+ end if
68
+ end repeat
69
+ end repeat
70
+ return _result
71
+ end tell`;
72
+ }
73
+ /**
74
+ * Build an AppleScript that navigates to a URL (opens new document if needed).
75
+ */
76
+ buildNavigateScript(url) {
77
+ const escapedUrl = url.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
78
+ return `tell application "Safari"
79
+ if (count of windows) is 0 then
80
+ make new document with properties {URL:"${escapedUrl}"}
81
+ else
82
+ set URL of current tab of front window to "${escapedUrl}"
83
+ end if
84
+ end tell`;
85
+ }
86
+ /**
87
+ * Build an AppleScript that opens a new tab (optionally in a private window).
88
+ */
89
+ buildNewTabScript(url, privateWindow = false) {
90
+ const escapedUrl = url.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
91
+ if (privateWindow) {
92
+ // Private windows require System Events menu click
93
+ return `tell application "Safari" to activate
94
+ tell application "System Events"
95
+ tell process "Safari"
96
+ click menu item "New Private Window" of menu "File" of menu bar 1
97
+ end tell
98
+ end tell
99
+ tell application "Safari"
100
+ set URL of current tab of front window to "${escapedUrl}"
101
+ end tell`;
102
+ }
103
+ return `tell application "Safari"
104
+ tell front window
105
+ set _tab to make new tab with properties {URL:"${escapedUrl}"}
106
+ set current tab to _tab
107
+ end tell
108
+ end tell`;
109
+ }
110
+ /**
111
+ * Build an AppleScript that closes the tab matching a given URL.
112
+ */
113
+ buildCloseTabScript(url) {
114
+ const escapedUrl = url.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
115
+ return `tell application "Safari"
116
+ repeat with _window in every window
117
+ repeat with _tab in every tab of _window
118
+ if URL of _tab is "${escapedUrl}" then
119
+ close _tab
120
+ return true
121
+ end if
122
+ end repeat
123
+ end repeat
124
+ return false
125
+ end tell`;
126
+ }
127
+ /**
128
+ * Build an AppleScript that lists all open tabs across all windows.
129
+ */
130
+ buildListTabsScript() {
131
+ return `tell application "Safari"
132
+ set _output to ""
133
+ repeat with _window in every window
134
+ repeat with _tab in every tab of _window
135
+ set _output to _output & (URL of _tab) & "\\t" & (name of _tab) & "\\n"
136
+ end repeat
137
+ end repeat
138
+ return _output
139
+ end tell`;
140
+ }
141
+ /**
142
+ * Execute JavaScript in a tab identified by its current URL.
143
+ * Wraps the JS in a try/catch serialization harness so `return` works
144
+ * and results are properly JSON-serialized. Parses the wrapper on the
145
+ * way back so callers get the inner value directly.
146
+ */
147
+ async executeJsInTab(tabUrl, jsCode, timeout) {
148
+ const wrapped = this.wrapJavaScript(jsCode);
149
+ const script = this.buildTabScript(tabUrl, wrapped);
150
+ const result = await this.execute(script, timeout);
151
+ // Parse the JSON wrapper from wrapJavaScript
152
+ if (result.ok && result.value) {
153
+ const parsed = this.parseJsResult(result.value);
154
+ return { ...parsed, elapsed_ms: result.elapsed_ms };
155
+ }
156
+ return result;
157
+ }
158
+ // ── JS wrapping & result parsing ────────────────────────────────────────────
159
+ /**
160
+ * Wrap a JS snippet in a try/catch serialization harness.
161
+ * Returns a single-line string safe for embedding in AppleScript do JavaScript.
162
+ */
163
+ wrapJavaScript(jsCode) {
164
+ return `(function(){try{var __r=(function(){${jsCode}})();return JSON.stringify({ok:true,value:__r});}catch(e){return JSON.stringify({ok:false,error:{message:e.message,name:e.name}});}})()`;
165
+ }
166
+ /**
167
+ * Parse the raw string returned by do JavaScript / osascript stdout.
168
+ * Handles JSON envelope, CSP/ShadowDOM signals, and bare string results.
169
+ */
170
+ parseJsResult(raw) {
171
+ const start = Date.now();
172
+ // Detect CSP-blocked execution (Safari returns empty or specific error text)
173
+ if (raw === '' ||
174
+ raw.toLowerCase().includes('content security policy') ||
175
+ raw.toLowerCase().includes('blocked by csp')) {
176
+ if (raw === '' || raw.toLowerCase().includes('content security policy') || raw.toLowerCase().includes('blocked by csp')) {
177
+ // Only classify empty as CSP_BLOCKED if it looks like a blocked signal,
178
+ // but for the harness we rely on the structured envelope. Bare empty = CSP.
179
+ if (raw.toLowerCase().includes('content security policy') || raw.toLowerCase().includes('blocked by csp')) {
180
+ return {
181
+ ok: false,
182
+ error: { code: 'CSP_BLOCKED', message: 'JavaScript execution blocked by Content Security Policy', retryable: false },
183
+ elapsed_ms: Date.now() - start,
184
+ };
185
+ }
186
+ }
187
+ }
188
+ // Detect shadow DOM closed signal
189
+ if (raw.toLowerCase().includes('shadow') && raw.toLowerCase().includes('closed')) {
190
+ return {
191
+ ok: false,
192
+ error: { code: 'SHADOW_DOM_CLOSED', message: 'Cannot access closed shadow root', retryable: false },
193
+ elapsed_ms: Date.now() - start,
194
+ };
195
+ }
196
+ // Try to parse JSON envelope
197
+ try {
198
+ const parsed = JSON.parse(raw);
199
+ if (typeof parsed === 'object' && parsed !== null && 'ok' in parsed) {
200
+ if (parsed.ok) {
201
+ const val = parsed.value;
202
+ return {
203
+ ok: true,
204
+ value: val === undefined || val === null
205
+ ? undefined
206
+ : typeof val === 'string'
207
+ ? val
208
+ : JSON.stringify(val),
209
+ elapsed_ms: Date.now() - start,
210
+ };
211
+ }
212
+ else {
213
+ const errName = parsed.error?.name ?? 'Error';
214
+ const errMsg = parsed.error?.message ?? 'Unknown error';
215
+ // Map known JS error names to engine codes
216
+ const code = this.mapJsErrorName(errName);
217
+ return {
218
+ ok: false,
219
+ error: { code, message: errMsg, retryable: false },
220
+ elapsed_ms: Date.now() - start,
221
+ };
222
+ }
223
+ }
224
+ }
225
+ catch {
226
+ // Not JSON — treat raw string as successful result
227
+ }
228
+ // Raw non-JSON string → treat as success value
229
+ return { ok: true, value: raw, elapsed_ms: Date.now() - start };
230
+ }
231
+ /**
232
+ * Parse an AppleScript error from stderr or error message text.
233
+ * Extracts OSA error codes via regex.
234
+ */
235
+ parseAppleScriptError(stderr) {
236
+ // AppleScript errors embed the code in parens: "... (-600)"
237
+ const match = stderr.match(/\((-?\d+)\)/);
238
+ if (match) {
239
+ const code = parseInt(match[1], 10);
240
+ const known = AS_ERROR_CODES[code];
241
+ if (known) {
242
+ return {
243
+ code: known.code,
244
+ message: stderr.trim(),
245
+ retryable: known.retryable,
246
+ };
247
+ }
248
+ }
249
+ return {
250
+ code: 'APPLESCRIPT_ERROR',
251
+ message: stderr.trim() || 'Unknown AppleScript error',
252
+ retryable: false,
253
+ };
254
+ }
255
+ // ── Private helpers ─────────────────────────────────────────────────────────
256
+ classifyError(err) {
257
+ if (err && typeof err === 'object') {
258
+ const e = err;
259
+ // Timeout / killed process
260
+ if (e.killed === true || e.signal === 'SIGTERM' || e.signal === 'SIGKILL') {
261
+ return { code: 'TIMEOUT', message: 'osascript process timed out', retryable: true };
262
+ }
263
+ // stderr contains AppleScript error code
264
+ if (e.stderr && typeof e.stderr === 'string' && e.stderr.length > 0) {
265
+ return this.parseAppleScriptError(e.stderr);
266
+ }
267
+ // message may contain the error text
268
+ if (e.message && typeof e.message === 'string') {
269
+ const asMatch = e.message.match(/\((-?\d+)\)/);
270
+ if (asMatch) {
271
+ return this.parseAppleScriptError(e.message);
272
+ }
273
+ }
274
+ }
275
+ return {
276
+ code: 'UNKNOWN_ERROR',
277
+ message: err instanceof Error ? err.message : String(err),
278
+ retryable: false,
279
+ };
280
+ }
281
+ mapJsErrorName(name) {
282
+ switch (name) {
283
+ case 'SecurityError': return 'PERMISSION_DENIED';
284
+ case 'TypeError': return 'TYPE_ERROR';
285
+ case 'ReferenceError': return 'REFERENCE_ERROR';
286
+ default: return 'JS_ERROR';
287
+ }
288
+ }
289
+ }
290
+ //# sourceMappingURL=applescript.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"applescript.js","sourceRoot":"","sources":["../../src/engines/applescript.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import { BaseEngine } from './engine.js';
2
+ import type { Engine, EngineResult } from '../types.js';
3
+ export declare class DaemonEngine extends BaseEngine {
4
+ readonly name: Engine;
5
+ private proc;
6
+ private pending;
7
+ private daemonPath;
8
+ private reconnectAttempted;
9
+ private shuttingDown;
10
+ constructor(daemonPath?: string);
11
+ isAvailable(): Promise<boolean>;
12
+ execute(script: string, timeout?: number): Promise<EngineResult>;
13
+ shutdown(): Promise<void>;
14
+ private ensureRunning;
15
+ private spawnDaemon;
16
+ private sendCommand;
17
+ private _dispatchResponse;
18
+ private _rejectAllPending;
19
+ }
@@ -0,0 +1,187 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { BaseEngine } from './engine.js';
3
+ const DEFAULT_TIMEOUT_MS = 30_000;
4
+ let _idCounter = 0;
5
+ class DaemonTimeoutError extends Error {
6
+ constructor(method, timeout) {
7
+ super(`Daemon command "${method}" timed out after ${timeout}ms`);
8
+ this.name = 'DaemonTimeoutError';
9
+ }
10
+ }
11
+ function nextId() {
12
+ return `req-${Date.now()}-${++_idCounter}`;
13
+ }
14
+ export class DaemonEngine extends BaseEngine {
15
+ name = 'daemon';
16
+ proc = null;
17
+ pending = new Map();
18
+ daemonPath;
19
+ reconnectAttempted = false;
20
+ shuttingDown = false;
21
+ constructor(daemonPath) {
22
+ super();
23
+ this.daemonPath = daemonPath
24
+ ?? process.env['SAFARI_PILOT_DAEMON']
25
+ ?? './bin/SafariPilotd';
26
+ }
27
+ // ── Public interface ────────────────────────────────────────────────────────
28
+ async isAvailable() {
29
+ try {
30
+ await this.ensureRunning();
31
+ const response = await this.sendCommand('ping', {});
32
+ return response.ok === true && response.value === 'pong';
33
+ }
34
+ catch {
35
+ return false;
36
+ }
37
+ }
38
+ async execute(script, timeout = DEFAULT_TIMEOUT_MS) {
39
+ const start = Date.now();
40
+ try {
41
+ await this.ensureRunning();
42
+ const response = await this.sendCommand('execute', { script }, timeout);
43
+ if (response.ok) {
44
+ // Normalize: trim trailing whitespace (daemon may include trailing newline)
45
+ const value = response.value?.trimEnd();
46
+ return {
47
+ ok: true,
48
+ value,
49
+ elapsed_ms: Date.now() - start,
50
+ };
51
+ }
52
+ return {
53
+ ok: false,
54
+ error: {
55
+ code: response.error?.code ?? 'DAEMON_ERROR',
56
+ message: response.error?.message ?? 'Daemon returned an error',
57
+ retryable: true,
58
+ },
59
+ elapsed_ms: Date.now() - start,
60
+ };
61
+ }
62
+ catch (err) {
63
+ const msg = err instanceof Error ? err.message : String(err);
64
+ // DaemonTimeoutError is a tagged subclass so we can identify it precisely
65
+ const isTimeout = err instanceof DaemonTimeoutError;
66
+ return {
67
+ ok: false,
68
+ error: {
69
+ code: isTimeout ? 'TIMEOUT' : 'DAEMON_ERROR',
70
+ message: msg,
71
+ retryable: true,
72
+ },
73
+ elapsed_ms: Date.now() - start,
74
+ };
75
+ }
76
+ }
77
+ async shutdown() {
78
+ this.shuttingDown = true;
79
+ if (this.proc && !this.proc.killed) {
80
+ // Best-effort graceful shutdown command
81
+ try {
82
+ if (this.proc.stdin?.writable) {
83
+ this.proc.stdin.write(JSON.stringify({ method: 'shutdown' }) + '\n');
84
+ }
85
+ }
86
+ catch {
87
+ // ignore write errors during shutdown
88
+ }
89
+ this.proc.kill('SIGTERM');
90
+ }
91
+ this._rejectAllPending(new Error('DaemonEngine is shutting down'));
92
+ this.proc = null;
93
+ }
94
+ // ── Private: process lifecycle ───────────────────────────────────────────
95
+ async ensureRunning() {
96
+ if (this.proc && !this.proc.killed) {
97
+ return;
98
+ }
99
+ this.spawnDaemon();
100
+ }
101
+ spawnDaemon() {
102
+ const proc = spawn(this.daemonPath, [], {
103
+ stdio: ['pipe', 'pipe', 'pipe'],
104
+ });
105
+ this.proc = proc;
106
+ this.reconnectAttempted = false;
107
+ // Parse NDJSON lines from stdout using manual line buffering.
108
+ // Using raw `data` events (not readline) so the code works with both
109
+ // real Readable streams and the EventEmitter-based mocks used in tests.
110
+ let lineBuffer = '';
111
+ proc.stdout.on('data', (chunk) => {
112
+ lineBuffer += typeof chunk === 'string' ? chunk : chunk.toString('utf8');
113
+ let newlineIdx;
114
+ while ((newlineIdx = lineBuffer.indexOf('\n')) !== -1) {
115
+ const line = lineBuffer.slice(0, newlineIdx).trim();
116
+ lineBuffer = lineBuffer.slice(newlineIdx + 1);
117
+ if (!line)
118
+ continue;
119
+ try {
120
+ const msg = JSON.parse(line);
121
+ this._dispatchResponse(msg);
122
+ }
123
+ catch {
124
+ // Malformed JSON line — ignore
125
+ }
126
+ }
127
+ });
128
+ // Handle process exit: reject all pending requests
129
+ proc.on('exit', (code, signal) => {
130
+ if (this.shuttingDown)
131
+ return;
132
+ const reason = `Daemon exited with code=${code ?? 'null'} signal=${signal ?? 'none'}`;
133
+ this._rejectAllPending(new Error(reason));
134
+ // Auto-reconnect once
135
+ if (!this.reconnectAttempted) {
136
+ this.reconnectAttempted = true;
137
+ this.proc = null;
138
+ // Don't auto-spawn here — let the next ensureRunning() call do it
139
+ // to avoid infinite respawn loops
140
+ }
141
+ else {
142
+ this.proc = null;
143
+ }
144
+ });
145
+ proc.on('error', (err) => {
146
+ this._rejectAllPending(err);
147
+ this.proc = null;
148
+ });
149
+ }
150
+ // ── Private: request/response ────────────────────────────────────────────
151
+ sendCommand(method, params, timeout = DEFAULT_TIMEOUT_MS) {
152
+ const id = nextId();
153
+ const payload = JSON.stringify({ id, method, params }) + '\n';
154
+ return new Promise((resolve, reject) => {
155
+ const timer = setTimeout(() => {
156
+ this.pending.delete(id);
157
+ reject(new DaemonTimeoutError(method, timeout));
158
+ }, timeout);
159
+ this.pending.set(id, { resolve, reject, timer });
160
+ if (!this.proc || !this.proc.stdin?.writable) {
161
+ clearTimeout(timer);
162
+ this.pending.delete(id);
163
+ reject(new Error('Daemon process is not running or stdin is closed'));
164
+ return;
165
+ }
166
+ this.proc.stdin.write(payload);
167
+ });
168
+ }
169
+ _dispatchResponse(msg) {
170
+ if (!msg.id)
171
+ return;
172
+ const pending = this.pending.get(msg.id);
173
+ if (!pending)
174
+ return;
175
+ clearTimeout(pending.timer);
176
+ this.pending.delete(msg.id);
177
+ pending.resolve(msg);
178
+ }
179
+ _rejectAllPending(reason) {
180
+ for (const [id, req] of this.pending) {
181
+ clearTimeout(req.timer);
182
+ req.reject(reason);
183
+ this.pending.delete(id);
184
+ }
185
+ }
186
+ }
187
+ //# sourceMappingURL=daemon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon.js","sourceRoot":"","sources":["../../src/engines/daemon.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGzC,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAClC,IAAI,UAAU,GAAG,CAAC,CAAC;AAEnB,MAAM,kBAAmB,SAAQ,KAAK;IACpC,YAAY,MAAc,EAAE,OAAe;QACzC,KAAK,CAAC,mBAAmB,MAAM,qBAAqB,OAAO,IAAI,CAAC,CAAC;QACjE,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IACnC,CAAC;CACF;AAED,SAAS,MAAM;IACb,OAAO,OAAO,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;AAC7C,CAAC;AAeD,MAAM,OAAO,YAAa,SAAQ,UAAU;IACjC,IAAI,GAAW,QAAQ,CAAC;IAEzB,IAAI,GAAwB,IAAI,CAAC;IACjC,OAAO,GAAgC,IAAI,GAAG,EAAE,CAAC;IACjD,UAAU,CAAS;IACnB,kBAAkB,GAAG,KAAK,CAAC;IAC3B,YAAY,GAAG,KAAK,CAAC;IAE7B,YAAY,UAAmB;QAC7B,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,UAAU,GAAG,UAAU;eACvB,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC;eAClC,oBAAoB,CAAC;IAC5B,CAAC;IAED,+EAA+E;IAE/E,KAAK,CAAC,WAAW;QACf,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC;YAC3B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;YACpD,OAAO,QAAQ,CAAC,EAAE,KAAK,IAAI,IAAI,QAAQ,CAAC,KAAK,KAAK,MAAM,CAAC;QAC3D,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,MAAc,EAAE,UAAkB,kBAAkB;QAChE,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC;YAC3B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,SAAS,EAAE,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,CAAC;YACxE,IAAI,QAAQ,CAAC,EAAE,EAAE,CAAC;gBAChB,4EAA4E;gBAC5E,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC;gBACxC,OAAO;oBACL,EAAE,EAAE,IAAI;oBACR,KAAK;oBACL,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK;iBAC/B,CAAC;YACJ,CAAC;YACD,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,KAAK,EAAE;oBACL,IAAI,EAAE,QAAQ,CAAC,KAAK,EAAE,IAAI,IAAI,cAAc;oBAC5C,OAAO,EAAE,QAAQ,CAAC,KAAK,EAAE,OAAO,IAAI,0BAA0B;oBAC9D,SAAS,EAAE,IAAI;iBAChB;gBACD,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK;aAC/B,CAAC;QACJ,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACtB,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC7D,0EAA0E;YAC1E,MAAM,SAAS,GAAG,GAAG,YAAY,kBAAkB,CAAC;YACpD,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,KAAK,EAAE;oBACL,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,cAAc;oBAC5C,OAAO,EAAE,GAAG;oBACZ,SAAS,EAAE,IAAI;iBAChB;gBACD,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK;aAC/B,CAAC;QACJ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACnC,wCAAwC;YACxC,IAAI,CAAC;gBACH,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC;oBAC9B,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;gBACvE,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,sCAAsC;YACxC,CAAC;YACD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC5B,CAAC;QACD,IAAI,CAAC,iBAAiB,CAAC,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC,CAAC;QACnE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,4EAA4E;IAEpE,KAAK,CAAC,aAAa;QACzB,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACnC,OAAO;QACT,CAAC;QACD,IAAI,CAAC,WAAW,EAAE,CAAC;IACrB,CAAC;IAEO,WAAW;QACjB,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,EAAE;YACtC,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;SAChC,CAAC,CAAC;QAEH,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,kBAAkB,GAAG,KAAK,CAAC;QAEhC,8DAA8D;QAC9D,qEAAqE;QACrE,wEAAwE;QACxE,IAAI,UAAU,GAAG,EAAE,CAAC;QACpB,IAAI,CAAC,MAAO,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAsB,EAAE,EAAE;YACjD,UAAU,IAAI,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YACzE,IAAI,UAAkB,CAAC;YACvB,OAAO,CAAC,UAAU,GAAG,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;gBACtD,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,IAAI,EAAE,CAAC;gBACpD,UAAU,GAAG,UAAU,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;gBAC9C,IAAI,CAAC,IAAI;oBAAE,SAAS;gBACpB,IAAI,CAAC;oBACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAmB,CAAC;oBAC/C,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC;gBAC9B,CAAC;gBAAC,MAAM,CAAC;oBACP,+BAA+B;gBACjC,CAAC;YACH,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,mDAAmD;QACnD,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAmB,EAAE,MAA6B,EAAE,EAAE;YACrE,IAAI,IAAI,CAAC,YAAY;gBAAE,OAAO;YAE9B,MAAM,MAAM,GAAG,2BAA2B,IAAI,IAAI,MAAM,WAAW,MAAM,IAAI,MAAM,EAAE,CAAC;YACtF,IAAI,CAAC,iBAAiB,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;YAE1C,sBAAsB;YACtB,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC;gBAC7B,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC;gBAC/B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;gBACjB,kEAAkE;gBAClE,kCAAkC;YACpC,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;YACnB,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAU,EAAE,EAAE;YAC9B,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC;YAC5B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACnB,CAAC,CAAC,CAAC;IACL,CAAC;IAED,4EAA4E;IAEpE,WAAW,CACjB,MAAc,EACd,MAA+B,EAC/B,UAAkB,kBAAkB;QAEpC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC;QACpB,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC;QAE9D,OAAO,IAAI,OAAO,CAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrD,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC5B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBACxB,MAAM,CAAC,IAAI,kBAAkB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;YAClD,CAAC,EAAE,OAAO,CAAC,CAAC;YAEZ,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;YAEjD,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC;gBAC7C,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBACxB,MAAM,CAAC,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC,CAAC;gBACtE,OAAO;YACT,CAAC;YAED,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACjC,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,iBAAiB,CAAC,GAAmB;QAC3C,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,OAAO;QACpB,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACzC,IAAI,CAAC,OAAO;YAAE,OAAO;QACrB,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAC5B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC5B,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACvB,CAAC;IAEO,iBAAiB,CAAC,MAAa;QACrC,KAAK,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACrC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACxB,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACnB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,15 @@
1
+ import type { Engine, EngineResult } from '../types.js';
2
+ export interface IEngine {
3
+ readonly name: Engine;
4
+ isAvailable(): Promise<boolean>;
5
+ execute(script: string, timeout?: number): Promise<EngineResult>;
6
+ shutdown(): Promise<void>;
7
+ }
8
+ export declare abstract class BaseEngine implements IEngine {
9
+ abstract readonly name: Engine;
10
+ abstract isAvailable(): Promise<boolean>;
11
+ abstract execute(script: string, timeout?: number): Promise<EngineResult>;
12
+ shutdown(): Promise<void>;
13
+ protected wrapScript(jsCode: string): string;
14
+ protected parseResult(raw: string): EngineResult;
15
+ }
@@ -0,0 +1,42 @@
1
+ export class BaseEngine {
2
+ async shutdown() { }
3
+ wrapScript(jsCode) {
4
+ return `(() => {
5
+ try {
6
+ const __result = (() => { ${jsCode} })();
7
+ return JSON.stringify({ ok: true, value: __result });
8
+ } catch (e) {
9
+ return JSON.stringify({
10
+ ok: false,
11
+ error: { message: e.message, name: e.name, stack: e.stack }
12
+ });
13
+ }
14
+ })()`;
15
+ }
16
+ parseResult(raw) {
17
+ const start = Date.now();
18
+ try {
19
+ const parsed = JSON.parse(raw);
20
+ return {
21
+ ok: parsed.ok,
22
+ value: parsed.ok
23
+ ? typeof parsed.value === 'string'
24
+ ? parsed.value
25
+ : JSON.stringify(parsed.value)
26
+ : undefined,
27
+ error: parsed.ok
28
+ ? undefined
29
+ : {
30
+ code: parsed.error?.name || 'INTERNAL_ERROR',
31
+ message: parsed.error?.message || 'Unknown error',
32
+ retryable: false,
33
+ },
34
+ elapsed_ms: Date.now() - start,
35
+ };
36
+ }
37
+ catch {
38
+ return { ok: true, value: raw, elapsed_ms: Date.now() - start };
39
+ }
40
+ }
41
+ }
42
+ //# sourceMappingURL=engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/engines/engine.ts"],"names":[],"mappings":"AASA,MAAM,OAAgB,UAAU;IAK9B,KAAK,CAAC,QAAQ,KAAmB,CAAC;IAExB,UAAU,CAAC,MAAc;QACjC,OAAO;;gCAEqB,MAAM;;;;;;;;KAQjC,CAAC;IACJ,CAAC;IAES,WAAW,CAAC,GAAW;QAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC/B,OAAO;gBACL,EAAE,EAAE,MAAM,CAAC,EAAE;gBACb,KAAK,EAAE,MAAM,CAAC,EAAE;oBACd,CAAC,CAAC,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ;wBAChC,CAAC,CAAC,MAAM,CAAC,KAAK;wBACd,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC;oBAChC,CAAC,CAAC,SAAS;gBACb,KAAK,EAAE,MAAM,CAAC,EAAE;oBACd,CAAC,CAAC,SAAS;oBACX,CAAC,CAAC;wBACE,IAAI,EAAE,MAAM,CAAC,KAAK,EAAE,IAAI,IAAI,gBAAgB;wBAC5C,OAAO,EAAE,MAAM,CAAC,KAAK,EAAE,OAAO,IAAI,eAAe;wBACjD,SAAS,EAAE,KAAK;qBACjB;gBACL,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK;aAC/B,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC;QAClE,CAAC;IACH,CAAC;CACF"}