@monotykamary/localterm-server 1.17.2 → 1.19.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 (53) hide show
  1. package/dist/automation-scheduler.d.ts.map +1 -1
  2. package/dist/automation-scheduler.js +8 -2
  3. package/dist/automation-scheduler.js.map +1 -1
  4. package/dist/automation-store.d.ts +5 -2
  5. package/dist/automation-store.d.ts.map +1 -1
  6. package/dist/automation-store.js +128 -13
  7. package/dist/automation-store.js.map +1 -1
  8. package/dist/cdp/cdp-client.d.ts +76 -0
  9. package/dist/cdp/cdp-client.d.ts.map +1 -0
  10. package/dist/cdp/cdp-client.js +263 -0
  11. package/dist/cdp/cdp-client.js.map +1 -0
  12. package/dist/cdp/detect-chromium.d.ts +38 -0
  13. package/dist/cdp/detect-chromium.d.ts.map +1 -0
  14. package/dist/cdp/detect-chromium.js +97 -0
  15. package/dist/cdp/detect-chromium.js.map +1 -0
  16. package/dist/cdp/open-background-tab.d.ts +33 -0
  17. package/dist/cdp/open-background-tab.d.ts.map +1 -0
  18. package/dist/cdp/open-background-tab.js +98 -0
  19. package/dist/cdp/open-background-tab.js.map +1 -0
  20. package/dist/constants.d.ts +8 -1
  21. package/dist/constants.d.ts.map +1 -1
  22. package/dist/constants.js +26 -1
  23. package/dist/constants.js.map +1 -1
  24. package/dist/heartbeat-store.d.ts +7 -0
  25. package/dist/heartbeat-store.d.ts.map +1 -0
  26. package/dist/heartbeat-store.js +45 -0
  27. package/dist/heartbeat-store.js.map +1 -0
  28. package/dist/index.d.ts +19 -1
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +208 -25
  31. package/dist/index.js.map +1 -1
  32. package/dist/protocol.d.ts +4 -3
  33. package/dist/protocol.d.ts.map +1 -1
  34. package/dist/protocol.js +3 -2
  35. package/dist/protocol.js.map +1 -1
  36. package/dist/schemas.d.ts +633 -16
  37. package/dist/schemas.d.ts.map +1 -1
  38. package/dist/schemas.js +185 -14
  39. package/dist/schemas.js.map +1 -1
  40. package/dist/types.d.ts +8 -1
  41. package/dist/types.d.ts.map +1 -1
  42. package/dist/utils/compile-schedule.d.ts +6 -0
  43. package/dist/utils/compile-schedule.d.ts.map +1 -0
  44. package/dist/utils/compile-schedule.js +173 -0
  45. package/dist/utils/compile-schedule.js.map +1 -0
  46. package/dist/utils/compute-next-automation-run-at.d.ts.map +1 -1
  47. package/dist/utils/compute-next-automation-run-at.js +14 -3
  48. package/dist/utils/compute-next-automation-run-at.js.map +1 -1
  49. package/dist/utils/reconcile-downtime.d.ts +3 -0
  50. package/dist/utils/reconcile-downtime.d.ts.map +1 -0
  51. package/dist/utils/reconcile-downtime.js +25 -0
  52. package/dist/utils/reconcile-downtime.js.map +1 -0
  53. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"automation-scheduler.d.ts","sourceRoot":"","sources":["../src/automation-scheduler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAG7D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,UAAU,yBAAyB;IACjC,GAAG,EAAE,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IAC9B,IAAI,EAAE,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;CACnB;AAED,qBAAa,mBAAoB,SAAQ,YAAY,CAAC,yBAAyB,CAAC;IAKlE,OAAO,CAAC,QAAQ,CAAC,KAAK;IAJlC,OAAO,CAAC,SAAS,CAA+B;IAChD,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAC,6BAA6B,CAA6B;gBAE9C,KAAK,EAAE,eAAe;IAInD,KAAK,IAAI,IAAI;IAKb,OAAO,IAAI,IAAI;IASf,OAAO,CAAC,GAAG,GAAE,IAAiB,GAAG,IAAI;IAmBrC,OAAO,CAAC,gBAAgB;CAWzB"}
1
+ {"version":3,"file":"automation-scheduler.d.ts","sourceRoot":"","sources":["../src/automation-scheduler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAG7D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAG7C,UAAU,yBAAyB;IACjC,GAAG,EAAE,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IAC9B,IAAI,EAAE,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;CACnB;AAED,qBAAa,mBAAoB,SAAQ,YAAY,CAAC,yBAAyB,CAAC;IAKlE,OAAO,CAAC,QAAQ,CAAC,KAAK;IAJlC,OAAO,CAAC,SAAS,CAA+B;IAChD,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAC,6BAA6B,CAA6B;gBAE9C,KAAK,EAAE,eAAe;IAInD,KAAK,IAAI,IAAI;IAKb,OAAO,IAAI,IAAI;IASf,OAAO,CAAC,GAAG,GAAE,IAAiB,GAAG,IAAI;IAuBrC,OAAO,CAAC,gBAAgB;CAWzB"}
@@ -1,6 +1,7 @@
1
1
  import { EventEmitter } from "node:events";
2
2
  import { AUTOMATION_TICK_ALIGNMENT_DELAY_MS, MS_PER_MINUTE } from "./constants.js";
3
3
  import { cronMatchesDate, parseCronExpression } from "./cron-expression.js";
4
+ import { compileScheduleAll } from "./utils/compile-schedule.js";
4
5
  export class AutomationScheduler extends EventEmitter {
5
6
  store;
6
7
  tickTimer = null;
@@ -31,8 +32,13 @@ export class AutomationScheduler extends EventEmitter {
31
32
  for (const automation of automations) {
32
33
  if (!automation.enabled)
33
34
  continue;
34
- const parsed = parseCronExpression(automation.schedule);
35
- if (!parsed || !cronMatchesDate(parsed, now))
35
+ if (automation.lifecycle === "finished")
36
+ continue;
37
+ const matched = compileScheduleAll(automation.schedule).some((expression) => {
38
+ const parsed = parseCronExpression(expression);
39
+ return parsed !== null && cronMatchesDate(parsed, now);
40
+ });
41
+ if (!matched)
36
42
  continue;
37
43
  if (this.lastFiredMinuteByAutomationId.get(automation.id) === minuteKey)
38
44
  continue;
@@ -1 +1 @@
1
- {"version":3,"file":"automation-scheduler.js","sourceRoot":"","sources":["../src/automation-scheduler.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"automation-scheduler.js","sourceRoot":"","sources":["../src/automation-scheduler.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { Automation, AutomationLastRun, CreateAutomationInput, UpdateAutomationInput } from "./types.js";
1
+ import type { Automation, AutomationRunRecord, CreateAutomationInput, UpdateAutomationInput } from "./types.js";
2
2
  export declare class AutomationStore {
3
3
  private readonly filePath;
4
4
  private automations;
@@ -9,7 +9,10 @@ export declare class AutomationStore {
9
9
  create(input: CreateAutomationInput): Automation;
10
10
  update(id: string, patch: UpdateAutomationInput): Automation | null;
11
11
  remove(id: string): boolean;
12
- recordLastRun(id: string, lastRun: AutomationLastRun): Automation | null;
12
+ appendRun(id: string, record: AutomationRunRecord): Automation | null;
13
+ updateRun(id: string, runId: string, patch: Partial<AutomationRunRecord>): Automation | null;
14
+ incrementRunCount(id: string): Automation | null;
15
+ reset(id: string, clearHistory?: boolean): Automation | null;
13
16
  private load;
14
17
  private persist;
15
18
  }
@@ -1 +1 @@
1
- {"version":3,"file":"automation-store.d.ts","sourceRoot":"","sources":["../src/automation-store.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,UAAU,EACV,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACtB,MAAM,YAAY,CAAC;AAEpB,qBAAa,eAAe;IAGd,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAFrC,OAAO,CAAC,WAAW,CAAoB;gBAEV,QAAQ,EAAE,MAAM;IAI7C,IAAI,IAAI,UAAU,EAAE;IAIpB,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IAIlC,IAAI,IAAI,MAAM;IAId,MAAM,CAAC,KAAK,EAAE,qBAAqB,GAAG,UAAU;IAkBhD,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,qBAAqB,GAAG,UAAU,GAAG,IAAI;IAkBnE,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAQ3B,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,UAAU,GAAG,IAAI;IASxE,OAAO,CAAC,IAAI;IAmBZ,OAAO,CAAC,OAAO;CAOhB"}
1
+ {"version":3,"file":"automation-store.d.ts","sourceRoot":"","sources":["../src/automation-store.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,UAAU,EACV,mBAAmB,EAEnB,qBAAqB,EACrB,qBAAqB,EACtB,MAAM,YAAY,CAAC;AA2CpB,qBAAa,eAAe;IAGd,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAFrC,OAAO,CAAC,WAAW,CAAoB;gBAEV,QAAQ,EAAE,MAAM;IAI7C,IAAI,IAAI,UAAU,EAAE;IAIpB,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IAIlC,IAAI,IAAI,MAAM;IAId,MAAM,CAAC,KAAK,EAAE,qBAAqB,GAAG,UAAU;IAsBhD,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,qBAAqB,GAAG,UAAU,GAAG,IAAI;IA0BnE,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAS3B,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,mBAAmB,GAAG,UAAU,GAAG,IAAI;IAarE,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,mBAAmB,CAAC,GAAG,UAAU,GAAG,IAAI;IAiB5F,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IAiBhD,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,YAAY,UAAQ,GAAG,UAAU,GAAG,IAAI;IAiB1D,OAAO,CAAC,IAAI;IA8BZ,OAAO,CAAC,OAAO;CAOhB"}
@@ -1,8 +1,47 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import fs from "node:fs";
3
3
  import path from "node:path";
4
- import { AUTOMATIONS_FILE_VERSION } from "./constants.js";
5
- import { automationsFileSchema } from "./schemas.js";
4
+ import { AUTOMATION_RUN_HISTORY_CAP, AUTOMATIONS_FILE_VERSION } from "./constants.js";
5
+ import { automationsFileSchema, automationsFileV1Schema } from "./schemas.js";
6
+ import { normalizeScheduleInput } from "./utils/compile-schedule.js";
7
+ // A run is terminal once it has a definitive outcome; only those carry a
8
+ // finishedAt when migrated from a v1 lastRun.
9
+ const V1_TERMINAL_STATUSES = new Set(["completed", "failed", "missed"]);
10
+ // v1 stored a single `lastRun` and a raw cron string. Lift each into the v2
11
+ // shape: recognize the cron as a friendly preset where provably lossless (else
12
+ // keep it as {kind:"cron"}), seed runCount at 0 (so a migrated automation can
13
+ // never be spuriously "finished"), and fold lastRun into a one-entry history.
14
+ const migrateV1Automation = (v1) => {
15
+ const runs = v1.lastRun
16
+ ? [
17
+ {
18
+ runId: v1.lastRun.runId,
19
+ scheduledFor: v1.lastRun.at,
20
+ startedAt: v1.lastRun.at,
21
+ finishedAt: V1_TERMINAL_STATUSES.has(v1.lastRun.status) ? v1.lastRun.at : null,
22
+ status: v1.lastRun.status,
23
+ exitCode: v1.lastRun.exitCode,
24
+ trigger: "schedule",
25
+ countsTowardLimit: v1.lastRun.status !== "missed",
26
+ },
27
+ ]
28
+ : [];
29
+ return {
30
+ id: v1.id,
31
+ name: v1.name,
32
+ schedule: normalizeScheduleInput(v1.schedule),
33
+ cwd: v1.cwd,
34
+ command: v1.command,
35
+ enabled: v1.enabled,
36
+ limit: { kind: "forever" },
37
+ closeOnFinish: false,
38
+ runCount: 0,
39
+ lifecycle: "active",
40
+ runs,
41
+ createdAt: v1.createdAt,
42
+ updatedAt: v1.updatedAt,
43
+ };
44
+ };
6
45
  export class AutomationStore {
7
46
  filePath;
8
47
  automations = [];
@@ -24,13 +63,17 @@ export class AutomationStore {
24
63
  const automation = {
25
64
  id: randomUUID(),
26
65
  name: input.name,
27
- schedule: input.schedule,
66
+ schedule: normalizeScheduleInput(input.schedule),
28
67
  cwd: input.cwd,
29
68
  command: input.command,
30
69
  enabled: input.enabled ?? true,
70
+ limit: input.limit ?? { kind: "forever" },
71
+ closeOnFinish: input.closeOnFinish ?? false,
72
+ runCount: 0,
73
+ lifecycle: "active",
74
+ runs: [],
31
75
  createdAt: now,
32
76
  updatedAt: now,
33
- lastRun: null,
34
77
  };
35
78
  this.automations.push(automation);
36
79
  this.persist();
@@ -41,13 +84,20 @@ export class AutomationStore {
41
84
  if (index === -1)
42
85
  return null;
43
86
  const current = this.automations[index];
87
+ const limit = patch.limit !== undefined ? patch.limit : current.limit;
88
+ // "finished" is sticky: a PATCH never un-finishes (only reset does), but a
89
+ // PATCH that lowers the limit below the current count finishes immediately.
90
+ const lifecycle = limit.kind === "count" && current.runCount >= limit.max ? "finished" : current.lifecycle;
44
91
  const updated = {
45
92
  ...current,
46
93
  ...(patch.name !== undefined ? { name: patch.name } : {}),
47
- ...(patch.schedule !== undefined ? { schedule: patch.schedule } : {}),
94
+ ...(patch.schedule !== undefined ? { schedule: normalizeScheduleInput(patch.schedule) } : {}),
48
95
  ...(patch.cwd !== undefined ? { cwd: patch.cwd } : {}),
49
96
  ...(patch.command !== undefined ? { command: patch.command } : {}),
50
97
  ...(patch.enabled !== undefined ? { enabled: patch.enabled } : {}),
98
+ ...(patch.closeOnFinish !== undefined ? { closeOnFinish: patch.closeOnFinish } : {}),
99
+ limit,
100
+ lifecycle,
51
101
  updatedAt: Date.now(),
52
102
  };
53
103
  this.automations[index] = updated;
@@ -62,11 +112,65 @@ export class AutomationStore {
62
112
  this.persist();
63
113
  return true;
64
114
  }
65
- recordLastRun(id, lastRun) {
115
+ // Push a new run onto the newest-first history ring, trimming to the cap.
116
+ appendRun(id, record) {
66
117
  const index = this.automations.findIndex((automation) => automation.id === id);
67
118
  if (index === -1)
68
119
  return null;
69
- const updated = { ...this.automations[index], lastRun };
120
+ const current = this.automations[index];
121
+ const runs = [record, ...current.runs].slice(0, AUTOMATION_RUN_HISTORY_CAP);
122
+ const updated = { ...current, runs };
123
+ this.automations[index] = updated;
124
+ this.persist();
125
+ return updated;
126
+ }
127
+ // Advance an existing run in place (launched -> running -> completed/failed/
128
+ // missed). No-op (null) if the run has already aged out of the ring.
129
+ updateRun(id, runId, patch) {
130
+ const index = this.automations.findIndex((automation) => automation.id === id);
131
+ if (index === -1)
132
+ return null;
133
+ const current = this.automations[index];
134
+ const runIndex = current.runs.findIndex((run) => run.runId === runId);
135
+ if (runIndex === -1)
136
+ return null;
137
+ const runs = current.runs.map((run, position) => position === runIndex ? { ...run, ...patch } : run);
138
+ const updated = { ...current, runs };
139
+ this.automations[index] = updated;
140
+ this.persist();
141
+ return updated;
142
+ }
143
+ // Count a launched run toward the limit, flipping to "finished" when the
144
+ // budget is exhausted. Only scheduled launches call this.
145
+ incrementRunCount(id) {
146
+ const index = this.automations.findIndex((automation) => automation.id === id);
147
+ if (index === -1)
148
+ return null;
149
+ const current = this.automations[index];
150
+ const runCount = current.runCount + 1;
151
+ const lifecycle = current.limit.kind === "count" && runCount >= current.limit.max
152
+ ? "finished"
153
+ : current.lifecycle;
154
+ const updated = { ...current, runCount, lifecycle };
155
+ this.automations[index] = updated;
156
+ this.persist();
157
+ return updated;
158
+ }
159
+ // The only un-finish path: zero the count, reactivate, re-enable. History is
160
+ // preserved unless explicitly cleared.
161
+ reset(id, clearHistory = false) {
162
+ const index = this.automations.findIndex((automation) => automation.id === id);
163
+ if (index === -1)
164
+ return null;
165
+ const current = this.automations[index];
166
+ const updated = {
167
+ ...current,
168
+ runCount: 0,
169
+ lifecycle: "active",
170
+ enabled: true,
171
+ runs: clearHistory ? [] : current.runs,
172
+ updatedAt: Date.now(),
173
+ };
70
174
  this.automations[index] = updated;
71
175
  this.persist();
72
176
  return updated;
@@ -79,15 +183,26 @@ export class AutomationStore {
79
183
  catch {
80
184
  return;
81
185
  }
186
+ let json;
82
187
  try {
83
- const parsed = automationsFileSchema.safeParse(JSON.parse(raw));
84
- if (parsed.success) {
85
- this.automations = parsed.data.automations;
86
- return;
87
- }
188
+ json = JSON.parse(raw);
88
189
  }
89
190
  catch {
90
- /* malformed JSON falls through to the warning below */
191
+ console.warn(`automations file invalid; starting with an empty list (${this.filePath})`);
192
+ return;
193
+ }
194
+ // Fast path: already v2.
195
+ const v2 = automationsFileSchema.safeParse(json);
196
+ if (v2.success) {
197
+ this.automations = v2.data.automations;
198
+ return;
199
+ }
200
+ // Migrate v1 -> v2 once, then persist so later loads hit the fast path.
201
+ const v1 = automationsFileV1Schema.safeParse(json);
202
+ if (v1.success) {
203
+ this.automations = v1.data.automations.map(migrateV1Automation);
204
+ this.persist();
205
+ return;
91
206
  }
92
207
  console.warn(`automations file invalid; starting with an empty list (${this.filePath})`);
93
208
  }
@@ -1 +1 @@
1
- {"version":3,"file":"automation-store.js","sourceRoot":"","sources":["../src/automation-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"automation-store.js","sourceRoot":"","sources":["../src/automation-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Persistent Chrome DevTools Protocol client for opening background tabs.
3
+ *
4
+ * One WebSocket to a debug-enabled Chromium browser, established once and kept
5
+ * open for the daemon's lifetime, so the user only clears the browser's
6
+ * remote-debugging prompt a single time (at `start`) instead of on every run.
7
+ *
8
+ * `Target.createTarget({ background: true })` creates the tab *behind* the
9
+ * active one — a true background tab that never steals focus. The `background`
10
+ * flag is the deterministic switch; without it the browser foregrounds the tab.
11
+ *
12
+ * Connection logic is ported from the sibling browser-harness-js CDP SDK. If
13
+ * the socket drops (browser quit/restarted) it transparently re-detects and
14
+ * reconnects on the next open; when no browser is reachable, `openBackgroundTab`
15
+ * resolves null so the caller can fall back to the OS opener.
16
+ */
17
+ import { type DetectedBrowser } from "./detect-chromium.js";
18
+ export type CdpClientOptions = {
19
+ /** Override browser detection (tests). Defaults to detectChromiumBrowsers. */
20
+ detect?: () => Promise<DetectedBrowser[]>;
21
+ /** Per-candidate WS-open timeout. */
22
+ connectTimeoutMs?: number;
23
+ /** Per-CDP-call timeout. */
24
+ callTimeoutMs?: number;
25
+ };
26
+ export declare class CdpClient {
27
+ private ws;
28
+ private connecting;
29
+ private nextId;
30
+ private readonly pending;
31
+ private readonly detect;
32
+ private readonly connectTimeoutMs;
33
+ private readonly callTimeoutMs;
34
+ /** Serializes closeTab() so concurrent closes don't orphan tabs. */
35
+ private closeQueue;
36
+ /** The browser the live socket is attached to, for diagnostics. */
37
+ connectedBrowser: DetectedBrowser | undefined;
38
+ constructor(options?: CdpClientOptions);
39
+ isConnected(): boolean;
40
+ /**
41
+ * Ensure a live socket, detecting and attaching to the most-recently-launched
42
+ * debug-enabled browser. Concurrent callers ride the same in-flight attempt.
43
+ * Throws when no browser is reachable.
44
+ */
45
+ connect(): Promise<void>;
46
+ private establish;
47
+ private openSocket;
48
+ /** Reject in-flight calls and drop the socket when it's the active one. */
49
+ private failPending;
50
+ private onMessage;
51
+ private call;
52
+ /**
53
+ * Open `url` as a background tab. Returns the new target's id on success
54
+ * (used later to close the tab), or null when no browser is reachable
55
+ * (connect failed) or the call errored — the caller's cue to fall back to the
56
+ * OS opener. One reconnect is attempted if the persistent socket has gone
57
+ * stale. Never throws.
58
+ */
59
+ openBackgroundTab(url: string): Promise<string | null>;
60
+ /**
61
+ * Close a tab previously opened by `openBackgroundTab`. Mirrors
62
+ * browser-harness-js: drive the browser's own close path via
63
+ * `window.close()` (reliable on forks like Dia/Arc that ignore a bare
64
+ * `Target.closeTarget`), then tear down the CDP target. Best-effort and never
65
+ * throws — a missing browser/target is treated as already closed.
66
+ *
67
+ * Closes are SERIALIZED through `closeQueue`: each waits for the previous to
68
+ * finish. Without this, concurrent closes over the one shared socket can
69
+ * interleave — a `Target.closeTarget` detaching a session before another
70
+ * tab's `window.close()` has taken effect in the browser — which leaves stale
71
+ * tabs behind. (Same reason browser-harness-js serializes.)
72
+ */
73
+ closeTab(targetId: string): Promise<void>;
74
+ close(): void;
75
+ }
76
+ //# sourceMappingURL=cdp-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cdp-client.d.ts","sourceRoot":"","sources":["../../src/cdp/cdp-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAA0B,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAapF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;IAC1C,qCAAqC;IACrC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,4BAA4B;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,qBAAa,SAAS;IACpB,OAAO,CAAC,EAAE,CAAwB;IAClC,OAAO,CAAC,UAAU,CAA4B;IAC9C,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA8B;IACtD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmC;IAC1D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,oEAAoE;IACpE,OAAO,CAAC,UAAU,CAAoC;IACtD,mEAAmE;IACnE,gBAAgB,EAAE,eAAe,GAAG,SAAS,CAAC;gBAElC,OAAO,GAAE,gBAAqB;IAM1C,WAAW,IAAI,OAAO;IAItB;;;;OAIG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;YAUhB,SAAS;IAkBvB,OAAO,CAAC,UAAU;IA2ClB,2EAA2E;IAC3E,OAAO,CAAC,WAAW;IAQnB,OAAO,CAAC,SAAS;IAgBjB,OAAO,CAAC,IAAI;IAiCZ;;;;;;OAMG;IACG,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAsB5D;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmCzC,KAAK,IAAI,IAAI;CASd"}
@@ -0,0 +1,263 @@
1
+ /**
2
+ * Persistent Chrome DevTools Protocol client for opening background tabs.
3
+ *
4
+ * One WebSocket to a debug-enabled Chromium browser, established once and kept
5
+ * open for the daemon's lifetime, so the user only clears the browser's
6
+ * remote-debugging prompt a single time (at `start`) instead of on every run.
7
+ *
8
+ * `Target.createTarget({ background: true })` creates the tab *behind* the
9
+ * active one — a true background tab that never steals focus. The `background`
10
+ * flag is the deterministic switch; without it the browser foregrounds the tab.
11
+ *
12
+ * Connection logic is ported from the sibling browser-harness-js CDP SDK. If
13
+ * the socket drops (browser quit/restarted) it transparently re-detects and
14
+ * reconnects on the next open; when no browser is reachable, `openBackgroundTab`
15
+ * resolves null so the caller can fall back to the OS opener.
16
+ */
17
+ import { detectChromiumBrowsers } from "./detect-chromium.js";
18
+ const WS_OPEN = 1; // WebSocket.OPEN
19
+ const DEFAULT_CONNECT_TIMEOUT_MS = 5_000;
20
+ const DEFAULT_CALL_TIMEOUT_MS = 5_000;
21
+ // Give the browser a beat to process window.close() before tearing down the
22
+ // CDP target — some Chromium forks (Dia, Arc) leave the tab in the strip
23
+ // otherwise (see browser-harness-js closeTab).
24
+ const CLOSE_SETTLE_MS = 100;
25
+ export class CdpClient {
26
+ ws;
27
+ connecting;
28
+ nextId = 1;
29
+ pending = new Map();
30
+ detect;
31
+ connectTimeoutMs;
32
+ callTimeoutMs;
33
+ /** Serializes closeTab() so concurrent closes don't orphan tabs. */
34
+ closeQueue = Promise.resolve();
35
+ /** The browser the live socket is attached to, for diagnostics. */
36
+ connectedBrowser;
37
+ constructor(options = {}) {
38
+ this.detect = options.detect ?? detectChromiumBrowsers;
39
+ this.connectTimeoutMs = options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS;
40
+ this.callTimeoutMs = options.callTimeoutMs ?? DEFAULT_CALL_TIMEOUT_MS;
41
+ }
42
+ isConnected() {
43
+ return this.ws?.readyState === WS_OPEN;
44
+ }
45
+ /**
46
+ * Ensure a live socket, detecting and attaching to the most-recently-launched
47
+ * debug-enabled browser. Concurrent callers ride the same in-flight attempt.
48
+ * Throws when no browser is reachable.
49
+ */
50
+ async connect() {
51
+ if (this.isConnected())
52
+ return;
53
+ if (!this.connecting) {
54
+ this.connecting = this.establish().finally(() => {
55
+ this.connecting = undefined;
56
+ });
57
+ }
58
+ return this.connecting;
59
+ }
60
+ async establish() {
61
+ const browsers = await this.detect();
62
+ if (browsers.length === 0) {
63
+ throw new Error("no debug-enabled Chromium browser detected");
64
+ }
65
+ const errors = [];
66
+ for (const browser of browsers) {
67
+ try {
68
+ await this.openSocket(browser.wsUrl);
69
+ this.connectedBrowser = browser;
70
+ return;
71
+ }
72
+ catch (error) {
73
+ errors.push(`${browser.name}: ${error instanceof Error ? error.message : String(error)}`);
74
+ }
75
+ }
76
+ throw new Error(`no detected browser accepted a connection (${errors.join("; ")})`);
77
+ }
78
+ openSocket(wsUrl) {
79
+ return new Promise((resolve, reject) => {
80
+ const ws = new WebSocket(wsUrl);
81
+ let settled = false;
82
+ const timer = setTimeout(() => {
83
+ if (settled)
84
+ return;
85
+ settled = true;
86
+ try {
87
+ ws.close();
88
+ }
89
+ catch {
90
+ /* ignore */
91
+ }
92
+ reject(new Error(`timed out after ${this.connectTimeoutMs}ms`));
93
+ }, this.connectTimeoutMs);
94
+ ws.addEventListener("open", () => {
95
+ if (settled)
96
+ return;
97
+ settled = true;
98
+ clearTimeout(timer);
99
+ this.ws = ws;
100
+ resolve();
101
+ });
102
+ ws.addEventListener("message", (event) => this.onMessage(String(event.data)));
103
+ ws.addEventListener("error", () => {
104
+ if (settled) {
105
+ this.failPending(ws, new Error("CDP websocket error"));
106
+ return;
107
+ }
108
+ settled = true;
109
+ clearTimeout(timer);
110
+ reject(new Error("websocket error (likely 403 or port closed)"));
111
+ });
112
+ ws.addEventListener("close", () => {
113
+ clearTimeout(timer);
114
+ this.failPending(ws, new Error("CDP websocket closed"));
115
+ if (!settled) {
116
+ settled = true;
117
+ reject(new Error("websocket closed before open"));
118
+ }
119
+ });
120
+ });
121
+ }
122
+ /** Reject in-flight calls and drop the socket when it's the active one. */
123
+ failPending(ws, error) {
124
+ if (this.ws !== ws)
125
+ return;
126
+ this.ws = undefined;
127
+ this.connectedBrowser = undefined;
128
+ for (const pending of this.pending.values())
129
+ pending.reject(error);
130
+ this.pending.clear();
131
+ }
132
+ onMessage(raw) {
133
+ let message;
134
+ try {
135
+ message = JSON.parse(raw);
136
+ }
137
+ catch {
138
+ return;
139
+ }
140
+ if (typeof message.id !== "number")
141
+ return; // an event, not a reply
142
+ const pending = this.pending.get(message.id);
143
+ if (!pending)
144
+ return;
145
+ this.pending.delete(message.id);
146
+ if (message.error)
147
+ pending.reject(new Error(`CDP error: ${message.error.message ?? "unknown"}`));
148
+ else
149
+ pending.resolve(message.result);
150
+ }
151
+ call(method, params, sessionId) {
152
+ const ws = this.ws;
153
+ if (!ws || ws.readyState !== WS_OPEN) {
154
+ return Promise.reject(new Error("CDP not connected"));
155
+ }
156
+ const id = this.nextId++;
157
+ return new Promise((resolve, reject) => {
158
+ const timer = setTimeout(() => {
159
+ this.pending.delete(id);
160
+ reject(new Error(`CDP call timed out after ${this.callTimeoutMs}ms`));
161
+ }, this.callTimeoutMs);
162
+ this.pending.set(id, {
163
+ resolve: (value) => {
164
+ clearTimeout(timer);
165
+ resolve(value);
166
+ },
167
+ reject: (error) => {
168
+ clearTimeout(timer);
169
+ reject(error);
170
+ },
171
+ });
172
+ // flatten:true routes session-scoped replies back over this one socket,
173
+ // tagged by id, so the pending map matches them with no extra envelope.
174
+ ws.send(JSON.stringify(sessionId ? { id, method, params, sessionId } : { id, method, params }));
175
+ });
176
+ }
177
+ /**
178
+ * Open `url` as a background tab. Returns the new target's id on success
179
+ * (used later to close the tab), or null when no browser is reachable
180
+ * (connect failed) or the call errored — the caller's cue to fall back to the
181
+ * OS opener. One reconnect is attempted if the persistent socket has gone
182
+ * stale. Never throws.
183
+ */
184
+ async openBackgroundTab(url) {
185
+ for (let attempt = 0; attempt < 2; attempt++) {
186
+ try {
187
+ await this.connect();
188
+ }
189
+ catch {
190
+ return null; // no reachable browser — fall back
191
+ }
192
+ try {
193
+ const result = (await this.call("Target.createTarget", { url, background: true }));
194
+ return typeof result?.targetId === "string" ? result.targetId : null;
195
+ }
196
+ catch {
197
+ // Socket likely went stale between runs (browser restarted). Drop it and
198
+ // let the next loop iteration re-detect and reconnect once.
199
+ this.ws = undefined;
200
+ this.connectedBrowser = undefined;
201
+ }
202
+ }
203
+ return null;
204
+ }
205
+ /**
206
+ * Close a tab previously opened by `openBackgroundTab`. Mirrors
207
+ * browser-harness-js: drive the browser's own close path via
208
+ * `window.close()` (reliable on forks like Dia/Arc that ignore a bare
209
+ * `Target.closeTarget`), then tear down the CDP target. Best-effort and never
210
+ * throws — a missing browser/target is treated as already closed.
211
+ *
212
+ * Closes are SERIALIZED through `closeQueue`: each waits for the previous to
213
+ * finish. Without this, concurrent closes over the one shared socket can
214
+ * interleave — a `Target.closeTarget` detaching a session before another
215
+ * tab's `window.close()` has taken effect in the browser — which leaves stale
216
+ * tabs behind. (Same reason browser-harness-js serializes.)
217
+ */
218
+ closeTab(targetId) {
219
+ const doClose = async () => {
220
+ if (!this.isConnected())
221
+ return;
222
+ try {
223
+ const attached = (await this.call("Target.attachToTarget", {
224
+ targetId,
225
+ flatten: true,
226
+ }));
227
+ if (attached?.sessionId) {
228
+ try {
229
+ await this.call("Runtime.evaluate", { expression: "window.close()" }, attached.sessionId);
230
+ }
231
+ catch {
232
+ /* tab may already be navigating/closing */
233
+ }
234
+ await new Promise((resolve) => setTimeout(resolve, CLOSE_SETTLE_MS));
235
+ }
236
+ }
237
+ catch {
238
+ /* attach failed (already gone, or fork without attach) — try closeTarget */
239
+ }
240
+ try {
241
+ await this.call("Target.closeTarget", { targetId });
242
+ }
243
+ catch {
244
+ /* already closed */
245
+ }
246
+ };
247
+ // Chain onto the queue with doClose as both handlers so a failed close
248
+ // never wedges the chain. Callers get the tail (fine for fire-and-forget).
249
+ this.closeQueue = this.closeQueue.then(doClose, doClose);
250
+ return this.closeQueue;
251
+ }
252
+ close() {
253
+ try {
254
+ this.ws?.close();
255
+ }
256
+ catch {
257
+ /* ignore */
258
+ }
259
+ this.ws = undefined;
260
+ this.connectedBrowser = undefined;
261
+ }
262
+ }
263
+ //# sourceMappingURL=cdp-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cdp-client.js","sourceRoot":"","sources":["../../src/cdp/cdp-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,sBAAsB,EAAwB,MAAM,sBAAsB,CAAC;AAEpF,MAAM,OAAO,GAAG,CAAC,CAAC,CAAC,iBAAiB;AAEpC,MAAM,0BAA0B,GAAG,KAAK,CAAC;AACzC,MAAM,uBAAuB,GAAG,KAAK,CAAC;AACtC,4EAA4E;AAC5E,yEAAyE;AACzE,+CAA+C;AAC/C,MAAM,eAAe,GAAG,GAAG,CAAC;AAa5B,MAAM,OAAO,SAAS;IACZ,EAAE,CAAwB;IAC1B,UAAU,CAA4B;IACtC,MAAM,GAAG,CAAC,CAAC;IACF,OAAO,GAAG,IAAI,GAAG,EAAmB,CAAC;IACrC,MAAM,CAAmC;IACzC,gBAAgB,CAAS;IACzB,aAAa,CAAS;IACvC,oEAAoE;IAC5D,UAAU,GAAkB,OAAO,CAAC,OAAO,EAAE,CAAC;IACtD,mEAAmE;IACnE,gBAAgB,CAA8B;IAE9C,YAAY,UAA4B,EAAE;QACxC,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,sBAAsB,CAAC;QACvD,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,IAAI,0BAA0B,CAAC;QAC/E,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,uBAAuB,CAAC;IACxE,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC,EAAE,EAAE,UAAU,KAAK,OAAO,CAAC;IACzC,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,OAAO;QACX,IAAI,IAAI,CAAC,WAAW,EAAE;YAAE,OAAO;QAC/B,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;YACrB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE;gBAC9C,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;YAC9B,CAAC,CAAC,CAAC;QACL,CAAC;QACD,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAEO,KAAK,CAAC,SAAS;QACrB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;QACrC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;QAChE,CAAC;QACD,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;gBACrC,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC;gBAChC,OAAO;YACT,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YAC5F,CAAC;QACH,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,8CAA8C,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACtF,CAAC;IAEO,UAAU,CAAC,KAAa;QAC9B,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC3C,MAAM,EAAE,GAAG,IAAI,SAAS,CAAC,KAAK,CAAC,CAAC;YAChC,IAAI,OAAO,GAAG,KAAK,CAAC;YACpB,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC5B,IAAI,OAAO;oBAAE,OAAO;gBACpB,OAAO,GAAG,IAAI,CAAC;gBACf,IAAI,CAAC;oBACH,EAAE,CAAC,KAAK,EAAE,CAAC;gBACb,CAAC;gBAAC,MAAM,CAAC;oBACP,YAAY;gBACd,CAAC;gBACD,MAAM,CAAC,IAAI,KAAK,CAAC,mBAAmB,IAAI,CAAC,gBAAgB,IAAI,CAAC,CAAC,CAAC;YAClE,CAAC,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAC;YAE1B,EAAE,CAAC,gBAAgB,CAAC,MAAM,EAAE,GAAG,EAAE;gBAC/B,IAAI,OAAO;oBAAE,OAAO;gBACpB,OAAO,GAAG,IAAI,CAAC;gBACf,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;gBACb,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC,CAAC;YACH,EAAE,CAAC,gBAAgB,CAAC,SAAS,EAAE,CAAC,KAAmB,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC5F,EAAE,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE;gBAChC,IAAI,OAAO,EAAE,CAAC;oBACZ,IAAI,CAAC,WAAW,CAAC,EAAE,EAAE,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC,CAAC;oBACvD,OAAO;gBACT,CAAC;gBACD,OAAO,GAAG,IAAI,CAAC;gBACf,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,MAAM,CAAC,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC,CAAC;YACnE,CAAC,CAAC,CAAC;YACH,EAAE,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE;gBAChC,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,IAAI,CAAC,WAAW,CAAC,EAAE,EAAE,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC,CAAC;gBACxD,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,OAAO,GAAG,IAAI,CAAC;oBACf,MAAM,CAAC,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC,CAAC;gBACpD,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED,2EAA2E;IACnE,WAAW,CAAC,EAAa,EAAE,KAAY;QAC7C,IAAI,IAAI,CAAC,EAAE,KAAK,EAAE;YAAE,OAAO;QAC3B,IAAI,CAAC,EAAE,GAAG,SAAS,CAAC;QACpB,IAAI,CAAC,gBAAgB,GAAG,SAAS,CAAC;QAClC,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE;YAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACnE,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;IAEO,SAAS,CAAC,GAAW;QAC3B,IAAI,OAAwE,CAAC;QAC7E,IAAI,CAAC;YACH,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC5B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;QACT,CAAC;QACD,IAAI,OAAO,OAAO,CAAC,EAAE,KAAK,QAAQ;YAAE,OAAO,CAAC,wBAAwB;QACpE,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC7C,IAAI,CAAC,OAAO;YAAE,OAAO;QACrB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAChC,IAAI,OAAO,CAAC,KAAK;YACf,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,cAAc,OAAO,CAAC,KAAK,CAAC,OAAO,IAAI,SAAS,EAAE,CAAC,CAAC,CAAC;;YAC3E,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACvC,CAAC;IAEO,IAAI,CACV,MAAc,EACd,MAA+B,EAC/B,SAAkB;QAElB,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC;QACnB,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,UAAU,KAAK,OAAO,EAAE,CAAC;YACrC,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC,CAAC;QACxD,CAAC;QACD,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QACzB,OAAO,IAAI,OAAO,CAAU,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC9C,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC5B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBACxB,MAAM,CAAC,IAAI,KAAK,CAAC,4BAA4B,IAAI,CAAC,aAAa,IAAI,CAAC,CAAC,CAAC;YACxE,CAAC,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;YACvB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE;gBACnB,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;oBACjB,YAAY,CAAC,KAAK,CAAC,CAAC;oBACpB,OAAO,CAAC,KAAK,CAAC,CAAC;gBACjB,CAAC;gBACD,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;oBAChB,YAAY,CAAC,KAAK,CAAC,CAAC;oBACpB,MAAM,CAAC,KAAK,CAAC,CAAC;gBAChB,CAAC;aACF,CAAC,CAAC;YACH,wEAAwE;YACxE,wEAAwE;YACxE,EAAE,CAAC,IAAI,CACL,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CACvF,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,iBAAiB,CAAC,GAAW;QACjC,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC;YAC7C,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;YACvB,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,IAAI,CAAC,CAAC,mCAAmC;YAClD,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,qBAAqB,EAAE,EAAE,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAEhF,CAAC;gBACF,OAAO,OAAO,MAAM,EAAE,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;YACvE,CAAC;YAAC,MAAM,CAAC;gBACP,yEAAyE;gBACzE,4DAA4D;gBAC5D,IAAI,CAAC,EAAE,GAAG,SAAS,CAAC;gBACpB,IAAI,CAAC,gBAAgB,GAAG,SAAS,CAAC;YACpC,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,QAAgB;QACvB,MAAM,OAAO,GAAG,KAAK,IAAI,EAAE;YACzB,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE;gBAAE,OAAO;YAChC,IAAI,CAAC;gBACH,MAAM,QAAQ,GAAG,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,uBAAuB,EAAE;oBACzD,QAAQ;oBACR,OAAO,EAAE,IAAI;iBACd,CAAC,CAA2B,CAAC;gBAC9B,IAAI,QAAQ,EAAE,SAAS,EAAE,CAAC;oBACxB,IAAI,CAAC;wBACH,MAAM,IAAI,CAAC,IAAI,CACb,kBAAkB,EAClB,EAAE,UAAU,EAAE,gBAAgB,EAAE,EAChC,QAAQ,CAAC,SAAS,CACnB,CAAC;oBACJ,CAAC;oBAAC,MAAM,CAAC;wBACP,2CAA2C;oBAC7C,CAAC;oBACD,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC;gBACvE,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,4EAA4E;YAC9E,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;YACtD,CAAC;YAAC,MAAM,CAAC;gBACP,oBAAoB;YACtB,CAAC;QACH,CAAC,CAAC;QACF,uEAAuE;QACvE,2EAA2E;QAC3E,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACzD,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED,KAAK;QACH,IAAI,CAAC;YACH,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC;QACnB,CAAC;QAAC,MAAM,CAAC;YACP,YAAY;QACd,CAAC;QACD,IAAI,CAAC,EAAE,GAAG,SAAS,CAAC;QACpB,IAAI,CAAC,gBAAgB,GAAG,SAAS,CAAC;IACpC,CAAC;CACF"}