@monoes/monobrowse 1.0.2 → 1.0.4
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/src/__tests__/batch-eval-parsing.test.d.ts +2 -0
- package/dist/src/__tests__/batch-eval-parsing.test.d.ts.map +1 -0
- package/dist/src/__tests__/batch-eval-parsing.test.js +47 -0
- package/dist/src/__tests__/batch-eval-parsing.test.js.map +1 -0
- package/dist/src/__tests__/output.test.d.ts +2 -0
- package/dist/src/__tests__/output.test.d.ts.map +1 -0
- package/dist/src/__tests__/output.test.js +54 -0
- package/dist/src/__tests__/output.test.js.map +1 -0
- package/dist/src/__tests__/ref-cache.test.d.ts +2 -0
- package/dist/src/__tests__/ref-cache.test.d.ts.map +1 -0
- package/dist/src/__tests__/ref-cache.test.js +145 -0
- package/dist/src/__tests__/ref-cache.test.js.map +1 -0
- package/dist/src/browser/action-builder/analyzer.d.ts +0 -0
- package/dist/src/browser/action-builder/analyzer.d.ts.map +0 -0
- package/dist/src/browser/action-builder/analyzer.js +0 -0
- package/dist/src/browser/action-builder/analyzer.js.map +1 -1
- package/dist/src/browser/action-builder/types.d.ts +0 -0
- package/dist/src/browser/action-builder/types.d.ts.map +0 -0
- package/dist/src/browser/action-builder/types.js +0 -0
- package/dist/src/browser/action-builder/types.js.map +0 -0
- 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 +19 -2
- package/dist/src/browser/actions.js.map +1 -1
- package/dist/src/browser/adapters/gemini.d.ts +0 -0
- package/dist/src/browser/adapters/gemini.d.ts.map +0 -0
- package/dist/src/browser/adapters/gemini.js +0 -0
- package/dist/src/browser/adapters/gemini.js.map +0 -0
- package/dist/src/browser/adapters/google.d.ts +0 -0
- package/dist/src/browser/adapters/google.d.ts.map +0 -0
- package/dist/src/browser/adapters/google.js +0 -0
- package/dist/src/browser/adapters/google.js.map +0 -0
- package/dist/src/browser/adapters/index.d.ts +0 -0
- package/dist/src/browser/adapters/index.d.ts.map +0 -0
- package/dist/src/browser/adapters/index.js +0 -0
- package/dist/src/browser/adapters/index.js.map +0 -0
- package/dist/src/browser/adapters/instagram.d.ts +0 -0
- package/dist/src/browser/adapters/instagram.d.ts.map +0 -0
- package/dist/src/browser/adapters/instagram.js +0 -0
- package/dist/src/browser/adapters/instagram.js.map +0 -0
- package/dist/src/browser/adapters/linkedin.d.ts +0 -0
- package/dist/src/browser/adapters/linkedin.d.ts.map +0 -0
- package/dist/src/browser/adapters/linkedin.js +0 -0
- package/dist/src/browser/adapters/linkedin.js.map +0 -0
- package/dist/src/browser/adapters/microsoft.d.ts +0 -0
- package/dist/src/browser/adapters/microsoft.d.ts.map +0 -0
- package/dist/src/browser/adapters/microsoft.js +0 -0
- package/dist/src/browser/adapters/microsoft.js.map +0 -0
- package/dist/src/browser/adapters/x.d.ts +0 -0
- package/dist/src/browser/adapters/x.d.ts.map +0 -0
- package/dist/src/browser/adapters/x.js +0 -0
- package/dist/src/browser/adapters/x.js.map +0 -0
- package/dist/src/browser/batch.d.ts +0 -0
- package/dist/src/browser/batch.d.ts.map +0 -0
- package/dist/src/browser/batch.js +0 -0
- package/dist/src/browser/batch.js.map +0 -0
- package/dist/src/browser/browser.d.ts +10 -0
- package/dist/src/browser/browser.d.ts.map +1 -1
- package/dist/src/browser/browser.js +111 -3
- package/dist/src/browser/browser.js.map +1 -1
- package/dist/src/browser/cdp.d.ts +0 -0
- package/dist/src/browser/cdp.d.ts.map +1 -1
- package/dist/src/browser/cdp.js +1 -1
- package/dist/src/browser/cdp.js.map +1 -1
- package/dist/src/browser/console-log.d.ts +0 -0
- package/dist/src/browser/console-log.d.ts.map +0 -0
- package/dist/src/browser/console-log.js +0 -0
- package/dist/src/browser/console-log.js.map +0 -0
- package/dist/src/browser/dashboard/api-types.d.ts +0 -0
- package/dist/src/browser/dashboard/api-types.d.ts.map +0 -0
- package/dist/src/browser/dashboard/api-types.js +0 -0
- package/dist/src/browser/dashboard/api-types.js.map +0 -0
- package/dist/src/browser/dashboard/server.d.ts +0 -0
- package/dist/src/browser/dashboard/server.d.ts.map +1 -1
- package/dist/src/browser/dashboard/server.js +37 -2
- package/dist/src/browser/dashboard/server.js.map +1 -1
- package/dist/src/browser/dashboard/ui.html +1 -1
- package/dist/src/browser/dialog.d.ts +0 -0
- package/dist/src/browser/dialog.d.ts.map +0 -0
- package/dist/src/browser/dialog.js +0 -0
- package/dist/src/browser/dialog.js.map +0 -0
- package/dist/src/browser/emulation.d.ts +0 -0
- package/dist/src/browser/emulation.d.ts.map +0 -0
- package/dist/src/browser/emulation.js +0 -0
- package/dist/src/browser/emulation.js.map +0 -0
- package/dist/src/browser/find.d.ts +0 -0
- package/dist/src/browser/find.d.ts.map +0 -0
- package/dist/src/browser/find.js +0 -0
- package/dist/src/browser/find.js.map +1 -1
- package/dist/src/browser/har.d.ts +0 -0
- package/dist/src/browser/har.d.ts.map +0 -0
- package/dist/src/browser/har.js +0 -0
- package/dist/src/browser/har.js.map +0 -0
- package/dist/src/browser/index.d.ts +1 -0
- package/dist/src/browser/index.d.ts.map +1 -1
- package/dist/src/browser/index.js +1 -0
- package/dist/src/browser/index.js.map +1 -1
- package/dist/src/browser/network.d.ts +7 -0
- package/dist/src/browser/network.d.ts.map +1 -1
- package/dist/src/browser/network.js +10 -0
- package/dist/src/browser/network.js.map +1 -1
- package/dist/src/browser/pdf.d.ts +0 -0
- package/dist/src/browser/pdf.d.ts.map +0 -0
- package/dist/src/browser/pdf.js +0 -0
- package/dist/src/browser/pdf.js.map +1 -1
- package/dist/src/browser/profiler.d.ts +0 -0
- package/dist/src/browser/profiler.d.ts.map +0 -0
- package/dist/src/browser/profiler.js +0 -0
- package/dist/src/browser/profiler.js.map +1 -1
- package/dist/src/browser/record.d.ts +3 -0
- package/dist/src/browser/record.d.ts.map +1 -1
- package/dist/src/browser/record.js +37 -5
- package/dist/src/browser/record.js.map +1 -1
- package/dist/src/browser/ref-cache.d.ts +41 -0
- package/dist/src/browser/ref-cache.d.ts.map +1 -0
- package/dist/src/browser/ref-cache.js +120 -0
- package/dist/src/browser/ref-cache.js.map +1 -0
- package/dist/src/browser/screenshot.d.ts +0 -0
- package/dist/src/browser/screenshot.d.ts.map +0 -0
- package/dist/src/browser/screenshot.js +0 -0
- package/dist/src/browser/screenshot.js.map +1 -1
- package/dist/src/browser/session.d.ts +0 -0
- package/dist/src/browser/session.d.ts.map +1 -1
- package/dist/src/browser/session.js +12 -7
- package/dist/src/browser/session.js.map +1 -1
- package/dist/src/browser/snapshot.d.ts +0 -0
- package/dist/src/browser/snapshot.d.ts.map +0 -0
- package/dist/src/browser/snapshot.js +0 -0
- package/dist/src/browser/snapshot.js.map +1 -1
- package/dist/src/browser/storage.d.ts +0 -0
- package/dist/src/browser/storage.d.ts.map +0 -0
- package/dist/src/browser/storage.js +0 -0
- package/dist/src/browser/storage.js.map +0 -0
- package/dist/src/browser/tabs.d.ts +1 -1
- package/dist/src/browser/tabs.d.ts.map +1 -1
- package/dist/src/browser/tabs.js +20 -4
- package/dist/src/browser/tabs.js.map +1 -1
- package/dist/src/browser/trace.d.ts +0 -0
- package/dist/src/browser/trace.d.ts.map +0 -0
- package/dist/src/browser/trace.js +0 -0
- package/dist/src/browser/trace.js.map +1 -1
- package/dist/src/browser/types.d.ts +0 -0
- package/dist/src/browser/types.d.ts.map +0 -0
- package/dist/src/browser/types.js +0 -0
- package/dist/src/browser/types.js.map +0 -0
- package/dist/src/browser/vitals.d.ts +0 -0
- package/dist/src/browser/vitals.d.ts.map +0 -0
- package/dist/src/browser/vitals.js +0 -0
- package/dist/src/browser/vitals.js.map +0 -0
- package/dist/src/browser/wait.d.ts +0 -0
- package/dist/src/browser/wait.d.ts.map +0 -0
- package/dist/src/browser/wait.js +0 -0
- package/dist/src/browser/wait.js.map +0 -0
- package/dist/src/cli/action.d.ts +0 -0
- package/dist/src/cli/action.d.ts.map +1 -1
- package/dist/src/cli/action.js +87 -2
- package/dist/src/cli/action.js.map +1 -1
- package/dist/src/cli/commands.d.ts +22 -0
- package/dist/src/cli/commands.d.ts.map +1 -1
- package/dist/src/cli/commands.js +276 -28
- package/dist/src/cli/commands.js.map +1 -1
- package/dist/src/cli/output.d.ts +0 -0
- package/dist/src/cli/output.d.ts.map +1 -1
- package/dist/src/cli/output.js +0 -0
- package/dist/src/cli/output.js.map +0 -0
- package/dist/src/cli/platform.d.ts +0 -0
- package/dist/src/cli/platform.d.ts.map +0 -0
- package/dist/src/cli/platform.js +0 -0
- package/dist/src/cli/platform.js.map +0 -0
- package/dist/src/cli/types.d.ts +0 -0
- package/dist/src/cli/types.d.ts.map +0 -0
- package/dist/src/cli/types.js +0 -0
- package/dist/src/cli/types.js.map +0 -0
- package/dist/src/cli.d.ts +0 -0
- package/dist/src/cli.d.ts.map +0 -0
- package/dist/src/cli.js.map +1 -1
- package/dist/src/index.d.ts +0 -0
- package/dist/src/index.d.ts.map +0 -0
- package/dist/src/index.js +0 -0
- package/dist/src/index.js.map +0 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/__tests__/batch-eval-parsing.test.ts +52 -0
- package/src/__tests__/output.test.ts +62 -0
- package/src/__tests__/ref-cache.test.ts +175 -0
- package/src/browser/action-builder/analyzer.ts +0 -0
- package/src/browser/action-builder/types.ts +0 -0
- package/src/browser/actions.ts +22 -2
- package/src/browser/adapters/gemini.ts +0 -0
- package/src/browser/adapters/google.ts +0 -0
- package/src/browser/adapters/index.ts +0 -0
- package/src/browser/adapters/instagram.ts +0 -0
- package/src/browser/adapters/linkedin.ts +0 -0
- package/src/browser/adapters/microsoft.ts +0 -0
- package/src/browser/adapters/x.ts +0 -0
- package/src/browser/batch.ts +0 -0
- package/src/browser/browser.ts +115 -2
- package/src/browser/cdp.ts +1 -1
- package/src/browser/console-log.ts +0 -0
- package/src/browser/dashboard/api-types.ts +0 -0
- package/src/browser/dashboard/server.ts +37 -2
- package/src/browser/dashboard/ui.html +1 -1
- package/src/browser/dialog.ts +0 -0
- package/src/browser/emulation.ts +0 -0
- package/src/browser/find.ts +0 -0
- package/src/browser/har.ts +0 -0
- package/src/browser/index.ts +1 -0
- package/src/browser/network.ts +11 -0
- package/src/browser/pdf.ts +0 -0
- package/src/browser/profiler.ts +0 -0
- package/src/browser/record.ts +43 -6
- package/src/browser/ref-cache.ts +141 -0
- package/src/browser/screenshot.ts +0 -0
- package/src/browser/session.ts +13 -7
- package/src/browser/snapshot.ts +0 -0
- package/src/browser/storage.ts +0 -0
- package/src/browser/tabs.ts +36 -7
- package/src/browser/trace.ts +0 -0
- package/src/browser/types.ts +0 -0
- package/src/browser/vitals.ts +0 -0
- package/src/browser/wait.ts +0 -0
- package/src/cli/action.ts +85 -3
- package/src/cli/commands.ts +272 -28
- package/src/cli/output.ts +0 -0
- package/src/cli/platform.ts +0 -0
- package/src/cli/types.ts +0 -0
- package/src/cli.ts +0 -0
- package/src/index.ts +0 -0
- package/dist/package.json +0 -71
- package/dist/src/browser/playbook/browser-handlers.d.ts +0 -3
- package/dist/src/browser/playbook/browser-handlers.d.ts.map +0 -1
- package/dist/src/browser/playbook/browser-handlers.js +0 -377
- package/dist/src/browser/playbook/browser-handlers.js.map +0 -1
- package/dist/src/browser/playbook/builtin-handlers.d.ts +0 -3
- package/dist/src/browser/playbook/builtin-handlers.d.ts.map +0 -1
- package/dist/src/browser/playbook/builtin-handlers.js +0 -364
- package/dist/src/browser/playbook/builtin-handlers.js.map +0 -1
- package/dist/src/browser/playbook/index.d.ts +0 -3
- package/dist/src/browser/playbook/index.d.ts.map +0 -1
- package/dist/src/browser/playbook/index.js +0 -3
- package/dist/src/browser/playbook/index.js.map +0 -1
- package/dist/src/browser/playbook/store.d.ts +0 -8
- package/dist/src/browser/playbook/store.d.ts.map +0 -1
- package/dist/src/browser/playbook/store.js +0 -87
- package/dist/src/browser/playbook/store.js.map +0 -1
- package/dist/src/browser/workflow/builtin-handlers.d.ts +0 -3
- package/dist/src/browser/workflow/builtin-handlers.d.ts.map +0 -1
- package/dist/src/browser/workflow/builtin-handlers.js +0 -343
- package/dist/src/browser/workflow/builtin-handlers.js.map +0 -1
- package/dist/src/browser/workflow/engine.d.ts +0 -10
- package/dist/src/browser/workflow/engine.d.ts.map +0 -1
- package/dist/src/browser/workflow/engine.js +0 -174
- package/dist/src/browser/workflow/engine.js.map +0 -1
- package/dist/src/browser/workflow/expression.d.ts +0 -4
- package/dist/src/browser/workflow/expression.d.ts.map +0 -1
- package/dist/src/browser/workflow/expression.js +0 -89
- package/dist/src/browser/workflow/expression.js.map +0 -1
- package/dist/src/browser/workflow/index.d.ts +0 -6
- package/dist/src/browser/workflow/index.d.ts.map +0 -1
- package/dist/src/browser/workflow/index.js +0 -6
- package/dist/src/browser/workflow/index.js.map +0 -1
- package/dist/src/browser/workflow/store.d.ts +0 -8
- package/dist/src/browser/workflow/store.d.ts.map +0 -1
- package/dist/src/browser/workflow/store.js +0 -87
- package/dist/src/browser/workflow/store.js.map +0 -1
- package/dist/src/browser/workflow/types.d.ts +0 -62
- package/dist/src/browser/workflow/types.d.ts.map +0 -1
- package/dist/src/browser/workflow/types.js +0 -2
- package/dist/src/browser/workflow/types.js.map +0 -1
- package/dist/src/cli/playbook.d.ts +0 -3
- package/dist/src/cli/playbook.d.ts.map +0 -1
- package/dist/src/cli/playbook.js +0 -265
- package/dist/src/cli/playbook.js.map +0 -1
- package/dist/src/cli/workflow.d.ts +0 -2
- package/dist/src/cli/workflow.d.ts.map +0 -1
- package/dist/src/cli/workflow.js +0 -3
- package/dist/src/cli/workflow.js.map +0 -1
package/src/cli/commands.ts
CHANGED
|
@@ -10,16 +10,42 @@ import { createPlatformCommand } from './platform.js';
|
|
|
10
10
|
import type { CdpClient, ElementRef, NetworkRoute, FindAction } from '../index.js';
|
|
11
11
|
|
|
12
12
|
// Runtime state (single session per CLI process)
|
|
13
|
+
const DEFAULT_PORT = 9222;
|
|
13
14
|
let _client: CdpClient | null = null;
|
|
14
15
|
let _sessionId = '';
|
|
15
16
|
let _targetId = '';
|
|
16
|
-
let _port =
|
|
17
|
+
let _port = DEFAULT_PORT;
|
|
17
18
|
let _refs: Map<string, ElementRef> = new Map();
|
|
18
19
|
|
|
19
20
|
async function getBrowser() {
|
|
20
21
|
return import('../index.js');
|
|
21
22
|
}
|
|
22
23
|
|
|
24
|
+
// Each CLI invocation is a fresh process, so this module's `_port` variable
|
|
25
|
+
// is always freshly initialized to DEFAULT_PORT — a `--port 9333` passed to
|
|
26
|
+
// an earlier `open` command has no effect on this process's own default.
|
|
27
|
+
// Resolve the persisted "active port" (written by `open --port N`, read via
|
|
28
|
+
// ref-cache.ts's saveActivePort/loadActivePort) so subsequent commands
|
|
29
|
+
// attach to the browser the user actually opened instead of silently
|
|
30
|
+
// launching/attaching to a second, unrelated Chrome instance on 9222.
|
|
31
|
+
async function resolveDefaultPort(browser: Awaited<ReturnType<typeof getBrowser>>): Promise<number> {
|
|
32
|
+
const persisted = await browser.loadActivePortInfo();
|
|
33
|
+
if (!persisted) return DEFAULT_PORT;
|
|
34
|
+
if (!persisted.launched) {
|
|
35
|
+
// connect-origin port: the browser belongs to someone else. If it died,
|
|
36
|
+
// relaunching our own headless Chrome on that port would squat the
|
|
37
|
+
// user's debug port and silently swap which browser commands act on —
|
|
38
|
+
// fail loudly instead.
|
|
39
|
+
try {
|
|
40
|
+
await fetch(`http://127.0.0.1:${persisted.port}/json/version`, { signal: AbortSignal.timeout(1500) });
|
|
41
|
+
} catch {
|
|
42
|
+
await browser.clearActivePort();
|
|
43
|
+
throw new Error(`Connected browser on port ${persisted.port} is gone — re-run \`connect\` (or \`open\` to launch a fresh one).`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return persisted.port;
|
|
47
|
+
}
|
|
48
|
+
|
|
23
49
|
async function ensureConnected(port: number, targetId?: string) {
|
|
24
50
|
const browser = await getBrowser();
|
|
25
51
|
if (!_client || !_client.isConnected()) {
|
|
@@ -30,16 +56,54 @@ async function ensureConnected(port: number, targetId?: string) {
|
|
|
30
56
|
browser.teardownConsoleCapture(_sessionId);
|
|
31
57
|
_client.close();
|
|
32
58
|
}
|
|
33
|
-
|
|
59
|
+
const effectivePort = port === DEFAULT_PORT ? await resolveDefaultPort(browser) : port;
|
|
60
|
+
_port = await browser.launchBrowser({ port: effectivePort, headless: true });
|
|
34
61
|
const conn = await browser.connectToTarget(_port, targetId);
|
|
35
62
|
_client = conn.client;
|
|
36
63
|
_sessionId = conn.sessionId;
|
|
37
64
|
_targetId = conn.target.id;
|
|
38
65
|
_refs = new Map();
|
|
66
|
+
await hydrateRefsFromCache(browser, _targetId, conn.target.url);
|
|
39
67
|
}
|
|
40
68
|
return { client: _client!, sessionId: _sessionId, targetId: _targetId };
|
|
41
69
|
}
|
|
42
70
|
|
|
71
|
+
// Each CLI invocation is a fresh process, so the in-memory _refs Map built by
|
|
72
|
+
// a prior `snapshot` command is gone by the time a later `find`/`click`/etc.
|
|
73
|
+
// command runs. Rehydrate it from the on-disk ref cache (written by
|
|
74
|
+
// captureSnapshot call sites below) so refs resolved by a previous process
|
|
75
|
+
// remain usable. Falls back silently to an empty Map if no cache exists.
|
|
76
|
+
//
|
|
77
|
+
// The cached `url` field (captured at snapshot time) is compared against the
|
|
78
|
+
// browser's CURRENT url (`currentUrl`, from the just-fetched target info).
|
|
79
|
+
// A stale `backendDOMNodeId` can still successfully resolve via
|
|
80
|
+
// DOM.getBoxModel even after a same-tab SPA navigation or DOM mutation
|
|
81
|
+
// changed what's actually at those coordinates — so a mismatch here means
|
|
82
|
+
// EVERY ref in the cache is potentially pointing at the wrong element. We
|
|
83
|
+
// hard-invalidate (skip hydration entirely, so any @eN lookup fails loudly
|
|
84
|
+
// via resolveRef's "not found" error) rather than the weaker 30s time-based
|
|
85
|
+
// staleness check below, which only warns.
|
|
86
|
+
async function hydrateRefsFromCache(
|
|
87
|
+
browser: Awaited<ReturnType<typeof getBrowser>>,
|
|
88
|
+
targetId: string,
|
|
89
|
+
currentUrl: string
|
|
90
|
+
): Promise<void> {
|
|
91
|
+
const cached = await browser.loadRefCache(targetId);
|
|
92
|
+
if (!cached) return;
|
|
93
|
+
if (currentUrl && cached.url && currentUrl !== cached.url) {
|
|
94
|
+
output.printError(
|
|
95
|
+
`Stale references — page has navigated (cache: ${cached.url} → current: ${currentUrl}). Re-run snapshot before using @eN refs.`
|
|
96
|
+
);
|
|
97
|
+
return; // leave _refs empty — do not attempt to resolve refs against a different page
|
|
98
|
+
}
|
|
99
|
+
_refs = cached.refs;
|
|
100
|
+
if (cached.stale) {
|
|
101
|
+
output.printWarning(
|
|
102
|
+
`AX ref cache is ${Math.round(cached.ageMs / 1000)}s old — page may have changed since the last snapshot; re-run snapshot if refs don't resolve as expected`
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
43
107
|
// URL patterns that signal an unambiguous login/auth wall.
|
|
44
108
|
// Excludes 'auth', 'oauth', 'sso', 'saml' — their callback/ACS/token paths
|
|
45
109
|
// (/auth/callback, /sso/callback, /saml/acs) are completion endpoints, not walls.
|
|
@@ -128,11 +192,14 @@ async function switchToHeaded(url: string, port: number): Promise<void> {
|
|
|
128
192
|
const authCookies = await browser.getCookies(_client, _sessionId).catch(() => [] as unknown[]);
|
|
129
193
|
const authLocalStorage = await browser.getLocalStorage(_client, _sessionId).catch(() => ({}) as Record<string, string>);
|
|
130
194
|
|
|
131
|
-
// Close headed session
|
|
195
|
+
// Close headed session — actually terminate the underlying Chrome process
|
|
196
|
+
// (Browser.close CDP command, PID-kill fallback), not just our CDP client
|
|
197
|
+
// connection, so the visible authenticated window doesn't linger forever.
|
|
132
198
|
browser.teardownRouteInterception(_sessionId);
|
|
133
199
|
browser.stopRequestCapture(_sessionId);
|
|
134
200
|
browser.teardownDialogHandling(_sessionId);
|
|
135
201
|
browser.teardownConsoleCapture(_sessionId);
|
|
202
|
+
await browser.closeBrowser(_client, headedPort);
|
|
136
203
|
_client.close();
|
|
137
204
|
_client = null;
|
|
138
205
|
_sessionId = '';
|
|
@@ -154,6 +221,18 @@ async function switchToHeaded(url: string, port: number): Promise<void> {
|
|
|
154
221
|
}
|
|
155
222
|
}
|
|
156
223
|
|
|
224
|
+
/**
|
|
225
|
+
* Sanitize an image-format flag. The host CLI (monomind) defines a GLOBAL
|
|
226
|
+
* --format flag for output shaping (text|json|table, default 'text') that
|
|
227
|
+
* leaks into subcommand flags — so ctx.flags.format arrives as 'text' even
|
|
228
|
+
* when the user never passed it, and Chrome rejects it with
|
|
229
|
+
* "CDP error -32602: Invalid image format". Only trust values that are real
|
|
230
|
+
* image formats; otherwise fall back to the subcommand's own default.
|
|
231
|
+
*/
|
|
232
|
+
function imageFormat<T extends string>(value: unknown, allowed: readonly T[], fallback: T): T {
|
|
233
|
+
return allowed.includes(value as T) ? (value as T) : fallback;
|
|
234
|
+
}
|
|
235
|
+
|
|
157
236
|
function print(msg: string) {
|
|
158
237
|
process.stdout.write(msg + '\n');
|
|
159
238
|
}
|
|
@@ -203,11 +282,18 @@ const openCommand: Command = {
|
|
|
203
282
|
}
|
|
204
283
|
|
|
205
284
|
_port = await browser.launchBrowser({ port, headless: !forceHeaded });
|
|
285
|
+
// Persist the active port so subsequent CLI invocations (each a fresh
|
|
286
|
+
// process) default to attaching here instead of hardcoded 9222.
|
|
287
|
+
await browser.saveActivePort(_port);
|
|
206
288
|
const conn = await browser.connectToTarget(_port);
|
|
207
289
|
_client = conn.client;
|
|
208
290
|
_sessionId = conn.sessionId;
|
|
209
291
|
_targetId = conn.target.id;
|
|
210
292
|
_refs = new Map();
|
|
293
|
+
// A snapshot taken before this navigation is no longer valid for the new
|
|
294
|
+
// page — drop the persisted ref cache so a stale process's weak
|
|
295
|
+
// time-based check can't resurrect it before the next explicit snapshot.
|
|
296
|
+
await browser.clearRefCache();
|
|
211
297
|
|
|
212
298
|
if (ctx.flags.state && ctx.flags.session) {
|
|
213
299
|
output.printWarning('Both --state and --session provided; --state takes precedence');
|
|
@@ -266,13 +352,11 @@ const snapshotCommand: Command = {
|
|
|
266
352
|
});
|
|
267
353
|
|
|
268
354
|
_refs = result.refs;
|
|
355
|
+
await browser.saveRefCache(_targetId, result.url, _refs);
|
|
269
356
|
|
|
270
357
|
const applyOutputLimits = (text: string): string => {
|
|
271
|
-
let out = text;
|
|
272
358
|
const maxOutput = ctx.flags['max-output'] as number | undefined;
|
|
273
|
-
|
|
274
|
-
out = out.slice(0, maxOutput) + `\n[... truncated at ${maxOutput} chars]`;
|
|
275
|
-
}
|
|
359
|
+
let out = maxOutput ? truncateForOutput(text, maxOutput) : text;
|
|
276
360
|
if (ctx.flags['content-boundaries']) {
|
|
277
361
|
const nonce = Math.random().toString(36).slice(2, 10);
|
|
278
362
|
out = `MONOMIND_PAGE_CONTENT nonce=${nonce} origin=${result.url}\n${out}\nEND_MONOMIND_PAGE_CONTENT nonce=${nonce}`;
|
|
@@ -470,16 +554,29 @@ const waitCommand: Command = {
|
|
|
470
554
|
const rawTimeout = Math.min((ctx.flags.timeout as number) ?? 30000, MAX_DOWNLOAD_TIMEOUT);
|
|
471
555
|
const finalPath = await new Promise<string>((resolve, reject) => {
|
|
472
556
|
let guid = '';
|
|
557
|
+
let settled = false;
|
|
473
558
|
// C2: capture off() functions to avoid listener leaks
|
|
474
559
|
const offBegin = client.on('Browser.downloadWillBegin', (params: Record<string, unknown>) => { guid = params.guid as string; });
|
|
475
560
|
let offProgress: (() => void) | undefined;
|
|
476
561
|
// cleanup defined before setTimeout so the timeout callback can call it
|
|
477
562
|
let tid: ReturnType<typeof setTimeout>;
|
|
478
|
-
|
|
479
|
-
|
|
563
|
+
let pollTid: ReturnType<typeof setInterval> | undefined;
|
|
564
|
+
const cleanup = () => { clearTimeout(tid); clearInterval(pollTid); offBegin?.(); offProgress?.(); };
|
|
565
|
+
const finish = (path: string) => {
|
|
566
|
+
if (settled) return;
|
|
567
|
+
settled = true;
|
|
568
|
+
cleanup();
|
|
569
|
+
resolve(path);
|
|
570
|
+
};
|
|
571
|
+
const fail = (err: Error) => {
|
|
572
|
+
if (settled) return;
|
|
573
|
+
settled = true;
|
|
574
|
+
cleanup();
|
|
575
|
+
reject(err);
|
|
576
|
+
};
|
|
577
|
+
tid = setTimeout(() => fail(new Error('Download timed out')), rawTimeout);
|
|
480
578
|
offProgress = client.on('Browser.downloadProgress', async (params: Record<string, unknown>) => {
|
|
481
579
|
if (params.guid === guid && params.state === 'completed') {
|
|
482
|
-
cleanup();
|
|
483
580
|
const { readdir, rename, rmdir } = await import('fs/promises');
|
|
484
581
|
const files = await readdir(downloadDir);
|
|
485
582
|
if (files.length > 0) {
|
|
@@ -487,16 +584,40 @@ const waitCommand: Command = {
|
|
|
487
584
|
await mkdir(dirname(savePath), { recursive: true });
|
|
488
585
|
await rename(src, savePath);
|
|
489
586
|
await rmdir(downloadDir).catch(() => {}); // I1: cleanup temp dir
|
|
490
|
-
|
|
587
|
+
finish(savePath);
|
|
491
588
|
} else {
|
|
492
589
|
await rmdir(downloadDir).catch(() => {}); // I1: cleanup temp dir
|
|
493
|
-
|
|
590
|
+
fail(new Error('Download completed but no file found'));
|
|
494
591
|
}
|
|
495
592
|
} else if (params.guid === guid && params.state === 'canceled') {
|
|
496
|
-
|
|
497
|
-
reject(new Error('Download was canceled'));
|
|
593
|
+
fail(new Error('Download was canceled'));
|
|
498
594
|
}
|
|
499
595
|
});
|
|
596
|
+
|
|
597
|
+
// Fallback for the empty-guid race: this process's listener attaches
|
|
598
|
+
// AFTER a separate `click` process may have already started (and even
|
|
599
|
+
// finished) the download — so downloadWillBegin/downloadProgress for it
|
|
600
|
+
// were never observed here. While guid is still empty, poll the expected
|
|
601
|
+
// output path directly: if it already exists with a stable, non-zero
|
|
602
|
+
// size across two consecutive checks, treat the download as complete.
|
|
603
|
+
let lastSize = -1;
|
|
604
|
+
let stableReads = 0;
|
|
605
|
+
pollTid = setInterval(async () => {
|
|
606
|
+
if (settled || guid) return; // a real CDP event has taken over
|
|
607
|
+
try {
|
|
608
|
+
const { stat } = await import('fs/promises');
|
|
609
|
+
const st = await stat(savePath);
|
|
610
|
+
if (st.isFile() && st.size > 0 && st.size === lastSize) {
|
|
611
|
+
stableReads++;
|
|
612
|
+
if (stableReads >= 2) finish(savePath);
|
|
613
|
+
} else {
|
|
614
|
+
stableReads = 0;
|
|
615
|
+
}
|
|
616
|
+
lastSize = st.size;
|
|
617
|
+
} catch {
|
|
618
|
+
// savePath not present yet — keep polling until timeout
|
|
619
|
+
}
|
|
620
|
+
}, 300);
|
|
500
621
|
});
|
|
501
622
|
output.printSuccess(`Download saved: ${finalPath}`);
|
|
502
623
|
return { success: true, data: { path: finalPath } };
|
|
@@ -545,7 +666,7 @@ const screenshotCommand: Command = {
|
|
|
545
666
|
result = await browser.captureScreenshot(client, sessionId, {
|
|
546
667
|
path: ctx.args[0] as string,
|
|
547
668
|
fullPage: ctx.flags.full as boolean,
|
|
548
|
-
format: ctx.flags.format
|
|
669
|
+
format: imageFormat(ctx.flags.format, ['png', 'jpeg', 'webp'] as const, 'png'),
|
|
549
670
|
quality: ctx.flags.quality as number,
|
|
550
671
|
annotate,
|
|
551
672
|
refs: annotate ? _refs : undefined,
|
|
@@ -772,6 +893,10 @@ const navigateCommand: Command = {
|
|
|
772
893
|
throw new Error(`Unknown direction: ${direction}. Use: back|forward|reload`);
|
|
773
894
|
}
|
|
774
895
|
|
|
896
|
+
// Refs captured before this navigation may now point at different content.
|
|
897
|
+
_refs = new Map();
|
|
898
|
+
await browser.clearRefCache();
|
|
899
|
+
|
|
775
900
|
output.printSuccess(`Navigated: ${direction}`);
|
|
776
901
|
return { success: true };
|
|
777
902
|
},
|
|
@@ -918,6 +1043,7 @@ const stateCommand: Command = {
|
|
|
918
1043
|
await browser.clearLocalStorage(c, sid);
|
|
919
1044
|
await browser.clearSessionStorage(c, sid);
|
|
920
1045
|
_refs = new Map();
|
|
1046
|
+
await browser.clearRefCache();
|
|
921
1047
|
output.printSuccess('Browser state cleared (cookies, localStorage, sessionStorage, refs)');
|
|
922
1048
|
return { success: true };
|
|
923
1049
|
}
|
|
@@ -1076,12 +1202,26 @@ const networkCommand: Command = {
|
|
|
1076
1202
|
},
|
|
1077
1203
|
};
|
|
1078
1204
|
|
|
1205
|
+
// Default output cap for eval when --max-output isn't passed explicitly —
|
|
1206
|
+
// print(String(result)) had NO limit at all (unlike snapshot's --max-output),
|
|
1207
|
+
// so e.g. `eval "document.documentElement.outerHTML"` could dump megabytes
|
|
1208
|
+
// straight into an agent's context. An explicit --max-output still overrides
|
|
1209
|
+
// this default.
|
|
1210
|
+
const DEFAULT_EVAL_MAX_OUTPUT = 50_000;
|
|
1211
|
+
|
|
1212
|
+
function truncateForOutput(text: string, maxOutput: number): string {
|
|
1213
|
+
if (!(maxOutput > 0) || text.length <= maxOutput) return text;
|
|
1214
|
+
return text.slice(0, maxOutput) + `\n[... truncated at ${maxOutput} chars]`;
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1079
1217
|
const evalCommand: Command = {
|
|
1080
1218
|
name: 'eval',
|
|
1081
1219
|
description: 'Evaluate JavaScript in page context. Usage: monomind browse eval "document.title"',
|
|
1082
1220
|
options: [
|
|
1083
1221
|
{ name: 'json', type: 'boolean', description: 'Output as JSON', default: false },
|
|
1084
1222
|
{ name: 'stdin', type: 'boolean', description: 'Read JS expression from stdin (heredoc-friendly for multiline scripts)', default: false },
|
|
1223
|
+
{ name: 'max-output', type: 'number', description: `Truncate printed output to N characters (default ${DEFAULT_EVAL_MAX_OUTPUT}; 0 disables truncation)` },
|
|
1224
|
+
{ name: 'timeout', type: 'number', description: 'Max ms to wait for evaluation to settle (default 30000)' },
|
|
1085
1225
|
],
|
|
1086
1226
|
action: async (ctx: CommandContext): Promise<CommandResult> => {
|
|
1087
1227
|
const { client, sessionId } = await ensureConnected(_port);
|
|
@@ -1095,12 +1235,14 @@ const evalCommand: Command = {
|
|
|
1095
1235
|
}
|
|
1096
1236
|
if (!expr) throw new Error('Usage: monomind browse eval "<expression>" (or pipe with --stdin)');
|
|
1097
1237
|
|
|
1098
|
-
const
|
|
1238
|
+
const timeoutMs = ctx.flags.timeout as number | undefined;
|
|
1239
|
+
const result = await browser.evaluateJs(client, sessionId, expr, timeoutMs);
|
|
1099
1240
|
|
|
1241
|
+
const maxOutput = (ctx.flags['max-output'] as number | undefined) ?? DEFAULT_EVAL_MAX_OUTPUT;
|
|
1100
1242
|
if (ctx.flags.json) {
|
|
1101
|
-
print(JSON.stringify({ data: result }));
|
|
1243
|
+
print(truncateForOutput(JSON.stringify({ data: result }), maxOutput));
|
|
1102
1244
|
} else {
|
|
1103
|
-
print(String(result ?? ''));
|
|
1245
|
+
print(truncateForOutput(String(result ?? ''), maxOutput));
|
|
1104
1246
|
}
|
|
1105
1247
|
|
|
1106
1248
|
return { success: true, data: { result } };
|
|
@@ -1134,9 +1276,54 @@ const closeCommand: Command = {
|
|
|
1134
1276
|
_sessionId = '';
|
|
1135
1277
|
_targetId = '';
|
|
1136
1278
|
_refs = new Map();
|
|
1279
|
+
// Session is gone — forget the persisted port and stale refs so later
|
|
1280
|
+
// invocations don't chase a dead endpoint (or the wrong elements).
|
|
1281
|
+
await browser.clearActivePort();
|
|
1282
|
+
await browser.clearRefCache();
|
|
1137
1283
|
output.printSuccess('Browser session closed');
|
|
1138
1284
|
} else {
|
|
1139
|
-
|
|
1285
|
+
// Each CLI invocation is a fresh process, so `close` almost always
|
|
1286
|
+
// lands here. The persisted port file is the real session handle:
|
|
1287
|
+
// if monobrowse LAUNCHED that browser (open), gracefully Browser.close
|
|
1288
|
+
// it — otherwise every open→close cycle leaks a headless Chrome. If we
|
|
1289
|
+
// merely ATTACHED to it (connect, launched:false), never kill it: it's
|
|
1290
|
+
// the user's own browser. Either way, forget the port and refs.
|
|
1291
|
+
const browser = await getBrowser();
|
|
1292
|
+
const persisted = await browser.loadActivePortInfo();
|
|
1293
|
+
if (persisted?.launched) {
|
|
1294
|
+
try {
|
|
1295
|
+
const conn = await browser.connectToTarget(persisted.port);
|
|
1296
|
+
try {
|
|
1297
|
+
await browser.closeBrowser(conn.client, persisted.port);
|
|
1298
|
+
} finally {
|
|
1299
|
+
// Always drop our websocket — a hung Browser.close must not keep
|
|
1300
|
+
// this CLI process's event loop alive.
|
|
1301
|
+
try { conn.client.close(); } catch { /* already gone */ }
|
|
1302
|
+
}
|
|
1303
|
+
// closeBrowser has no PID fallback in a fresh process (launchedPids
|
|
1304
|
+
// is per-process) — re-probe so we report what actually happened.
|
|
1305
|
+
// Poll briefly: Browser.close is acknowledged before the process
|
|
1306
|
+
// actually exits, so a single immediate probe false-alarms.
|
|
1307
|
+
let stillUp = true;
|
|
1308
|
+
const probeDeadline = Date.now() + 3000;
|
|
1309
|
+
while (stillUp && Date.now() < probeDeadline) {
|
|
1310
|
+
try {
|
|
1311
|
+
await fetch(`http://127.0.0.1:${persisted.port}/json/version`, { signal: AbortSignal.timeout(800) });
|
|
1312
|
+
await new Promise(r => setTimeout(r, 300));
|
|
1313
|
+
} catch { stillUp = false; }
|
|
1314
|
+
}
|
|
1315
|
+
if (stillUp) output.printWarning(`Browser on port ${persisted.port} did not exit — kill it manually if needed`);
|
|
1316
|
+
else output.printSuccess(`Closed browser on port ${persisted.port}`);
|
|
1317
|
+
} catch {
|
|
1318
|
+
output.printInfo(`No browser answering on port ${persisted.port} — nothing to close`);
|
|
1319
|
+
}
|
|
1320
|
+
} else {
|
|
1321
|
+
output.printInfo(persisted
|
|
1322
|
+
? `Detached from browser on port ${persisted.port} (attached via connect — left running)`
|
|
1323
|
+
: 'No active browser session');
|
|
1324
|
+
}
|
|
1325
|
+
await browser.clearActivePort();
|
|
1326
|
+
await browser.clearRefCache();
|
|
1140
1327
|
}
|
|
1141
1328
|
return { success: true };
|
|
1142
1329
|
},
|
|
@@ -2228,6 +2415,7 @@ const diffCommand: Command = {
|
|
|
2228
2415
|
const snap2 = await browser.captureSnapshot(client, sessionId, { interactiveOnly: ctx.flags.interactive as boolean });
|
|
2229
2416
|
|
|
2230
2417
|
_refs = snap2.refs;
|
|
2418
|
+
await browser.saveRefCache(_targetId, snap2.url, _refs);
|
|
2231
2419
|
|
|
2232
2420
|
// Text diff
|
|
2233
2421
|
const lines1 = snap1.text.split('\n');
|
|
@@ -2265,6 +2453,10 @@ const pushstateCommand: Command = {
|
|
|
2265
2453
|
const url = ctx.args[0] as string;
|
|
2266
2454
|
if (!url) throw new Error('Usage: monomind browse pushstate <url>');
|
|
2267
2455
|
await browser.pushState(client, sessionId, url);
|
|
2456
|
+
// SPA navigation changes what's on the page without a full page load —
|
|
2457
|
+
// refs captured before this pushState call may now resolve to different content.
|
|
2458
|
+
_refs = new Map();
|
|
2459
|
+
await browser.clearRefCache();
|
|
2268
2460
|
output.printSuccess(`pushState: ${url}`);
|
|
2269
2461
|
return { success: true };
|
|
2270
2462
|
},
|
|
@@ -2290,6 +2482,54 @@ function tokenizeBatchCommand(input: string): string[] {
|
|
|
2290
2482
|
return tokens;
|
|
2291
2483
|
}
|
|
2292
2484
|
|
|
2485
|
+
/**
|
|
2486
|
+
* Splits one line of a `batch` command string into a subcommand name, its
|
|
2487
|
+
* args, and any recognized leading flags.
|
|
2488
|
+
*
|
|
2489
|
+
* `eval` is special-cased: its sole argument is a raw JS expression, which
|
|
2490
|
+
* legitimately contains its own string-literal quotes (e.g.
|
|
2491
|
+
* `eval document.querySelector('a')`). tokenizeBatchCommand's shell-style
|
|
2492
|
+
* word-splitting treats `'...'`/`"..."` as grouping delimiters and discards
|
|
2493
|
+
* them — correct for a value like `fill @e1 "some text"`, but for `eval` it
|
|
2494
|
+
* silently deletes the expression's own quotes, turning a string literal
|
|
2495
|
+
* into a bare (undefined) identifier reference and producing a
|
|
2496
|
+
* ReferenceError instead of evaluating the intended expression. For `eval`,
|
|
2497
|
+
* only its own known --flags are consumed from the front; everything after
|
|
2498
|
+
* them is taken verbatim as one argument, untouched by tokenization.
|
|
2499
|
+
*
|
|
2500
|
+
* Exported for direct unit testing — not part of the CLI's public API.
|
|
2501
|
+
*/
|
|
2502
|
+
export function parseBatchCommandLine(cmdStr: string): { subName: string; subArgs: string[]; flags: Record<string, unknown> } {
|
|
2503
|
+
const trimmed = cmdStr.trim();
|
|
2504
|
+
const evalMatch = trimmed.match(/^eval\b\s*/);
|
|
2505
|
+
if (!evalMatch) {
|
|
2506
|
+
const parts = tokenizeBatchCommand(trimmed);
|
|
2507
|
+
return { subName: parts[0], subArgs: parts.slice(1), flags: {} };
|
|
2508
|
+
}
|
|
2509
|
+
|
|
2510
|
+
let rest = trimmed.slice(evalMatch[0].length);
|
|
2511
|
+
const flags: Record<string, unknown> = {};
|
|
2512
|
+
let consumedAnother = true;
|
|
2513
|
+
while (consumedAnother) {
|
|
2514
|
+
consumedAnother = false;
|
|
2515
|
+
const withValue = rest.match(/^--(max-output|timeout)\s+(-?\d+)\s*/);
|
|
2516
|
+
if (withValue) {
|
|
2517
|
+
flags[withValue[1]] = Number(withValue[2]);
|
|
2518
|
+
rest = rest.slice(withValue[0].length);
|
|
2519
|
+
consumedAnother = true;
|
|
2520
|
+
continue;
|
|
2521
|
+
}
|
|
2522
|
+
const boolFlag = rest.match(/^--(json|stdin)\b\s*/);
|
|
2523
|
+
if (boolFlag) {
|
|
2524
|
+
flags[boolFlag[1]] = true;
|
|
2525
|
+
rest = rest.slice(boolFlag[0].length);
|
|
2526
|
+
consumedAnother = true;
|
|
2527
|
+
continue;
|
|
2528
|
+
}
|
|
2529
|
+
}
|
|
2530
|
+
return { subName: 'eval', subArgs: [rest], flags };
|
|
2531
|
+
}
|
|
2532
|
+
|
|
2293
2533
|
const batchCommand: Command = {
|
|
2294
2534
|
name: 'batch',
|
|
2295
2535
|
description: 'Execute multiple commands. Usage: monomind browse batch "open url" "snapshot -i" "click @e1"',
|
|
@@ -2303,9 +2543,7 @@ const batchCommand: Command = {
|
|
|
2303
2543
|
|
|
2304
2544
|
const results: Array<{ command: string; success: boolean; error?: string }> = [];
|
|
2305
2545
|
for (const cmdStr of commands) {
|
|
2306
|
-
const
|
|
2307
|
-
const subName = parts[0];
|
|
2308
|
-
const subArgs = parts.slice(1);
|
|
2546
|
+
const { subName, subArgs, flags: preParsedFlags } = parseBatchCommandLine(cmdStr);
|
|
2309
2547
|
|
|
2310
2548
|
const subCmd = browseCommand.subcommands?.find((s) => s.name === subName);
|
|
2311
2549
|
if (!subCmd?.action) {
|
|
@@ -2316,7 +2554,7 @@ const batchCommand: Command = {
|
|
|
2316
2554
|
}
|
|
2317
2555
|
|
|
2318
2556
|
try {
|
|
2319
|
-
const parsedFlags: CommandContext['flags'] = { _: [] };
|
|
2557
|
+
const parsedFlags: CommandContext['flags'] = { _: [], ...preParsedFlags };
|
|
2320
2558
|
const consumedIndices = new Set<number>();
|
|
2321
2559
|
// Parse --flags from subArgs, tracking which indices are flag names/values
|
|
2322
2560
|
for (let i = 0; i < subArgs.length; i++) {
|
|
@@ -2420,7 +2658,7 @@ const removeinitscriptCommand: Command = {
|
|
|
2420
2658
|
|
|
2421
2659
|
const connectCommand: Command = {
|
|
2422
2660
|
name: 'connect',
|
|
2423
|
-
description: 'Connect to existing Chrome instance. Usage: monomind browse connect [--port 9222] [--target <id>] [--auto-connect]',
|
|
2661
|
+
description: 'Connect to existing Chrome instance; later commands reuse this session (note: `open` without --port still launches on its own default). Usage: monomind browse connect [--port 9222] [--target <id>] [--auto-connect]',
|
|
2424
2662
|
options: [
|
|
2425
2663
|
{ name: 'port', short: 'p', type: 'number', description: 'CDP port', default: 9222 },
|
|
2426
2664
|
{ name: 'target', type: 'string', description: 'Target ID to attach to' },
|
|
@@ -2472,6 +2710,12 @@ const connectCommand: Command = {
|
|
|
2472
2710
|
_targetId = conn.target.id;
|
|
2473
2711
|
_port = port;
|
|
2474
2712
|
_refs = new Map();
|
|
2713
|
+
// Persist the port like `open` does — without this, the NEXT CLI process
|
|
2714
|
+
// (each command is a fresh process) resolves the hardcoded default and
|
|
2715
|
+
// tries to launch its own Chrome on 9222 instead of reusing this session.
|
|
2716
|
+
// launched:false marks this browser as someone else's — close must never
|
|
2717
|
+
// kill it, and a dead endpoint must not be silently relaunched.
|
|
2718
|
+
await browser.saveActivePort(port, { launched: false });
|
|
2475
2719
|
const url = await browser.getCurrentUrl(_client, _sessionId);
|
|
2476
2720
|
const title = await browser.getCurrentTitle(_client, _sessionId);
|
|
2477
2721
|
output.printSuccess(`Connected: ${title} (${url})`);
|
|
@@ -2496,7 +2740,7 @@ const recordCommand: Command = {
|
|
|
2496
2740
|
switch (action) {
|
|
2497
2741
|
case 'start':
|
|
2498
2742
|
await browser.startRecording(client, sessionId, {
|
|
2499
|
-
format: ctx.flags.format
|
|
2743
|
+
format: imageFormat(ctx.flags.format, ['jpeg', 'png'] as const, 'jpeg'),
|
|
2500
2744
|
quality: ctx.flags.quality as number,
|
|
2501
2745
|
});
|
|
2502
2746
|
output.printSuccess('Recording started');
|
|
@@ -2510,12 +2754,12 @@ const recordCommand: Command = {
|
|
|
2510
2754
|
case 'restart': {
|
|
2511
2755
|
const prevStatus = browser.getRecordingStatus(sessionId);
|
|
2512
2756
|
let prevPath: string | undefined;
|
|
2513
|
-
if (prevStatus.recording) {
|
|
2757
|
+
if (prevStatus.recording || prevStatus.autoStopped) {
|
|
2514
2758
|
prevPath = await browser.stopRecording(client, sessionId, ctx.args[1] as string);
|
|
2515
2759
|
output.printInfo(`Previous recording saved: ${prevPath}`);
|
|
2516
2760
|
}
|
|
2517
2761
|
await browser.startRecording(client, sessionId, {
|
|
2518
|
-
format: ctx.flags.format
|
|
2762
|
+
format: imageFormat(ctx.flags.format, ['jpeg', 'png'] as const, 'jpeg'),
|
|
2519
2763
|
quality: ctx.flags.quality as number,
|
|
2520
2764
|
});
|
|
2521
2765
|
output.printSuccess('Recording restarted');
|
|
@@ -2524,7 +2768,7 @@ const recordCommand: Command = {
|
|
|
2524
2768
|
case 'status': {
|
|
2525
2769
|
const status = browser.getRecordingStatus(sessionId);
|
|
2526
2770
|
if (ctx.flags.json) print(JSON.stringify({ data: status }));
|
|
2527
|
-
else print(`Recording: ${status.recording} | Frames: ${status.frames}`);
|
|
2771
|
+
else print(`Recording: ${status.recording} | Frames: ${status.frames}${status.autoStopped ? ' (auto-stopped: buffer limit reached — run "record stop" to save)' : ''}`);
|
|
2528
2772
|
return { success: true, data: status };
|
|
2529
2773
|
}
|
|
2530
2774
|
default:
|
package/src/cli/output.ts
CHANGED
|
File without changes
|
package/src/cli/platform.ts
CHANGED
|
File without changes
|
package/src/cli/types.ts
CHANGED
|
File without changes
|
package/src/cli.ts
CHANGED
|
File without changes
|
package/src/index.ts
CHANGED
|
File without changes
|
package/dist/package.json
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@monoes/monobrowse",
|
|
3
|
-
"version": "1.0.1",
|
|
4
|
-
"description": "Native browser automation via Chrome DevTools Protocol — the engine powering monomind browse",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"monobrowse": "./src/cli.js"
|
|
8
|
-
},
|
|
9
|
-
"main": "./src/index.js",
|
|
10
|
-
"types": "./src/index.d.ts",
|
|
11
|
-
"exports": {
|
|
12
|
-
".": {
|
|
13
|
-
"types": "./src/index.d.ts",
|
|
14
|
-
"import": "./src/index.js"
|
|
15
|
-
},
|
|
16
|
-
"./cli": {
|
|
17
|
-
"types": "./src/cli.d.ts",
|
|
18
|
-
"import": "./src/cli.js"
|
|
19
|
-
},
|
|
20
|
-
"./cli/commands": {
|
|
21
|
-
"types": "./src/cli/commands.d.ts",
|
|
22
|
-
"import": "./src/cli/commands.js"
|
|
23
|
-
},
|
|
24
|
-
"./cli/types": {
|
|
25
|
-
"types": "./src/cli/types.d.ts",
|
|
26
|
-
"import": "./src/cli/types.js"
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
"files": [
|
|
30
|
-
"dist",
|
|
31
|
-
"src"
|
|
32
|
-
],
|
|
33
|
-
"engines": {
|
|
34
|
-
"node": ">=18.0.0"
|
|
35
|
-
},
|
|
36
|
-
"keywords": [
|
|
37
|
-
"browser-automation",
|
|
38
|
-
"cdp",
|
|
39
|
-
"chrome-devtools-protocol",
|
|
40
|
-
"monomind",
|
|
41
|
-
"web-scraping",
|
|
42
|
-
"accessibility"
|
|
43
|
-
],
|
|
44
|
-
"author": {
|
|
45
|
-
"name": "nokhodian",
|
|
46
|
-
"email": "nokhodian@gmail.com",
|
|
47
|
-
"url": "https://github.com/nokhodian"
|
|
48
|
-
},
|
|
49
|
-
"license": "MIT",
|
|
50
|
-
"repository": {
|
|
51
|
-
"type": "git",
|
|
52
|
-
"url": "https://github.com/monoes/monomind.git",
|
|
53
|
-
"directory": "packages/@monoes/monobrowse"
|
|
54
|
-
},
|
|
55
|
-
"dependencies": {
|
|
56
|
-
"commander": "^12.1.0",
|
|
57
|
-
"ws": "^8.21.0"
|
|
58
|
-
},
|
|
59
|
-
"peerDependencies": {
|
|
60
|
-
"@anthropic-ai/sdk": ">=0.20.0"
|
|
61
|
-
},
|
|
62
|
-
"peerDependenciesMeta": {
|
|
63
|
-
"@anthropic-ai/sdk": {
|
|
64
|
-
"optional": true
|
|
65
|
-
}
|
|
66
|
-
},
|
|
67
|
-
"devDependencies": {
|
|
68
|
-
"@anthropic-ai/sdk": "^0.39.0",
|
|
69
|
-
"typescript": "^5.8.3"
|
|
70
|
-
}
|
|
71
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"browser-handlers.d.ts","sourceRoot":"","sources":["../../../../src/browser/playbook/browser-handlers.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,WAAW,EAAQ,MAAM,sBAAsB,CAAC;AAkI9D,wBAAgB,qBAAqB,IAAI,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CA2ShE"}
|