@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.
- package/dist/automation-scheduler.d.ts.map +1 -1
- package/dist/automation-scheduler.js +8 -2
- package/dist/automation-scheduler.js.map +1 -1
- package/dist/automation-store.d.ts +5 -2
- package/dist/automation-store.d.ts.map +1 -1
- package/dist/automation-store.js +128 -13
- package/dist/automation-store.js.map +1 -1
- package/dist/cdp/cdp-client.d.ts +76 -0
- package/dist/cdp/cdp-client.d.ts.map +1 -0
- package/dist/cdp/cdp-client.js +263 -0
- package/dist/cdp/cdp-client.js.map +1 -0
- package/dist/cdp/detect-chromium.d.ts +38 -0
- package/dist/cdp/detect-chromium.d.ts.map +1 -0
- package/dist/cdp/detect-chromium.js +97 -0
- package/dist/cdp/detect-chromium.js.map +1 -0
- package/dist/cdp/open-background-tab.d.ts +33 -0
- package/dist/cdp/open-background-tab.d.ts.map +1 -0
- package/dist/cdp/open-background-tab.js +98 -0
- package/dist/cdp/open-background-tab.js.map +1 -0
- package/dist/constants.d.ts +8 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +26 -1
- package/dist/constants.js.map +1 -1
- package/dist/heartbeat-store.d.ts +7 -0
- package/dist/heartbeat-store.d.ts.map +1 -0
- package/dist/heartbeat-store.js +45 -0
- package/dist/heartbeat-store.js.map +1 -0
- package/dist/index.d.ts +19 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +208 -25
- package/dist/index.js.map +1 -1
- package/dist/protocol.d.ts +4 -3
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +3 -2
- package/dist/protocol.js.map +1 -1
- package/dist/schemas.d.ts +633 -16
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +185 -14
- package/dist/schemas.js.map +1 -1
- package/dist/types.d.ts +8 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/compile-schedule.d.ts +6 -0
- package/dist/utils/compile-schedule.d.ts.map +1 -0
- package/dist/utils/compile-schedule.js +173 -0
- package/dist/utils/compile-schedule.js.map +1 -0
- package/dist/utils/compute-next-automation-run-at.d.ts.map +1 -1
- package/dist/utils/compute-next-automation-run-at.js +14 -3
- package/dist/utils/compute-next-automation-run-at.js.map +1 -1
- package/dist/utils/reconcile-downtime.d.ts +3 -0
- package/dist/utils/reconcile-downtime.d.ts.map +1 -0
- package/dist/utils/reconcile-downtime.js +25 -0
- package/dist/utils/reconcile-downtime.js.map +1 -0
- 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;
|
|
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
|
-
|
|
35
|
-
|
|
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":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,kCAAkC,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACnF,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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"}
|
package/dist/automation-store.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
84
|
-
if (parsed.success) {
|
|
85
|
-
this.automations = parsed.data.automations;
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
188
|
+
json = JSON.parse(raw);
|
|
88
189
|
}
|
|
89
190
|
catch {
|
|
90
|
-
|
|
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":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;
|
|
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":"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"}
|