opensteer 0.9.6 → 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 (35) hide show
  1. package/README.md +2 -2
  2. package/dist/{chunk-3I3A5OLB.js → chunk-BPGXP3RF.js} +257 -24
  3. package/dist/chunk-BPGXP3RF.js.map +1 -0
  4. package/dist/{chunk-3XBQRZZC.js → chunk-EXXRLPLI.js} +158 -46
  5. package/dist/chunk-EXXRLPLI.js.map +1 -0
  6. package/dist/{chunk-T5P2QGZ3.js → chunk-GKYBP3KD.js} +154 -13
  7. package/dist/chunk-GKYBP3KD.js.map +1 -0
  8. package/dist/{chunk-BVRIPCWA.js → chunk-LFWP5RXF.js} +500 -513
  9. package/dist/chunk-LFWP5RXF.js.map +1 -0
  10. package/dist/{chunk-L4NF74KI.js → chunk-SOJEWKSW.js} +5 -5
  11. package/dist/{chunk-L4NF74KI.js.map → chunk-SOJEWKSW.js.map} +1 -1
  12. package/dist/cli/bin.cjs +1230 -660
  13. package/dist/cli/bin.cjs.map +1 -1
  14. package/dist/cli/bin.js +166 -72
  15. package/dist/cli/bin.js.map +1 -1
  16. package/dist/index.cjs +793 -565
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +36 -51
  19. package/dist/index.d.ts +36 -51
  20. package/dist/index.js +4 -4
  21. package/dist/local-view/public/assets/app.js +10 -1
  22. package/dist/local-view/serve-entry.cjs +1022 -591
  23. package/dist/local-view/serve-entry.cjs.map +1 -1
  24. package/dist/local-view/serve-entry.js +2 -2
  25. package/dist/opensteer-XLPY343Y.js +6 -0
  26. package/dist/{opensteer-UGA6YBRN.js.map → opensteer-XLPY343Y.js.map} +1 -1
  27. package/dist/{session-control-U3L5H2ZI.js → session-control-FVKKD45R.js} +4 -4
  28. package/dist/{session-control-U3L5H2ZI.js.map → session-control-FVKKD45R.js.map} +1 -1
  29. package/package.json +5 -5
  30. package/skills/recorder/SKILL.md +2 -2
  31. package/dist/chunk-3I3A5OLB.js.map +0 -1
  32. package/dist/chunk-3XBQRZZC.js.map +0 -1
  33. package/dist/chunk-BVRIPCWA.js.map +0 -1
  34. package/dist/chunk-T5P2QGZ3.js.map +0 -1
  35. package/dist/opensteer-UGA6YBRN.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, isProcessRunning, pathExists, readPersistedLocalBrowserSessionRecord, inspectCdpEndpoint } from './chunk-T5P2QGZ3.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';
@@ -52,13 +52,13 @@ async function resolveSessionSummary(manifest) {
52
52
  return {
53
53
  sessionId: manifest.sessionId,
54
54
  label: manifest.workspace ?? (path2.basename(manifest.rootPath) || manifest.sessionId),
55
- status: isProcessRunning(record.pid) ? "live" : "stale",
55
+ status: getPersistedLocalBrowserSessionOwnership(record) === "attached" || isProcessRunning(record.pid) ? "live" : "stale",
56
56
  ...manifest.workspace === void 0 ? {} : { workspace: manifest.workspace },
57
57
  rootPath: manifest.rootPath,
58
58
  engine: record.engine,
59
59
  ownership: manifest.ownership,
60
- pid: record.pid,
61
60
  startedAt: record.startedAt,
61
+ ...record.pid > 0 ? { pid: record.pid } : {},
62
62
  ...browserName === void 0 ? {} : { browserName }
63
63
  };
64
64
  }
@@ -86,7 +86,16 @@ async function readLiveRecord(manifest) {
86
86
  if (!record) {
87
87
  return void 0;
88
88
  }
89
- if (record.pid !== manifest.pid || record.startedAt !== manifest.startedAt || !isProcessRunning(record.pid)) {
89
+ if (buildLocalViewSessionIdForRecord({
90
+ rootPath: manifest.rootPath,
91
+ live: record
92
+ }) !== manifest.sessionId || record.startedAt !== manifest.startedAt || record.engine !== manifest.engine || getPersistedLocalBrowserSessionOwnership(record) !== manifest.ownership) {
93
+ return void 0;
94
+ }
95
+ if (getPersistedLocalBrowserSessionOwnership(record) === "attached") {
96
+ return await isAttachedLocalBrowserSessionReachable(record) ? record : void 0;
97
+ }
98
+ if (record.pid !== manifest.pid || !isProcessRunning(record.pid)) {
90
99
  return void 0;
91
100
  }
92
101
  return record;
@@ -375,6 +384,172 @@ function safeCloseSocket(socket) {
375
384
  }
376
385
  }
377
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
+
378
553
  // src/local-view/tab-state-tracker.ts
379
554
  var ACTIVATION_INTENT_DISCOVERY_GRACE_MS = 2e3;
380
555
  var TabStateTracker = class {
@@ -390,6 +565,7 @@ var TabStateTracker = class {
390
565
  boundContextCleanup = null;
391
566
  constructor(deps) {
392
567
  this.deps = deps;
568
+ this.lastActivePage = deps.initialActivePage ?? null;
393
569
  }
394
570
  start() {
395
571
  if (this.running) {
@@ -514,7 +690,7 @@ var TabStateTracker = class {
514
690
  this.updatePolling(pages.length);
515
691
  const preferredActivePage = this.lastActivePage ?? pages[0] ?? null;
516
692
  const pageStates = await Promise.all(
517
- pages.map(async (page, index) => {
693
+ pages.map(async (page, originalIndex) => {
518
694
  const metadata = await this.readPageMetadata(page, {
519
695
  refresh: args.refreshMetadata
520
696
  });
@@ -524,7 +700,7 @@ var TabStateTracker = class {
524
700
  };
525
701
  return {
526
702
  page,
527
- index,
703
+ originalIndex,
528
704
  targetId: metadata.targetId,
529
705
  url: page.url(),
530
706
  title: metadata.title,
@@ -533,17 +709,18 @@ var TabStateTracker = class {
533
709
  };
534
710
  })
535
711
  );
712
+ const orderedPageStates = await this.orderPageStates(pageStates);
536
713
  const activePage = this.pickActivePage(
537
- pageStates,
714
+ orderedPageStates,
538
715
  this.lastActivePage,
539
716
  preferredActivePage,
540
- this.resolveIntentPage(pageStates)
717
+ this.resolveIntentPage(orderedPageStates)
541
718
  );
542
719
  if (activePage && activePage !== this.lastActivePage) {
543
720
  this.lastActivePage = activePage;
544
721
  this.deps.onActivePageChanged(activePage);
545
722
  }
546
- const tabs = pageStates.map((state) => ({
723
+ const tabs = orderedPageStates.map((state) => ({
547
724
  index: state.index,
548
725
  ...state.targetId === void 0 ? {} : { targetId: state.targetId },
549
726
  url: state.url,
@@ -593,18 +770,11 @@ var TabStateTracker = class {
593
770
  if (cached) {
594
771
  return cached;
595
772
  }
596
- const cdp = await page.context().newCDPSession(page);
597
- try {
598
- const result = await cdp.send("Target.getTargetInfo");
599
- const targetId = result?.targetInfo?.targetId;
600
- if (typeof targetId === "string" && targetId.length > 0) {
601
- this.targetIdByPage.set(page, targetId);
602
- return targetId;
603
- }
604
- return null;
605
- } finally {
606
- await cdp.detach().catch(() => void 0);
773
+ const targetId = await readPageTargetId(page);
774
+ if (targetId) {
775
+ this.targetIdByPage.set(page, targetId);
607
776
  }
777
+ return targetId;
608
778
  }
609
779
  async readFocusState(page) {
610
780
  try {
@@ -672,6 +842,33 @@ var TabStateTracker = class {
672
842
  this.deps.runtimeState.clearPageActivationIntent(this.deps.sessionId, intent.targetId);
673
843
  return { page: matched.page };
674
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
+ }
675
872
  };
676
873
 
677
874
  // src/local-view/view-stream-capture-policy.ts
@@ -1035,6 +1232,7 @@ var SessionViewStreamProducer = class {
1035
1232
  sessionId: this.deps.sessionId,
1036
1233
  pollMs: TAB_STATE_POLL_MS,
1037
1234
  runtimeState: this.deps.runtimeState,
1235
+ initialActivePage: session.page,
1038
1236
  onActivePageChanged: (page) => {
1039
1237
  this.activePage = page;
1040
1238
  void this.queueBindToPage(page).catch(() => void 0);
@@ -1133,7 +1331,20 @@ var SessionViewStreamProducer = class {
1133
1331
  if (!context) {
1134
1332
  throw new Error("Connected browser did not expose a Chromium browser context.");
1135
1333
  }
1136
- 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];
1137
1348
  return {
1138
1349
  browser,
1139
1350
  context,
@@ -1478,6 +1689,28 @@ async function disconnectPlaywrightChromiumBrowser(browser) {
1478
1689
  const { disconnectPlaywrightChromiumBrowser: disconnect } = await import('@opensteer/engine-playwright');
1479
1690
  await disconnect(browser);
1480
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
+ }
1481
1714
 
1482
1715
  // src/local-view/server.ts
1483
1716
  var DEFAULT_MAX_FPS = 12;
@@ -1661,7 +1894,7 @@ async function handleHttpRequest(args) {
1661
1894
  return;
1662
1895
  }
1663
1896
  const sessionId = decodeURIComponent(closeMatch[1]);
1664
- const { closeLocalViewSessionBrowser, LocalViewSessionCloseError } = await import('./session-control-U3L5H2ZI.js');
1897
+ const { closeLocalViewSessionBrowser, LocalViewSessionCloseError } = await import('./session-control-FVKKD45R.js');
1665
1898
  try {
1666
1899
  await closeLocalViewSessionBrowser(sessionId);
1667
1900
  } catch (error) {
@@ -1796,5 +2029,5 @@ async function runLocalViewService() {
1796
2029
  }
1797
2030
 
1798
2031
  export { runLocalViewService };
1799
- //# sourceMappingURL=chunk-3I3A5OLB.js.map
1800
- //# sourceMappingURL=chunk-3I3A5OLB.js.map
2032
+ //# sourceMappingURL=chunk-BPGXP3RF.js.map
2033
+ //# sourceMappingURL=chunk-BPGXP3RF.js.map