dsh-inner-browser-use 0.34.98 → 0.34.99
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/client.js +33 -11
- package/index.js +35 -4
- package/package.json +1 -1
- package/sidecar.js +19 -2
package/client.js
CHANGED
|
@@ -73,7 +73,7 @@ window.__ModuleLoader__.load({
|
|
|
73
73
|
'.dshib-side-win[data-on="1"]{background:var(--dsw-alias-bg-subtle);color:var(--dsw-alias-label-primary)}',
|
|
74
74
|
'.dshib-side-win > span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}',
|
|
75
75
|
'.dshib-side-stage{flex:1 1 auto;position:relative;min-width:0;min-height:0;display:flex;align-items:center;justify-content:center;overflow:hidden;background:var(--dsw-alias-bg-base);outline:none;cursor:default}',
|
|
76
|
-
'.dshib-side-frame{display:block;user-select:none;-webkit-user-drag:none;cursor:default}',
|
|
76
|
+
'.dshib-side-frame{position:absolute;inset:0;display:block;width:100%;height:100%;object-fit:contain;user-select:none;-webkit-user-drag:none;cursor:default}',
|
|
77
77
|
'.dshib-side-ime{position:absolute;width:1px;height:1px;padding:0;margin:0;border:0;resize:none;overflow:hidden;opacity:0;pointer-events:none}',
|
|
78
78
|
/* The black arrow is the agent's pointer, not the person's cursor: both are
|
|
79
79
|
on screen at once, and moving the mouse never takes the arrow over. */
|
|
@@ -645,9 +645,27 @@ window.__ModuleLoader__.load({
|
|
|
645
645
|
.catch(() => {})
|
|
646
646
|
}
|
|
647
647
|
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
648
|
+
/* Where the page actually got drawn inside the column: the frame fills
|
|
649
|
+
the stage and the picture sits inside it, so the answer comes from the
|
|
650
|
+
element that is on screen rather than from the last size remembered.
|
|
651
|
+
A stale measure can then never shrink the picture to a thumbnail. */
|
|
652
|
+
const frameEl = React.useRef(null)
|
|
653
|
+
const picture = () => {
|
|
654
|
+
const at = frameEl.current
|
|
655
|
+
const areaEl = stage.current
|
|
656
|
+
if (at === null || at === undefined || areaEl === null || areaEl === undefined) return null
|
|
657
|
+
if (!(view.w > 0) || !(view.h > 0)) return null
|
|
658
|
+
const a = at.getBoundingClientRect()
|
|
659
|
+
const b = areaEl.getBoundingClientRect()
|
|
660
|
+
if (!(a.width > 1) || !(a.height > 1)) return null
|
|
661
|
+
const scale = Math.min(a.width / view.w, a.height / view.h)
|
|
662
|
+
if (!(scale > 0)) return null
|
|
663
|
+
return {
|
|
664
|
+
scale,
|
|
665
|
+
x: a.left - b.left + (a.width - view.w * scale) / 2,
|
|
666
|
+
y: a.top - b.top + (a.height - view.h * scale) / 2,
|
|
667
|
+
}
|
|
668
|
+
}
|
|
651
669
|
|
|
652
670
|
/* The column tells the page how big it is, a beat after the resize stops:
|
|
653
671
|
a page that relayouts on every pointer step would never settle. */
|
|
@@ -729,10 +747,12 @@ window.__ModuleLoader__.load({
|
|
|
729
747
|
}
|
|
730
748
|
const toView = (event) => {
|
|
731
749
|
const el = stage.current
|
|
732
|
-
if (el === null
|
|
750
|
+
if (el === null) return null
|
|
751
|
+
const spot = picture()
|
|
752
|
+
if (spot === null) return null
|
|
733
753
|
const box = el.getBoundingClientRect()
|
|
734
|
-
const x = (event.clientX - box.left -
|
|
735
|
-
const y = (event.clientY - box.top -
|
|
754
|
+
const x = (event.clientX - box.left - spot.x) / spot.scale
|
|
755
|
+
const y = (event.clientY - box.top - spot.y) / spot.scale
|
|
736
756
|
if (x < 0 || y < 0 || x > view.w || y > view.h) return null
|
|
737
757
|
return { x: Math.round(x), y: Math.round(y) }
|
|
738
758
|
}
|
|
@@ -828,6 +848,7 @@ window.__ModuleLoader__.load({
|
|
|
828
848
|
h('div', { className: 'dshib-side-note' }, L('side.foreign')))
|
|
829
849
|
}
|
|
830
850
|
const pointer = person === true || hand === true ? null : ptr
|
|
851
|
+
const spot = pointer === null ? null : (picture() ?? { scale: 1, x: 0, y: 0 })
|
|
831
852
|
const noteText = phase === 'failed' ? note || L('side.failed') : phase === 'live' ? L('side.empty') : L('side.connecting')
|
|
832
853
|
return h('div', { className: 'dshib-side' },
|
|
833
854
|
h('div', { className: 'dshib-side-bar' },
|
|
@@ -894,7 +915,7 @@ window.__ModuleLoader__.load({
|
|
|
894
915
|
tabIndex: 0,
|
|
895
916
|
spellCheck: false,
|
|
896
917
|
'aria-label': L('side.type'),
|
|
897
|
-
style: pointer === null ? undefined : { left: `${
|
|
918
|
+
style: pointer === null || spot === null ? undefined : { left: `${spot.x + pointer.x * spot.scale}px`, top: `${spot.y + pointer.y * spot.scale}px` },
|
|
898
919
|
onCompositionStart: () => {
|
|
899
920
|
composing.current = true
|
|
900
921
|
},
|
|
@@ -913,9 +934,10 @@ window.__ModuleLoader__.load({
|
|
|
913
934
|
}),
|
|
914
935
|
frame === null ? null : h('img', {
|
|
915
936
|
className: 'dshib-side-frame',
|
|
937
|
+
ref: frameEl,
|
|
916
938
|
src: `data:image/jpeg;base64,${frame.jpeg}`,
|
|
917
|
-
width: Math.max(1, Math.round(view.w
|
|
918
|
-
height: Math.max(1, Math.round(view.h
|
|
939
|
+
width: Math.max(1, Math.round(view.w)),
|
|
940
|
+
height: Math.max(1, Math.round(view.h)),
|
|
919
941
|
alt: '', // ui-copy-ignore
|
|
920
942
|
draggable: false,
|
|
921
943
|
}),
|
|
@@ -923,7 +945,7 @@ window.__ModuleLoader__.load({
|
|
|
923
945
|
className: 'dshib-side-ptr',
|
|
924
946
|
src: SIDE_POINTER, // ui-copy-ignore
|
|
925
947
|
alt: '', // ui-copy-ignore
|
|
926
|
-
style: { left: `${
|
|
948
|
+
style: spot === null ? undefined : { left: `${spot.x + pointer.x * spot.scale}px`, top: `${spot.y + pointer.y * spot.scale}px` },
|
|
927
949
|
})))
|
|
928
950
|
}
|
|
929
951
|
},
|
package/index.js
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* learns no port and no secret. // ui-copy-ignore
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
-
import { appendFileSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
23
|
+
import { appendFileSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs'
|
|
24
24
|
import { homedir } from 'node:os'
|
|
25
25
|
import { join } from 'node:path'
|
|
26
26
|
import { Sidecar } from './sidecar.js?v=1'
|
|
@@ -50,6 +50,27 @@ function writeMirror(value) {
|
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
/** The note stops growing: past this size the oldest half goes, so a long-lived run never fills the disk. */
|
|
54
|
+
const NOTES_MAX = 256 * 1024
|
|
55
|
+
const NOTES_KEEP = 128 * 1024
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Add one line to the mirror's own note, keeping the file bounded.
|
|
59
|
+
* @param {string} line - what happened, one line.
|
|
60
|
+
*/
|
|
61
|
+
export function note(line) {
|
|
62
|
+
const file = join(STATE_DIR, 'asks.log')
|
|
63
|
+
try {
|
|
64
|
+
mkdirSync(STATE_DIR, { recursive: true })
|
|
65
|
+
appendFileSync(file, `${new Date().toISOString()} ${line}\n`)
|
|
66
|
+
if (statSync(file).size <= NOTES_MAX) return
|
|
67
|
+
const whole = readFileSync(file, 'utf8')
|
|
68
|
+
writeFileSync(file, whole.slice(whole.length - NOTES_KEEP).replace(/^[^\n]*\n/, ''))
|
|
69
|
+
} catch {
|
|
70
|
+
/* a note that cannot be written must not take the mirror down */
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
53
74
|
/** Route prefix owned by this plugin. Never under `/api`, which is the Host's own channel. */ // ui-copy-ignore
|
|
54
75
|
export const ROUTE = '/dsh-inner-browser-use'
|
|
55
76
|
|
|
@@ -336,7 +357,7 @@ export function apply(ctx) {
|
|
|
336
357
|
if (due !== true) return
|
|
337
358
|
last = Date.now()
|
|
338
359
|
bridge.want(owner, Date.now(), 'keepalive')
|
|
339
|
-
|
|
360
|
+
note(`asked ${owner.slice(-8)} to draw the mirror again`)
|
|
340
361
|
} catch {}
|
|
341
362
|
}, 4000)
|
|
342
363
|
timer.unref?.()
|
|
@@ -511,7 +532,7 @@ export function apply(ctx) {
|
|
|
511
532
|
if (Date.now() - before > 2000) {
|
|
512
533
|
lastAsk.set(chat, Date.now())
|
|
513
534
|
try {
|
|
514
|
-
|
|
535
|
+
note(`${chat.slice(-8)} alive=${String(said?.alive === true)} viewers=${String(said?.viewers ?? 0)}`)
|
|
515
536
|
} catch {}
|
|
516
537
|
}
|
|
517
538
|
}
|
|
@@ -715,7 +736,17 @@ export function apply(ctx) {
|
|
|
715
736
|
const body = await readBody(req).then((raw) => JSON.parse(raw || '{}')).catch(() => ({}))
|
|
716
737
|
const who = acting(body)
|
|
717
738
|
if (who === '') return
|
|
718
|
-
|
|
739
|
+
const forgot = bridge.forget(who)
|
|
740
|
+
/* Nobody is watching a picture the person just closed: the browser itself
|
|
741
|
+
is put away a minute later rather than a quarter of an hour, unless an
|
|
742
|
+
agent asks for it again in the meantime. */
|
|
743
|
+
try {
|
|
744
|
+
if (sidecar.watched() === 0) {
|
|
745
|
+
sidecar.touch(REST_MS)
|
|
746
|
+
note('said ' + who.slice(-8) + ' closed the mirror - browser rests in a minute')
|
|
747
|
+
}
|
|
748
|
+
} catch { /* a browser that never started needs no rest timer */ }
|
|
749
|
+
json(200, { ok: true, forgot })
|
|
719
750
|
return
|
|
720
751
|
}
|
|
721
752
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-inner-browser-use",
|
|
3
|
-
"version": "0.34.
|
|
3
|
+
"version": "0.34.99",
|
|
4
4
|
"description": "An agent-owned Chromium sidecar mirrored into the DeepSeek Harness right column: the agent drives it, the person watches and can take the mouse, and logins survive restarts.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
package/sidecar.js
CHANGED
|
@@ -95,6 +95,12 @@ export function nextQuality(current, base64) {
|
|
|
95
95
|
|
|
96
96
|
/** Idle lifetime, so a forgotten sidecar does not sit on a login forever. */
|
|
97
97
|
const IDLE_MS = 15 * 60_000
|
|
98
|
+
/* A pane the person closed is a stronger signal than a pane nobody touched: the
|
|
99
|
+
browser is put away a minute later unless someone asks for it again. A whole
|
|
100
|
+
Chrome with its renderers is a few hundred megabytes, and leaving it up for a
|
|
101
|
+
quarter of an hour after a deliberate close is exactly the cost they did not
|
|
102
|
+
want. */
|
|
103
|
+
const REST_MS = 60_000
|
|
98
104
|
/* One browser serves every chat, but a chat that is not on screen need not keep
|
|
99
105
|
a full renderer alive for ever: past this many chats the oldest unwatched tab
|
|
100
106
|
is closed, and its address brings it back on the next visit. */
|
|
@@ -2836,7 +2842,18 @@ export class Sidecar {
|
|
|
2836
2842
|
}
|
|
2837
2843
|
}
|
|
2838
2844
|
|
|
2839
|
-
|
|
2845
|
+
/** @returns {number} how many panes are watching a picture right now. */
|
|
2846
|
+
watched() {
|
|
2847
|
+
let n = this.page?.frames?.size ?? 0
|
|
2848
|
+
for (const page of this.pages?.values() ?? []) n += page.frames?.size ?? 0
|
|
2849
|
+
return n
|
|
2850
|
+
}
|
|
2851
|
+
|
|
2852
|
+
/**
|
|
2853
|
+
* Starts the idle clock again.
|
|
2854
|
+
* @param {number} [ms] - how long a browser nobody watches may stay up.
|
|
2855
|
+
*/
|
|
2856
|
+
touch(ms = IDLE_MS) {
|
|
2840
2857
|
if (this.idle !== null) clearTimeout(this.idle)
|
|
2841
2858
|
this.idle = setTimeout(() => {
|
|
2842
2859
|
this.idle = null
|
|
@@ -2849,7 +2866,7 @@ export class Sidecar {
|
|
|
2849
2866
|
return
|
|
2850
2867
|
}
|
|
2851
2868
|
void this.shutdown()
|
|
2852
|
-
},
|
|
2869
|
+
}, ms)
|
|
2853
2870
|
this.idle.unref?.()
|
|
2854
2871
|
}
|
|
2855
2872
|
|