@poncho-ai/browser 0.6.25 → 0.6.26
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/.turbo/turbo-build.log +5 -5
- package/CHANGELOG.md +16 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +39 -24
- package/package.json +1 -1
- package/src/session.ts +43 -24
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @poncho-ai/browser@0.6.
|
|
2
|
+
> @poncho-ai/browser@0.6.26 build /home/runner/work/poncho-ai/poncho-ai/packages/browser
|
|
3
3
|
> tsup src/index.ts --format esm --dts
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
[34mCLI[39m tsup v8.5.1
|
|
8
8
|
[34mCLI[39m Target: es2022
|
|
9
9
|
[34mESM[39m Build start
|
|
10
|
-
[32mESM[39m [1mdist/index.js [22m[32m47.
|
|
11
|
-
[32mESM[39m ⚡️ Build success in
|
|
10
|
+
[32mESM[39m [1mdist/index.js [22m[32m47.98 KB[39m
|
|
11
|
+
[32mESM[39m ⚡️ Build success in 60ms
|
|
12
12
|
[34mDTS[39m Build start
|
|
13
|
-
[32mDTS[39m ⚡️ Build success in
|
|
14
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[32m13.
|
|
13
|
+
[32mDTS[39m ⚡️ Build success in 4894ms
|
|
14
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m13.77 KB[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @poncho-ai/browser
|
|
2
2
|
|
|
3
|
+
## 0.6.26
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#182](https://github.com/cesr/poncho-ai/pull/182) [`5ca3615`](https://github.com/cesr/poncho-ai/commit/5ca361576cbe1a97e6315f550a58a302b4e70aca) Thanks [@cesr](https://github.com/cesr)! - Keep host viewport listeners alive across browser sessions. `onFrame` /
|
|
8
|
+
`onStatus` listeners were stored inside the per-conversation `ConversationTab`
|
|
9
|
+
object, so `closeTab` (and LRU eviction) deleted them along with the tab. When
|
|
10
|
+
an agent closed one browser and opened another in the same conversation, the
|
|
11
|
+
new tab had empty listener sets — the host's live-viewport subscription was
|
|
12
|
+
silently orphaned, so the second session's `browser:status` / frames never
|
|
13
|
+
reached the client until it reconnected (the "pill/sheet doesn't appear, or is
|
|
14
|
+
left over after close, until I navigate away and back" bug). Listeners now live
|
|
15
|
+
in session-level maps keyed by conversationId, independent of any tab's
|
|
16
|
+
lifetime; they persist until the host unsubscribes, and `emitStatus` delivers
|
|
17
|
+
the final `active:false` on close before the tab is removed.
|
|
18
|
+
|
|
3
19
|
## 0.6.25
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
package/dist/index.d.ts
CHANGED
|
@@ -80,6 +80,8 @@ declare class BrowserSession {
|
|
|
80
80
|
private readonly sessionId;
|
|
81
81
|
private manager;
|
|
82
82
|
private readonly tabs;
|
|
83
|
+
private readonly frameListeners;
|
|
84
|
+
private readonly statusListeners;
|
|
83
85
|
private _contextStealthInstalled;
|
|
84
86
|
private readonly _uaOverrideApplied;
|
|
85
87
|
private _lockQueue;
|
package/dist/index.js
CHANGED
|
@@ -250,6 +250,15 @@ var BrowserSession = class {
|
|
|
250
250
|
manager;
|
|
251
251
|
// Tab management: conversationId → tab state
|
|
252
252
|
tabs = /* @__PURE__ */ new Map();
|
|
253
|
+
// Viewport listeners, keyed by conversationId and kept SEPARATE from the
|
|
254
|
+
// tab. A host (e.g. a live iOS viewport) subscribes once; its listeners must
|
|
255
|
+
// outlive any individual tab so that closing one browser and opening another
|
|
256
|
+
// in the same conversation — or an LRU tab eviction — doesn't silently
|
|
257
|
+
// orphan the subscription (the symptom: the second session's status/frames
|
|
258
|
+
// never reach the client until it reconnects). Tabs come and go; listeners
|
|
259
|
+
// persist until the host unsubscribes.
|
|
260
|
+
frameListeners = /* @__PURE__ */ new Map();
|
|
261
|
+
statusListeners = /* @__PURE__ */ new Map();
|
|
253
262
|
// Whether context-level stealth init script has been installed
|
|
254
263
|
_contextStealthInstalled = false;
|
|
255
264
|
// Track which tabs have had per-page CDP UA override applied
|
|
@@ -549,13 +558,10 @@ var BrowserSession = class {
|
|
|
549
558
|
if (realTabs > 0) {
|
|
550
559
|
await mgr.newTab();
|
|
551
560
|
}
|
|
552
|
-
const existing = tab;
|
|
553
561
|
tab = {
|
|
554
562
|
tabIndex: mgr.getActiveIndex(),
|
|
555
563
|
active: true,
|
|
556
|
-
lastUsed: Date.now()
|
|
557
|
-
frameListeners: existing?.frameListeners ?? /* @__PURE__ */ new Set(),
|
|
558
|
-
statusListeners: existing?.statusListeners ?? /* @__PURE__ */ new Set()
|
|
564
|
+
lastUsed: Date.now()
|
|
559
565
|
};
|
|
560
566
|
this.tabs.set(conversationId, tab);
|
|
561
567
|
} else {
|
|
@@ -829,15 +835,15 @@ var BrowserSession = class {
|
|
|
829
835
|
(frame) => {
|
|
830
836
|
const cid = this._screencastConversation;
|
|
831
837
|
if (!cid) return;
|
|
832
|
-
const
|
|
833
|
-
if (!
|
|
838
|
+
const listeners = this.frameListeners.get(cid);
|
|
839
|
+
if (!listeners || listeners.size === 0) return;
|
|
834
840
|
const browserFrame = {
|
|
835
841
|
data: frame.data,
|
|
836
842
|
width: frame.metadata.deviceWidth,
|
|
837
843
|
height: frame.metadata.deviceHeight,
|
|
838
844
|
timestamp: Date.now()
|
|
839
845
|
};
|
|
840
|
-
for (const listener of
|
|
846
|
+
for (const listener of listeners) {
|
|
841
847
|
try {
|
|
842
848
|
listener(browserFrame);
|
|
843
849
|
} catch {
|
|
@@ -865,29 +871,37 @@ var BrowserSession = class {
|
|
|
865
871
|
// Per-conversation event listeners
|
|
866
872
|
// -----------------------------------------------------------------------
|
|
867
873
|
onFrame(conversationId, listener) {
|
|
868
|
-
let
|
|
869
|
-
if (!
|
|
870
|
-
|
|
871
|
-
this.
|
|
874
|
+
let set = this.frameListeners.get(conversationId);
|
|
875
|
+
if (!set) {
|
|
876
|
+
set = /* @__PURE__ */ new Set();
|
|
877
|
+
this.frameListeners.set(conversationId, set);
|
|
872
878
|
}
|
|
873
|
-
|
|
879
|
+
set.add(listener);
|
|
874
880
|
return () => {
|
|
875
|
-
|
|
876
|
-
if (
|
|
877
|
-
|
|
878
|
-
|
|
881
|
+
const s = this.frameListeners.get(conversationId);
|
|
882
|
+
if (!s) return;
|
|
883
|
+
s.delete(listener);
|
|
884
|
+
if (s.size === 0) {
|
|
885
|
+
this.frameListeners.delete(conversationId);
|
|
886
|
+
if (this._screencastConversation === conversationId) {
|
|
887
|
+
this.stopScreencast().catch(() => {
|
|
888
|
+
});
|
|
889
|
+
}
|
|
879
890
|
}
|
|
880
891
|
};
|
|
881
892
|
}
|
|
882
893
|
onStatus(conversationId, listener) {
|
|
883
|
-
let
|
|
884
|
-
if (!
|
|
885
|
-
|
|
886
|
-
this.
|
|
894
|
+
let set = this.statusListeners.get(conversationId);
|
|
895
|
+
if (!set) {
|
|
896
|
+
set = /* @__PURE__ */ new Set();
|
|
897
|
+
this.statusListeners.set(conversationId, set);
|
|
887
898
|
}
|
|
888
|
-
|
|
899
|
+
set.add(listener);
|
|
889
900
|
return () => {
|
|
890
|
-
|
|
901
|
+
const s = this.statusListeners.get(conversationId);
|
|
902
|
+
if (!s) return;
|
|
903
|
+
s.delete(listener);
|
|
904
|
+
if (s.size === 0) this.statusListeners.delete(conversationId);
|
|
891
905
|
};
|
|
892
906
|
}
|
|
893
907
|
// -----------------------------------------------------------------------
|
|
@@ -1087,8 +1101,9 @@ var BrowserSession = class {
|
|
|
1087
1101
|
url: tab?.url,
|
|
1088
1102
|
interactionAllowed: tab?.active ?? false
|
|
1089
1103
|
};
|
|
1090
|
-
|
|
1091
|
-
|
|
1104
|
+
const listeners = this.statusListeners.get(conversationId);
|
|
1105
|
+
if (listeners) {
|
|
1106
|
+
for (const listener of listeners) {
|
|
1092
1107
|
try {
|
|
1093
1108
|
listener(status);
|
|
1094
1109
|
} catch {
|
package/package.json
CHANGED
package/src/session.ts
CHANGED
|
@@ -144,8 +144,6 @@ interface ConversationTab {
|
|
|
144
144
|
url?: string;
|
|
145
145
|
active: boolean;
|
|
146
146
|
lastUsed: number;
|
|
147
|
-
frameListeners: Set<FrameListener>;
|
|
148
|
-
statusListeners: Set<StatusListener>;
|
|
149
147
|
}
|
|
150
148
|
|
|
151
149
|
export class BrowserSession {
|
|
@@ -156,6 +154,16 @@ export class BrowserSession {
|
|
|
156
154
|
// Tab management: conversationId → tab state
|
|
157
155
|
private readonly tabs = new Map<string, ConversationTab>();
|
|
158
156
|
|
|
157
|
+
// Viewport listeners, keyed by conversationId and kept SEPARATE from the
|
|
158
|
+
// tab. A host (e.g. a live iOS viewport) subscribes once; its listeners must
|
|
159
|
+
// outlive any individual tab so that closing one browser and opening another
|
|
160
|
+
// in the same conversation — or an LRU tab eviction — doesn't silently
|
|
161
|
+
// orphan the subscription (the symptom: the second session's status/frames
|
|
162
|
+
// never reach the client until it reconnects). Tabs come and go; listeners
|
|
163
|
+
// persist until the host unsubscribes.
|
|
164
|
+
private readonly frameListeners = new Map<string, Set<FrameListener>>();
|
|
165
|
+
private readonly statusListeners = new Map<string, Set<StatusListener>>();
|
|
166
|
+
|
|
159
167
|
// Whether context-level stealth init script has been installed
|
|
160
168
|
private _contextStealthInstalled = false;
|
|
161
169
|
|
|
@@ -500,13 +508,10 @@ export class BrowserSession {
|
|
|
500
508
|
if (realTabs > 0) {
|
|
501
509
|
await mgr.newTab();
|
|
502
510
|
}
|
|
503
|
-
const existing = tab;
|
|
504
511
|
tab = {
|
|
505
512
|
tabIndex: mgr.getActiveIndex(),
|
|
506
513
|
active: true,
|
|
507
514
|
lastUsed: Date.now(),
|
|
508
|
-
frameListeners: existing?.frameListeners ?? new Set(),
|
|
509
|
-
statusListeners: existing?.statusListeners ?? new Set(),
|
|
510
515
|
};
|
|
511
516
|
this.tabs.set(conversationId, tab);
|
|
512
517
|
} else {
|
|
@@ -799,15 +804,15 @@ export class BrowserSession {
|
|
|
799
804
|
(frame) => {
|
|
800
805
|
const cid = this._screencastConversation;
|
|
801
806
|
if (!cid) return;
|
|
802
|
-
const
|
|
803
|
-
if (!
|
|
807
|
+
const listeners = this.frameListeners.get(cid);
|
|
808
|
+
if (!listeners || listeners.size === 0) return;
|
|
804
809
|
const browserFrame: BrowserFrame = {
|
|
805
810
|
data: frame.data,
|
|
806
811
|
width: frame.metadata.deviceWidth,
|
|
807
812
|
height: frame.metadata.deviceHeight,
|
|
808
813
|
timestamp: Date.now(),
|
|
809
814
|
};
|
|
810
|
-
for (const listener of
|
|
815
|
+
for (const listener of listeners) {
|
|
811
816
|
try { listener(browserFrame); } catch { /* */ }
|
|
812
817
|
}
|
|
813
818
|
},
|
|
@@ -835,28 +840,38 @@ export class BrowserSession {
|
|
|
835
840
|
// -----------------------------------------------------------------------
|
|
836
841
|
|
|
837
842
|
onFrame(conversationId: string, listener: FrameListener): () => void {
|
|
838
|
-
let
|
|
839
|
-
if (!
|
|
840
|
-
|
|
841
|
-
this.
|
|
843
|
+
let set = this.frameListeners.get(conversationId);
|
|
844
|
+
if (!set) {
|
|
845
|
+
set = new Set();
|
|
846
|
+
this.frameListeners.set(conversationId, set);
|
|
842
847
|
}
|
|
843
|
-
|
|
848
|
+
set.add(listener);
|
|
844
849
|
return () => {
|
|
845
|
-
|
|
846
|
-
if (
|
|
847
|
-
|
|
850
|
+
const s = this.frameListeners.get(conversationId);
|
|
851
|
+
if (!s) return;
|
|
852
|
+
s.delete(listener);
|
|
853
|
+
if (s.size === 0) {
|
|
854
|
+
this.frameListeners.delete(conversationId);
|
|
855
|
+
if (this._screencastConversation === conversationId) {
|
|
856
|
+
this.stopScreencast().catch(() => {});
|
|
857
|
+
}
|
|
848
858
|
}
|
|
849
859
|
};
|
|
850
860
|
}
|
|
851
861
|
|
|
852
862
|
onStatus(conversationId: string, listener: StatusListener): () => void {
|
|
853
|
-
let
|
|
854
|
-
if (!
|
|
855
|
-
|
|
856
|
-
this.
|
|
863
|
+
let set = this.statusListeners.get(conversationId);
|
|
864
|
+
if (!set) {
|
|
865
|
+
set = new Set();
|
|
866
|
+
this.statusListeners.set(conversationId, set);
|
|
857
867
|
}
|
|
858
|
-
|
|
859
|
-
return () => {
|
|
868
|
+
set.add(listener);
|
|
869
|
+
return () => {
|
|
870
|
+
const s = this.statusListeners.get(conversationId);
|
|
871
|
+
if (!s) return;
|
|
872
|
+
s.delete(listener);
|
|
873
|
+
if (s.size === 0) this.statusListeners.delete(conversationId);
|
|
874
|
+
};
|
|
860
875
|
}
|
|
861
876
|
|
|
862
877
|
// -----------------------------------------------------------------------
|
|
@@ -1068,8 +1083,12 @@ export class BrowserSession {
|
|
|
1068
1083
|
url: tab?.url,
|
|
1069
1084
|
interactionAllowed: tab?.active ?? false,
|
|
1070
1085
|
};
|
|
1071
|
-
|
|
1072
|
-
|
|
1086
|
+
// Listeners live at the session level, so a close (which deletes the tab)
|
|
1087
|
+
// still delivers the final active:false to the host before the tab is
|
|
1088
|
+
// gone — and a later reopen reuses the same subscription.
|
|
1089
|
+
const listeners = this.statusListeners.get(conversationId);
|
|
1090
|
+
if (listeners) {
|
|
1091
|
+
for (const listener of listeners) {
|
|
1073
1092
|
try { listener(status); } catch { /* */ }
|
|
1074
1093
|
}
|
|
1075
1094
|
}
|