@monotykamary/localterm-server 1.17.1 → 1.18.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 +125 -13
  7. package/dist/automation-store.js.map +1 -1
  8. package/dist/cdp/cdp-client.d.ts +59 -0
  9. package/dist/cdp/cdp-client.d.ts.map +1 -0
  10. package/dist/cdp/cdp-client.js +207 -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 +6 -0
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +155 -21
  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 +626 -16
  37. package/dist/schemas.d.ts.map +1 -1
  38. package/dist/schemas.js +179 -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;AA0CpB,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;IAqBhD,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,qBAAqB,GAAG,UAAU,GAAG,IAAI;IAyBnE,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,46 @@
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
+ runCount: 0,
38
+ lifecycle: "active",
39
+ runs,
40
+ createdAt: v1.createdAt,
41
+ updatedAt: v1.updatedAt,
42
+ };
43
+ };
6
44
  export class AutomationStore {
7
45
  filePath;
8
46
  automations = [];
@@ -24,13 +62,16 @@ export class AutomationStore {
24
62
  const automation = {
25
63
  id: randomUUID(),
26
64
  name: input.name,
27
- schedule: input.schedule,
65
+ schedule: normalizeScheduleInput(input.schedule),
28
66
  cwd: input.cwd,
29
67
  command: input.command,
30
68
  enabled: input.enabled ?? true,
69
+ limit: input.limit ?? { kind: "forever" },
70
+ runCount: 0,
71
+ lifecycle: "active",
72
+ runs: [],
31
73
  createdAt: now,
32
74
  updatedAt: now,
33
- lastRun: null,
34
75
  };
35
76
  this.automations.push(automation);
36
77
  this.persist();
@@ -41,13 +82,19 @@ export class AutomationStore {
41
82
  if (index === -1)
42
83
  return null;
43
84
  const current = this.automations[index];
85
+ const limit = patch.limit !== undefined ? patch.limit : current.limit;
86
+ // "finished" is sticky: a PATCH never un-finishes (only reset does), but a
87
+ // PATCH that lowers the limit below the current count finishes immediately.
88
+ const lifecycle = limit.kind === "count" && current.runCount >= limit.max ? "finished" : current.lifecycle;
44
89
  const updated = {
45
90
  ...current,
46
91
  ...(patch.name !== undefined ? { name: patch.name } : {}),
47
- ...(patch.schedule !== undefined ? { schedule: patch.schedule } : {}),
92
+ ...(patch.schedule !== undefined ? { schedule: normalizeScheduleInput(patch.schedule) } : {}),
48
93
  ...(patch.cwd !== undefined ? { cwd: patch.cwd } : {}),
49
94
  ...(patch.command !== undefined ? { command: patch.command } : {}),
50
95
  ...(patch.enabled !== undefined ? { enabled: patch.enabled } : {}),
96
+ limit,
97
+ lifecycle,
51
98
  updatedAt: Date.now(),
52
99
  };
53
100
  this.automations[index] = updated;
@@ -62,11 +109,65 @@ export class AutomationStore {
62
109
  this.persist();
63
110
  return true;
64
111
  }
65
- recordLastRun(id, lastRun) {
112
+ // Push a new run onto the newest-first history ring, trimming to the cap.
113
+ appendRun(id, record) {
66
114
  const index = this.automations.findIndex((automation) => automation.id === id);
67
115
  if (index === -1)
68
116
  return null;
69
- const updated = { ...this.automations[index], lastRun };
117
+ const current = this.automations[index];
118
+ const runs = [record, ...current.runs].slice(0, AUTOMATION_RUN_HISTORY_CAP);
119
+ const updated = { ...current, runs };
120
+ this.automations[index] = updated;
121
+ this.persist();
122
+ return updated;
123
+ }
124
+ // Advance an existing run in place (launched -> running -> completed/failed/
125
+ // missed). No-op (null) if the run has already aged out of the ring.
126
+ updateRun(id, runId, patch) {
127
+ const index = this.automations.findIndex((automation) => automation.id === id);
128
+ if (index === -1)
129
+ return null;
130
+ const current = this.automations[index];
131
+ const runIndex = current.runs.findIndex((run) => run.runId === runId);
132
+ if (runIndex === -1)
133
+ return null;
134
+ const runs = current.runs.map((run, position) => position === runIndex ? { ...run, ...patch } : run);
135
+ const updated = { ...current, runs };
136
+ this.automations[index] = updated;
137
+ this.persist();
138
+ return updated;
139
+ }
140
+ // Count a launched run toward the limit, flipping to "finished" when the
141
+ // budget is exhausted. Only scheduled launches call this.
142
+ incrementRunCount(id) {
143
+ const index = this.automations.findIndex((automation) => automation.id === id);
144
+ if (index === -1)
145
+ return null;
146
+ const current = this.automations[index];
147
+ const runCount = current.runCount + 1;
148
+ const lifecycle = current.limit.kind === "count" && runCount >= current.limit.max
149
+ ? "finished"
150
+ : current.lifecycle;
151
+ const updated = { ...current, runCount, lifecycle };
152
+ this.automations[index] = updated;
153
+ this.persist();
154
+ return updated;
155
+ }
156
+ // The only un-finish path: zero the count, reactivate, re-enable. History is
157
+ // preserved unless explicitly cleared.
158
+ reset(id, clearHistory = false) {
159
+ const index = this.automations.findIndex((automation) => automation.id === id);
160
+ if (index === -1)
161
+ return null;
162
+ const current = this.automations[index];
163
+ const updated = {
164
+ ...current,
165
+ runCount: 0,
166
+ lifecycle: "active",
167
+ enabled: true,
168
+ runs: clearHistory ? [] : current.runs,
169
+ updatedAt: Date.now(),
170
+ };
70
171
  this.automations[index] = updated;
71
172
  this.persist();
72
173
  return updated;
@@ -79,15 +180,26 @@ export class AutomationStore {
79
180
  catch {
80
181
  return;
81
182
  }
183
+ let json;
82
184
  try {
83
- const parsed = automationsFileSchema.safeParse(JSON.parse(raw));
84
- if (parsed.success) {
85
- this.automations = parsed.data.automations;
86
- return;
87
- }
185
+ json = JSON.parse(raw);
88
186
  }
89
187
  catch {
90
- /* malformed JSON falls through to the warning below */
188
+ console.warn(`automations file invalid; starting with an empty list (${this.filePath})`);
189
+ return;
190
+ }
191
+ // Fast path: already v2.
192
+ const v2 = automationsFileSchema.safeParse(json);
193
+ if (v2.success) {
194
+ this.automations = v2.data.automations;
195
+ return;
196
+ }
197
+ // Migrate v1 -> v2 once, then persist so later loads hit the fast path.
198
+ const v1 = automationsFileV1Schema.safeParse(json);
199
+ if (v1.success) {
200
+ this.automations = v1.data.automations.map(migrateV1Automation);
201
+ this.persist();
202
+ return;
91
203
  }
92
204
  console.warn(`automations file invalid; starting with an empty list (${this.filePath})`);
93
205
  }
@@ -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,59 @@
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 false 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
+ /** The browser the live socket is attached to, for diagnostics. */
35
+ connectedBrowser: DetectedBrowser | undefined;
36
+ constructor(options?: CdpClientOptions);
37
+ isConnected(): boolean;
38
+ /**
39
+ * Ensure a live socket, detecting and attaching to the most-recently-launched
40
+ * debug-enabled browser. Concurrent callers ride the same in-flight attempt.
41
+ * Throws when no browser is reachable.
42
+ */
43
+ connect(): Promise<void>;
44
+ private establish;
45
+ private openSocket;
46
+ /** Reject in-flight calls and drop the socket when it's the active one. */
47
+ private failPending;
48
+ private onMessage;
49
+ private call;
50
+ /**
51
+ * Open `url` as a background tab. Returns true on success, false when no
52
+ * browser is reachable (connect failed) or the call errored — the caller's
53
+ * cue to fall back to the OS opener. One reconnect is attempted if the
54
+ * persistent socket has gone stale. Never throws.
55
+ */
56
+ openBackgroundTab(url: string): Promise<boolean>;
57
+ close(): void;
58
+ }
59
+ //# 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;AASpF,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,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;IAyBZ;;;;;OAKG;IACG,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAsBtD,KAAK,IAAI,IAAI;CASd"}
@@ -0,0 +1,207 @@
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 false 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
+ export class CdpClient {
22
+ ws;
23
+ connecting;
24
+ nextId = 1;
25
+ pending = new Map();
26
+ detect;
27
+ connectTimeoutMs;
28
+ callTimeoutMs;
29
+ /** The browser the live socket is attached to, for diagnostics. */
30
+ connectedBrowser;
31
+ constructor(options = {}) {
32
+ this.detect = options.detect ?? detectChromiumBrowsers;
33
+ this.connectTimeoutMs = options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS;
34
+ this.callTimeoutMs = options.callTimeoutMs ?? DEFAULT_CALL_TIMEOUT_MS;
35
+ }
36
+ isConnected() {
37
+ return this.ws?.readyState === WS_OPEN;
38
+ }
39
+ /**
40
+ * Ensure a live socket, detecting and attaching to the most-recently-launched
41
+ * debug-enabled browser. Concurrent callers ride the same in-flight attempt.
42
+ * Throws when no browser is reachable.
43
+ */
44
+ async connect() {
45
+ if (this.isConnected())
46
+ return;
47
+ if (!this.connecting) {
48
+ this.connecting = this.establish().finally(() => {
49
+ this.connecting = undefined;
50
+ });
51
+ }
52
+ return this.connecting;
53
+ }
54
+ async establish() {
55
+ const browsers = await this.detect();
56
+ if (browsers.length === 0) {
57
+ throw new Error("no debug-enabled Chromium browser detected");
58
+ }
59
+ const errors = [];
60
+ for (const browser of browsers) {
61
+ try {
62
+ await this.openSocket(browser.wsUrl);
63
+ this.connectedBrowser = browser;
64
+ return;
65
+ }
66
+ catch (error) {
67
+ errors.push(`${browser.name}: ${error instanceof Error ? error.message : String(error)}`);
68
+ }
69
+ }
70
+ throw new Error(`no detected browser accepted a connection (${errors.join("; ")})`);
71
+ }
72
+ openSocket(wsUrl) {
73
+ return new Promise((resolve, reject) => {
74
+ const ws = new WebSocket(wsUrl);
75
+ let settled = false;
76
+ const timer = setTimeout(() => {
77
+ if (settled)
78
+ return;
79
+ settled = true;
80
+ try {
81
+ ws.close();
82
+ }
83
+ catch {
84
+ /* ignore */
85
+ }
86
+ reject(new Error(`timed out after ${this.connectTimeoutMs}ms`));
87
+ }, this.connectTimeoutMs);
88
+ ws.addEventListener("open", () => {
89
+ if (settled)
90
+ return;
91
+ settled = true;
92
+ clearTimeout(timer);
93
+ this.ws = ws;
94
+ resolve();
95
+ });
96
+ ws.addEventListener("message", (event) => this.onMessage(String(event.data)));
97
+ ws.addEventListener("error", () => {
98
+ if (settled) {
99
+ this.failPending(ws, new Error("CDP websocket error"));
100
+ return;
101
+ }
102
+ settled = true;
103
+ clearTimeout(timer);
104
+ reject(new Error("websocket error (likely 403 or port closed)"));
105
+ });
106
+ ws.addEventListener("close", () => {
107
+ clearTimeout(timer);
108
+ this.failPending(ws, new Error("CDP websocket closed"));
109
+ if (!settled) {
110
+ settled = true;
111
+ reject(new Error("websocket closed before open"));
112
+ }
113
+ });
114
+ });
115
+ }
116
+ /** Reject in-flight calls and drop the socket when it's the active one. */
117
+ failPending(ws, error) {
118
+ if (this.ws !== ws)
119
+ return;
120
+ this.ws = undefined;
121
+ this.connectedBrowser = undefined;
122
+ for (const pending of this.pending.values())
123
+ pending.reject(error);
124
+ this.pending.clear();
125
+ }
126
+ onMessage(raw) {
127
+ let message;
128
+ try {
129
+ message = JSON.parse(raw);
130
+ }
131
+ catch {
132
+ return;
133
+ }
134
+ if (typeof message.id !== "number")
135
+ return; // an event, not a reply
136
+ const pending = this.pending.get(message.id);
137
+ if (!pending)
138
+ return;
139
+ this.pending.delete(message.id);
140
+ if (message.error)
141
+ pending.reject(new Error(`CDP error: ${message.error.message ?? "unknown"}`));
142
+ else
143
+ pending.resolve(message.result);
144
+ }
145
+ call(method, params) {
146
+ const ws = this.ws;
147
+ if (!ws || ws.readyState !== WS_OPEN) {
148
+ return Promise.reject(new Error("CDP not connected"));
149
+ }
150
+ const id = this.nextId++;
151
+ return new Promise((resolve, reject) => {
152
+ const timer = setTimeout(() => {
153
+ this.pending.delete(id);
154
+ reject(new Error(`CDP call timed out after ${this.callTimeoutMs}ms`));
155
+ }, this.callTimeoutMs);
156
+ this.pending.set(id, {
157
+ resolve: (value) => {
158
+ clearTimeout(timer);
159
+ resolve(value);
160
+ },
161
+ reject: (error) => {
162
+ clearTimeout(timer);
163
+ reject(error);
164
+ },
165
+ });
166
+ ws.send(JSON.stringify({ id, method, params }));
167
+ });
168
+ }
169
+ /**
170
+ * Open `url` as a background tab. Returns true on success, false when no
171
+ * browser is reachable (connect failed) or the call errored — the caller's
172
+ * cue to fall back to the OS opener. One reconnect is attempted if the
173
+ * persistent socket has gone stale. Never throws.
174
+ */
175
+ async openBackgroundTab(url) {
176
+ for (let attempt = 0; attempt < 2; attempt++) {
177
+ try {
178
+ await this.connect();
179
+ }
180
+ catch {
181
+ return false; // no reachable browser — fall back
182
+ }
183
+ try {
184
+ const result = (await this.call("Target.createTarget", { url, background: true }));
185
+ return typeof result?.targetId === "string";
186
+ }
187
+ catch {
188
+ // Socket likely went stale between runs (browser restarted). Drop it and
189
+ // let the next loop iteration re-detect and reconnect once.
190
+ this.ws = undefined;
191
+ this.connectedBrowser = undefined;
192
+ }
193
+ }
194
+ return false;
195
+ }
196
+ close() {
197
+ try {
198
+ this.ws?.close();
199
+ }
200
+ catch {
201
+ /* ignore */
202
+ }
203
+ this.ws = undefined;
204
+ this.connectedBrowser = undefined;
205
+ }
206
+ }
207
+ //# sourceMappingURL=cdp-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cdp-client.js","sourceRoot":"","sources":["../../src/cdp/cdp-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Detect Chromium-based browsers that currently have remote debugging enabled.
3
+ *
4
+ * A browser writes a `DevToolsActivePort` file into its user-data dir while a
5
+ * debug endpoint is live (the user toggled "Discover network targets" in
6
+ * chrome://inspect, or launched with --remote-debugging-port). The file holds
7
+ * the port on line 1 and the browser-level WebSocket path on line 2, which is
8
+ * everything we need to open a CDP connection — no HTTP probe required.
9
+ *
10
+ * Ported from the browser-harness-js CDP SDK (Bun) to dependency-free Node.
11
+ */
12
+ export type BrowserCandidate = {
13
+ name: string;
14
+ profileDir: string;
15
+ };
16
+ export type DetectedBrowser = {
17
+ /** Short label, e.g. "Google Chrome", "Brave". */
18
+ name: string;
19
+ /** Absolute user-data dir that held the DevToolsActivePort file. */
20
+ profileDir: string;
21
+ /** Port from DevToolsActivePort line 1. */
22
+ port: number;
23
+ /** WebSocket path from DevToolsActivePort line 2. */
24
+ wsPath: string;
25
+ /** `ws://127.0.0.1:<port><wsPath>` — ready to connect. */
26
+ wsUrl: string;
27
+ /** DevToolsActivePort mtime (ms). Used to prefer the most-recently-launched. */
28
+ mtimeMs: number;
29
+ };
30
+ /** OS-specific user-data dirs for Chromium-based browsers, rough popularity order. */
31
+ export declare function getBrowserCandidates(home?: string, platform?: NodeJS.Platform): BrowserCandidate[];
32
+ /**
33
+ * Scan known user-data dirs and return every Chromium browser with a live
34
+ * DevToolsActivePort, most-recently-launched first. Does NOT verify the WS
35
+ * endpoint actually accepts — the caller does that by trying to connect.
36
+ */
37
+ export declare function detectChromiumBrowsers(candidates?: BrowserCandidate[]): Promise<DetectedBrowser[]>;
38
+ //# sourceMappingURL=detect-chromium.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect-chromium.d.ts","sourceRoot":"","sources":["../../src/cdp/detect-chromium.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH,MAAM,MAAM,gBAAgB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpE,MAAM,MAAM,eAAe,GAAG;IAC5B,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,oEAAoE;IACpE,UAAU,EAAE,MAAM,CAAC;IACnB,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAC;IACd,gFAAgF;IAChF,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,sFAAsF;AACtF,wBAAgB,oBAAoB,CAClC,IAAI,GAAE,MAAqB,EAC3B,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,gBAAgB,EAAE,CAuCpB;AAmBD;;;;GAIG;AACH,wBAAsB,sBAAsB,CAC1C,UAAU,GAAE,gBAAgB,EAA2B,GACtD,OAAO,CAAC,eAAe,EAAE,CAAC,CAgB5B"}