@monoes/monobrowse 1.0.9 → 1.0.12
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/README.md +1 -1
- package/dist/src/__tests__/adapters.test.js +5 -2
- package/dist/src/__tests__/adapters.test.js.map +1 -1
- package/dist/src/__tests__/batch-eval-parsing.test.js +4 -2
- package/dist/src/__tests__/batch-eval-parsing.test.js.map +1 -1
- package/dist/src/__tests__/box-model.test.js +9 -5
- package/dist/src/__tests__/box-model.test.js.map +1 -1
- package/dist/src/__tests__/browser-launch.test.d.ts +10 -0
- package/dist/src/__tests__/browser-launch.test.d.ts.map +1 -1
- package/dist/src/__tests__/browser-launch.test.js +17 -10
- package/dist/src/__tests__/browser-launch.test.js.map +1 -1
- package/dist/src/__tests__/cdp-client.test.js +9 -3
- package/dist/src/__tests__/cdp-client.test.js.map +1 -1
- package/dist/src/__tests__/close-browser.test.d.ts +13 -0
- package/dist/src/__tests__/close-browser.test.d.ts.map +1 -1
- package/dist/src/__tests__/close-browser.test.js +40 -26
- package/dist/src/__tests__/close-browser.test.js.map +1 -1
- package/dist/src/__tests__/console-log.test.js +17 -6
- package/dist/src/__tests__/console-log.test.js.map +1 -1
- package/dist/src/__tests__/find.test.js +26 -6
- package/dist/src/__tests__/find.test.js.map +1 -1
- package/dist/src/__tests__/frame-command.test.js +9 -3
- package/dist/src/__tests__/frame-command.test.js.map +1 -1
- package/dist/src/__tests__/open-url-navigate-error.test.js +1 -3
- package/dist/src/__tests__/open-url-navigate-error.test.js.map +1 -1
- package/dist/src/__tests__/output.test.js +1 -1
- package/dist/src/__tests__/output.test.js.map +1 -1
- package/dist/src/__tests__/reap-idle-browser.test.d.ts +15 -0
- package/dist/src/__tests__/reap-idle-browser.test.d.ts.map +1 -0
- package/dist/src/__tests__/reap-idle-browser.test.js +238 -0
- package/dist/src/__tests__/reap-idle-browser.test.js.map +1 -0
- package/dist/src/__tests__/ref-cache.test.js +18 -10
- package/dist/src/__tests__/ref-cache.test.js.map +1 -1
- package/dist/src/__tests__/session-state.test.d.ts +8 -0
- package/dist/src/__tests__/session-state.test.d.ts.map +1 -1
- package/dist/src/__tests__/session-state.test.js +10 -10
- package/dist/src/__tests__/session-state.test.js.map +1 -1
- package/dist/src/__tests__/snapshot.test.js +45 -15
- package/dist/src/__tests__/snapshot.test.js.map +1 -1
- package/dist/src/__tests__/storage-eval.test.js +14 -6
- package/dist/src/__tests__/storage-eval.test.js.map +1 -1
- package/dist/src/__tests__/tabs.test.js +25 -9
- package/dist/src/__tests__/tabs.test.js.map +1 -1
- package/dist/src/browser/action-builder/analyzer.d.ts +1 -1
- package/dist/src/browser/action-builder/analyzer.d.ts.map +1 -1
- package/dist/src/browser/action-builder/analyzer.js +25 -9
- package/dist/src/browser/action-builder/analyzer.js.map +1 -1
- package/dist/src/browser/action-builder/types.d.ts.map +1 -1
- package/dist/src/browser/actions.d.ts +1 -1
- package/dist/src/browser/actions.d.ts.map +1 -1
- package/dist/src/browser/actions.js +16 -6
- package/dist/src/browser/actions.js.map +1 -1
- package/dist/src/browser/adapters/gemini.d.ts.map +1 -1
- package/dist/src/browser/adapters/gemini.js.map +1 -1
- package/dist/src/browser/adapters/google.d.ts.map +1 -1
- package/dist/src/browser/adapters/google.js.map +1 -1
- package/dist/src/browser/adapters/index.d.ts.map +1 -1
- package/dist/src/browser/adapters/index.js +3 -3
- package/dist/src/browser/adapters/index.js.map +1 -1
- package/dist/src/browser/adapters/instagram.d.ts.map +1 -1
- package/dist/src/browser/adapters/instagram.js.map +1 -1
- package/dist/src/browser/adapters/linkedin.d.ts.map +1 -1
- package/dist/src/browser/adapters/linkedin.js.map +1 -1
- package/dist/src/browser/adapters/microsoft.d.ts.map +1 -1
- package/dist/src/browser/adapters/microsoft.js.map +1 -1
- package/dist/src/browser/adapters/x.d.ts.map +1 -1
- package/dist/src/browser/adapters/x.js.map +1 -1
- package/dist/src/browser/browser.d.ts +30 -0
- package/dist/src/browser/browser.d.ts.map +1 -1
- package/dist/src/browser/browser.js +122 -15
- package/dist/src/browser/browser.js.map +1 -1
- package/dist/src/browser/cdp.d.ts.map +1 -1
- package/dist/src/browser/cdp.js +21 -8
- package/dist/src/browser/cdp.js.map +1 -1
- package/dist/src/browser/console-log.d.ts.map +1 -1
- package/dist/src/browser/console-log.js +2 -2
- package/dist/src/browser/console-log.js.map +1 -1
- package/dist/src/browser/dashboard/server.d.ts.map +1 -1
- package/dist/src/browser/dashboard/server.js +29 -19
- package/dist/src/browser/dashboard/server.js.map +1 -1
- package/dist/src/browser/dashboard/ui.html +153 -165
- package/dist/src/browser/dialog.d.ts.map +1 -1
- package/dist/src/browser/dialog.js +3 -1
- package/dist/src/browser/dialog.js.map +1 -1
- package/dist/src/browser/emulation.d.ts.map +1 -1
- package/dist/src/browser/emulation.js +21 -6
- package/dist/src/browser/emulation.js.map +1 -1
- package/dist/src/browser/find.d.ts +4 -4
- package/dist/src/browser/find.d.ts.map +1 -1
- package/dist/src/browser/find.js +14 -6
- package/dist/src/browser/find.js.map +1 -1
- package/dist/src/browser/har.d.ts.map +1 -1
- package/dist/src/browser/har.js +14 -7
- package/dist/src/browser/har.js.map +1 -1
- package/dist/src/browser/index.d.ts +16 -16
- package/dist/src/browser/index.d.ts.map +1 -1
- package/dist/src/browser/index.js +16 -16
- package/dist/src/browser/index.js.map +1 -1
- package/dist/src/browser/network.d.ts.map +1 -1
- package/dist/src/browser/network.js +14 -4
- package/dist/src/browser/network.js.map +1 -1
- package/dist/src/browser/pdf.d.ts.map +1 -1
- package/dist/src/browser/pdf.js +3 -3
- package/dist/src/browser/pdf.js.map +1 -1
- package/dist/src/browser/profiler.d.ts.map +1 -1
- package/dist/src/browser/profiler.js +10 -5
- package/dist/src/browser/profiler.js.map +1 -1
- package/dist/src/browser/record.d.ts +1 -1
- package/dist/src/browser/record.d.ts.map +1 -1
- package/dist/src/browser/record.js +18 -7
- package/dist/src/browser/record.js.map +1 -1
- package/dist/src/browser/ref-cache.d.ts.map +1 -1
- package/dist/src/browser/ref-cache.js +9 -4
- package/dist/src/browser/ref-cache.js.map +1 -1
- package/dist/src/browser/screenshot.d.ts.map +1 -1
- package/dist/src/browser/screenshot.js +11 -7
- package/dist/src/browser/screenshot.js.map +1 -1
- package/dist/src/browser/session.d.ts +1 -1
- package/dist/src/browser/session.d.ts.map +1 -1
- package/dist/src/browser/session.js +27 -8
- package/dist/src/browser/session.js.map +1 -1
- package/dist/src/browser/snapshot.d.ts +1 -1
- package/dist/src/browser/snapshot.d.ts.map +1 -1
- package/dist/src/browser/snapshot.js +8 -4
- package/dist/src/browser/snapshot.js.map +1 -1
- package/dist/src/browser/storage.d.ts.map +1 -1
- package/dist/src/browser/storage.js.map +1 -1
- package/dist/src/browser/tabs.d.ts.map +1 -1
- package/dist/src/browser/tabs.js +9 -3
- package/dist/src/browser/tabs.js.map +1 -1
- package/dist/src/browser/trace.d.ts.map +1 -1
- package/dist/src/browser/trace.js +3 -3
- package/dist/src/browser/trace.js.map +1 -1
- package/dist/src/browser/types.d.ts.map +1 -1
- package/dist/src/browser/types.js +23 -4
- package/dist/src/browser/types.js.map +1 -1
- package/dist/src/browser/vitals.d.ts.map +1 -1
- package/dist/src/browser/vitals.js +1 -1
- package/dist/src/browser/vitals.js.map +1 -1
- package/dist/src/browser/wait.d.ts.map +1 -1
- package/dist/src/browser/wait.js +11 -5
- package/dist/src/browser/wait.js.map +1 -1
- package/dist/src/cli/action.d.ts.map +1 -1
- package/dist/src/cli/action.js +14 -11
- package/dist/src/cli/action.js.map +1 -1
- package/dist/src/cli/commands.d.ts.map +1 -1
- package/dist/src/cli/commands.js +453 -159
- package/dist/src/cli/commands.js.map +1 -1
- package/dist/src/cli/output.js +5 -5
- package/dist/src/cli/output.js.map +1 -1
- package/dist/src/cli/platform.d.ts.map +1 -1
- package/dist/src/cli/platform.js +15 -11
- package/dist/src/cli/platform.js.map +1 -1
- package/dist/src/cli/types.d.ts +1 -1
- package/dist/src/cli/types.d.ts.map +1 -1
- package/dist/src/cli.js +21 -9
- package/dist/src/cli.js.map +1 -1
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -2
- package/dist/src/index.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/__tests__/adapters.test.ts +15 -8
- package/src/__tests__/batch-eval-parsing.test.ts +4 -2
- package/src/__tests__/box-model.test.ts +9 -5
- package/src/__tests__/browser-launch.test.ts +26 -15
- package/src/__tests__/cdp-client.test.ts +25 -9
- package/src/__tests__/close-browser.test.ts +49 -26
- package/src/__tests__/console-log.test.ts +36 -13
- package/src/__tests__/find.test.ts +58 -20
- package/src/__tests__/frame-command.test.ts +12 -6
- package/src/__tests__/open-url-navigate-error.test.ts +2 -4
- package/src/__tests__/output.test.ts +1 -1
- package/src/__tests__/reap-idle-browser.test.ts +276 -0
- package/src/__tests__/ref-cache.test.ts +25 -12
- package/src/__tests__/session-state.test.ts +40 -28
- package/src/__tests__/snapshot.test.ts +61 -22
- package/src/__tests__/storage-eval.test.ts +30 -16
- package/src/__tests__/tabs.test.ts +26 -10
- package/src/browser/action-builder/analyzer.ts +39 -15
- package/src/browser/action-builder/types.ts +45 -8
- package/src/browser/actions.ts +288 -123
- package/src/browser/adapters/gemini.ts +7 -3
- package/src/browser/adapters/google.ts +5 -3
- package/src/browser/adapters/index.ts +5 -4
- package/src/browser/adapters/instagram.ts +4 -2
- package/src/browser/adapters/linkedin.ts +4 -2
- package/src/browser/adapters/microsoft.ts +9 -5
- package/src/browser/adapters/x.ts +7 -3
- package/src/browser/browser.ts +188 -42
- package/src/browser/cdp.ts +43 -11
- package/src/browser/console-log.ts +24 -7
- package/src/browser/dashboard/server.ts +44 -25
- package/src/browser/dashboard/ui.html +153 -165
- package/src/browser/dialog.ts +25 -8
- package/src/browser/emulation.ts +86 -36
- package/src/browser/find.ts +110 -47
- package/src/browser/har.ts +58 -26
- package/src/browser/index.ts +16 -16
- package/src/browser/network.ts +147 -46
- package/src/browser/pdf.ts +4 -4
- package/src/browser/profiler.ts +23 -10
- package/src/browser/record.ts +38 -19
- package/src/browser/ref-cache.ts +38 -17
- package/src/browser/screenshot.ts +58 -32
- package/src/browser/session.ts +51 -18
- package/src/browser/snapshot.ts +34 -20
- package/src/browser/storage.ts +73 -17
- package/src/browser/tabs.ts +29 -17
- package/src/browser/trace.ts +14 -10
- package/src/browser/types.ts +23 -4
- package/src/browser/vitals.ts +33 -16
- package/src/browser/wait.ts +47 -21
- package/src/cli/action.ts +52 -21
- package/src/cli/commands.ts +1093 -385
- package/src/cli/output.ts +5 -5
- package/src/cli/platform.ts +44 -23
- package/src/cli/types.ts +1 -1
- package/src/cli.ts +34 -18
- package/src/index.ts +2 -2
package/src/browser/types.ts
CHANGED
|
@@ -127,10 +127,29 @@ export interface BrowserSession {
|
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
export const INTERACTIVE_ROLES = new Set([
|
|
130
|
-
'button',
|
|
131
|
-
'
|
|
132
|
-
'
|
|
133
|
-
'
|
|
130
|
+
'button',
|
|
131
|
+
'link',
|
|
132
|
+
'textbox',
|
|
133
|
+
'combobox',
|
|
134
|
+
'listbox',
|
|
135
|
+
'checkbox',
|
|
136
|
+
'radio',
|
|
137
|
+
'slider',
|
|
138
|
+
'spinbutton',
|
|
139
|
+
'searchbox',
|
|
140
|
+
'option',
|
|
141
|
+
'menuitem',
|
|
142
|
+
'tab',
|
|
143
|
+
'switch',
|
|
144
|
+
'treeitem',
|
|
145
|
+
'menuitemcheckbox',
|
|
146
|
+
'menuitemradio',
|
|
147
|
+
'row',
|
|
148
|
+
'gridcell',
|
|
149
|
+
'cell',
|
|
150
|
+
'columnheader',
|
|
151
|
+
'rowheader',
|
|
152
|
+
'scrollbar',
|
|
134
153
|
]);
|
|
135
154
|
|
|
136
155
|
export const CHROME_EXECUTABLES = [
|
package/src/browser/vitals.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { CdpClient } from './cdp.js';
|
|
2
1
|
import { evaluateJs } from './actions.js';
|
|
2
|
+
import type { CdpClient } from './cdp.js';
|
|
3
3
|
|
|
4
4
|
export interface WebVitals {
|
|
5
5
|
lcp?: number;
|
|
@@ -16,7 +16,7 @@ export interface WebVitals {
|
|
|
16
16
|
export async function collectVitals(
|
|
17
17
|
client: CdpClient,
|
|
18
18
|
sessionId: string,
|
|
19
|
-
waitMs = 2000
|
|
19
|
+
waitMs = 2000,
|
|
20
20
|
): Promise<WebVitals> {
|
|
21
21
|
// Inject PerformanceObserver collectors and wait for data
|
|
22
22
|
const script = `
|
|
@@ -97,26 +97,43 @@ export async function collectVitals(
|
|
|
97
97
|
|
|
98
98
|
export function formatVitals(vitals: WebVitals): string {
|
|
99
99
|
const lines: string[] = [];
|
|
100
|
-
const ms = (v?: number) => v !== undefined ? `${Math.round(v)}ms` : 'n/a';
|
|
100
|
+
const ms = (v?: number) => (v !== undefined ? `${Math.round(v)}ms` : 'n/a');
|
|
101
101
|
const score = (metric: string, v?: number): string => {
|
|
102
102
|
if (v === undefined) return '';
|
|
103
|
-
if (metric === 'lcp')
|
|
104
|
-
|
|
105
|
-
if (metric === '
|
|
103
|
+
if (metric === 'lcp')
|
|
104
|
+
return v < 2500 ? ' ✓ good' : v < 4000 ? ' ~ needs improvement' : ' ✗ poor';
|
|
105
|
+
if (metric === 'fcp')
|
|
106
|
+
return v < 1800 ? ' ✓ good' : v < 3000 ? ' ~ needs improvement' : ' ✗ poor';
|
|
107
|
+
if (metric === 'cls')
|
|
108
|
+
return v < 0.1 ? ' ✓ good' : v < 0.25 ? ' ~ needs improvement' : ' ✗ poor';
|
|
106
109
|
if (metric === 'inp') return v < 200 ? ' ✓ good' : v < 500 ? ' ~ needs improvement' : ' ✗ poor';
|
|
107
|
-
if (metric === 'ttfb')
|
|
110
|
+
if (metric === 'ttfb')
|
|
111
|
+
return v < 800 ? ' ✓ good' : v < 1800 ? ' ~ needs improvement' : ' ✗ poor';
|
|
108
112
|
return '';
|
|
109
113
|
};
|
|
110
114
|
|
|
111
|
-
if (vitals.lcp !== undefined)
|
|
112
|
-
|
|
113
|
-
if (vitals.
|
|
114
|
-
|
|
115
|
-
if (vitals.
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
if (vitals.
|
|
115
|
+
if (vitals.lcp !== undefined)
|
|
116
|
+
lines.push(` LCP (Largest Contentful Paint): ${ms(vitals.lcp)}${score('lcp', vitals.lcp)}`);
|
|
117
|
+
if (vitals.fcp !== undefined)
|
|
118
|
+
lines.push(` FCP (First Contentful Paint): ${ms(vitals.fcp)}${score('fcp', vitals.fcp)}`);
|
|
119
|
+
if (vitals.cls !== undefined)
|
|
120
|
+
lines.push(
|
|
121
|
+
` CLS (Cumulative Layout Shift): ${vitals.cls?.toFixed(4)}${score('cls', vitals.cls)}`,
|
|
122
|
+
);
|
|
123
|
+
if (vitals.inp !== undefined)
|
|
124
|
+
lines.push(` INP (Interaction to Next Paint): ${ms(vitals.inp)}${score('inp', vitals.inp)}`);
|
|
125
|
+
if (vitals.ttfb !== undefined)
|
|
126
|
+
lines.push(
|
|
127
|
+
` TTFB (Time to First Byte): ${ms(vitals.ttfb)}${score('ttfb', vitals.ttfb)}`,
|
|
128
|
+
);
|
|
129
|
+
if (vitals.domInteractive !== undefined)
|
|
130
|
+
lines.push(` DOM Interactive: ${ms(vitals.domInteractive)}`);
|
|
131
|
+
if (vitals.domContentLoaded !== undefined)
|
|
132
|
+
lines.push(` DOMContentLoaded: ${ms(vitals.domContentLoaded)}`);
|
|
133
|
+
if (vitals.loadTime !== undefined)
|
|
134
|
+
lines.push(` Load: ${ms(vitals.loadTime)}`);
|
|
135
|
+
if (vitals.resources !== undefined)
|
|
136
|
+
lines.push(` Resources loaded: ${vitals.resources}`);
|
|
120
137
|
|
|
121
138
|
return lines.join('\n');
|
|
122
139
|
}
|
package/src/browser/wait.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { evaluateJs } from './actions.js';
|
|
2
|
+
import { getCurrentUrl } from './browser.js';
|
|
1
3
|
import type { CdpClient } from './cdp.js';
|
|
2
4
|
import type { WaitOptions } from './types.js';
|
|
3
|
-
import { getCurrentUrl } from './browser.js';
|
|
4
|
-
import { evaluateJs } from './actions.js';
|
|
5
5
|
|
|
6
6
|
const DEFAULT_TIMEOUT = 30_000;
|
|
7
7
|
const POLL_INTERVAL = 250;
|
|
@@ -9,7 +9,7 @@ const POLL_INTERVAL = 250;
|
|
|
9
9
|
export async function waitFor(
|
|
10
10
|
client: CdpClient,
|
|
11
11
|
sessionId: string,
|
|
12
|
-
options: WaitOptions
|
|
12
|
+
options: WaitOptions,
|
|
13
13
|
): Promise<void> {
|
|
14
14
|
const timeout = options.timeout ?? DEFAULT_TIMEOUT;
|
|
15
15
|
const deadline = Date.now() + timeout;
|
|
@@ -42,7 +42,7 @@ async function waitForLoad(
|
|
|
42
42
|
client: CdpClient,
|
|
43
43
|
sessionId: string,
|
|
44
44
|
condition: string,
|
|
45
|
-
timeout: number
|
|
45
|
+
timeout: number,
|
|
46
46
|
): Promise<void> {
|
|
47
47
|
if (condition === 'networkidle') {
|
|
48
48
|
await waitForNetworkIdle(client, sessionId, 500, timeout);
|
|
@@ -54,7 +54,10 @@ async function waitForLoad(
|
|
|
54
54
|
let timedOut = false;
|
|
55
55
|
let timeoutHandle: ReturnType<typeof setTimeout> | undefined;
|
|
56
56
|
const timeoutPromise = new Promise<void>((resolve) => {
|
|
57
|
-
timeoutHandle = setTimeout(() => {
|
|
57
|
+
timeoutHandle = setTimeout(() => {
|
|
58
|
+
timedOut = true;
|
|
59
|
+
resolve();
|
|
60
|
+
}, timeout);
|
|
58
61
|
});
|
|
59
62
|
try {
|
|
60
63
|
await Promise.race([eventPromise, timeoutPromise]);
|
|
@@ -69,7 +72,7 @@ async function waitForNetworkIdle(
|
|
|
69
72
|
client: CdpClient,
|
|
70
73
|
sessionId: string,
|
|
71
74
|
idleMs: number,
|
|
72
|
-
timeout: number
|
|
75
|
+
timeout: number,
|
|
73
76
|
): Promise<void> {
|
|
74
77
|
return new Promise((resolve, reject) => {
|
|
75
78
|
let pending = 0;
|
|
@@ -77,9 +80,15 @@ async function waitForNetworkIdle(
|
|
|
77
80
|
let idleTimer: ReturnType<typeof setTimeout> | null = null;
|
|
78
81
|
|
|
79
82
|
const cleanup = () => {
|
|
80
|
-
if (idleTimer) {
|
|
83
|
+
if (idleTimer) {
|
|
84
|
+
clearTimeout(idleTimer);
|
|
85
|
+
idleTimer = null;
|
|
86
|
+
}
|
|
81
87
|
clearTimeout(killTimer);
|
|
82
|
-
offReq();
|
|
88
|
+
offReq();
|
|
89
|
+
offResp();
|
|
90
|
+
offFail();
|
|
91
|
+
offCache();
|
|
83
92
|
};
|
|
84
93
|
|
|
85
94
|
const killTimer = setTimeout(() => {
|
|
@@ -87,31 +96,44 @@ async function waitForNetworkIdle(
|
|
|
87
96
|
reject(new Error('Timeout waiting for networkidle'));
|
|
88
97
|
}, timeout);
|
|
89
98
|
|
|
90
|
-
const settle = () => {
|
|
99
|
+
const settle = () => {
|
|
100
|
+
cleanup();
|
|
101
|
+
resolve();
|
|
102
|
+
};
|
|
91
103
|
|
|
92
104
|
const check = () => {
|
|
93
105
|
if (pending === 0) {
|
|
94
106
|
if (idleTimer) clearTimeout(idleTimer);
|
|
95
107
|
idleTimer = setTimeout(settle, idleMs);
|
|
96
108
|
} else {
|
|
97
|
-
if (idleTimer) {
|
|
109
|
+
if (idleTimer) {
|
|
110
|
+
clearTimeout(idleTimer);
|
|
111
|
+
idleTimer = null;
|
|
112
|
+
}
|
|
98
113
|
}
|
|
99
114
|
};
|
|
100
115
|
|
|
101
116
|
const offReq = client.on('Network.requestWillBeSent', (params, sid) => {
|
|
102
117
|
if (sid !== sessionId) return;
|
|
103
118
|
const id = params.requestId as string;
|
|
104
|
-
if (!inflight.has(id)) {
|
|
119
|
+
if (!inflight.has(id)) {
|
|
120
|
+
inflight.add(id);
|
|
121
|
+
pending++;
|
|
122
|
+
check();
|
|
123
|
+
}
|
|
105
124
|
});
|
|
106
125
|
|
|
107
126
|
const decrement = (params: Record<string, unknown>, sid?: string) => {
|
|
108
127
|
if (sid !== sessionId) return;
|
|
109
128
|
const id = params.requestId as string;
|
|
110
|
-
if (inflight.delete(id)) {
|
|
129
|
+
if (inflight.delete(id)) {
|
|
130
|
+
pending = Math.max(0, pending - 1);
|
|
131
|
+
check();
|
|
132
|
+
}
|
|
111
133
|
};
|
|
112
134
|
|
|
113
|
-
const offResp
|
|
114
|
-
const offFail
|
|
135
|
+
const offResp = client.on('Network.loadingFinished', decrement);
|
|
136
|
+
const offFail = client.on('Network.loadingFailed', decrement);
|
|
115
137
|
const offCache = client.on('Network.requestServedFromCache', decrement);
|
|
116
138
|
|
|
117
139
|
check();
|
|
@@ -122,7 +144,7 @@ async function waitForUrl(
|
|
|
122
144
|
client: CdpClient,
|
|
123
145
|
sessionId: string,
|
|
124
146
|
pattern: string,
|
|
125
|
-
deadline: number
|
|
147
|
+
deadline: number,
|
|
126
148
|
): Promise<void> {
|
|
127
149
|
const regex = globToRegex(pattern);
|
|
128
150
|
while (Date.now() < deadline) {
|
|
@@ -137,10 +159,14 @@ async function waitForText(
|
|
|
137
159
|
client: CdpClient,
|
|
138
160
|
sessionId: string,
|
|
139
161
|
text: string,
|
|
140
|
-
deadline: number
|
|
162
|
+
deadline: number,
|
|
141
163
|
): Promise<void> {
|
|
142
164
|
while (Date.now() < deadline) {
|
|
143
|
-
const bodyText = await evaluateJs(
|
|
165
|
+
const bodyText = (await evaluateJs(
|
|
166
|
+
client,
|
|
167
|
+
sessionId,
|
|
168
|
+
'document.body?.innerText ?? ""',
|
|
169
|
+
)) as string;
|
|
144
170
|
if (bodyText.includes(text)) return;
|
|
145
171
|
await sleep(POLL_INTERVAL);
|
|
146
172
|
}
|
|
@@ -151,14 +177,14 @@ async function waitForSelector(
|
|
|
151
177
|
client: CdpClient,
|
|
152
178
|
sessionId: string,
|
|
153
179
|
selector: string,
|
|
154
|
-
deadline: number
|
|
180
|
+
deadline: number,
|
|
155
181
|
): Promise<void> {
|
|
156
182
|
while (Date.now() < deadline) {
|
|
157
|
-
const found = await evaluateJs(
|
|
183
|
+
const found = (await evaluateJs(
|
|
158
184
|
client,
|
|
159
185
|
sessionId,
|
|
160
|
-
`!!document.querySelector(${JSON.stringify(selector)})
|
|
161
|
-
) as boolean;
|
|
186
|
+
`!!document.querySelector(${JSON.stringify(selector)})`,
|
|
187
|
+
)) as boolean;
|
|
162
188
|
if (found) return;
|
|
163
189
|
await sleep(POLL_INTERVAL);
|
|
164
190
|
}
|
package/src/cli/action.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
// src/commands/browse-action.ts
|
|
2
|
-
|
|
3
|
-
import { readdir, readFile, writeFile, mkdir } from 'node:fs/promises';
|
|
2
|
+
|
|
4
3
|
import { existsSync } from 'node:fs';
|
|
4
|
+
import { mkdir, readdir, readFile, writeFile } from 'node:fs/promises';
|
|
5
5
|
import { join } from 'node:path';
|
|
6
|
-
import {
|
|
6
|
+
import { Command } from 'commander';
|
|
7
7
|
import type { ActionDef, StepDef } from '../index.js';
|
|
8
|
+
import { type AnalyzerPage, analyzePageForAction } from '../index.js';
|
|
8
9
|
|
|
9
10
|
async function readAction(filePath: string): Promise<ActionDef> {
|
|
10
11
|
return JSON.parse(await readFile(filePath, 'utf8')) as ActionDef;
|
|
@@ -34,7 +35,7 @@ const BUILTIN_ACTIONS: { id: string; platform: string; name: string }[] = [
|
|
|
34
35
|
async function getCustomActions(): Promise<ActionDef[]> {
|
|
35
36
|
const dir = join(process.cwd(), '.monomind', 'actions');
|
|
36
37
|
if (!existsSync(dir)) return [];
|
|
37
|
-
const files = (await readdir(dir)).filter(f => f.endsWith('.json'));
|
|
38
|
+
const files = (await readdir(dir)).filter((f) => f.endsWith('.json'));
|
|
38
39
|
const results: ActionDef[] = [];
|
|
39
40
|
for (const f of files) {
|
|
40
41
|
try {
|
|
@@ -63,10 +64,16 @@ export function createActionCommand(): Command {
|
|
|
63
64
|
try {
|
|
64
65
|
// Dynamic import to avoid crashing if CDP is unavailable
|
|
65
66
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
66
|
-
const mod = await (import('@monoes/monobrowse' as string) as Promise<any>).catch(
|
|
67
|
-
|
|
67
|
+
const mod = await (import('@monoes/monobrowse' as string) as Promise<any>).catch(
|
|
68
|
+
() => null,
|
|
69
|
+
);
|
|
70
|
+
const createBrowserPage = mod?.createBrowserPage as
|
|
71
|
+
| ((url: string) => Promise<AnalyzerPage>)
|
|
72
|
+
| null;
|
|
68
73
|
if (!createBrowserPage) {
|
|
69
|
-
throw new Error(
|
|
74
|
+
throw new Error(
|
|
75
|
+
'Browser CDP client not available. Ensure Chrome is running with --remote-debugging-port=9222',
|
|
76
|
+
);
|
|
70
77
|
}
|
|
71
78
|
const page = await createBrowserPage(opts.url);
|
|
72
79
|
actionDef = await analyzePageForAction(page, opts.task);
|
|
@@ -98,10 +105,12 @@ export function createActionCommand(): Command {
|
|
|
98
105
|
console.log(`Running action: ${actionId} with account: ${opts.account}`);
|
|
99
106
|
|
|
100
107
|
const custom = await getCustomActions();
|
|
101
|
-
const def = custom.find(a => a.id === actionId);
|
|
108
|
+
const def = custom.find((a) => a.id === actionId);
|
|
102
109
|
if (!def) {
|
|
103
110
|
console.error(`Action not found in custom actions: ${actionId}`);
|
|
104
|
-
console.error(
|
|
111
|
+
console.error(
|
|
112
|
+
'Built-in actions require adapter step definitions — use "action build" to create a custom action.',
|
|
113
|
+
);
|
|
105
114
|
process.exit(1);
|
|
106
115
|
}
|
|
107
116
|
|
|
@@ -126,8 +135,13 @@ export function createActionCommand(): Command {
|
|
|
126
135
|
break;
|
|
127
136
|
case 'find': {
|
|
128
137
|
for (const sel of step.selectors) {
|
|
129
|
-
const found = await findBySelector(client, sessionId, refs, interpolate(sel)).catch(
|
|
130
|
-
|
|
138
|
+
const found = await findBySelector(client, sessionId, refs, interpolate(sel)).catch(
|
|
139
|
+
() => null,
|
|
140
|
+
);
|
|
141
|
+
if (found) {
|
|
142
|
+
refs.set(step.as, found);
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
131
145
|
}
|
|
132
146
|
console.log(` find → ${step.as}`);
|
|
133
147
|
break;
|
|
@@ -150,9 +164,12 @@ export function createActionCommand(): Command {
|
|
|
150
164
|
if (step.condition === 'network_idle') {
|
|
151
165
|
await waitFor(client, sessionId, { load: 'networkidle', timeout: step.timeout });
|
|
152
166
|
} else if (step.condition === 'selector' && step.selector) {
|
|
153
|
-
await waitFor(client, sessionId, {
|
|
167
|
+
await waitFor(client, sessionId, {
|
|
168
|
+
selector: step.selector,
|
|
169
|
+
timeout: step.timeout,
|
|
170
|
+
});
|
|
154
171
|
} else if (step.condition === 'duration') {
|
|
155
|
-
await new Promise(r => setTimeout(r, step.timeout ?? 1000));
|
|
172
|
+
await new Promise((r) => setTimeout(r, step.timeout ?? 1000));
|
|
156
173
|
}
|
|
157
174
|
console.log(` wait → ${step.condition}`);
|
|
158
175
|
break;
|
|
@@ -160,8 +177,16 @@ export function createActionCommand(): Command {
|
|
|
160
177
|
const ref = refs.get(step.target);
|
|
161
178
|
if (!ref) throw new Error(`Element "${step.target}" not found`);
|
|
162
179
|
const val = step.attribute
|
|
163
|
-
? await evaluateJs(
|
|
164
|
-
|
|
180
|
+
? await evaluateJs(
|
|
181
|
+
client,
|
|
182
|
+
sessionId,
|
|
183
|
+
`document.querySelector('[data-ref="${ref.ref}"]')?.getAttribute('${step.attribute}')`,
|
|
184
|
+
)
|
|
185
|
+
: await evaluateJs(
|
|
186
|
+
client,
|
|
187
|
+
sessionId,
|
|
188
|
+
`document.querySelector('[data-ref="${ref.ref}"]')?.textContent`,
|
|
189
|
+
);
|
|
165
190
|
console.log(` extract → ${step.as}: ${val}`);
|
|
166
191
|
break;
|
|
167
192
|
}
|
|
@@ -189,10 +214,10 @@ export function createActionCommand(): Command {
|
|
|
189
214
|
.action(async (opts: { platform?: string }) => {
|
|
190
215
|
const custom = await getCustomActions();
|
|
191
216
|
const all = [
|
|
192
|
-
...BUILTIN_ACTIONS.map(a => ({ ...a, source: 'built-in' })),
|
|
193
|
-
...custom.map(a => ({ id: a.id, platform: a.platform, name: a.name, source: 'custom' })),
|
|
217
|
+
...BUILTIN_ACTIONS.map((a) => ({ ...a, source: 'built-in' })),
|
|
218
|
+
...custom.map((a) => ({ id: a.id, platform: a.platform, name: a.name, source: 'custom' })),
|
|
194
219
|
];
|
|
195
|
-
const filtered = opts.platform ? all.filter(a => a.platform === opts.platform) : all;
|
|
220
|
+
const filtered = opts.platform ? all.filter((a) => a.platform === opts.platform) : all;
|
|
196
221
|
if (filtered.length === 0) {
|
|
197
222
|
console.log('No actions found.');
|
|
198
223
|
return;
|
|
@@ -210,7 +235,7 @@ export function createActionCommand(): Command {
|
|
|
210
235
|
.action(async (actionId: string) => {
|
|
211
236
|
// Check custom actions first
|
|
212
237
|
const dir = join(process.cwd(), '.monomind', 'actions');
|
|
213
|
-
const fileName = actionId.replace(':', '-')
|
|
238
|
+
const fileName = `${actionId.replace(':', '-')}.json`;
|
|
214
239
|
const customPath = join(dir, fileName);
|
|
215
240
|
if (existsSync(customPath)) {
|
|
216
241
|
const def = await readAction(customPath);
|
|
@@ -218,9 +243,15 @@ export function createActionCommand(): Command {
|
|
|
218
243
|
return;
|
|
219
244
|
}
|
|
220
245
|
// Check built-ins
|
|
221
|
-
const builtin = BUILTIN_ACTIONS.find(a => a.id === actionId);
|
|
246
|
+
const builtin = BUILTIN_ACTIONS.find((a) => a.id === actionId);
|
|
222
247
|
if (builtin) {
|
|
223
|
-
console.log(
|
|
248
|
+
console.log(
|
|
249
|
+
JSON.stringify(
|
|
250
|
+
{ ...builtin, note: 'Built-in action — steps defined in browser/adapters/' },
|
|
251
|
+
null,
|
|
252
|
+
2,
|
|
253
|
+
),
|
|
254
|
+
);
|
|
224
255
|
return;
|
|
225
256
|
}
|
|
226
257
|
console.error(`Action not found: ${actionId}`);
|