@crouton-kit/capture 1.4.15 → 1.4.16
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 +46 -15
- package/package.json +1 -1
- package/src/cdp/targets.ts +22 -0
- package/src/session/commands.ts +24 -12
package/bin/capture
CHANGED
|
@@ -5910,6 +5910,7 @@ var targets_exports = {};
|
|
|
5910
5910
|
__export(targets_exports, {
|
|
5911
5911
|
AmbiguousPageTargetError: () => AmbiguousPageTargetError,
|
|
5912
5912
|
closeTarget: () => closeTarget,
|
|
5913
|
+
findResponsivePageTabByUrl: () => findResponsivePageTabByUrl,
|
|
5913
5914
|
findTab: () => findTab,
|
|
5914
5915
|
findTabById: () => findTabById,
|
|
5915
5916
|
findTabByIdAcrossEndpoints: () => findTabByIdAcrossEndpoints,
|
|
@@ -6049,6 +6050,25 @@ async function findTabByIdAcrossEndpoints(targetId, preferredPort) {
|
|
|
6049
6050
|
const ports = resolveSearchPorts(await detectCdpPortsAsync(), preferredPort);
|
|
6050
6051
|
return findTabByIdInPorts(targetId, ports);
|
|
6051
6052
|
}
|
|
6053
|
+
async function findResponsivePageTabByUrl(port, url) {
|
|
6054
|
+
const pages = (await listTargets(port)).filter((target) => target.type === "page");
|
|
6055
|
+
const scores = pages.map((target) => scoreTabUrlMatch(target.url, url));
|
|
6056
|
+
const bestScore = Math.max(0, ...scores);
|
|
6057
|
+
if (bestScore === 0) return null;
|
|
6058
|
+
for (const target of pages.filter((_, index) => scores[index] === bestScore)) {
|
|
6059
|
+
if (!target.webSocketDebuggerUrl) continue;
|
|
6060
|
+
const client = new CDPClient(target.webSocketDebuggerUrl);
|
|
6061
|
+
try {
|
|
6062
|
+
await client.waitReady();
|
|
6063
|
+
await client.send("Page.captureScreenshot", { format: "png" }, 5e3);
|
|
6064
|
+
return target;
|
|
6065
|
+
} catch {
|
|
6066
|
+
} finally {
|
|
6067
|
+
client.close();
|
|
6068
|
+
}
|
|
6069
|
+
}
|
|
6070
|
+
return null;
|
|
6071
|
+
}
|
|
6052
6072
|
async function findTabByUrlAcrossEndpoints(url, preferredPort) {
|
|
6053
6073
|
const ports = resolveSearchPorts(await detectCdpPortsAsync(), preferredPort);
|
|
6054
6074
|
let best = null;
|
|
@@ -10541,7 +10561,7 @@ function validateHarAppendBatch(value, source) {
|
|
|
10541
10561
|
return { entries, incompleteLifecycles };
|
|
10542
10562
|
}
|
|
10543
10563
|
function captureVersion() {
|
|
10544
|
-
const version = "1.4.
|
|
10564
|
+
const version = "1.4.16";
|
|
10545
10565
|
return typeof version === "string" && version.length > 0 ? version : "development";
|
|
10546
10566
|
}
|
|
10547
10567
|
function token() {
|
|
@@ -12643,7 +12663,7 @@ function wallTime(value) {
|
|
|
12643
12663
|
}
|
|
12644
12664
|
}
|
|
12645
12665
|
function captureVersion2() {
|
|
12646
|
-
const version = "1.4.
|
|
12666
|
+
const version = "1.4.16";
|
|
12647
12667
|
return typeof version === "string" && version.length > 0 ? version : "development";
|
|
12648
12668
|
}
|
|
12649
12669
|
function deepFreeze(value) {
|
|
@@ -15140,6 +15160,7 @@ async function start(parsed) {
|
|
|
15140
15160
|
let bridgeSocket = null;
|
|
15141
15161
|
let bridgePid = null;
|
|
15142
15162
|
let cdpPort = null;
|
|
15163
|
+
let targetWasOpened = false;
|
|
15143
15164
|
let selectedBrowser = null;
|
|
15144
15165
|
let endpointSelectionReason = null;
|
|
15145
15166
|
try {
|
|
@@ -15166,22 +15187,28 @@ async function start(parsed) {
|
|
|
15166
15187
|
}
|
|
15167
15188
|
}
|
|
15168
15189
|
if (url) {
|
|
15190
|
+
let opened = false;
|
|
15169
15191
|
try {
|
|
15170
15192
|
target = await startWorld.openTab(cdpPort, url);
|
|
15193
|
+
opened = true;
|
|
15194
|
+
targetWasOpened = true;
|
|
15171
15195
|
} catch (error4) {
|
|
15172
15196
|
const message = error4 instanceof Error ? error4.message : String(error4);
|
|
15173
|
-
if (message.toLowerCase().includes("not supported"))
|
|
15197
|
+
if (!message.toLowerCase().includes("not supported")) throw error4;
|
|
15198
|
+
target = await startWorld.findResponsivePageTabByUrl?.(cdpPort, url) ?? null;
|
|
15199
|
+
if (!target) {
|
|
15174
15200
|
throw worldFailure(
|
|
15175
|
-
`Target.createTarget is unsupported on port ${cdpPort}
|
|
15201
|
+
`Target.createTarget is unsupported on port ${cdpPort}, and no matching existing page target answered a screenshot. Run \`capture tab list --port ${cdpPort}\` to inspect its targets.`,
|
|
15176
15202
|
error4
|
|
15177
15203
|
);
|
|
15178
15204
|
}
|
|
15179
|
-
throw error4;
|
|
15180
15205
|
}
|
|
15181
|
-
|
|
15182
|
-
|
|
15183
|
-
|
|
15184
|
-
|
|
15206
|
+
if (opened) {
|
|
15207
|
+
const openedPort = cdpPort;
|
|
15208
|
+
const openedTargetId = target.id;
|
|
15209
|
+
acquired.push({ label: "opened target", release: () => startWorld.closeTarget(openedPort, openedTargetId) });
|
|
15210
|
+
pageLoadTimedOut = await startWorld.awaitTabReady(target, url);
|
|
15211
|
+
}
|
|
15185
15212
|
}
|
|
15186
15213
|
if (hold) {
|
|
15187
15214
|
const bridge = await startWorld.startBridge(dir, cdpPort);
|
|
@@ -15211,7 +15238,7 @@ async function start(parsed) {
|
|
|
15211
15238
|
return;
|
|
15212
15239
|
}
|
|
15213
15240
|
const rows = [fact`bundle dir: ${dir}`];
|
|
15214
|
-
if (target) rows.push(
|
|
15241
|
+
if (target) rows.push(targetWasOpened ? fact`tab ${target.id} opened at ${url}` : fact`tab ${target.id} adopted at ${target.url}`);
|
|
15215
15242
|
if (cdpPort !== null) rows.push(
|
|
15216
15243
|
parsed.portSource === "env" ? fact`CDP endpoint on port ${cdpPort} selected from ambient CDP_PORT; --port overrides endpoint selection and --target overrides tab selection.` : parsed.port !== void 0 ? fact`CDP endpoint on port ${cdpPort} selected by explicit --port; --port overrides endpoint selection and --target overrides tab selection.` : fact`${selectedBrowser ?? "CDP endpoint"} on port ${cdpPort} auto-selected because it ${endpointSelectionReason ?? "was selected by automatic endpoint detection"}; capture-launched browser recency does not affect selection. --port overrides endpoint selection and --target overrides tab selection.`
|
|
15217
15244
|
);
|
|
@@ -16128,10 +16155,10 @@ use when collecting or reading the traffic, logs, and artifacts of scoped work a
|
|
|
16128
16155
|
|
|
16129
16156
|
input:
|
|
16130
16157
|
--url <url> Absolute URL to open in a fresh tab through CDP's
|
|
16131
|
-
Target.createTarget.
|
|
16132
|
-
|
|
16133
|
-
|
|
16134
|
-
--target.
|
|
16158
|
+
Target.createTarget. If an endpoint, including Electron,
|
|
16159
|
+
rejects Target.createTarget, capture adopts the matching
|
|
16160
|
+
existing page target that answers a screenshot. Conflicts
|
|
16161
|
+
with --target.
|
|
16135
16162
|
--target <tab-id> Existing page tab to adopt. Resolves a full target id or
|
|
16136
16163
|
an unambiguous prefix of at least four characters at the
|
|
16137
16164
|
selected CDP endpoint; start fails if it does not resolve.
|
|
@@ -16277,6 +16304,10 @@ effects:
|
|
|
16277
16304
|
const { findTabById: findTabById2 } = await Promise.resolve().then(() => (init_targets(), targets_exports));
|
|
16278
16305
|
return findTabById2(port, targetId);
|
|
16279
16306
|
},
|
|
16307
|
+
async findResponsivePageTabByUrl(port, url) {
|
|
16308
|
+
const { findResponsivePageTabByUrl: findResponsivePageTabByUrl2 } = await Promise.resolve().then(() => (init_targets(), targets_exports));
|
|
16309
|
+
return findResponsivePageTabByUrl2(port, url);
|
|
16310
|
+
},
|
|
16280
16311
|
async closeTarget(port, targetId) {
|
|
16281
16312
|
const { closeTarget: closeTarget2 } = await Promise.resolve().then(() => (init_targets(), targets_exports));
|
|
16282
16313
|
return closeTarget2(port, targetId);
|
|
@@ -267328,7 +267359,7 @@ I/O contract: flags and positionals on input; one rendered prose block on stdout
|
|
|
267328
267359
|
A CDP-enabled browser must be running; \`capture tab list\` is the probe, and \`capture tab launch\` starts one capture owns and reaps when nothing is (never hand-roll a detached browser \u2014 nothing reaps that). Capture never signals a browser it did not start, so attaching to your own with --port is unchanged. CDP_PORT / CDP_TARGET pin the browser + tab for target-selecting commands \u2014 precedence: explicit flag > active session > env. \`session start\` adopts only an explicit --target.`;
|
|
267329
267360
|
}
|
|
267330
267361
|
function printVersion() {
|
|
267331
|
-
const declared = "1.4.
|
|
267362
|
+
const declared = "1.4.16";
|
|
267332
267363
|
if (declared) {
|
|
267333
267364
|
console.log(declared);
|
|
267334
267365
|
return;
|
package/package.json
CHANGED
package/src/cdp/targets.ts
CHANGED
|
@@ -205,6 +205,28 @@ export async function findTabByIdAcrossEndpoints(
|
|
|
205
205
|
return findTabByIdInPorts(targetId, ports);
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
+
export async function findResponsivePageTabByUrl(port: number, url: string): Promise<CDPTarget | null> {
|
|
209
|
+
const pages = (await listTargets(port)).filter((target) => target.type === 'page');
|
|
210
|
+
const scores = pages.map((target) => scoreTabUrlMatch(target.url, url));
|
|
211
|
+
const bestScore = Math.max(0, ...scores);
|
|
212
|
+
if (bestScore === 0) return null;
|
|
213
|
+
|
|
214
|
+
for (const target of pages.filter((_, index) => scores[index] === bestScore)) {
|
|
215
|
+
if (!target.webSocketDebuggerUrl) continue;
|
|
216
|
+
const client = new CDPClient(target.webSocketDebuggerUrl);
|
|
217
|
+
try {
|
|
218
|
+
await client.waitReady();
|
|
219
|
+
await client.send('Page.captureScreenshot', { format: 'png' }, 5_000);
|
|
220
|
+
return target;
|
|
221
|
+
} catch {
|
|
222
|
+
// Electron can expose a page target that never answers screenshot requests.
|
|
223
|
+
} finally {
|
|
224
|
+
client.close();
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
|
|
208
230
|
export async function findTabByUrlAcrossEndpoints(
|
|
209
231
|
url: string,
|
|
210
232
|
preferredPort?: number,
|
package/src/session/commands.ts
CHANGED
|
@@ -391,10 +391,10 @@ const START_USAGE = `capture session start [--url <url> | --target <tab-id>] [--
|
|
|
391
391
|
|
|
392
392
|
input:
|
|
393
393
|
--url <url> Absolute URL to open in a fresh tab through CDP's
|
|
394
|
-
Target.createTarget.
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
--target.
|
|
394
|
+
Target.createTarget. If an endpoint, including Electron,
|
|
395
|
+
rejects Target.createTarget, capture adopts the matching
|
|
396
|
+
existing page target that answers a screenshot. Conflicts
|
|
397
|
+
with --target.
|
|
398
398
|
--target <tab-id> Existing page tab to adopt. Resolves a full target id or
|
|
399
399
|
an unambiguous prefix of at least four characters at the
|
|
400
400
|
selected CDP endpoint; start fails if it does not resolve.
|
|
@@ -563,6 +563,7 @@ export interface SessionStartWorld {
|
|
|
563
563
|
detectCdpPort(): Promise<number | { port: number; app: string; selectionReason: string }>;
|
|
564
564
|
openTab(port: number, url: string): Promise<CDPTarget>;
|
|
565
565
|
findTabById(port: number, targetId: string): Promise<CDPTarget | null>;
|
|
566
|
+
findResponsivePageTabByUrl?(port: number, url: string): Promise<CDPTarget | null>;
|
|
566
567
|
closeTarget(port: number, targetId: string): Promise<void>;
|
|
567
568
|
/** Attach to the freshly-opened tab and wait for load; returns page-load-timed-out. */
|
|
568
569
|
awaitTabReady(target: CDPTarget, url: string): Promise<boolean>;
|
|
@@ -588,6 +589,10 @@ const productionStartWorld: SessionStartWorld = {
|
|
|
588
589
|
const { findTabById } = await import('../cdp/targets.js');
|
|
589
590
|
return findTabById(port, targetId);
|
|
590
591
|
},
|
|
592
|
+
async findResponsivePageTabByUrl(port, url) {
|
|
593
|
+
const { findResponsivePageTabByUrl } = await import('../cdp/targets.js');
|
|
594
|
+
return findResponsivePageTabByUrl(port, url);
|
|
595
|
+
},
|
|
591
596
|
async closeTarget(port, targetId) {
|
|
592
597
|
const { closeTarget } = await import('../cdp/targets.js');
|
|
593
598
|
return closeTarget(port, targetId);
|
|
@@ -692,6 +697,7 @@ async function start(parsed: ParsedArgs): Promise<void> {
|
|
|
692
697
|
let bridgeSocket: string | null = null;
|
|
693
698
|
let bridgePid: number | null = null;
|
|
694
699
|
let cdpPort: number | null = null;
|
|
700
|
+
let targetWasOpened = false;
|
|
695
701
|
let selectedBrowser: string | null = null;
|
|
696
702
|
let endpointSelectionReason: string | null = null;
|
|
697
703
|
|
|
@@ -723,22 +729,28 @@ async function start(parsed: ParsedArgs): Promise<void> {
|
|
|
723
729
|
}
|
|
724
730
|
|
|
725
731
|
if (url) {
|
|
732
|
+
let opened = false;
|
|
726
733
|
try {
|
|
727
734
|
target = await startWorld.openTab(cdpPort!, url);
|
|
735
|
+
opened = true;
|
|
736
|
+
targetWasOpened = true;
|
|
728
737
|
} catch (error) {
|
|
729
738
|
const message = error instanceof Error ? error.message : String(error);
|
|
730
|
-
if (message.toLowerCase().includes('not supported'))
|
|
739
|
+
if (!message.toLowerCase().includes('not supported')) throw error;
|
|
740
|
+
target = await startWorld.findResponsivePageTabByUrl?.(cdpPort!, url) ?? null;
|
|
741
|
+
if (!target) {
|
|
731
742
|
throw worldFailure(
|
|
732
|
-
`Target.createTarget is unsupported on port ${cdpPort}
|
|
743
|
+
`Target.createTarget is unsupported on port ${cdpPort}, and no matching existing page target answered a screenshot. Run \`capture tab list --port ${cdpPort}\` to inspect its targets.`,
|
|
733
744
|
error,
|
|
734
745
|
);
|
|
735
746
|
}
|
|
736
|
-
throw error;
|
|
737
747
|
}
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
748
|
+
if (opened) {
|
|
749
|
+
const openedPort = cdpPort!;
|
|
750
|
+
const openedTargetId = target.id;
|
|
751
|
+
acquired.push({ label: 'opened target', release: () => startWorld.closeTarget(openedPort, openedTargetId) });
|
|
752
|
+
pageLoadTimedOut = await startWorld.awaitTabReady(target, url);
|
|
753
|
+
}
|
|
742
754
|
}
|
|
743
755
|
|
|
744
756
|
// Hold a CDP browser connection open for the session's lifetime so
|
|
@@ -778,7 +790,7 @@ async function start(parsed: ParsedArgs): Promise<void> {
|
|
|
778
790
|
}
|
|
779
791
|
|
|
780
792
|
const rows: FactLine[] = [fact`bundle dir: ${dir}`];
|
|
781
|
-
if (target) rows.push(
|
|
793
|
+
if (target) rows.push(targetWasOpened ? fact`tab ${target.id} opened at ${url}` : fact`tab ${target.id} adopted at ${target.url}`);
|
|
782
794
|
if (cdpPort !== null) rows.push(
|
|
783
795
|
parsed.portSource === 'env'
|
|
784
796
|
? fact`CDP endpoint on port ${cdpPort} selected from ambient CDP_PORT; --port overrides endpoint selection and --target overrides tab selection.`
|