@crouton-kit/capture 1.3.8 → 1.3.10
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/bin/capture +29841 -8539
- package/package.json +6 -3
- package/src/capture.ts +86 -173
- package/src/cdp/a11y.ts +113 -14
- package/src/cdp/args.ts +300 -205
- package/src/cdp/bridge/protocol.ts +93 -6
- package/src/cdp/bridge/server.ts +118 -53
- package/src/cdp/bridge/spawn.ts +53 -6
- package/src/cdp/client.ts +13 -3
- package/src/cdp/commands/bridge-serve.ts +20 -14
- package/src/cdp/commands/cdp.ts +259 -90
- package/src/cdp/commands/gate-guard.ts +43 -0
- package/src/cdp/commands/lib.ts +339 -213
- package/src/cdp/commands/measure/census.ts +76 -20
- package/src/cdp/commands/measure/check.ts +100 -26
- package/src/cdp/commands/measure/diff.ts +109 -21
- package/src/cdp/commands/measure/explain.ts +121 -19
- package/src/cdp/commands/measure/index.ts +31 -39
- package/src/cdp/commands/measure/map-ax.ts +52 -0
- package/src/cdp/commands/measure/map-focus.ts +41 -13
- package/src/cdp/commands/measure/map-layers.ts +49 -15
- package/src/cdp/commands/measure/map-scroll.ts +85 -15
- package/src/cdp/commands/measure/snap.ts +405 -25
- package/src/cdp/commands/measure/sweep.ts +217 -20
- package/src/cdp/commands/motion/index.ts +18 -17
- package/src/cdp/commands/motion/jank.ts +119 -9
- package/src/cdp/commands/motion/mask.ts +81 -12
- package/src/cdp/commands/motion/rec.ts +520 -19
- package/src/cdp/commands/motion/response.ts +65 -15
- package/src/cdp/commands/motion/timeline.ts +139 -14
- package/src/cdp/commands/page/click.ts +194 -0
- package/src/cdp/commands/page/elements.ts +154 -0
- package/src/cdp/commands/page/exec.ts +317 -0
- package/src/cdp/commands/page/index.ts +72 -0
- package/src/cdp/commands/page/navigate.ts +330 -0
- package/src/cdp/commands/page/scroll.ts +107 -0
- package/src/cdp/commands/page/shot.ts +208 -0
- package/src/cdp/commands/page/type.ts +104 -0
- package/src/cdp/commands/tab/index.ts +59 -0
- package/src/cdp/commands/tab/list.ts +167 -0
- package/src/cdp/commands/tab/network.ts +162 -0
- package/src/cdp/commands/tab/open.ts +90 -0
- package/src/cdp/commands/tab/reset.ts +160 -0
- package/src/cdp/connection.ts +368 -37
- package/src/cdp/detect.ts +42 -13
- package/src/cdp/dispatch.ts +74 -28
- package/src/cdp/har-recorder.ts +648 -115
- package/src/cdp/leaf-grammar.ts +99 -0
- package/src/cdp/measure/census.ts +205 -0
- package/src/cdp/measure/check.ts +318 -0
- package/src/cdp/measure/collectors/animation.ts +385 -0
- package/src/cdp/measure/collectors/ax.ts +278 -0
- package/src/cdp/measure/collectors/focus.ts +1041 -0
- package/src/cdp/measure/collectors/forms.ts +905 -0
- package/src/cdp/measure/collectors/geometry.ts +1038 -0
- package/src/cdp/measure/collectors/hittest.ts +835 -0
- package/src/cdp/measure/collectors/layers.ts +697 -0
- package/src/cdp/measure/collectors/media.ts +522 -0
- package/src/cdp/measure/collectors/pixels.ts +1584 -0
- package/src/cdp/measure/collectors/queries.ts +464 -0
- package/src/cdp/measure/collectors/scroll.ts +599 -0
- package/src/cdp/measure/collectors/states.ts +992 -0
- package/src/cdp/measure/collectors/style-provenance.ts +643 -0
- package/src/cdp/measure/collectors/styles.ts +355 -0
- package/src/cdp/measure/collectors/text.ts +713 -0
- package/src/cdp/measure/diff.ts +470 -0
- package/src/cdp/measure/explain.ts +498 -0
- package/src/cdp/measure/map-ax.ts +633 -0
- package/src/cdp/measure/map-focus.ts +188 -0
- package/src/cdp/measure/map-layers.ts +177 -0
- package/src/cdp/measure/map-scroll.ts +317 -0
- package/src/cdp/measure/redaction.ts +51 -0
- package/src/cdp/measure/settle.ts +1051 -0
- package/src/cdp/measure/snapshot.ts +390 -0
- package/src/cdp/measure/sweep.ts +330 -0
- package/src/cdp/measure/types.ts +220 -0
- package/src/cdp/measure/v2/collectors.ts +19 -0
- package/src/cdp/measure/v2/orchestrator.ts +46 -0
- package/src/cdp/measure/v2/snap-selection.ts +33 -0
- package/src/cdp/measure/v2/target-lock.ts +15 -0
- package/src/cdp/measure/v2/target.ts +16 -0
- package/src/cdp/motion/jank.ts +433 -0
- package/src/cdp/motion/mask.ts +421 -0
- package/src/cdp/motion/recorder.ts +764 -0
- package/src/cdp/motion/response.ts +356 -0
- package/src/cdp/motion/timeline.ts +215 -0
- package/src/cdp/record.ts +5 -212
- package/src/cdp/recorder-bridge.ts +1478 -105
- package/src/cdp/recorder-client.ts +326 -0
- package/src/cdp/scope-lock.ts +73 -0
- package/src/cdp/screenshot.ts +199 -66
- package/src/cdp/targets.ts +21 -1
- package/src/cdp/timing.ts +15 -10
- package/src/cdp/types.ts +22 -7
- package/src/cdp/viewport.ts +21 -0
- package/src/cdp.ts +12 -58
- package/src/command-help.ts +29 -0
- package/src/command-resolution.ts +22 -0
- package/src/command-walker.ts +35 -0
- package/src/contracts/a11y.ts +33 -0
- package/src/contracts/cursor.ts +35 -0
- package/src/contracts/index.ts +15 -0
- package/src/contracts/motion.ts +19 -0
- package/src/contracts/neutral.ts +56 -0
- package/src/contracts/primitives.ts +205 -0
- package/src/contracts/release.ts +17 -0
- package/src/contracts/results.ts +208 -0
- package/src/contracts/routes.ts +140 -0
- package/src/contracts/snapshot.ts +92 -0
- package/src/cursor/index.ts +13 -0
- package/src/cursor/service.ts +310 -0
- package/src/errors.ts +71 -0
- package/src/har-manager.ts +992 -41
- package/src/interact.ts +410 -98
- package/src/output/artifact-lifecycle.ts +371 -0
- package/src/output/artifact.ts +6 -0
- package/src/output/exact-raw.ts +114 -0
- package/src/output/render.ts +396 -2
- package/src/output/structured.ts +211 -0
- package/src/registry.ts +208 -0
- package/src/session/artifacts.ts +452 -200
- package/src/session/commands.ts +1162 -397
- package/src/session/coordinator.ts +251 -0
- package/src/session/har-filter.ts +57 -0
- package/src/session/log-tailer.ts +653 -0
- package/src/session-context.ts +279 -51
- package/src/vault/bundle.ts +32 -17
- package/src/cdp/commands/exec.ts +0 -105
- package/src/cdp/commands/har.ts +0 -128
- package/src/cdp/commands/tabs.ts +0 -183
- package/src/cdp/commands/traffic.ts +0 -121
- package/src/cdp/commands/ui.ts +0 -172
- package/src/cdp/execute.ts +0 -113
- package/src/cdp/har-output.ts +0 -47
- package/src/cdp/usage.ts +0 -79
- package/src/tab-session.ts +0 -91
package/src/cdp/detect.ts
CHANGED
|
@@ -118,6 +118,37 @@ function getLocalhostListeningPorts(): number[] {
|
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
+
function getListeningProcessNames(): Map<number, string> {
|
|
122
|
+
const names = new Map<number, string>();
|
|
123
|
+
try {
|
|
124
|
+
const output = execSync('lsof -nP -iTCP -sTCP:LISTEN -Fpcn 2>/dev/null', {
|
|
125
|
+
encoding: 'utf-8',
|
|
126
|
+
});
|
|
127
|
+
let command: string | null = null;
|
|
128
|
+
for (const line of output.split('\n')) {
|
|
129
|
+
if (line.startsWith('c')) {
|
|
130
|
+
command = line.slice(1);
|
|
131
|
+
} else if (command && line.startsWith('n')) {
|
|
132
|
+
const match = line.match(/:(\d+)(?:\s|$)/);
|
|
133
|
+
if (match) names.set(Number(match[1]), command);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
} catch {
|
|
137
|
+
// Process identity is an optional refinement; CDP probing still works.
|
|
138
|
+
}
|
|
139
|
+
return names;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function identifyBrowserBundleId(browser: string, processName?: string): string {
|
|
143
|
+
// A listening process is stronger identity than Chromium's generic
|
|
144
|
+
// Browser/User-Agent strings: Arc and Spotify both report as "Chrome".
|
|
145
|
+
const identity = (processName ?? browser).toLowerCase();
|
|
146
|
+
for (const [bundleId, patterns] of Object.entries(BROWSER_PATTERNS)) {
|
|
147
|
+
if (patterns.some((pattern) => identity.includes(pattern))) return bundleId;
|
|
148
|
+
}
|
|
149
|
+
return 'unknown';
|
|
150
|
+
}
|
|
151
|
+
|
|
121
152
|
export interface CdpEndpoint {
|
|
122
153
|
port: number;
|
|
123
154
|
app: string;
|
|
@@ -128,25 +159,18 @@ export interface CdpEndpoint {
|
|
|
128
159
|
|
|
129
160
|
export async function detectCdpPortsAsync(): Promise<CdpEndpoint[]> {
|
|
130
161
|
const ports = getLocalhostListeningPorts();
|
|
162
|
+
const processNames = getListeningProcessNames();
|
|
131
163
|
const results: CdpEndpoint[] = [];
|
|
132
164
|
|
|
133
165
|
// Probe ports in parallel for speed
|
|
134
166
|
const probes = ports.map(async (port) => {
|
|
135
167
|
const probe = await probeCdpPort(port);
|
|
136
168
|
if (probe) {
|
|
137
|
-
|
|
138
|
-
const lower = probe.browser.toLowerCase();
|
|
139
|
-
let bundleId = 'unknown';
|
|
140
|
-
for (const [bid, patterns] of Object.entries(BROWSER_PATTERNS)) {
|
|
141
|
-
if (patterns.some((p) => lower.includes(p))) {
|
|
142
|
-
bundleId = bid;
|
|
143
|
-
break;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
169
|
+
const processName = processNames.get(port);
|
|
146
170
|
results.push({
|
|
147
171
|
port,
|
|
148
|
-
app: probe.app,
|
|
149
|
-
bundleId,
|
|
172
|
+
app: processName ?? probe.app,
|
|
173
|
+
bundleId: identifyBrowserBundleId(probe.browser, processName),
|
|
150
174
|
isElectron: probe.isElectron,
|
|
151
175
|
hasPageTarget: probe.hasPageTarget,
|
|
152
176
|
});
|
|
@@ -179,8 +203,13 @@ export function pickPreferredEndpoint(
|
|
|
179
203
|
if (match) return match;
|
|
180
204
|
}
|
|
181
205
|
|
|
182
|
-
//
|
|
183
|
-
|
|
206
|
+
// Prefer a recognized browser over other Chromium hosts (Spotify, app
|
|
207
|
+
// shells), then fall back to the original non-Electron ordering.
|
|
208
|
+
const knownBrowser = candidates.find(
|
|
209
|
+
(endpoint) => endpoint.bundleId !== 'unknown' && !endpoint.isElectron,
|
|
210
|
+
);
|
|
211
|
+
if (knownBrowser) return knownBrowser;
|
|
212
|
+
const nonElectron = candidates.find((endpoint) => !endpoint.isElectron);
|
|
184
213
|
return nonElectron ?? candidates[0];
|
|
185
214
|
}
|
|
186
215
|
|
package/src/cdp/dispatch.ts
CHANGED
|
@@ -1,40 +1,86 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Shared dispatch for every routed command. Two jobs live here:
|
|
3
|
+
*
|
|
4
|
+
* 1. Route each root (`session`, `page`, `tab`, `measure`, `motion`,
|
|
5
|
+
* `cdp`, `lib`, hidden `__bridge-serve`) to its branch main.
|
|
6
|
+
* 2. Invoke the `--gate` guard ONCE for the whole surface: every command
|
|
7
|
+
* that is not `measure check|diff` rejects the flag structurally here,
|
|
8
|
+
* before any branch main runs.
|
|
9
|
+
*/
|
|
10
|
+
import { parseCliSyntax, resolveCliContext, validateCliInvocation } from './args.js';
|
|
11
|
+
import { type ParsedArgs } from './types.js';
|
|
12
|
+
import { isGateLeaf, rejectUnsupportedGate } from './commands/gate-guard.js';
|
|
13
|
+
import { getActiveSession } from '../session-context.js';
|
|
14
|
+
import { admitSessionOperation } from '../session/coordinator.js';
|
|
15
|
+
import { sessionMain } from '../session/commands.js';
|
|
16
|
+
import { pageMain } from './commands/page/index.js';
|
|
17
|
+
import { tabMain } from './commands/tab/index.js';
|
|
8
18
|
import { cmdLib } from './commands/lib.js';
|
|
9
19
|
import { cmdCdp } from './commands/cdp.js';
|
|
10
20
|
import { cmdBridgeServe } from './commands/bridge-serve.js';
|
|
11
21
|
import { measureMain } from './commands/measure/index.js';
|
|
12
22
|
import { motionMain } from './commands/motion/index.js';
|
|
13
23
|
|
|
24
|
+
/** The dotted leaf name the gate rejection reports, derived from the parsed
|
|
25
|
+
* command plus its first positional (the branch-leaf token). */
|
|
26
|
+
function gateLeafName(parsed: ParsedArgs): string {
|
|
27
|
+
const branch =
|
|
28
|
+
parsed.command === 'session' ||
|
|
29
|
+
parsed.command === 'page' ||
|
|
30
|
+
parsed.command === 'tab' ||
|
|
31
|
+
parsed.command === 'measure' ||
|
|
32
|
+
parsed.command === 'motion' ||
|
|
33
|
+
parsed.command === 'lib';
|
|
34
|
+
const leaf = parsed.positional[0];
|
|
35
|
+
return branch && leaf ? `${parsed.command} ${leaf}` : parsed.command;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Registers a session-bound operation (`page`/`measure`/`motion`) with the
|
|
40
|
+
* lifecycle coordinator BEFORE its first side effect and releases in `finally`,
|
|
41
|
+
* so a concurrent `session stop` (via `beginSessionStop`) waits for in-flight
|
|
42
|
+
* artifact writers to drain and excludes any operation that starts after stop
|
|
43
|
+
* marking (admission throws `session_stopping`). When there is no active
|
|
44
|
+
* session the operation is a one-shot with nothing to coordinate against, so it
|
|
45
|
+
* runs unwrapped. The active-session read here is never the first for these
|
|
46
|
+
* leaves — `resolveCliContext` already hydrated the same session — so it adds
|
|
47
|
+
* no new active-index side effect on the validation-rejected path.
|
|
48
|
+
*/
|
|
49
|
+
async function withActiveSessionAdmission(parsed: ParsedArgs, run: () => Promise<void>): Promise<void> {
|
|
50
|
+
const active = getActiveSession();
|
|
51
|
+
if (!active) return run();
|
|
52
|
+
const operation = await admitSessionOperation(active.dir);
|
|
53
|
+
try {
|
|
54
|
+
return await run();
|
|
55
|
+
} finally {
|
|
56
|
+
await operation.release();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
14
60
|
export async function cdpMain(): Promise<void> {
|
|
15
61
|
const args = process.argv.slice(2);
|
|
16
|
-
const parsed =
|
|
62
|
+
const parsed = parseCliSyntax(args);
|
|
63
|
+
|
|
64
|
+
if (parsed.gate && !isGateLeaf(parsed)) {
|
|
65
|
+
rejectUnsupportedGate(parsed, gateLeafName(parsed));
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
validateCliInvocation(parsed);
|
|
70
|
+
const resolved = resolveCliContext(parsed);
|
|
17
71
|
|
|
18
|
-
switch (
|
|
19
|
-
case '
|
|
20
|
-
case '
|
|
21
|
-
case '
|
|
22
|
-
case '
|
|
23
|
-
case '
|
|
24
|
-
case '
|
|
25
|
-
case '
|
|
26
|
-
case '
|
|
27
|
-
case 'a11y': return cmdA11y(parsed, args);
|
|
28
|
-
case 'record': return cmdRecord(parsed, args);
|
|
29
|
-
case 'navigate': return cmdNavigate(parsed, args);
|
|
30
|
-
case 'network': return cmdNetwork(parsed, args);
|
|
31
|
-
case 'har': return cmdHar(parsed, args);
|
|
32
|
-
case 'lib': return cmdLib(parsed, args);
|
|
33
|
-
case 'cdp': return cmdCdp(parsed, args);
|
|
34
|
-
case 'measure': return measureMain(parsed, args);
|
|
35
|
-
case 'motion': return motionMain(parsed, args);
|
|
36
|
-
case '__bridge-serve': return cmdBridgeServe(parsed, args);
|
|
72
|
+
switch (resolved.command) {
|
|
73
|
+
case 'session': return sessionMain(resolved, args);
|
|
74
|
+
case 'page': return withActiveSessionAdmission(resolved, () => pageMain(resolved, args));
|
|
75
|
+
case 'tab': return tabMain(resolved, args);
|
|
76
|
+
case 'lib': return cmdLib(resolved, args);
|
|
77
|
+
case 'cdp': return cmdCdp(resolved, args);
|
|
78
|
+
case 'measure': return withActiveSessionAdmission(resolved, () => measureMain(resolved, args));
|
|
79
|
+
case 'motion': return withActiveSessionAdmission(resolved, () => motionMain(resolved, args));
|
|
80
|
+
case '__bridge-serve': return cmdBridgeServe(resolved, args);
|
|
37
81
|
default:
|
|
38
|
-
|
|
82
|
+
// capture.ts routes only the known roots into cdpMain(); an unknown
|
|
83
|
+
// token gets its structured error there and never reaches this switch.
|
|
84
|
+
throw new Error(`dispatch: unroutable command ${JSON.stringify(resolved.command)}`);
|
|
39
85
|
}
|
|
40
86
|
}
|