opensteer 0.9.7 → 0.9.8

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.
Files changed (34) hide show
  1. package/README.md +2 -2
  2. package/dist/{chunk-R33BXCMQ.js → chunk-BPGXP3RF.js} +245 -21
  3. package/dist/chunk-BPGXP3RF.js.map +1 -0
  4. package/dist/{chunk-PJXN7HED.js → chunk-EXXRLPLI.js} +46 -35
  5. package/dist/chunk-EXXRLPLI.js.map +1 -0
  6. package/dist/{chunk-U4BUCIZ4.js → chunk-GKYBP3KD.js} +4 -4
  7. package/dist/chunk-GKYBP3KD.js.map +1 -0
  8. package/dist/{chunk-3OHKIPBD.js → chunk-LFWP5RXF.js} +189 -53
  9. package/dist/chunk-LFWP5RXF.js.map +1 -0
  10. package/dist/{chunk-52UNH5UW.js → chunk-SOJEWKSW.js} +5 -5
  11. package/dist/{chunk-52UNH5UW.js.map → chunk-SOJEWKSW.js.map} +1 -1
  12. package/dist/cli/bin.cjs +570 -119
  13. package/dist/cli/bin.cjs.map +1 -1
  14. package/dist/cli/bin.js +106 -28
  15. package/dist/cli/bin.js.map +1 -1
  16. package/dist/index.cjs +211 -72
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +10 -1
  19. package/dist/index.d.ts +10 -1
  20. package/dist/index.js +4 -4
  21. package/dist/local-view/serve-entry.cjs +288 -50
  22. package/dist/local-view/serve-entry.cjs.map +1 -1
  23. package/dist/local-view/serve-entry.js +2 -2
  24. package/dist/opensteer-XLPY343Y.js +6 -0
  25. package/dist/{opensteer-CY2QUJEG.js.map → opensteer-XLPY343Y.js.map} +1 -1
  26. package/dist/{session-control-FIP6ZJLH.js → session-control-FVKKD45R.js} +4 -4
  27. package/dist/{session-control-FIP6ZJLH.js.map → session-control-FVKKD45R.js.map} +1 -1
  28. package/package.json +7 -7
  29. package/skills/recorder/SKILL.md +2 -2
  30. package/dist/chunk-3OHKIPBD.js.map +0 -1
  31. package/dist/chunk-PJXN7HED.js.map +0 -1
  32. package/dist/chunk-R33BXCMQ.js.map +0 -1
  33. package/dist/chunk-U4BUCIZ4.js.map +0 -1
  34. package/dist/opensteer-CY2QUJEG.js +0 -6
package/README.md CHANGED
@@ -143,8 +143,8 @@ See the full [Workflow Guide](https://github.com/steerlabs/opensteer/blob/main/d
143
143
 
144
144
  ## Documentation
145
145
 
146
- | Resource | Description |
147
- | -------------------------------------------------- | -------------------------------------- |
146
+ | Resource | Description |
147
+ | ------------------------------------------------------------------------------------------------- | -------------------------------------- |
148
148
  | [Package Guide](https://github.com/steerlabs/opensteer/blob/main/packages/opensteer/README.md) | Full CLI and SDK reference |
149
149
  | [Workflow Guide](https://github.com/steerlabs/opensteer/blob/main/docs/workflows.md) | Discover-then-codify methodology |
150
150
  | [Instrumentation Guide](https://github.com/steerlabs/opensteer/blob/main/docs/instrumentation.md) | Tracing and observability |
@@ -1,4 +1,4 @@
1
- import { writeLocalViewServiceState, CURRENT_PROCESS_OWNER, OPENSTEER_LOCAL_VIEW_SERVICE_VERSION, OPENSTEER_LOCAL_VIEW_SERVICE_LAYOUT, clearLocalViewServiceState, readLocalViewSessionManifest, listLocalViewSessionManifests, deleteLocalViewSessionManifest, getPersistedLocalBrowserSessionOwnership, isProcessRunning, pathExists, readPersistedLocalBrowserSessionRecord, buildLocalViewSessionIdForRecord, isAttachedLocalBrowserSessionReachable, inspectCdpEndpoint } from './chunk-U4BUCIZ4.js';
1
+ import { writeLocalViewServiceState, CURRENT_PROCESS_OWNER, OPENSTEER_LOCAL_VIEW_SERVICE_VERSION, OPENSTEER_LOCAL_VIEW_SERVICE_LAYOUT, clearLocalViewServiceState, readLocalViewSessionManifest, listLocalViewSessionManifests, deleteLocalViewSessionManifest, getPersistedLocalBrowserSessionOwnership, isProcessRunning, pathExists, readPersistedLocalBrowserSessionRecord, buildLocalViewSessionIdForRecord, isAttachedLocalBrowserSessionReachable, inspectCdpEndpoint } from './chunk-GKYBP3KD.js';
2
2
  import { randomBytes } from 'crypto';
3
3
  import { createServer } from 'http';
4
4
  import { readFile } from 'fs/promises';
@@ -384,6 +384,172 @@ function safeCloseSocket(socket) {
384
384
  }
385
385
  }
386
386
 
387
+ // src/local-view/browser-target-order.ts
388
+ async function readPageTargetId(page) {
389
+ const cdp = await page.context().newCDPSession(page);
390
+ try {
391
+ const result = await cdp.send("Target.getTargetInfo");
392
+ const targetId = result?.targetInfo?.targetId;
393
+ return typeof targetId === "string" && targetId.length > 0 ? targetId : null;
394
+ } finally {
395
+ await cdp.detach().catch(() => void 0);
396
+ }
397
+ }
398
+ async function readBrowserPageTargetOrder(browserContext) {
399
+ const browser = browserContext.browser();
400
+ if (!browser || !hasBrowserCdpSession(browser)) {
401
+ return [];
402
+ }
403
+ const cdp = await browser.newBrowserCDPSession();
404
+ try {
405
+ const result = await cdp.send("Target.getTargets");
406
+ return normalizeBrowserPageTargetOrder(result?.targetInfos ?? []);
407
+ } catch {
408
+ return [];
409
+ } finally {
410
+ await cdp.detach().catch(() => void 0);
411
+ }
412
+ }
413
+ async function orderPagesByBrowserTargetOrder(browserContext, pages) {
414
+ if (pages.length < 2) {
415
+ return pages;
416
+ }
417
+ const orderedTargetIds = await readBrowserPageTargetOrder(browserContext);
418
+ if (orderedTargetIds.length === 0) {
419
+ return pages;
420
+ }
421
+ const rankByTargetId = new Map(orderedTargetIds.map((targetId, index) => [targetId, index]));
422
+ const targetIds = await Promise.all(
423
+ pages.map((page) => readPageTargetId(page).catch(() => null))
424
+ );
425
+ return pages.map((page, index) => {
426
+ const targetId = targetIds[index] ?? void 0;
427
+ return {
428
+ page,
429
+ index,
430
+ rank: targetId === void 0 ? void 0 : rankByTargetId.get(targetId)
431
+ };
432
+ }).sort((left, right) => {
433
+ if (left.rank !== void 0 && right.rank !== void 0) {
434
+ return left.rank - right.rank;
435
+ }
436
+ if (left.rank !== void 0) {
437
+ return -1;
438
+ }
439
+ if (right.rank !== void 0) {
440
+ return 1;
441
+ }
442
+ return left.index - right.index;
443
+ }).map((entry) => entry.page);
444
+ }
445
+ function hasBrowserCdpSession(browser) {
446
+ return typeof browser.newBrowserCDPSession === "function";
447
+ }
448
+ function normalizeBrowserPageTargetOrder(targetInfos) {
449
+ const reversedPageInfos = [];
450
+ for (const targetInfo of targetInfos) {
451
+ if (targetInfo.type !== "page") {
452
+ continue;
453
+ }
454
+ const targetId = typeof targetInfo.targetId === "string" && targetInfo.targetId.length > 0 ? targetInfo.targetId : void 0;
455
+ if (targetId === void 0) {
456
+ continue;
457
+ }
458
+ reversedPageInfos.push({
459
+ targetId,
460
+ openerId: typeof targetInfo.openerId === "string" && targetInfo.openerId.length > 0 ? targetInfo.openerId : void 0
461
+ });
462
+ }
463
+ reversedPageInfos.reverse();
464
+ const rawTargetInfoById = new Map(
465
+ reversedPageInfos.map((targetInfo) => [targetInfo.targetId, targetInfo])
466
+ );
467
+ const targetInfoById = new Map(
468
+ reversedPageInfos.map(
469
+ (targetInfo) => [
470
+ targetInfo.targetId,
471
+ {
472
+ ...targetInfo,
473
+ openerId: resolveAcyclicOpenerId(targetInfo.targetId, rawTargetInfoById)
474
+ }
475
+ ]
476
+ )
477
+ );
478
+ const orderedTargetIds = [];
479
+ const placed = /* @__PURE__ */ new Set();
480
+ const placeTarget = (targetId) => {
481
+ if (placed.has(targetId)) {
482
+ return;
483
+ }
484
+ const targetInfo = targetInfoById.get(targetId);
485
+ if (!targetInfo) {
486
+ return;
487
+ }
488
+ const openerId = targetInfo.openerId;
489
+ if (openerId === void 0) {
490
+ orderedTargetIds.push(targetId);
491
+ placed.add(targetId);
492
+ return;
493
+ }
494
+ placeTarget(openerId);
495
+ const openerIndex = orderedTargetIds.indexOf(openerId);
496
+ const insertionIndex = openerIndex === -1 ? orderedTargetIds.length : findPopupInsertionIndex(orderedTargetIds, openerIndex, openerId, targetInfoById);
497
+ orderedTargetIds.splice(insertionIndex, 0, targetId);
498
+ placed.add(targetId);
499
+ };
500
+ for (const targetInfo of reversedPageInfos) {
501
+ placeTarget(targetInfo.targetId);
502
+ }
503
+ return orderedTargetIds;
504
+ }
505
+ function resolveAcyclicOpenerId(targetId, targetInfoById) {
506
+ const openerId = targetInfoById.get(targetId)?.openerId;
507
+ if (openerId === void 0 || !targetInfoById.has(openerId)) {
508
+ return void 0;
509
+ }
510
+ const visitedTargetIds = /* @__PURE__ */ new Set([targetId]);
511
+ let currentTargetId = openerId;
512
+ while (currentTargetId !== void 0) {
513
+ if (visitedTargetIds.has(currentTargetId)) {
514
+ return void 0;
515
+ }
516
+ visitedTargetIds.add(currentTargetId);
517
+ currentTargetId = targetInfoById.get(currentTargetId)?.openerId;
518
+ }
519
+ return openerId;
520
+ }
521
+ function findPopupInsertionIndex(orderedTargetIds, openerIndex, openerTargetId, targetInfoById) {
522
+ let index = openerIndex + 1;
523
+ while (index < orderedTargetIds.length) {
524
+ const candidateTargetId = orderedTargetIds[index];
525
+ if (!candidateTargetId || !isDescendantTarget(candidateTargetId, openerTargetId, targetInfoById)) {
526
+ break;
527
+ }
528
+ index += 1;
529
+ }
530
+ return index;
531
+ }
532
+ function isDescendantTarget(targetId, ancestorTargetId, targetInfoById) {
533
+ const visitedTargetIds = /* @__PURE__ */ new Set();
534
+ let currentTargetId = targetId;
535
+ while (currentTargetId !== void 0) {
536
+ if (visitedTargetIds.has(currentTargetId)) {
537
+ return false;
538
+ }
539
+ visitedTargetIds.add(currentTargetId);
540
+ const currentTargetInfo = targetInfoById.get(currentTargetId);
541
+ const openerId = currentTargetInfo?.openerId;
542
+ if (openerId === void 0) {
543
+ return false;
544
+ }
545
+ if (openerId === ancestorTargetId) {
546
+ return true;
547
+ }
548
+ currentTargetId = openerId;
549
+ }
550
+ return false;
551
+ }
552
+
387
553
  // src/local-view/tab-state-tracker.ts
388
554
  var ACTIVATION_INTENT_DISCOVERY_GRACE_MS = 2e3;
389
555
  var TabStateTracker = class {
@@ -399,6 +565,7 @@ var TabStateTracker = class {
399
565
  boundContextCleanup = null;
400
566
  constructor(deps) {
401
567
  this.deps = deps;
568
+ this.lastActivePage = deps.initialActivePage ?? null;
402
569
  }
403
570
  start() {
404
571
  if (this.running) {
@@ -523,7 +690,7 @@ var TabStateTracker = class {
523
690
  this.updatePolling(pages.length);
524
691
  const preferredActivePage = this.lastActivePage ?? pages[0] ?? null;
525
692
  const pageStates = await Promise.all(
526
- pages.map(async (page, index) => {
693
+ pages.map(async (page, originalIndex) => {
527
694
  const metadata = await this.readPageMetadata(page, {
528
695
  refresh: args.refreshMetadata
529
696
  });
@@ -533,7 +700,7 @@ var TabStateTracker = class {
533
700
  };
534
701
  return {
535
702
  page,
536
- index,
703
+ originalIndex,
537
704
  targetId: metadata.targetId,
538
705
  url: page.url(),
539
706
  title: metadata.title,
@@ -542,17 +709,18 @@ var TabStateTracker = class {
542
709
  };
543
710
  })
544
711
  );
712
+ const orderedPageStates = await this.orderPageStates(pageStates);
545
713
  const activePage = this.pickActivePage(
546
- pageStates,
714
+ orderedPageStates,
547
715
  this.lastActivePage,
548
716
  preferredActivePage,
549
- this.resolveIntentPage(pageStates)
717
+ this.resolveIntentPage(orderedPageStates)
550
718
  );
551
719
  if (activePage && activePage !== this.lastActivePage) {
552
720
  this.lastActivePage = activePage;
553
721
  this.deps.onActivePageChanged(activePage);
554
722
  }
555
- const tabs = pageStates.map((state) => ({
723
+ const tabs = orderedPageStates.map((state) => ({
556
724
  index: state.index,
557
725
  ...state.targetId === void 0 ? {} : { targetId: state.targetId },
558
726
  url: state.url,
@@ -602,18 +770,11 @@ var TabStateTracker = class {
602
770
  if (cached) {
603
771
  return cached;
604
772
  }
605
- const cdp = await page.context().newCDPSession(page);
606
- try {
607
- const result = await cdp.send("Target.getTargetInfo");
608
- const targetId = result?.targetInfo?.targetId;
609
- if (typeof targetId === "string" && targetId.length > 0) {
610
- this.targetIdByPage.set(page, targetId);
611
- return targetId;
612
- }
613
- return null;
614
- } finally {
615
- await cdp.detach().catch(() => void 0);
773
+ const targetId = await readPageTargetId(page);
774
+ if (targetId) {
775
+ this.targetIdByPage.set(page, targetId);
616
776
  }
777
+ return targetId;
617
778
  }
618
779
  async readFocusState(page) {
619
780
  try {
@@ -681,6 +842,33 @@ var TabStateTracker = class {
681
842
  this.deps.runtimeState.clearPageActivationIntent(this.deps.sessionId, intent.targetId);
682
843
  return { page: matched.page };
683
844
  }
845
+ async orderPageStates(pageStates) {
846
+ if (pageStates.length < 2) {
847
+ return pageStates.map(({ originalIndex: _originalIndex, ...state }, index) => ({
848
+ ...state,
849
+ index
850
+ }));
851
+ }
852
+ const orderedTargetIds = await readBrowserPageTargetOrder(this.deps.browserContext);
853
+ const rankByTargetId = new Map(orderedTargetIds.map((targetId, index) => [targetId, index]));
854
+ return [...pageStates].sort((left, right) => {
855
+ const leftRank = left.targetId === void 0 ? void 0 : rankByTargetId.get(left.targetId);
856
+ const rightRank = right.targetId === void 0 ? void 0 : rankByTargetId.get(right.targetId);
857
+ if (leftRank !== void 0 && rightRank !== void 0) {
858
+ return leftRank - rightRank;
859
+ }
860
+ if (leftRank !== void 0) {
861
+ return -1;
862
+ }
863
+ if (rightRank !== void 0) {
864
+ return 1;
865
+ }
866
+ return left.originalIndex - right.originalIndex;
867
+ }).map(({ originalIndex: _originalIndex, ...state }, index) => ({
868
+ ...state,
869
+ index
870
+ }));
871
+ }
684
872
  };
685
873
 
686
874
  // src/local-view/view-stream-capture-policy.ts
@@ -1044,6 +1232,7 @@ var SessionViewStreamProducer = class {
1044
1232
  sessionId: this.deps.sessionId,
1045
1233
  pollMs: TAB_STATE_POLL_MS,
1046
1234
  runtimeState: this.deps.runtimeState,
1235
+ initialActivePage: session.page,
1047
1236
  onActivePageChanged: (page) => {
1048
1237
  this.activePage = page;
1049
1238
  void this.queueBindToPage(page).catch(() => void 0);
@@ -1142,7 +1331,20 @@ var SessionViewStreamProducer = class {
1142
1331
  if (!context) {
1143
1332
  throw new Error("Connected browser did not expose a Chromium browser context.");
1144
1333
  }
1145
- const page = context.pages()[0] ?? await context.newPage();
1334
+ const existingPages = context.pages();
1335
+ if (existingPages.length === 0) {
1336
+ const page2 = await context.newPage();
1337
+ return {
1338
+ browser,
1339
+ context,
1340
+ page: page2
1341
+ };
1342
+ }
1343
+ const orderedPages = await orderPagesByBrowserTargetOrder(context, existingPages);
1344
+ const page = await resolvePersistedActivePage(orderedPages, {
1345
+ ...resolved.record.activePageUrl === void 0 ? {} : { activePageUrl: resolved.record.activePageUrl },
1346
+ ...resolved.record.activePageTitle === void 0 ? {} : { activePageTitle: resolved.record.activePageTitle }
1347
+ }) ?? orderedPages[0];
1146
1348
  return {
1147
1349
  browser,
1148
1350
  context,
@@ -1487,6 +1689,28 @@ async function disconnectPlaywrightChromiumBrowser(browser) {
1487
1689
  const { disconnectPlaywrightChromiumBrowser: disconnect } = await import('@opensteer/engine-playwright');
1488
1690
  await disconnect(browser);
1489
1691
  }
1692
+ async function resolvePersistedActivePage(pages, input) {
1693
+ if (pages.length === 0) {
1694
+ return null;
1695
+ }
1696
+ if (input.activePageUrl === void 0 && input.activePageTitle === void 0) {
1697
+ return null;
1698
+ }
1699
+ const matchesByUrl = input.activePageUrl === void 0 ? pages : pages.filter((page) => page.url() === input.activePageUrl);
1700
+ if (matchesByUrl.length === 0) {
1701
+ return null;
1702
+ }
1703
+ if (input.activePageTitle === void 0) {
1704
+ return matchesByUrl[0] ?? null;
1705
+ }
1706
+ for (const page of matchesByUrl) {
1707
+ const title = await page.title().catch(() => "");
1708
+ if (title === input.activePageTitle) {
1709
+ return page;
1710
+ }
1711
+ }
1712
+ return matchesByUrl[0] ?? null;
1713
+ }
1490
1714
 
1491
1715
  // src/local-view/server.ts
1492
1716
  var DEFAULT_MAX_FPS = 12;
@@ -1670,7 +1894,7 @@ async function handleHttpRequest(args) {
1670
1894
  return;
1671
1895
  }
1672
1896
  const sessionId = decodeURIComponent(closeMatch[1]);
1673
- const { closeLocalViewSessionBrowser, LocalViewSessionCloseError } = await import('./session-control-FIP6ZJLH.js');
1897
+ const { closeLocalViewSessionBrowser, LocalViewSessionCloseError } = await import('./session-control-FVKKD45R.js');
1674
1898
  try {
1675
1899
  await closeLocalViewSessionBrowser(sessionId);
1676
1900
  } catch (error) {
@@ -1805,5 +2029,5 @@ async function runLocalViewService() {
1805
2029
  }
1806
2030
 
1807
2031
  export { runLocalViewService };
1808
- //# sourceMappingURL=chunk-R33BXCMQ.js.map
1809
- //# sourceMappingURL=chunk-R33BXCMQ.js.map
2032
+ //# sourceMappingURL=chunk-BPGXP3RF.js.map
2033
+ //# sourceMappingURL=chunk-BPGXP3RF.js.map