@houwert/conductor 0.15.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,19 +5,20 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.HELP = void 0;
7
7
  exports.screenshot = screenshot;
8
- exports.HELP = ` take-screenshot [--output <path>] Take screenshot`;
8
+ exports.HELP = ` take-screenshot [--output <path>] [--full-page]
9
+ Take screenshot (--full-page: web only, capture entire scrollable page)`;
9
10
  const path_1 = __importDefault(require("path"));
10
11
  const promises_1 = __importDefault(require("fs/promises"));
11
12
  const runner_js_1 = require("../runner.js");
12
13
  const output_js_1 = require("../output.js");
13
- async function screenshot(outputPath, opts = {}, sessionName = 'default') {
14
+ async function screenshot(outputPath, opts = {}, sessionName = 'default', fullPage = false) {
14
15
  const timestamp = Date.now();
15
16
  const defaultName = `screenshot-${timestamp}.png`;
16
17
  const resolvedPath = outputPath
17
18
  ? path_1.default.resolve(outputPath)
18
19
  : path_1.default.resolve(process.cwd(), defaultName);
19
20
  const result = await (0, runner_js_1.runDirect)(async (driver) => {
20
- const buf = await driver.screenshot();
21
+ const buf = await driver.screenshot({ fullPage });
21
22
  await promises_1.default.writeFile(resolvedPath, buf);
22
23
  }, sessionName);
23
24
  if (result.success) {
@@ -437,6 +437,24 @@ let _server = null;
437
437
  let _cdpMode = false;
438
438
  let _cdpPort = 0;
439
439
  let _pageTargetId = null;
440
+ /**
441
+ * Default User-Agent applied to created contexts. Derived from the launched
442
+ * browser's UA with "HeadlessChrome" rewritten to "Chrome" so remote servers
443
+ * don't see automation/headless markers in the UA string.
444
+ */
445
+ let _defaultUserAgent;
446
+ async function deriveDefaultUserAgent(browser) {
447
+ try {
448
+ const tmpCtx = await browser.newContext();
449
+ const tmpPage = await tmpCtx.newPage();
450
+ const ua = await tmpPage.evaluate(() => globalThis.navigator.userAgent);
451
+ await tmpCtx.close().catch(() => { });
452
+ return ua.replace(/HeadlessChrome/g, 'Chrome');
453
+ }
454
+ catch {
455
+ return undefined;
456
+ }
457
+ }
440
458
  /** Find a free TCP port by briefly binding to port 0. */
441
459
  function findFreePort() {
442
460
  return new Promise((resolve, reject) => {
@@ -557,8 +575,10 @@ async function startWebServer(port, browserName = 'chromium', dlog = () => { },
557
575
  ? [`--remote-debugging-port=${_cdpPort}`, '--disable-search-engine-choice-screen']
558
576
  : undefined,
559
577
  });
578
+ _defaultUserAgent = await deriveDefaultUserAgent(_browser);
560
579
  _context = await _browser.newContext({
561
580
  viewport: DEFAULT_VIEWPORT,
581
+ userAgent: _defaultUserAgent,
562
582
  });
563
583
  _page = await _context.newPage();
564
584
  attachConsoleListeners(_page);
@@ -692,6 +712,7 @@ async function recreateBrowserContext(dlog) {
692
712
  _page = null;
693
713
  _context = await _browser.newContext({
694
714
  viewport: DEFAULT_VIEWPORT,
715
+ userAgent: _defaultUserAgent,
695
716
  });
696
717
  _page = await _context.newPage();
697
718
  attachConsoleListeners(_page);
@@ -791,7 +812,9 @@ async function handleRequest(req, res, dlog) {
791
812
  return;
792
813
  }
793
814
  case '/screenshot': {
794
- const buf = await (await getPage(dlog)).screenshot({ type: 'png' });
815
+ const fullPageRaw = parsedUrl.query.fullPage;
816
+ const fullPage = fullPageRaw === '1' || fullPageRaw === 'true';
817
+ const buf = await (await getPage(dlog)).screenshot({ type: 'png', fullPage });
795
818
  res.writeHead(200, {
796
819
  'Content-Type': 'image/png',
797
820
  'Content-Length': buf.length,
@@ -1077,7 +1100,7 @@ async function handleRequest(req, res, dlog) {
1077
1100
  await _context.close().catch(() => { });
1078
1101
  _context = await _browser.newContext({
1079
1102
  viewport: { width: vpWidth, height: vpHeight },
1080
- userAgent: vpUserAgent,
1103
+ userAgent: vpUserAgent ?? _defaultUserAgent,
1081
1104
  deviceScaleFactor: vpScaleFactor,
1082
1105
  isMobile: vpIsMobile,
1083
1106
  hasTouch: vpIsMobile,
@@ -142,7 +142,7 @@ class AndroidDriver {
142
142
  const resp = await this.call('viewHierarchy', {});
143
143
  return resp.hierarchy;
144
144
  }
145
- async screenshot() {
145
+ async screenshot(_opts = {}) {
146
146
  const resp = await this.call('screenshot', {});
147
147
  return Buffer.from(resp.bytes);
148
148
  }
@@ -308,7 +308,7 @@ class IOSDriver {
308
308
  }
309
309
  return JSON.parse(data.toString('utf-8'));
310
310
  }
311
- async screenshot() {
311
+ async screenshot(_opts = {}) {
312
312
  const { status, data } = await this.request('GET', '/screenshot');
313
313
  if (status < 200 || status >= 300) {
314
314
  throw new Error(`iOS driver screenshot failed (HTTP ${status})`);
@@ -121,8 +121,9 @@ class WebDriver {
121
121
  async viewHierarchy() {
122
122
  return this.get('viewHierarchy');
123
123
  }
124
- async screenshot() {
125
- const { status, data } = await this.request('GET', '/screenshot');
124
+ async screenshot(opts = {}) {
125
+ const path = opts.fullPage ? '/screenshot?fullPage=1' : '/screenshot';
126
+ const { status, data } = await this.request('GET', path);
126
127
  if (status < 200 || status >= 300) {
127
128
  throw new Error(`Web driver screenshot failed (HTTP ${status})`);
128
129
  }
package/dist/index.js CHANGED
@@ -461,7 +461,8 @@ async function main() {
461
461
  }
462
462
  case 'take-screenshot': {
463
463
  const outPath = argv['output'];
464
- exitCode = await (0, screenshot_js_1.screenshot)(outPath, opts, sessionName);
464
+ const fullPage = Boolean(argv['full-page']);
465
+ exitCode = await (0, screenshot_js_1.screenshot)(outPath, opts, sessionName, fullPage);
465
466
  break;
466
467
  }
467
468
  case 'capture-ui': {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@houwert/conductor",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "description": "CLI tool for mobile app interactions — optimized for AI agents",
5
5
  "license": "MIT",
6
6
  "repository": {