@deepseek-insight/dsi 0.8.4 → 0.9.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.
- package/.agents/skills/glass-dsi/SKILL.md +195 -0
- package/.agents/skills/glass-dsi/references/cdp-verification.md +132 -0
- package/.agents/skills/glass-dsi/scripts/_connect.mjs +81 -0
- package/.agents/skills/glass-dsi/scripts/glass-url.mjs +48 -0
- package/.agents/skills/glass-dsi/scripts/glass.mjs +84 -0
- package/.agents/skills/glass-url/SKILL.md +117 -0
- package/.agents/skills/glass-url/scripts/_args.mjs +49 -0
- package/.agents/skills/glass-url/scripts/_connect.mjs +99 -0
- package/.agents/skills/glass-url/scripts/glass-url.mjs +114 -0
- package/bin/dsi.mjs +23 -74
- package/bin/lib/zai-patch.mjs +115 -0
- package/build/client/_app/immutable/assets/0.Ch5VtsKp.css +2 -0
- package/build/client/_app/immutable/assets/2.D0QOh44D.css +1 -0
- package/build/client/_app/immutable/chunks/{DwcY-69G.js → 02Wfo2G3.js} +1 -1
- package/build/client/_app/immutable/chunks/0Tfhif1V.js +3 -0
- package/build/client/_app/immutable/chunks/{GKkjunMm.js → 0ruuBBbh.js} +2 -2
- package/build/client/_app/immutable/chunks/{DUCaFYlx.js → 4bHE1npv.js} +1 -1
- package/build/client/_app/immutable/chunks/{4cwyag_I.js → B4hDUAJl.js} +1 -1
- package/build/client/_app/immutable/chunks/{Cr2ck_ss.js → BIPXB-2I.js} +1 -1
- package/build/client/_app/immutable/chunks/{CeLBjOd9.js → BIgv-kQR.js} +1 -1
- package/build/client/_app/immutable/chunks/BK6k1nBs.js +1 -0
- package/build/client/_app/immutable/chunks/{SN3Efo-Q.js → BKwmKcDY.js} +1 -1
- package/build/client/_app/immutable/chunks/{CRFuOqJI.js → BORjuqef.js} +1 -1
- package/build/client/_app/immutable/chunks/{CH_bR_0j.js → BPmZB0-N.js} +1 -1
- package/build/client/_app/immutable/chunks/BROtno1Y.js +1 -0
- package/build/client/_app/immutable/chunks/{DEVxi5CK.js → BT5Iel00.js} +1 -1
- package/build/client/_app/immutable/chunks/{DdI16loP.js → BX3y_1Hh.js} +1 -1
- package/build/client/_app/immutable/chunks/{DuiFyTVb.js → Bea1gxLq.js} +1 -1
- package/build/client/_app/immutable/chunks/{D0uxHECr.js → BpkpgvVw.js} +1 -1
- package/build/client/_app/immutable/chunks/{CB55Rp7-.js → BreZS7EW.js} +1 -1
- package/build/client/_app/immutable/chunks/{BmDADyfH.js → BsS_byzk.js} +1 -1
- package/build/client/_app/immutable/chunks/{BWRXzjmQ.js → Bslg-dr5.js} +1 -1
- package/build/client/_app/immutable/chunks/{BkYT72a1.js → C-YczRN1.js} +1 -1
- package/build/client/_app/immutable/chunks/{DBKMIEwD.js → C2aW4lzo.js} +1 -1
- package/build/client/_app/immutable/chunks/{C4gs6zoI.js → C2qFXaz5.js} +1 -1
- package/build/client/_app/immutable/chunks/{BBd0nTHG.js → C5RyRpOh.js} +1 -1
- package/build/client/_app/immutable/chunks/{DJQr4efH.js → C5iHDVU_.js} +1 -1
- package/build/client/_app/immutable/chunks/{B4F5liKq.js → C7opVlLF.js} +1 -1
- package/build/client/_app/immutable/chunks/{DIxy2USu.js → CAuEkmZ4.js} +1 -1
- package/build/client/_app/immutable/chunks/{BO99LSSA.js → CIkiqgtA.js} +1 -1
- package/build/client/_app/immutable/chunks/{Bpd0I3U7.js → CJesWaZn.js} +1 -1
- package/build/client/_app/immutable/chunks/{at6PQqQ4.js → CRsoS5Y9.js} +1 -1
- package/build/client/_app/immutable/chunks/CTWtsoJD.js +6 -0
- package/build/client/_app/immutable/chunks/CWk5gmU2.js +1 -0
- package/build/client/_app/immutable/chunks/{lF8CNVOA.js → CXkrluSK.js} +1 -1
- package/build/client/_app/immutable/chunks/{DmRY6FWm.js → C_8kFZ_X.js} +4 -4
- package/build/client/_app/immutable/chunks/{D6KenyiF.js → Ch7hUoAH.js} +1 -1
- package/build/client/_app/immutable/chunks/{CnzmIbjr.js → ChmmHPdM.js} +1 -1
- package/build/client/_app/immutable/chunks/{DfUgh5E_.js → CjajmcJs.js} +1 -1
- package/build/client/_app/immutable/chunks/{q5PRaRZk.js → Ck6GS6Vb.js} +1 -1
- package/build/client/_app/immutable/chunks/{eFcFJ9Ld.js → ClGoDr_f.js} +1 -1
- package/build/client/_app/immutable/chunks/{BQC6O_RV.js → ClqGHlec.js} +1 -1
- package/build/client/_app/immutable/chunks/{zypfUZYM.js → CnW6hje6.js} +1 -1
- package/build/client/_app/immutable/chunks/{OW5Q7tGb.js → CpI6VYCH.js} +1 -1
- package/build/client/_app/immutable/chunks/{BXKc4uuX.js → CygwwaiE.js} +1 -1
- package/build/client/_app/immutable/chunks/{Bqwvv7Vf.js → CyhZtsZO.js} +35 -35
- package/build/client/_app/immutable/chunks/{BenkQ_4r.js → D-E2Noqq.js} +1 -1
- package/build/client/_app/immutable/chunks/{DuRZnRVw.js → D4Btzp_A.js} +1 -1
- package/build/client/_app/immutable/chunks/{CdaJvl_A.js → D4C3Jijv.js} +1 -1
- package/build/client/_app/immutable/chunks/D9jhmf3I.js +1 -0
- package/build/client/_app/immutable/chunks/{Cf0aMC4n.js → DCw0gAtv.js} +1 -1
- package/build/client/_app/immutable/chunks/{B6bTO8_7.js → DEW3KpiC.js} +1 -1
- package/build/client/_app/immutable/chunks/{O6xxeEm8.js → DYLFbKJe.js} +1 -1
- package/build/client/_app/immutable/chunks/{BsBBq-Qk.js → DYu7kgVa.js} +1 -1
- package/build/client/_app/immutable/chunks/Dfexc5qV.js +1 -0
- package/build/client/_app/immutable/chunks/{kzciT-3i.js → DhmUE8Ri.js} +1 -1
- package/build/client/_app/immutable/chunks/{DdJNsJBH.js → DjwX3rAC.js} +1 -1
- package/build/client/_app/immutable/chunks/{CajOZJDl.js → DlOfy8Sx.js} +1 -1
- package/build/client/_app/immutable/chunks/{CK5vhXvt.js → Do7Y1_MQ.js} +1 -1
- package/build/client/_app/immutable/chunks/{ap4GAVq-.js → Dq2XIb1m.js} +1 -1
- package/build/client/_app/immutable/chunks/{Czey8tDK.js → DuT_Qr5k.js} +1 -1
- package/build/client/_app/immutable/chunks/{4B6J_kM2.js → DyGggFea.js} +1 -1
- package/build/client/_app/immutable/chunks/{D0ChUnpi.js → Dym4i64U.js} +1 -1
- package/build/client/_app/immutable/chunks/FPsIAiN_.js +1 -0
- package/build/client/_app/immutable/chunks/{CevKuuic.js → GIczpP_D.js} +1 -1
- package/build/client/_app/immutable/chunks/{B2jrDnt8.js → LjOLSmU7.js} +1 -1
- package/build/client/_app/immutable/chunks/VtK73aN-.js +8 -0
- package/build/client/_app/immutable/chunks/{AcmOeh2U.js → ZqoQH_jm.js} +1 -1
- package/build/client/_app/immutable/chunks/{BlwWx85l.js → aXn6rE_7.js} +1 -1
- package/build/client/_app/immutable/chunks/{Czc4_W7v.js → dUOeoGwg.js} +1 -1
- package/build/client/_app/immutable/chunks/f50lp1rr.js +1 -0
- package/build/client/_app/immutable/chunks/{7FnrqGK9.js → lvLrv1cG.js} +1 -1
- package/build/client/_app/immutable/chunks/{CZoElI5B.js → pt3_jHwh.js} +1 -1
- package/build/client/_app/immutable/chunks/{Byu_teLA.js → q8irUX-p.js} +1 -1
- package/build/client/_app/immutable/chunks/{CnxoqrkQ.js → qmOgUo3c.js} +1 -1
- package/build/client/_app/immutable/chunks/vpNeypNM.js +1 -0
- package/build/client/_app/immutable/chunks/{DU8C5thd.js → xvTPLhcB.js} +1 -1
- package/build/client/_app/immutable/chunks/{DFBKiA2Z.js → zp7hrpsC.js} +1 -1
- package/build/client/_app/immutable/entry/app.D4oc_iez.js +2 -0
- package/build/client/_app/immutable/entry/start.Xkr2MCWg.js +1 -0
- package/build/client/_app/immutable/nodes/0.fsrqKU9i.js +1 -0
- package/build/client/_app/immutable/nodes/1.3F37GwcO.js +1 -0
- package/build/client/_app/immutable/nodes/2.DsWNNw0K.js +340 -0
- package/build/client/_app/version.json +1 -1
- package/build/handler.js +6 -6
- package/build/index.js +6 -6
- package/build/server/chunks/chunks/{app-config.svelte.js-BM4WbMFB.js → app-config.svelte.js-BXjvTmJu.js} +8 -3
- package/build/server/chunks/chunks/{config.js-vThvQeMn.js → config.js-r_jw_bQQ.js} +54 -2
- package/build/server/chunks/chunks/{db.js-DQm6CZ_r.js → db.js-f2PmBjyD.js} +2 -2
- package/build/server/chunks/chunks/{db2.js-DUhFxA3a.js → db2.js-DkwY64WO.js} +2 -2
- package/build/server/chunks/chunks/{dsh-connection.js-BgeraFA2.js → dsh-connection.js-BW07Aicl.js} +18 -10
- package/build/server/chunks/chunks/{dsh-events.js-BUkLR6ZV.js → dsh-events.js-BHf0lLZ5.js} +16 -3
- package/build/server/chunks/chunks/{git-probe.js-CLF37SRh.js → git-probe.js-CQqTV7XX.js} +3 -3
- package/build/server/chunks/chunks/{index-server.js-DmIEbDVz.js → index-server.js-CPkVoRxB.js} +2 -2
- package/build/server/chunks/chunks/{insight-config.js-Dp_2gB5J.js → insight-config.js-C5-Rhh5A.js} +19 -6
- package/build/server/chunks/chunks/{internal2.js-J3h-US64.js → internal2.js-DbC8VGqD.js} +4 -4
- package/build/server/chunks/chunks/ledger-stats.js-BWACyDPg.js +72 -0
- package/build/server/chunks/chunks/{registry.js-CZNJZ0C-.js → registry.js-CWnRQ4vx.js} +751 -53
- package/build/server/chunks/chunks/{registry2.js-D3Pr1Rv9.js → registry2.js-CFzpvj9c.js} +17 -1
- package/build/server/chunks/chunks/{server.js-B1bDXhHu.js → server.js-Dijo7DtV.js} +2 -2
- package/build/server/chunks/chunks/{settings-document.js-Gw1puaik.js → settings-document.js-DxKURfc_.js} +2 -2
- package/build/server/chunks/chunks/{state.js-8RBD56dL.js → state.js-DR4xiIVw.js} +5 -5
- package/build/server/chunks/chunks/{watcher.js-BVKnoQUw.js → watcher.js-DvlNn8X0.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/a2a/_id_/{_server.ts.js-BjzdVwdY.js → _server.ts.js-DItDXV23.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/a2a/{_server.ts.js-3kQCQuHT.js → _server.ts.js-Hor8QZb1.js} +8 -8
- package/build/server/chunks/entries/endpoints/api/a2a/register/{_server.ts.js-BUBTCLFn.js → _server.ts.js-CEvvsa8B.js} +8 -8
- package/build/server/chunks/entries/endpoints/api/browser/_id_/activate/{_server.ts.js-BdMJzENm.js → _server.ts.js-sXqf-okw.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/browser/_id_/close/{_server.ts.js-B5z9-N8Y.js → _server.ts.js-Byj9ApJh.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/browser/_id_/closetab/{_server.ts.js-CnZHun5r.js → _server.ts.js-DOIsrzvg.js} +16 -9
- package/build/server/chunks/entries/endpoints/api/browser/_id_/element/{_server.ts.js-C1oM2Ihf.js → _server.ts.js-Cq3gmRfE.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/browser/_id_/history/_server.ts.js-BGSLIV0t.js +61 -0
- package/build/server/chunks/entries/endpoints/api/browser/_id_/input/{_server.ts.js-DmbqGRMg.js → _server.ts.js-8slca75-.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/browser/_id_/mcp/{_server.ts.js-Cj80T1yH.js → _server.ts.js-CpbdTYds.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/browser/_id_/navigate/{_server.ts.js-6gi1UYTJ.js → _server.ts.js-Bls9LtLY.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/browser/_id_/opentab/{_server.ts.js-Bhx_J7ot.js → _server.ts.js-DqNi_iW_.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/browser/_id_/page/{_server.ts.js-DvQXNnX9.js → _server.ts.js-DuXWwo7i.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/browser/_id_/stream/{_server.ts.js-MflkVWUn.js → _server.ts.js-DXfqBDEw.js} +38 -9
- package/build/server/chunks/entries/endpoints/api/browser/_id_/tabs/{_server.ts.js-CoUSlLcP.js → _server.ts.js-Bgnl8lYs.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/browser/_id_/viewport/{_server.ts.js-DNk4EgjJ.js → _server.ts.js-FHmQVbrv.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/browser/_id_/zoom/_server.ts.js-BV4cBtSA.js +62 -0
- package/build/server/chunks/entries/endpoints/api/browser/{_server.ts.js-CECKap7O.js → _server.ts.js-4vgkwh7v.js} +9 -6
- package/build/server/chunks/entries/endpoints/api/config/{_server.ts.js-C8XDnDwe.js → _server.ts.js-C_9kwANR.js} +16 -6
- package/build/server/chunks/entries/endpoints/api/dsh/directory/{_server.ts.js-BFmWshpv.js → _server.ts.js-h1OTHcL4.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/jobs/{_server.ts.js-DATWy5mQ.js → _server.ts.js-B6nih9Sd.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/jobs/follow/{_server.ts.js-CugOD1Zd.js → _server.ts.js-Cf17SVmD.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/pick-directory/{_server.ts.js-DCupM4da.js → _server.ts.js-DjNwMbdS.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/presets/{_server.ts.js-Bh1btvr9.js → _server.ts.js-Cz5aaEhC.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/attachment/_server.ts.js-BFBy5O9Y.js +78 -0
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/cancel/{_server.ts.js-WI9yH2iG.js → _server.ts.js-CgEdCo3T.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/catalog/{_server.ts.js-bN9adUqz.js → _server.ts.js-Bo_aEnHh.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/changes/{_server.ts.js-Bxlm966G.js → _server.ts.js-Bd2G_GaH.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/command/{_server.ts.js-CM-raN_Q.js → _server.ts.js-DL64FyRw.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/events/{_server.ts.js-BzP30TXW.js → _server.ts.js-MhOGB66D.js} +7 -70
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/fork/{_server.ts.js-DvNkrTaY.js → _server.ts.js-MjPgXgGO.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/goal/{_server.ts.js-DXbCrefw.js → _server.ts.js-BUjDae5H.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/history/{_server.ts.js-B7YRQhuJ.js → _server.ts.js-dTKd7Hsv.js} +6 -6
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/models/{_server.ts.js-C_AEaLLJ.js → _server.ts.js-B82rvKoI.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/path/{_server.ts.js-Dj-pF5ik.js → _server.ts.js-Cqijsi-c.js} +4 -4
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/permission/{_server.ts.js-BX9FWJ6g.js → _server.ts.js-CL_vVv2p.js} +29 -6
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/prompt/{_server.ts.js-CQ2Bmtu4.js → _server.ts.js-WS4W9Ly4.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/rename/{_server.ts.js-Ha7xjnKW.js → _server.ts.js-B72KGRGh.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/respond/{_server.ts.js-BOg6llHk.js → _server.ts.js-DK5xb2b5.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/select-model/{_server.ts.js-DBqGALzZ.js → _server.ts.js-lhT87EQu.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/sessions/{_server.ts.js-CvA8WDDb.js → _server.ts.js-CMhfhbkN.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/subagents/_server.ts.js-BLvcFsg6.js +72 -0
- package/build/server/chunks/entries/endpoints/api/dsh/workspace-file/{_server.ts.js-BfjNH40j.js → _server.ts.js-D_rw2Uq6.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/workspace-tree/{_server.ts.js-De_qumyr.js → _server.ts.js-DfXpMMpw.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/workspaces/{_server.ts.js-tFpJJHCb.js → _server.ts.js-2mJCJVxk.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/workspaces/_workspaceId_/delete/{_server.ts.js-DVx5GGA_.js → _server.ts.js-BCx0Lzjf.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/workspaces/_workspaceId_/rename/{_server.ts.js-DuKT-aZp.js → _server.ts.js-DECaSjwp.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/prompts/_id_/{_server.ts.js-DsWFidFY.js → _server.ts.js-i_ciJzlb.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/prompts/{_server.ts.js-C6j0adQs.js → _server.ts.js-BO1pScn7.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/prompts/use/{_server.ts.js-BChAg4hm.js → _server.ts.js-Buhpf7WE.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/settings/{_server.ts.js-BPSHqbZN.js → _server.ts.js-DngUQzcT.js} +4 -4
- package/build/server/chunks/entries/endpoints/api/terminal/_id_/output/{_server.ts.js-BNBxebgG.js → _server.ts.js-DLETP4yQ.js} +2 -2
- package/build/server/chunks/entries/endpoints/api/terminal/_id_/reattach/{_server.ts.js-DDRJBGIr.js → _server.ts.js-uWl6oxPS.js} +2 -2
- package/build/server/chunks/entries/endpoints/api/terminal/_id_/send/{_server.ts.js-CA0vc_Cm.js → _server.ts.js-DYNmXaAd.js} +9 -2
- package/build/server/chunks/entries/endpoints/api/terminal/_id_/stream/{_server.ts.js-D6L8Gd_f.js → _server.ts.js-C5m3Ktho.js} +2 -2
- package/build/server/chunks/entries/endpoints/api/terminal/{_server.ts.js-D5CbUe5k.js → _server.ts.js-DoTNvcOa.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/workspace/file-write/{_server.ts.js-B-5YihUA.js → _server.ts.js-BzYOJSkI.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/workspace/git-events/{_server.ts.js-DT5jRZFE.js → _server.ts.js-xcNesei9.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/workspace/git-file-head/{_server.ts.js-DtW-utrd.js → _server.ts.js-DsJWYdxt.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/workspace/git-map/{_server.ts.js-8AIAo9jX.js → _server.ts.js-CNCku9BR.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/workspace/git-status/{_server.ts.js-CMw96vjY.js → _server.ts.js-nLsNjOxj.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/workspace/write-gate/{_server.ts.js-DKufa14_.js → _server.ts.js-urQk2-ds.js} +7 -7
- package/build/server/chunks/entries/{hooks.server.js-B-e4_ekh.js → hooks.server.js-CU2RE5vs.js} +3 -3
- package/build/server/chunks/entries/pages/{_error.svelte.js-DRPduJdP.js → _error.svelte.js-e018KpwE.js} +6 -6
- package/build/server/chunks/entries/pages/{_page.server.ts.js-7x6ZfGoB.js → _page.server.ts.js-BK3Y6eff.js} +3 -3
- package/build/server/chunks/entries/pages/{_page.svelte.js-Dek8Q1_v.js → _page.svelte.js-DQWBiRk1.js} +4185 -1734
- package/build/server/chunks/{handler-DUyvzXsb.js → handler-DsD7I1_u.js} +3 -3
- package/build/server/chunks/{index.js-BrP1SRzN.js → index.js-C_WAkwJs.js} +4 -4
- package/build/server/chunks/{manifest.js-C4z6aoYx.js → manifest.js-DUy52VaP.js} +85 -64
- package/build/server/chunks/nodes/{0.js-BuB3t8Br.js → 0.js-CuV3dXVD.js} +3 -3
- package/build/server/chunks/nodes/1.js-Dc5QC-NJ.js +9 -0
- package/build/server/chunks/nodes/{2.js-C-o9KF10.js → 2.js-PwcF4_kT.js} +10 -10
- package/package.json +2 -2
- package/skills/{dsi-kb-scenario → dsi-fc-scenario}/SKILL.md +11 -6
- package/template/.dsi/prompts.sqlite +0 -0
- package/template/.dsi/prompts.sqlite-shm +0 -0
- package/template/.dsi/prompts.sqlite-wal +0 -0
- package/template/.dsi/settings.yaml +7 -2
- package/build/client/_app/immutable/assets/0.cswkTiBk.css +0 -2
- package/build/client/_app/immutable/assets/2.BPprd30B.css +0 -1
- package/build/client/_app/immutable/chunks/BHDqH9nE.js +0 -1
- package/build/client/_app/immutable/chunks/C7Sg_49n.js +0 -1
- package/build/client/_app/immutable/chunks/CAneZX4t.js +0 -8
- package/build/client/_app/immutable/chunks/D-Rolhzs.js +0 -1
- package/build/client/_app/immutable/chunks/DQXo7qdJ.js +0 -1
- package/build/client/_app/immutable/chunks/DY0MQ1mO.js +0 -1
- package/build/client/_app/immutable/chunks/K3xyLTzZ.js +0 -1
- package/build/client/_app/immutable/chunks/NcOjFWr3.js +0 -6
- package/build/client/_app/immutable/chunks/cHj8EuyS.js +0 -3
- package/build/client/_app/immutable/chunks/o7DVZODA.js +0 -1
- package/build/client/_app/immutable/chunks/pdYhlwhf.js +0 -1
- package/build/client/_app/immutable/entry/app.DYNedUxy.js +0 -2
- package/build/client/_app/immutable/entry/start.BzyrBssE.js +0 -1
- package/build/client/_app/immutable/nodes/0.DQYP6BCS.js +0 -1
- package/build/client/_app/immutable/nodes/1.CHB5qqQ9.js +0 -1
- package/build/client/_app/immutable/nodes/2.C6No1310.js +0 -340
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/attachment/_server.ts.js-Cb8SBWVw.js +0 -46
- package/build/server/chunks/nodes/1.js-CW4V8ni8.js +0 -9
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { d as readBrowserConfig } from './insight-config.js-
|
|
1
|
+
import { d as readBrowserConfig } from './insight-config.js-C5-Rhh5A.js';
|
|
2
2
|
import { mkdirSync, rmSync, readdirSync } from 'node:fs';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { spawn, spawnSync } from 'node:child_process';
|
|
@@ -157,6 +157,29 @@ var BrowserRelay = class {
|
|
|
157
157
|
}
|
|
158
158
|
};
|
|
159
159
|
Number(process.env.DSI_MCP_PORT ?? 9515);
|
|
160
|
+
/** Layout-scale zoom (ADR-0041 amendment, 2026-10-31): the zoom knob is
|
|
161
|
+
* LAYOUT SCALE — the emulated viewport is the panel box divided by the
|
|
162
|
+
* zoom, and the mirror's contain-fit does the visual shrinking. The page
|
|
163
|
+
* keeps ONE coordinate space (no CSS zoom), so every consumer's own
|
|
164
|
+
* geometry math — YouTube's seek preview included — stays truthful.
|
|
165
|
+
* Rejected: documentElement CSS zoom (the 2026-10-28..31 implementation) —
|
|
166
|
+
* Chromium hit-testing stayed correct but the PAGE's own width perception
|
|
167
|
+
* split in two, and YouTube's player computed seek previews at exactly
|
|
168
|
+
* the zoom factor off (54:58 vs 1:08:44 at 0.8). */
|
|
169
|
+
function layoutViewport(width, height, zoom) {
|
|
170
|
+
const z = zoom > 0 ? zoom : 1;
|
|
171
|
+
return {
|
|
172
|
+
width: Math.max(200, Math.min(8192, Math.round(width / z))),
|
|
173
|
+
height: Math.max(120, Math.min(8192, Math.round(height / z)))
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
/** Live zoom slider (2026-10-28): the slider's wire bounds — null when the
|
|
177
|
+
* value is not a finite number in 0.5..1.25 (the route answers 400). */
|
|
178
|
+
function clampZoom(z) {
|
|
179
|
+
if (typeof z !== "number" || !Number.isFinite(z)) return null;
|
|
180
|
+
if (z < .5 || z > 1.25) return null;
|
|
181
|
+
return z;
|
|
182
|
+
}
|
|
160
183
|
/** The reaper's ONLY test — pure: a session is reaper-eligible when nobody
|
|
161
184
|
* watches its stream AND its last MCP tool traffic is older than idleMs.
|
|
162
185
|
* An active agent (traffic younger than idleMs) is NEVER eligible (D2). */
|
|
@@ -164,6 +187,100 @@ function reaperEligible(e, idleMs, now) {
|
|
|
164
187
|
if (e.streamSubscribers > 0) return false;
|
|
165
188
|
return now - e.lastMcpActivity >= idleMs;
|
|
166
189
|
}
|
|
190
|
+
/** Mint the next ordinal for targetId (idempotent: an already-minted
|
|
191
|
+
* targetId returns its existing ordinal — lazy attach and heal re-enter). */
|
|
192
|
+
function mintTabId(entry, targetId) {
|
|
193
|
+
const tabIds = entry.tabIds ??= /* @__PURE__ */ new Map();
|
|
194
|
+
for (const [ord, tid] of tabIds) if (tid === targetId) return ord;
|
|
195
|
+
entry.nextTabId ??= 1;
|
|
196
|
+
const ord = entry.nextTabId;
|
|
197
|
+
entry.nextTabId = ord + 1;
|
|
198
|
+
entry.tabIds.set(ord, targetId);
|
|
199
|
+
return ord;
|
|
200
|
+
}
|
|
201
|
+
/** ADR-0040 Amendment 4 (D-A5): the OWNER hand claims a newborn. Called
|
|
202
|
+
* from the entry's own context 'page' listener — a claimed targetId is
|
|
203
|
+
* owner-born no matter what the browser-level watcher saw first. Pure. */
|
|
204
|
+
function claimOwnerBirth(entry, targetId) {
|
|
205
|
+
(entry.ownBirths ??= /* @__PURE__ */ new Set()).add(targetId);
|
|
206
|
+
}
|
|
207
|
+
/** ADR-0040 Amendment 4 (D-A6): a GUEST birth is a citizen without the
|
|
208
|
+
* throne — mint an ordinal, append tabOrder (lands LAST), and NEVER move
|
|
209
|
+
* the active pointer (appear without stealing focus). Guards: closed
|
|
210
|
+
* sessions refuse; a target the entry already knows (or the owner hand
|
|
211
|
+
* claimed) is a no-op. Pure. Returns true when the guest was admitted. */
|
|
212
|
+
function admitGuestBirth(e, targetId) {
|
|
213
|
+
if (e.closed) return false;
|
|
214
|
+
if (e.ownBirths?.has(targetId)) return false;
|
|
215
|
+
if (e.tabOrder.includes(targetId) || e.tabs.has(targetId)) return false;
|
|
216
|
+
e.tabOrder.push(targetId);
|
|
217
|
+
mintTabId(e, targetId);
|
|
218
|
+
return true;
|
|
219
|
+
}
|
|
220
|
+
/** Retire a closed tab: drop both map sides. The ordinal itself is gone —
|
|
221
|
+
* mintTabId never hands it out again. */
|
|
222
|
+
function retireTabId(entry, targetId) {
|
|
223
|
+
if (!entry.tabIds) return;
|
|
224
|
+
for (const [ord, tid] of entry.tabIds) if (tid === targetId) {
|
|
225
|
+
entry.tabIds.delete(ord);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
/** HEAL PRESERVES ORDINALS: healHandles swaps a dead CDP handle for a
|
|
230
|
+
* fresh session — the tab's ordinal must survive with its value
|
|
231
|
+
* rewritten to the new targetId. An unknown page (first sight during
|
|
232
|
+
* heal) falls back to a fresh mint. */
|
|
233
|
+
function healPreserveOrdinal(entry, oldTargetId, newTargetId) {
|
|
234
|
+
const tabIds = entry.tabIds ??= /* @__PURE__ */ new Map();
|
|
235
|
+
for (const [ord, tid] of tabIds) if (tid === oldTargetId) {
|
|
236
|
+
entry.tabIds.set(ord, newTargetId);
|
|
237
|
+
return ord;
|
|
238
|
+
}
|
|
239
|
+
return mintTabId(entry, newTargetId);
|
|
240
|
+
}
|
|
241
|
+
/** Resolve a tab reference — ordinal (number / numeric string), the
|
|
242
|
+
* 'active' pointer, or a raw targetId — to { targetId, tabId }. A
|
|
243
|
+
* retired or unknown ordinal is an EXPLICIT error: no silent
|
|
244
|
+
* nearest-match (glass-tab-ids global constraint). */
|
|
245
|
+
function resolveTab(entry, ref) {
|
|
246
|
+
if (ref === "active") {
|
|
247
|
+
if (!entry.activeTargetId) return {
|
|
248
|
+
ok: false,
|
|
249
|
+
error: "no active tab"
|
|
250
|
+
};
|
|
251
|
+
for (const [ord, tid] of entry.tabIds ?? []) if (tid === entry.activeTargetId) return {
|
|
252
|
+
ok: true,
|
|
253
|
+
targetId: tid,
|
|
254
|
+
tabId: ord
|
|
255
|
+
};
|
|
256
|
+
return {
|
|
257
|
+
ok: false,
|
|
258
|
+
error: "active tab has no ordinal: " + entry.activeTargetId
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
if (typeof ref === "number" || /^\d+$/.test(ref)) {
|
|
262
|
+
const ord = Number(ref);
|
|
263
|
+
const tid = entry.tabIds?.get(ord);
|
|
264
|
+
if (tid === void 0) return {
|
|
265
|
+
ok: false,
|
|
266
|
+
error: "unknown or retired tab ordinal: " + ord
|
|
267
|
+
};
|
|
268
|
+
return {
|
|
269
|
+
ok: true,
|
|
270
|
+
targetId: tid,
|
|
271
|
+
tabId: ord
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
for (const [ord, tid] of entry.tabIds ?? []) if (tid === ref) return {
|
|
275
|
+
ok: true,
|
|
276
|
+
targetId: tid,
|
|
277
|
+
tabId: ord
|
|
278
|
+
};
|
|
279
|
+
return {
|
|
280
|
+
ok: false,
|
|
281
|
+
error: "unknown tab: " + ref
|
|
282
|
+
};
|
|
283
|
+
}
|
|
167
284
|
var BrowserRegistryRefusal = class extends Error {
|
|
168
285
|
code;
|
|
169
286
|
constructor(code, message) {
|
|
@@ -190,10 +307,52 @@ function state() {
|
|
|
190
307
|
function sweepOrphanProfiles(keep) {
|
|
191
308
|
try {
|
|
192
309
|
const root = readBrowserConfig().profileRoot;
|
|
193
|
-
for (const name of readdirSync(root))
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
310
|
+
for (const name of readdirSync(root)) {
|
|
311
|
+
if (keep.has(name)) continue;
|
|
312
|
+
if (!process.env.VITEST) killOrphanChrome(name, {
|
|
313
|
+
listPids: () => spawnSync("ps", ["axo", "pid=,command="], {
|
|
314
|
+
encoding: "utf8",
|
|
315
|
+
timeout: 5e3
|
|
316
|
+
}).stdout ?? "",
|
|
317
|
+
killPid: (pid, sig) => process.kill(pid, sig)
|
|
318
|
+
});
|
|
319
|
+
rmSync(join(root, name), {
|
|
320
|
+
recursive: true,
|
|
321
|
+
force: true
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
} catch {}
|
|
325
|
+
}
|
|
326
|
+
/** Ghost Glass Fix 3 (RCA 2026-10-31 Bug 3): PURE matcher — which process
|
|
327
|
+
* rows hold a Chrome --user-data-dir that ENDS at the swept profile name?
|
|
328
|
+
* The name must terminate the dir (path separator before it, end/space
|
|
329
|
+
* after it), so a PREFIX-sharing sibling profile never matches. */
|
|
330
|
+
function orphanChromePids(psOutput, profileName) {
|
|
331
|
+
const needle = "--user-data-dir=";
|
|
332
|
+
const pids = [];
|
|
333
|
+
for (const line of psOutput.split("\n")) {
|
|
334
|
+
const at = line.indexOf(needle);
|
|
335
|
+
if (at === -1) continue;
|
|
336
|
+
const rest = line.slice(at + 16);
|
|
337
|
+
const end = rest.search(/[\s"']/);
|
|
338
|
+
if (!(end === -1 ? rest : rest.slice(0, end)).replace(/\/+$/, "").endsWith("/" + profileName)) continue;
|
|
339
|
+
const pid = Number.parseInt(line.trimStart(), 10);
|
|
340
|
+
if (Number.isFinite(pid)) pids.push(pid);
|
|
341
|
+
}
|
|
342
|
+
return pids;
|
|
343
|
+
}
|
|
344
|
+
/** Ghost Glass Fix 3: the sweep kill arm with the OS injected — SIGKILL
|
|
345
|
+
* every holder of the profile dir. Best-effort both ways: a failed ps
|
|
346
|
+
* listing or an already-dead pid (ESRCH) never blocks the dir sweep. */
|
|
347
|
+
function killOrphanChrome(profileName, deps) {
|
|
348
|
+
let out;
|
|
349
|
+
try {
|
|
350
|
+
out = deps.listPids();
|
|
351
|
+
} catch {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
for (const pid of orphanChromePids(out, profileName)) try {
|
|
355
|
+
deps.killPid(pid, "SIGKILL");
|
|
197
356
|
} catch {}
|
|
198
357
|
}
|
|
199
358
|
/** Brain re-homing (BUGFIX 2026-10-25): release the stable brain port
|
|
@@ -313,6 +472,7 @@ var BrowserRegistry = class {
|
|
|
313
472
|
pid: e.pid,
|
|
314
473
|
profileDir: e.profileDir,
|
|
315
474
|
createdAt: e.createdAt,
|
|
475
|
+
ownerSessionId: e.ownerSessionId ?? null,
|
|
316
476
|
mcp: e.mcpChild ? "child" : e.mcpInProcess ? "in-process" : "none",
|
|
317
477
|
mcpHealth: e.mcpHealth,
|
|
318
478
|
mcpError: e.mcpError
|
|
@@ -366,7 +526,7 @@ var BrowserRegistry = class {
|
|
|
366
526
|
const now = Date.now();
|
|
367
527
|
for (const e of state().entries.values()) {
|
|
368
528
|
if (!reaperEligible(e, idleMs, now)) continue;
|
|
369
|
-
this.close(e.id).catch(() => void 0);
|
|
529
|
+
this.close(e.id, "reaped").catch(() => void 0);
|
|
370
530
|
}
|
|
371
531
|
}, Math.min(idleMs / 2, 3e4));
|
|
372
532
|
s.reaperTimer.unref?.();
|
|
@@ -378,6 +538,31 @@ var BrowserRegistry = class {
|
|
|
378
538
|
if (!h) throw new BrowserRegistryRefusal("CLOSED", "no active tab handle");
|
|
379
539
|
return h.relay;
|
|
380
540
|
}
|
|
541
|
+
/** D5 amendment 2026-10-27: watch the active pointer. The stream route
|
|
542
|
+
* re-binds its relay on every fire so a brain-driven activate (creation
|
|
543
|
+
* auto-focus) repoints the MIRROR, not just the strip highlight. */
|
|
544
|
+
/** Ghost Glass Fix 2 (RCA 2026-10-31): the death handshake's registry
|
|
545
|
+
* side. Subscribers hear ONE reason ('closed' human, 'reaped' reaper);
|
|
546
|
+
* subscribing to an already-dead id hears the truth immediately instead
|
|
547
|
+
* of polling a ghost. Same shape as onActiveChange. */
|
|
548
|
+
onDeath(id, cb) {
|
|
549
|
+
const e = state().entries.get(id);
|
|
550
|
+
if (!e || e.closed) {
|
|
551
|
+
cb("closed");
|
|
552
|
+
return () => void 0;
|
|
553
|
+
}
|
|
554
|
+
(e.deathListeners ??= /* @__PURE__ */ new Set()).add(cb);
|
|
555
|
+
return () => {
|
|
556
|
+
e.deathListeners?.delete(cb);
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
onActiveChange(id, cb) {
|
|
560
|
+
const e = this.peek(id);
|
|
561
|
+
(e.activeListeners ??= /* @__PURE__ */ new Set()).add(cb);
|
|
562
|
+
return () => {
|
|
563
|
+
e.activeListeners?.delete(cb);
|
|
564
|
+
};
|
|
565
|
+
}
|
|
381
566
|
/** Resolve a verb's target handle: explicit targetId, else the active tab
|
|
382
567
|
* (ADR-0040 D3 — one verb seam, optional pointer). An ENUMERATED but
|
|
383
568
|
* not-yet-attached target attaches lazily here — attach is not stream
|
|
@@ -413,7 +598,12 @@ var BrowserRegistry = class {
|
|
|
413
598
|
relay: handle.relay,
|
|
414
599
|
page
|
|
415
600
|
}).catch(() => void 0);
|
|
416
|
-
e
|
|
601
|
+
const ord = mintTabId(e, tid);
|
|
602
|
+
e.tabs.set(tid, {
|
|
603
|
+
...handle,
|
|
604
|
+
tabId: ord
|
|
605
|
+
});
|
|
606
|
+
(e.tabOrder ??= []).push(tid);
|
|
417
607
|
if (tid === key) break;
|
|
418
608
|
}
|
|
419
609
|
}
|
|
@@ -421,19 +611,137 @@ var BrowserRegistry = class {
|
|
|
421
611
|
if (!h) throw new BrowserRegistryRefusal("NO_TARGET", "no such tab: " + key);
|
|
422
612
|
return h;
|
|
423
613
|
}
|
|
614
|
+
/** BUG 2026-10-27 (brain churn leaves stale handles): the registry's
|
|
615
|
+
* cached CDP sessions can DIE while the pages live on (brain re-home,
|
|
616
|
+
* MCP client reconnect) — listTabs then answers empty, every verb
|
|
617
|
+
* NO_TARGETs, and the mirror starves on one frozen frame. Heal: re-probe
|
|
618
|
+
* every live page with a FRESH CDP session, replace dead handles, and
|
|
619
|
+
* remap the pointer when its target is gone. Called from listTabs when
|
|
620
|
+
* enumeration comes back empty — the panel's 2s poll then heals the
|
|
621
|
+
* whole session without operator action. */
|
|
622
|
+
async healHandles(e) {
|
|
623
|
+
for (const page of e.context.pages()) {
|
|
624
|
+
let scdp = null;
|
|
625
|
+
let tid = null;
|
|
626
|
+
try {
|
|
627
|
+
scdp = await e.context.newCDPSession(page);
|
|
628
|
+
tid = (await scdp.send("Target.getTargetInfo")).targetInfo?.targetId ?? null;
|
|
629
|
+
} catch {
|
|
630
|
+
continue;
|
|
631
|
+
}
|
|
632
|
+
if (!tid || !scdp) {
|
|
633
|
+
await scdp?.detach().catch(() => void 0);
|
|
634
|
+
continue;
|
|
635
|
+
}
|
|
636
|
+
const old = e.tabs.get(tid);
|
|
637
|
+
if (old) {
|
|
638
|
+
if (!await old.cdp.send("Target.getTargetInfo").then(() => false).catch(() => true)) {
|
|
639
|
+
await scdp.detach().catch(() => void 0);
|
|
640
|
+
continue;
|
|
641
|
+
}
|
|
642
|
+
old.relay.stop();
|
|
643
|
+
await old.cdp.detach().catch(() => void 0);
|
|
644
|
+
const cfg = readBrowserConfig();
|
|
645
|
+
const ord = healPreserveOrdinal(e, tid, tid);
|
|
646
|
+
e.tabs.set(tid, {
|
|
647
|
+
targetId: tid,
|
|
648
|
+
tabId: ord,
|
|
649
|
+
cdp: scdp,
|
|
650
|
+
relay: new BrowserRelay(scdp, cfg.screencastQuality, cfg.screencastMaxFps),
|
|
651
|
+
page
|
|
652
|
+
});
|
|
653
|
+
if (!e.tabOrder.includes(tid)) e.tabOrder.push(tid);
|
|
654
|
+
} else await scdp.detach().catch(() => void 0);
|
|
655
|
+
}
|
|
656
|
+
if (e.activeTargetId && !e.tabs.has(e.activeTargetId)) e.activeTargetId = [...e.tabs.keys()][0] ?? null;
|
|
657
|
+
}
|
|
424
658
|
/** Enumerate the context's page targets (ADR-0040 D5 feed) — type:page
|
|
425
659
|
* only; iframes/omnibox/service workers are chrome noise (wire capture
|
|
426
|
-
* 2026-10-24).
|
|
660
|
+
* 2026-10-24). Mirror-lifecycle (ADR "The Mirror Must Face a Living
|
|
661
|
+
* Tab", D1/D2): enumeration ADOPTS unknown live pages (mint + lazy
|
|
662
|
+
* seam — survivors' ordinals never move) and RE-POINTS the active
|
|
663
|
+
* pointer when its target is gone, re-seeding the mirror through
|
|
664
|
+
* activate's idiom — the panel never faces a corpse (the 2026-10-28
|
|
665
|
+
* frozen-frame bug). */
|
|
427
666
|
async listTabs(id) {
|
|
428
667
|
const e = this.peek(id);
|
|
429
|
-
const
|
|
430
|
-
|
|
431
|
-
|
|
668
|
+
const pickSrc = () => (e.activeTargetId ? e.tabs.get(e.activeTargetId) : null)?.cdp ?? [...e.tabs.values()][0]?.cdp ?? null;
|
|
669
|
+
const readTargets = async (h) => {
|
|
670
|
+
return (await h.send("Target.getTargets").catch(() => null))?.targetInfos ?? null;
|
|
671
|
+
};
|
|
672
|
+
let src = pickSrc();
|
|
673
|
+
let infos = (src ? await readTargets(src) : null) ?? [];
|
|
674
|
+
if (infos === null || infos.length === 0 && e.context.pages().length > 0) {
|
|
675
|
+
await this.healHandles(e);
|
|
676
|
+
src = pickSrc();
|
|
677
|
+
infos = (src ? await readTargets(src) : null) ?? [];
|
|
678
|
+
}
|
|
679
|
+
let pages = infos.filter((t) => t.type === "page");
|
|
680
|
+
if (pages.length === 0 && e.context.pages().length > 0) {
|
|
681
|
+
const probed = [];
|
|
682
|
+
for (const page of e.context.pages()) try {
|
|
683
|
+
const scdp = await e.context.newCDPSession(page);
|
|
684
|
+
const info = await scdp.send("Target.getTargetInfo");
|
|
685
|
+
await scdp.detach().catch(() => void 0);
|
|
686
|
+
if (info.targetInfo?.targetId) probed.push({
|
|
687
|
+
targetId: info.targetInfo.targetId,
|
|
688
|
+
type: "page",
|
|
689
|
+
url: info.targetInfo.url ?? page.url(),
|
|
690
|
+
title: info.targetInfo.title ?? ""
|
|
691
|
+
});
|
|
692
|
+
} catch {
|
|
693
|
+
continue;
|
|
694
|
+
}
|
|
695
|
+
pages = probed;
|
|
696
|
+
}
|
|
697
|
+
const known = new Set(e.tabs.keys());
|
|
698
|
+
const unknown = pages.filter((t) => !known.has(t.targetId));
|
|
699
|
+
if (unknown.length > 0) {
|
|
700
|
+
const cfg = readBrowserConfig();
|
|
701
|
+
for (const page of e.context.pages()) {
|
|
702
|
+
let tid = null;
|
|
703
|
+
let scdp = null;
|
|
704
|
+
try {
|
|
705
|
+
scdp = await e.context.newCDPSession(page);
|
|
706
|
+
tid = (await scdp.send("Target.getTargetInfo")).targetInfo?.targetId ?? null;
|
|
707
|
+
} catch {
|
|
708
|
+
continue;
|
|
709
|
+
}
|
|
710
|
+
if (!tid || !unknown.some((u) => u.targetId === tid)) {
|
|
711
|
+
await scdp.detach().catch(() => void 0);
|
|
712
|
+
continue;
|
|
713
|
+
}
|
|
714
|
+
const ord = mintTabId(e, tid);
|
|
715
|
+
if (!e.tabOrder.includes(tid)) e.tabOrder.push(tid);
|
|
716
|
+
e.tabs.set(tid, {
|
|
717
|
+
targetId: tid,
|
|
718
|
+
tabId: ord,
|
|
719
|
+
cdp: scdp,
|
|
720
|
+
relay: new BrowserRelay(scdp, cfg.screencastQuality, cfg.screencastMaxFps),
|
|
721
|
+
page
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
const out = pages.map((t) => ({
|
|
726
|
+
tabId: mintTabId(e, t.targetId),
|
|
432
727
|
targetId: t.targetId,
|
|
433
728
|
url: t.url,
|
|
434
729
|
title: t.title,
|
|
435
730
|
active: t.targetId === e.activeTargetId
|
|
436
|
-
}))
|
|
731
|
+
})).sort((a, b) => {
|
|
732
|
+
const order = e.tabOrder ?? [];
|
|
733
|
+
const ia = order.indexOf(a.targetId);
|
|
734
|
+
const ib = order.indexOf(b.targetId);
|
|
735
|
+
return (ia === -1 ? Infinity : ia) - (ib === -1 ? Infinity : ib);
|
|
736
|
+
});
|
|
737
|
+
if (e.activeTargetId && !out.some((r) => r.targetId === e.activeTargetId)) {
|
|
738
|
+
const survivor = out[0]?.targetId ?? null;
|
|
739
|
+
if (survivor) {
|
|
740
|
+
await this.activate(id, survivor).catch(() => void 0);
|
|
741
|
+
for (const r of out) r.active = r.targetId === e.activeTargetId;
|
|
742
|
+
} else e.activeTargetId = null;
|
|
743
|
+
}
|
|
744
|
+
return out;
|
|
437
745
|
}
|
|
438
746
|
/** Attach (lazily), seed the first frame, re-apply the viewport, and move
|
|
439
747
|
* the active pointer (ADR-0040 D1/D2/D4). The OLD active relay pauses —
|
|
@@ -462,6 +770,9 @@ var BrowserRegistry = class {
|
|
|
462
770
|
});
|
|
463
771
|
}
|
|
464
772
|
e.activeTargetId = targetId;
|
|
773
|
+
for (const l of e.activeListeners ?? []) try {
|
|
774
|
+
l(targetId);
|
|
775
|
+
} catch {}
|
|
465
776
|
let url = "";
|
|
466
777
|
let title = "";
|
|
467
778
|
try {
|
|
@@ -483,22 +794,25 @@ var BrowserRegistry = class {
|
|
|
483
794
|
const port = freePort(options.port);
|
|
484
795
|
const profileDir = join(cfg.profileRoot, id.slice(0, 8));
|
|
485
796
|
mkdirSync(profileDir, { recursive: true });
|
|
486
|
-
const ignoreDefaultArgs = [
|
|
797
|
+
const ignoreDefaultArgs = [
|
|
798
|
+
"--enable-automation",
|
|
799
|
+
"--mute-audio",
|
|
800
|
+
"--hide-scrollbars"
|
|
801
|
+
];
|
|
487
802
|
const launch = (useChrome) => chromium.launchPersistentContext(profileDir, {
|
|
488
803
|
...useChrome ? { channel: "chrome" } : {},
|
|
489
804
|
headless: options.headless ?? cfg.headless,
|
|
490
805
|
ignoreDefaultArgs,
|
|
491
806
|
args: [
|
|
492
|
-
"--remote-debugging-port=" + port,
|
|
493
807
|
"--no-first-run",
|
|
494
|
-
"--
|
|
495
|
-
"--disable-features=IsolateOrigins,site-per-process",
|
|
808
|
+
"--remote-debugging-port=" + port,
|
|
496
809
|
"--disable-dev-shm-usage",
|
|
497
|
-
"--disable-backgrounding-occluded-windows",
|
|
498
810
|
"--disable-renderer-backgrounding",
|
|
499
811
|
"--disable-background-timer-throttling",
|
|
812
|
+
"--disable-backgrounding-occluded-windows",
|
|
813
|
+
"--disable-blink-features=AutomationControlled",
|
|
814
|
+
"--disable-features=IsolateOrigins,site-per-process",
|
|
500
815
|
"--autoplay-policy=no-user-gesture-required",
|
|
501
|
-
"--window-position=-32000,-32000",
|
|
502
816
|
"--window-size=" + (Math.max(200, Math.min(1920, options.width ?? 730)) + 6) + "," + (Math.max(120, Math.min(1080, options.height ?? 830)) + 70)
|
|
503
817
|
],
|
|
504
818
|
viewport: options.headless ?? cfg.headless ? {
|
|
@@ -524,12 +838,19 @@ var BrowserRegistry = class {
|
|
|
524
838
|
try {
|
|
525
839
|
activeTargetId = (await cdp.send("Target.getTargetInfo")).targetInfo?.targetId ?? null;
|
|
526
840
|
} catch {}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
841
|
+
const tabIds = /* @__PURE__ */ new Map();
|
|
842
|
+
let nextTabId = 1;
|
|
843
|
+
if (activeTargetId) {
|
|
844
|
+
tabIds.set(1, activeTargetId);
|
|
845
|
+
nextTabId = 2;
|
|
846
|
+
tabs.set(activeTargetId, {
|
|
847
|
+
targetId: activeTargetId,
|
|
848
|
+
tabId: 1,
|
|
849
|
+
cdp,
|
|
850
|
+
relay,
|
|
851
|
+
page: context.pages()[0]
|
|
852
|
+
});
|
|
853
|
+
}
|
|
533
854
|
await relay.start();
|
|
534
855
|
const start = options.url ?? "data:text/html,<title>Glass Browser</title><body style=\"font-family:system-ui;color:gray;display:grid;place-items:center;height:100vh;margin:0\">Glass Browser — waiting for the agent</body>";
|
|
535
856
|
await cdp.send("Page.enable").catch(() => void 0);
|
|
@@ -547,6 +868,20 @@ var BrowserRegistry = class {
|
|
|
547
868
|
metadata: { seeded: true }
|
|
548
869
|
});
|
|
549
870
|
}
|
|
871
|
+
if (options.headless ?? cfg.headless) {
|
|
872
|
+
const birth = context.pages()[0];
|
|
873
|
+
if (birth && typeof birth.evaluate === "function") {
|
|
874
|
+
if (await birth.evaluate(() => {
|
|
875
|
+
const d = document.createElement("div");
|
|
876
|
+
d.style.cssText = "position:fixed;left:-100px;top:0;width:50px;height:50px;overflow:auto";
|
|
877
|
+
d.innerHTML = "<div style=\"width:200px;height:200px\"></div>";
|
|
878
|
+
document.body.appendChild(d);
|
|
879
|
+
const f = d.offsetHeight - d.clientHeight;
|
|
880
|
+
d.remove();
|
|
881
|
+
return f;
|
|
882
|
+
}).catch(() => null) === 0) throw new Error("glass scrollbar gate: bar footprint is 0 in a headless launch — --hide-scrollbars leaked past ignoreDefaultArgs (RCA 2026-10-28)");
|
|
883
|
+
}
|
|
884
|
+
}
|
|
550
885
|
const brainPort = Number(process.env.DSI_MCP_PORT ?? cfg.brainPort);
|
|
551
886
|
if (cfg.enabled) this.ensureReaper(cfg.idleMs);
|
|
552
887
|
const entry = {
|
|
@@ -554,10 +889,14 @@ var BrowserRegistry = class {
|
|
|
554
889
|
context,
|
|
555
890
|
tabs,
|
|
556
891
|
activeTargetId,
|
|
892
|
+
tabOrder: activeTargetId ? [activeTargetId] : [],
|
|
893
|
+
tabIds,
|
|
894
|
+
nextTabId,
|
|
557
895
|
viewport: {
|
|
558
896
|
width: Math.max(200, Math.min(1920, options.width ?? 730)),
|
|
559
897
|
height: Math.max(120, Math.min(1080, options.height ?? 830))
|
|
560
898
|
},
|
|
899
|
+
zoom: cfg.contentZoom,
|
|
561
900
|
headless: options.headless ?? cfg.headless,
|
|
562
901
|
port,
|
|
563
902
|
pid,
|
|
@@ -570,9 +909,70 @@ var BrowserRegistry = class {
|
|
|
570
909
|
mcpHealth: "up",
|
|
571
910
|
mcpError: null,
|
|
572
911
|
closed: false,
|
|
912
|
+
ownerSessionId: options.ownerSessionId ?? null,
|
|
913
|
+
ownBirths: /* @__PURE__ */ new Set(),
|
|
573
914
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
574
915
|
};
|
|
575
916
|
state().entries.set(id, entry);
|
|
917
|
+
if (typeof context.on === "function") context.on("page", (page) => {
|
|
918
|
+
(async () => {
|
|
919
|
+
for (let i = 0; i < 10; i += 1) {
|
|
920
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
921
|
+
try {
|
|
922
|
+
const scdp = await context.newCDPSession(page);
|
|
923
|
+
const info = await scdp.send("Target.getTargetInfo").catch(() => null);
|
|
924
|
+
await scdp.detach().catch(() => void 0);
|
|
925
|
+
const tid = info?.targetInfo?.targetId;
|
|
926
|
+
if (!tid) continue;
|
|
927
|
+
if (!entry.tabOrder.includes(tid)) entry.tabOrder.push(tid);
|
|
928
|
+
mintTabId(entry, tid);
|
|
929
|
+
claimOwnerBirth(entry, tid);
|
|
930
|
+
if (entry.activeTargetId !== tid && !entry.closed && !entry.tabs.has(tid)) await this.activate(id, tid);
|
|
931
|
+
return;
|
|
932
|
+
} catch {}
|
|
933
|
+
}
|
|
934
|
+
})();
|
|
935
|
+
});
|
|
936
|
+
try {
|
|
937
|
+
const browser = context.browser?.();
|
|
938
|
+
if (browser && typeof browser.newBrowserCDPSession === "function") {
|
|
939
|
+
const bcdp = await browser.newBrowserCDPSession();
|
|
940
|
+
entry.browserCdp = bcdp;
|
|
941
|
+
await bcdp.send("Target.setDiscoverTargets", { discover: true }).catch(() => void 0);
|
|
942
|
+
bcdp.on("Target.targetCreated", (evt) => {
|
|
943
|
+
const tid = evt?.targetInfo?.targetId;
|
|
944
|
+
if (!tid || evt.targetInfo?.type !== "page") return;
|
|
945
|
+
if (entry.tabOrder.includes(tid) || entry.tabs.has(tid)) return;
|
|
946
|
+
(async () => {
|
|
947
|
+
for (let g = 0; g < 10; g += 1) {
|
|
948
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
949
|
+
if (entry.closed) return;
|
|
950
|
+
if (entry.ownBirths?.has(tid)) return;
|
|
951
|
+
if (entry.tabOrder.includes(tid) || entry.tabs.has(tid)) return;
|
|
952
|
+
}
|
|
953
|
+
admitGuestBirth(entry, tid);
|
|
954
|
+
})();
|
|
955
|
+
});
|
|
956
|
+
}
|
|
957
|
+
} catch {}
|
|
958
|
+
if (!entry.headless) {
|
|
959
|
+
await this.applyPanelViewport(entry, {
|
|
960
|
+
cdp,
|
|
961
|
+
relay
|
|
962
|
+
}).catch(() => void 0);
|
|
963
|
+
const birthShot = await cdp.send("Page.captureScreenshot", {
|
|
964
|
+
format: "jpeg",
|
|
965
|
+
quality: cfg.screencastQuality
|
|
966
|
+
}).catch(() => null);
|
|
967
|
+
if (birthShot?.data) relay.reseed({
|
|
968
|
+
seq: 0,
|
|
969
|
+
jpeg: birthShot.data,
|
|
970
|
+
metadata: {
|
|
971
|
+
seeded: true,
|
|
972
|
+
birth: true
|
|
973
|
+
}
|
|
974
|
+
});
|
|
975
|
+
}
|
|
576
976
|
if (options.spawnMcp ?? true) {
|
|
577
977
|
if (process.env.DSI_MCP_IN_PROCESS === "1") {
|
|
578
978
|
evictBrainPort(brainPort, id);
|
|
@@ -592,6 +992,7 @@ var BrowserRegistry = class {
|
|
|
592
992
|
pid,
|
|
593
993
|
profileDir,
|
|
594
994
|
createdAt: entry.createdAt,
|
|
995
|
+
ownerSessionId: entry.ownerSessionId,
|
|
595
996
|
mcp: entry.mcpChild ? "child" : entry.mcpInProcess ? "in-process" : "none",
|
|
596
997
|
mcpHealth: entry.mcpHealth,
|
|
597
998
|
mcpError: entry.mcpError
|
|
@@ -621,7 +1022,7 @@ var BrowserRegistry = class {
|
|
|
621
1022
|
});
|
|
622
1023
|
}
|
|
623
1024
|
/** D8 owner-release. Idempotent: closing a closed id resolves cleanly. */
|
|
624
|
-
async close(id) {
|
|
1025
|
+
async close(id, reason = "closed") {
|
|
625
1026
|
const entry = state().entries.get(id);
|
|
626
1027
|
if (!entry) return {
|
|
627
1028
|
ok: true,
|
|
@@ -636,6 +1037,10 @@ var BrowserRegistry = class {
|
|
|
636
1037
|
}
|
|
637
1038
|
entry.closed = true;
|
|
638
1039
|
state().closed.add(id);
|
|
1040
|
+
for (const cb of entry.deathListeners ?? []) try {
|
|
1041
|
+
cb(reason);
|
|
1042
|
+
} catch {}
|
|
1043
|
+
await entry.browserCdp?.detach().catch(() => void 0);
|
|
639
1044
|
if (state().closed.size > 256) state().closed.delete(state().closed.values().next().value);
|
|
640
1045
|
const errs = [];
|
|
641
1046
|
if (entry.mcpInProcess) try {
|
|
@@ -731,24 +1136,244 @@ var BrowserRegistry = class {
|
|
|
731
1136
|
await handle.cdp.send("Page.enable").catch(() => void 0);
|
|
732
1137
|
const prevSeq = handle.relay.latestFrame()?.seq ?? -1;
|
|
733
1138
|
await this.applyPanelViewport(e, handle);
|
|
734
|
-
await this.
|
|
735
|
-
}
|
|
736
|
-
/** ADR-0041
|
|
737
|
-
*
|
|
738
|
-
*
|
|
739
|
-
*
|
|
740
|
-
*
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
if (
|
|
1139
|
+
await this.repairSurfaceAfterResize(e, handle, prevSeq).catch(() => void 0);
|
|
1140
|
+
}
|
|
1141
|
+
/** Layout-scale zoom (ADR-0041 amendment, 2026-10-31): store the session
|
|
1142
|
+
* scale and re-apply the panel viewport — the emulation is box/zoom, so
|
|
1143
|
+
* the slider's LIVE effect rides the resize. Bad input (clampZoom null)
|
|
1144
|
+
* KEEPS the current zoom and returns silently: unreachable from the wire
|
|
1145
|
+
* (the route's clampZoom already answered 400) — documented keep. */
|
|
1146
|
+
async setZoom(id, zoom) {
|
|
1147
|
+
const e = this.peek(id);
|
|
1148
|
+
if (e.closed) throw new BrowserRegistryRefusal("CLOSED", "session is closing");
|
|
1149
|
+
const clamped = clampZoom(zoom);
|
|
1150
|
+
if (clamped === null) return;
|
|
1151
|
+
e.zoom = clamped;
|
|
1152
|
+
const handle = await this.handleOf(e);
|
|
1153
|
+
await this.applyPanelViewport(e, handle);
|
|
1154
|
+
}
|
|
1155
|
+
/** The window id, retried — Target.getWindowForTarget on a Playwright
|
|
1156
|
+
* page session races out on a COLD headed launch (measured in the
|
|
1157
|
+
* surface-lag test: both resizes skipped, the birth window kept its
|
|
1158
|
+
* size). Three bounded tries; the caller's race caps still apply. */
|
|
1159
|
+
async windowIdOf(anyCdp, race, targetId) {
|
|
1160
|
+
for (let i = 0; i < 3; i += 1) {
|
|
1161
|
+
const t = await race(anyCdp.send("Target.getWindowForTarget"), null);
|
|
1162
|
+
if (t && t.windowId) return t;
|
|
1163
|
+
const b = await race(anyCdp.send("Browser.getWindowForTarget", targetId ? { targetId } : {}), null);
|
|
1164
|
+
if (b && b.windowId) return b;
|
|
1165
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
1166
|
+
}
|
|
1167
|
+
return null;
|
|
1168
|
+
}
|
|
1169
|
+
/** ADR-0041 D3 + the surface-lag repair (BUG 2026-10-26): a resize must
|
|
1170
|
+
* REPAINT at the REQUESTED size, not just emit SOME fresh frame. Two
|
|
1171
|
+
* failure shapes live here:
|
|
1172
|
+
* 1. the SILENT page — the screencast speaks only on change, so a
|
|
1173
|
+
* static page never repaints (the 2026-10-25 black bands);
|
|
1174
|
+
* 2. the STALE SURFACE — a headed off-screen window takes the new
|
|
1175
|
+
* RENDERER size but macOS never reallocates the compositor surface:
|
|
1176
|
+
* every frame (even captureScreenshot) keeps arriving at the OLD
|
|
1177
|
+
* height, and the panel letterboxes it (the operator's top/bottom
|
|
1178
|
+
* bands at every height grow). Measured: 53 frames over 6s all at
|
|
1179
|
+
* the old 832 while innerHeight was 919; captureScreenshot agreed
|
|
1180
|
+
* with the stale surface. A window-state BOUNCE (minimize ->
|
|
1181
|
+
* normal — invisible at -32000,-32000) forces the window server to
|
|
1182
|
+
* reallocate; frames then arrive at the requested size.
|
|
1183
|
+
* Ladder: grace for a frame whose JPEG dims MATCH the request ->
|
|
1184
|
+
* bounce (headed only) -> second grace -> deterministic
|
|
1185
|
+
* captureScreenshot reseed (reseed: a stale REAL frame may sit atop
|
|
1186
|
+
* the ring, and seed() refuses to cover one). Capped throughout —
|
|
1187
|
+
* nothing here may hang the verb (the 2.5s rule). */
|
|
1188
|
+
async repairSurfaceAfterResize(e, handle, prevSeq) {
|
|
1189
|
+
const want0 = {
|
|
1190
|
+
w: e.viewport.width,
|
|
1191
|
+
h: e.viewport.height
|
|
1192
|
+
};
|
|
1193
|
+
const lvw = layoutViewport(want0.w, want0.h, e.zoom ?? readBrowserConfig().contentZoom);
|
|
1194
|
+
const want = {
|
|
1195
|
+
w: lvw.width,
|
|
1196
|
+
h: lvw.height
|
|
1197
|
+
};
|
|
1198
|
+
const dimsOf = (f) => {
|
|
1199
|
+
if (!f) return null;
|
|
1200
|
+
const md = f.metadata;
|
|
1201
|
+
const mw = Number(md?.deviceWidth);
|
|
1202
|
+
const mh = Number(md?.deviceHeight);
|
|
1203
|
+
if (Number.isFinite(mw) && Number.isFinite(mh) && mw > 0 && mh > 0) return {
|
|
1204
|
+
w: mw,
|
|
1205
|
+
h: mh
|
|
1206
|
+
};
|
|
1207
|
+
const b = Buffer.from(f.jpeg, "base64");
|
|
1208
|
+
let i = 2;
|
|
1209
|
+
while (i < b.length - 9) {
|
|
1210
|
+
if (b[i] !== 255) {
|
|
1211
|
+
i += 1;
|
|
1212
|
+
continue;
|
|
1213
|
+
}
|
|
1214
|
+
const marker = b[i + 1];
|
|
1215
|
+
if (marker >= 192 && marker <= 207 && marker !== 196 && marker !== 200 && marker !== 204) return {
|
|
1216
|
+
h: b.readUInt16BE(i + 5),
|
|
1217
|
+
w: b.readUInt16BE(i + 7)
|
|
1218
|
+
};
|
|
1219
|
+
i += Math.max(2, b.readUInt16BE(i + 2));
|
|
1220
|
+
}
|
|
1221
|
+
return null;
|
|
1222
|
+
};
|
|
1223
|
+
const matched = () => {
|
|
1224
|
+
const f = handle.relay.latestFrame();
|
|
1225
|
+
if (!f || f.seq === prevSeq) return false;
|
|
1226
|
+
const d = dimsOf(f);
|
|
1227
|
+
return d !== null && Math.abs(d.w - want.w) < 3 && Math.abs(d.h - want.h) < 3;
|
|
1228
|
+
};
|
|
1229
|
+
for (let i = 0; i < 10 && !matched(); i += 1) await new Promise((r) => setTimeout(r, 100));
|
|
1230
|
+
if (matched()) return;
|
|
1231
|
+
if (e.headless) {
|
|
1232
|
+
const raceH = async (p, fb) => {
|
|
1233
|
+
const timeout = new Promise((res) => setTimeout(() => res(fb), 2500));
|
|
1234
|
+
return await Promise.race([p.catch(() => fb), timeout]);
|
|
1235
|
+
};
|
|
1236
|
+
if (handle.page) await raceH(handle.page.setViewportSize({
|
|
1237
|
+
width: want.w,
|
|
1238
|
+
height: want.h
|
|
1239
|
+
}).catch(() => void 0).then(() => null), null);
|
|
1240
|
+
await new Promise((r) => setTimeout(r, 400));
|
|
1241
|
+
const shotH = await raceH(handle.cdp.send("Page.captureScreenshot", {
|
|
1242
|
+
format: "jpeg",
|
|
1243
|
+
quality: readBrowserConfig().screencastQuality
|
|
1244
|
+
}).catch(() => null), null);
|
|
1245
|
+
if (shotH?.data) handle.relay.reseed({
|
|
1246
|
+
seq: 0,
|
|
1247
|
+
jpeg: shotH.data,
|
|
1248
|
+
metadata: {
|
|
1249
|
+
seeded: true,
|
|
1250
|
+
resize: true
|
|
1251
|
+
}
|
|
1252
|
+
});
|
|
1253
|
+
return;
|
|
1254
|
+
}
|
|
1255
|
+
if (!e.headless) {
|
|
1256
|
+
const race = async (p, fb) => {
|
|
1257
|
+
const timeout = new Promise((res) => setTimeout(() => res(fb), 2500));
|
|
1258
|
+
return await Promise.race([p.catch(() => fb), timeout]);
|
|
1259
|
+
};
|
|
1260
|
+
const anyCdp = handle.cdp;
|
|
1261
|
+
const win = await this.windowIdOf(anyCdp, race, e.activeTargetId ?? void 0);
|
|
1262
|
+
if (win?.windowId) {
|
|
1263
|
+
let cw = e.chrome?.w ?? 0;
|
|
1264
|
+
let ch = e.chrome?.h ?? 0;
|
|
1265
|
+
console.log("[surface] ladder start want", want.w + "x" + want.h, "learned chrome", cw + "/" + ch);
|
|
1266
|
+
for (let i = 0; i < 3; i += 1) {
|
|
1267
|
+
await race(anyCdp.send("Browser.setWindowBounds", {
|
|
1268
|
+
windowId: win.windowId,
|
|
1269
|
+
bounds: {
|
|
1270
|
+
width: Math.max(200, want.w + cw),
|
|
1271
|
+
height: Math.max(120, want.h + ch),
|
|
1272
|
+
windowState: "normal"
|
|
1273
|
+
}
|
|
1274
|
+
}), null);
|
|
1275
|
+
await new Promise((r) => setTimeout(r, 350));
|
|
1276
|
+
const shot = await race(anyCdp.send("Page.captureScreenshot", {
|
|
1277
|
+
format: "jpeg",
|
|
1278
|
+
quality: 60
|
|
1279
|
+
}), null);
|
|
1280
|
+
let d = shot?.data ? dimsOf({
|
|
1281
|
+
jpeg: shot.data,
|
|
1282
|
+
metadata: {}
|
|
1283
|
+
}) : null;
|
|
1284
|
+
if (!d) d = dimsOf(handle.relay.latestFrame());
|
|
1285
|
+
if (d && Math.abs(d.w - want.w) < 3 && Math.abs(d.h - want.h) < 3) {
|
|
1286
|
+
console.log("[surface] iter", i, "converged: capture", d.w + "x" + d.h, "chrome", cw + "/" + ch);
|
|
1287
|
+
e.chrome = {
|
|
1288
|
+
w: cw,
|
|
1289
|
+
h: ch
|
|
1290
|
+
};
|
|
1291
|
+
if (shot?.data) handle.relay.reseed({
|
|
1292
|
+
seq: 0,
|
|
1293
|
+
jpeg: shot.data,
|
|
1294
|
+
metadata: {
|
|
1295
|
+
seeded: true,
|
|
1296
|
+
resize: true
|
|
1297
|
+
}
|
|
1298
|
+
});
|
|
1299
|
+
return;
|
|
1300
|
+
}
|
|
1301
|
+
console.log("[surface] iter", i, "miss: capture", d ? d.w + "x" + d.h : "null", "at bounds", want.w + cw + "x" + (want.h + ch));
|
|
1302
|
+
if (!d) continue;
|
|
1303
|
+
const cur = await race(anyCdp.send("Browser.getWindowForTarget", { targetId: e.activeTargetId }), null);
|
|
1304
|
+
if (cur?.bounds?.width && cur?.bounds?.height) {
|
|
1305
|
+
cw = Math.max(0, cur.bounds.width - d.w);
|
|
1306
|
+
ch = Math.max(0, cur.bounds.height - d.h);
|
|
1307
|
+
} else {
|
|
1308
|
+
cw += Math.max(0, want.w - d.w);
|
|
1309
|
+
ch += Math.max(0, want.h - d.h);
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
e.chrome = {
|
|
1313
|
+
w: cw,
|
|
1314
|
+
h: ch
|
|
1315
|
+
};
|
|
1316
|
+
await race(anyCdp.send("Browser.setWindowBounds", {
|
|
1317
|
+
windowId: win.windowId,
|
|
1318
|
+
bounds: { windowState: "minimized" }
|
|
1319
|
+
}), null);
|
|
1320
|
+
await race(anyCdp.send("Browser.setWindowBounds", {
|
|
1321
|
+
windowId: win.windowId,
|
|
1322
|
+
bounds: { windowState: "normal" }
|
|
1323
|
+
}), null);
|
|
1324
|
+
await race(anyCdp.send("Browser.setWindowBounds", {
|
|
1325
|
+
windowId: win.windowId,
|
|
1326
|
+
bounds: {
|
|
1327
|
+
width: Math.max(200, want.w + cw),
|
|
1328
|
+
height: Math.max(120, want.h + ch),
|
|
1329
|
+
windowState: "normal"
|
|
1330
|
+
}
|
|
1331
|
+
}), null);
|
|
1332
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
1333
|
+
for (let i = 0; i < 10 && !matched(); i += 1) await new Promise((r) => setTimeout(r, 100));
|
|
1334
|
+
if (matched()) return;
|
|
1335
|
+
const parked = {
|
|
1336
|
+
width: Math.max(200, want.w + cw),
|
|
1337
|
+
height: Math.max(120, want.h + ch),
|
|
1338
|
+
windowState: "normal"
|
|
1339
|
+
};
|
|
1340
|
+
await race(anyCdp.send("Browser.setWindowBounds", {
|
|
1341
|
+
windowId: win.windowId,
|
|
1342
|
+
bounds: {
|
|
1343
|
+
...parked,
|
|
1344
|
+
left: 10,
|
|
1345
|
+
top: 10
|
|
1346
|
+
}
|
|
1347
|
+
}), null);
|
|
1348
|
+
await new Promise((r) => setTimeout(r, 400));
|
|
1349
|
+
await race(anyCdp.send("Browser.setWindowBounds", {
|
|
1350
|
+
windowId: win.windowId,
|
|
1351
|
+
bounds: {
|
|
1352
|
+
...parked,
|
|
1353
|
+
left: -32e3,
|
|
1354
|
+
top: -32e3
|
|
1355
|
+
}
|
|
1356
|
+
}), null);
|
|
1357
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
1358
|
+
for (let i = 0; i < 10 && !matched(); i += 1) await new Promise((r) => setTimeout(r, 100));
|
|
1359
|
+
if (matched()) return;
|
|
1360
|
+
console.log("[surface] ladder exhausted: bounce+flash ran, stream frames still", (() => {
|
|
1361
|
+
const d = dimsOf(handle.relay.latestFrame());
|
|
1362
|
+
return d ? d.w + "x" + d.h : "unknown";
|
|
1363
|
+
})());
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
746
1366
|
const cfg = readBrowserConfig();
|
|
747
1367
|
const shot = await handle.cdp.send("Page.captureScreenshot", {
|
|
748
1368
|
format: "jpeg",
|
|
749
1369
|
quality: cfg.screencastQuality
|
|
750
1370
|
}).catch(() => null);
|
|
751
|
-
|
|
1371
|
+
const fd = shot?.data ? dimsOf({
|
|
1372
|
+
jpeg: shot.data,
|
|
1373
|
+
metadata: {}
|
|
1374
|
+
}) : null;
|
|
1375
|
+
console.log("[surface] final reseed capture:", fd ? fd.w + "x" + fd.h : "null", "want", want.w + "x" + want.h);
|
|
1376
|
+
if (shot?.data) handle.relay.reseed({
|
|
752
1377
|
seq: 0,
|
|
753
1378
|
jpeg: shot.data,
|
|
754
1379
|
metadata: {
|
|
@@ -790,6 +1415,44 @@ var BrowserRegistry = class {
|
|
|
790
1415
|
entryCount: hist.entries.length
|
|
791
1416
|
};
|
|
792
1417
|
}
|
|
1418
|
+
/** The FULL navigation history of one tab (long-press dropdown,
|
|
1419
|
+
* operator-approved 2026-10-28): the attached CDP session's
|
|
1420
|
+
* Page.getNavigationHistory, entries as { index, url, title } — index
|
|
1421
|
+
* is the LIST position the UI filters by, id is the CDP entryId the
|
|
1422
|
+
* travel verb needs. Read-only; the same handleOf refusals as navInfo. */
|
|
1423
|
+
async navEntries(id, targetId) {
|
|
1424
|
+
const e = this.peek(id);
|
|
1425
|
+
const h = await this.handleOf(e, targetId);
|
|
1426
|
+
await h.cdp.send("Page.enable").catch(() => void 0);
|
|
1427
|
+
const hist = await h.cdp.send("Page.getNavigationHistory", {});
|
|
1428
|
+
return {
|
|
1429
|
+
currentIndex: hist.currentIndex,
|
|
1430
|
+
entries: hist.entries.map((en, index) => ({
|
|
1431
|
+
index,
|
|
1432
|
+
url: en.url,
|
|
1433
|
+
title: en.title
|
|
1434
|
+
}))
|
|
1435
|
+
};
|
|
1436
|
+
}
|
|
1437
|
+
/** TRUE history travel (long-press dropdown, operator-approved
|
|
1438
|
+
* 2026-10-28): Page.navigateToHistoryEntry to the entry AT the list
|
|
1439
|
+
* position — never a fresh navigate, which would append a new entry
|
|
1440
|
+
* and desync the dropdown from Chrome's own back/forward list. A
|
|
1441
|
+
* position outside the list is an explicit refusal. */
|
|
1442
|
+
async historyTravel(id, index, targetId) {
|
|
1443
|
+
const e = this.peek(id);
|
|
1444
|
+
const h = await this.handleOf(e, targetId);
|
|
1445
|
+
await h.cdp.send("Page.enable").catch(() => void 0);
|
|
1446
|
+
const hist = await h.cdp.send("Page.getNavigationHistory", {});
|
|
1447
|
+
if (!Number.isInteger(index) || index < 0 || index >= hist.entries.length) throw new BrowserRegistryRefusal("NO_TARGET", "HISTORY_INDEX — no entry at index " + index + " of " + hist.entries.length);
|
|
1448
|
+
const loaded = this.waitLoad(h);
|
|
1449
|
+
await h.cdp.send("Page.navigateToHistoryEntry", { entryId: hist.entries[index].id });
|
|
1450
|
+
await loaded;
|
|
1451
|
+
return {
|
|
1452
|
+
currentIndex: index,
|
|
1453
|
+
entryCount: hist.entries.length
|
|
1454
|
+
};
|
|
1455
|
+
}
|
|
793
1456
|
/** Reload the session's active page (ADR-0031 D4): Page.reload keeps
|
|
794
1457
|
* the history entry, unlike re-navigating the current URL. */
|
|
795
1458
|
async reload(id, targetId) {
|
|
@@ -901,12 +1564,15 @@ var BrowserRegistry = class {
|
|
|
901
1564
|
waitLoad: true,
|
|
902
1565
|
targetId
|
|
903
1566
|
});
|
|
1567
|
+
const tabId = mintTabId(e, targetId);
|
|
904
1568
|
if (opts?.activate ?? true) return {
|
|
905
1569
|
...await this.activate(id, targetId),
|
|
906
|
-
targetId
|
|
1570
|
+
targetId,
|
|
1571
|
+
tabId
|
|
907
1572
|
};
|
|
908
1573
|
const h = e.tabs.get(targetId);
|
|
909
1574
|
return {
|
|
1575
|
+
tabId,
|
|
910
1576
|
targetId,
|
|
911
1577
|
seq: h?.relay.latestFrame()?.seq ?? 0,
|
|
912
1578
|
jpeg: h?.relay.latestFrame()?.jpeg ?? null,
|
|
@@ -930,10 +1596,23 @@ var BrowserRegistry = class {
|
|
|
930
1596
|
async applyPanelViewport(e, handle) {
|
|
931
1597
|
await handle.cdp.send("Page.enable").catch(() => void 0);
|
|
932
1598
|
if (e.headless && handle.page) {
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
1599
|
+
const lv = layoutViewport(e.viewport.width, e.viewport.height, e.zoom ?? readBrowserConfig().contentZoom);
|
|
1600
|
+
const resized = handle.page.setViewportSize({
|
|
1601
|
+
width: lv.width,
|
|
1602
|
+
height: lv.height
|
|
1603
|
+
}).catch(() => void 0);
|
|
1604
|
+
await Promise.race([resized, new Promise((res) => setTimeout(res, 2500))]);
|
|
1605
|
+
await Promise.race([resized, new Promise((res) => setTimeout(res, 5e3))]);
|
|
1606
|
+
try {
|
|
1607
|
+
const cdp = await handle.page.context().newCDPSession(handle.page);
|
|
1608
|
+
const dprEval = await Promise.race([handle.page.evaluate("devicePixelRatio").catch(() => 1), new Promise((res) => setTimeout(() => res(1), 500))]);
|
|
1609
|
+
await cdp.send("Emulation.setDeviceMetricsOverride", {
|
|
1610
|
+
width: lv.width,
|
|
1611
|
+
height: lv.height,
|
|
1612
|
+
deviceScaleFactor: typeof dprEval === "number" && dprEval > 0 ? dprEval : 1,
|
|
1613
|
+
mobile: false
|
|
1614
|
+
}).catch(() => void 0);
|
|
1615
|
+
} catch {}
|
|
937
1616
|
return;
|
|
938
1617
|
}
|
|
939
1618
|
const race = async (p, fb) => await Promise.race([p.catch(() => fb), new Promise((res) => setTimeout(() => res(fb), 2500))]);
|
|
@@ -942,25 +1621,35 @@ var BrowserRegistry = class {
|
|
|
942
1621
|
expression: "JSON.stringify({ oh: window.outerHeight - window.innerHeight, ow: window.outerWidth - window.innerWidth })",
|
|
943
1622
|
returnByValue: true
|
|
944
1623
|
}), null);
|
|
945
|
-
let chromeH =
|
|
1624
|
+
let chromeH = e.chrome?.h ?? 0;
|
|
1625
|
+
let chromeW = e.chrome?.w ?? 0;
|
|
946
1626
|
try {
|
|
947
1627
|
const d = JSON.parse(dims && dims.result && dims.result.value || "{}");
|
|
948
|
-
|
|
949
|
-
|
|
1628
|
+
const oh = Number(d.oh) || 0;
|
|
1629
|
+
const ow = Number(d.ow) || 0;
|
|
1630
|
+
if (oh > 0 || ow > 0) {
|
|
1631
|
+
chromeH = oh;
|
|
1632
|
+
chromeW = ow;
|
|
1633
|
+
}
|
|
950
1634
|
} catch {}
|
|
951
1635
|
const anyCdp2 = handle.cdp;
|
|
952
|
-
const win = await
|
|
1636
|
+
const win = await this.windowIdOf(anyCdp2, race);
|
|
953
1637
|
if (win && win.windowId) {
|
|
1638
|
+
const lv = layoutViewport(e.viewport.width, e.viewport.height, e.zoom ?? readBrowserConfig().contentZoom);
|
|
954
1639
|
await race(anyCdp2.send("Browser.setWindowBounds", {
|
|
955
1640
|
windowId: win.windowId,
|
|
956
1641
|
bounds: {
|
|
957
|
-
width: Math.max(200,
|
|
958
|
-
height: Math.max(120,
|
|
1642
|
+
width: Math.max(200, lv.width + chromeW),
|
|
1643
|
+
height: Math.max(120, lv.height + chromeH),
|
|
959
1644
|
windowState: "normal"
|
|
960
1645
|
}
|
|
961
1646
|
}), null);
|
|
1647
|
+
if (chromeW > 0 || chromeH > 0) e.chrome = {
|
|
1648
|
+
w: chromeW,
|
|
1649
|
+
h: chromeH
|
|
1650
|
+
};
|
|
962
1651
|
const check = await race(anyCdp2.send("Browser.getWindowForTarget", { targetId: e.activeTargetId }), null);
|
|
963
|
-
console.log("[window] resized ->", JSON.stringify(check && check.bounds ? check.bounds : check), "want",
|
|
1652
|
+
console.log("[window] resized ->", JSON.stringify(check && check.bounds ? check.bounds : check), "want", lv.width + chromeW, "x", lv.height + chromeH);
|
|
964
1653
|
} else console.log("[window] no windowId — resize skipped (raced out)");
|
|
965
1654
|
await race(anyCdp2.send("Emulation.clearDeviceMetricsOverride"), null);
|
|
966
1655
|
}
|
|
@@ -969,14 +1658,23 @@ var BrowserRegistry = class {
|
|
|
969
1658
|
* to the first surviving tab (re-activated, first-frame contract
|
|
970
1659
|
* re-fired). Closing the LAST tab is refused: the kill ladder owns
|
|
971
1660
|
* session death (the trash is the operator-facing kill). */
|
|
972
|
-
async closeTab(id,
|
|
1661
|
+
async closeTab(id, ref) {
|
|
973
1662
|
const e = this.peek(id);
|
|
974
1663
|
if (e.closed) throw new BrowserRegistryRefusal("CLOSED", "session is closing");
|
|
1664
|
+
let targetId = ref;
|
|
1665
|
+
if (typeof ref === "number" || /^\d+$/.test(ref)) {
|
|
1666
|
+
const r = resolveTab(e, ref);
|
|
1667
|
+
if (!r.ok) throw new BrowserRegistryRefusal("NO_TARGET", r.error);
|
|
1668
|
+
targetId = r.targetId;
|
|
1669
|
+
}
|
|
975
1670
|
if (e.tabs.size <= 1) throw new BrowserRegistryRefusal("NO_TARGET", "cannot close the last tab — use the trash to close the glass");
|
|
976
1671
|
const victim = e.tabs.get(targetId);
|
|
977
1672
|
if (!victim) throw new BrowserRegistryRefusal("NO_TARGET", "no such tab: " + targetId);
|
|
978
1673
|
victim.relay.stop();
|
|
979
1674
|
e.tabs.delete(targetId);
|
|
1675
|
+
retireTabId(e, targetId);
|
|
1676
|
+
const ordIdx = (e.tabOrder ?? []).indexOf(targetId);
|
|
1677
|
+
if (ordIdx !== -1) e.tabOrder.splice(ordIdx, 1);
|
|
980
1678
|
await victim.cdp.send("Target.closeTarget", { targetId }).catch(() => void 0);
|
|
981
1679
|
await victim.cdp.detach().catch(() => void 0);
|
|
982
1680
|
if (e.activeTargetId === targetId) {
|
|
@@ -999,5 +1697,5 @@ var BrowserRegistry = class {
|
|
|
999
1697
|
};
|
|
1000
1698
|
var browserRegistry = new BrowserRegistry();
|
|
1001
1699
|
|
|
1002
|
-
export { BrowserRegistryRefusal as B, browserRegistry as b };
|
|
1003
|
-
//# sourceMappingURL=registry.js-
|
|
1700
|
+
export { BrowserRegistryRefusal as B, browserRegistry as b, clampZoom as c };
|
|
1701
|
+
//# sourceMappingURL=registry.js-CWnRQ4vx.js.map
|