dsh-browser-plus 0.0.0-stage → 0.5.1

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 (89) hide show
  1. package/CHANGELOG.md +153 -0
  2. package/LICENSE +22 -0
  3. package/NOTICE.md +7 -0
  4. package/README.en.md +119 -0
  5. package/README.md +118 -2
  6. package/assets/dsh-browser-plus-256.png +0 -0
  7. package/assets/dsh-browser-plus-512.png +0 -0
  8. package/assets/dsh-browser-plus-small.svg +9 -0
  9. package/assets/dsh-browser-plus.ico +0 -0
  10. package/assets/dsh-browser-plus.svg +11 -0
  11. package/assets/readme-workspace.png +0 -0
  12. package/client/index.js +185 -0
  13. package/cordis.patch.yml +41 -0
  14. package/docs/MIGRATION.md +48 -0
  15. package/docs/README.md +22 -0
  16. package/docs/SOAK-CHECKLIST.md +98 -0
  17. package/docs/architecture.md +88 -0
  18. package/docs/tool-reference.md +126 -0
  19. package/docs/user-guide.md +137 -0
  20. package/docs/why-browser.md +45 -0
  21. package/lib/browser/runtime.d.ts +238 -0
  22. package/lib/browser/runtime.js +330 -0
  23. package/lib/browser/types.d.ts +758 -0
  24. package/lib/browser/types.js +18 -0
  25. package/lib/browser-electron/auth-cookies.d.ts +54 -0
  26. package/lib/browser-electron/auth-cookies.js +83 -0
  27. package/lib/browser-electron/chrome-state.d.ts +211 -0
  28. package/lib/browser-electron/chrome-state.js +12 -0
  29. package/lib/browser-electron/entry.d.ts +73 -0
  30. package/lib/browser-electron/entry.js +65 -0
  31. package/lib/browser-electron/fingerprint.d.ts +29 -0
  32. package/lib/browser-electron/fingerprint.js +42 -0
  33. package/lib/browser-electron/host-main.d.ts +19 -0
  34. package/lib/browser-electron/host-main.js +2691 -0
  35. package/lib/browser-electron/icon.d.ts +11 -0
  36. package/lib/browser-electron/icon.js +23 -0
  37. package/lib/browser-electron/page-chrome.d.ts +21 -0
  38. package/lib/browser-electron/page-chrome.js +2269 -0
  39. package/lib/browser-electron/provider.d.ts +767 -0
  40. package/lib/browser-electron/provider.js +2825 -0
  41. package/lib/browser-electron/remote-host.d.ts +145 -0
  42. package/lib/browser-electron/remote-host.js +993 -0
  43. package/lib/browser-electron/task-summary.d.ts +2 -0
  44. package/lib/browser-electron/task-summary.js +12 -0
  45. package/lib/browser-electron/task-thumbnail.d.ts +11 -0
  46. package/lib/browser-electron/task-thumbnail.js +9 -0
  47. package/lib/browser-electron/write-guard.d.ts +41 -0
  48. package/lib/browser-electron/write-guard.js +123 -0
  49. package/lib/client.js +185 -0
  50. package/lib/command-browser/index.d.ts +20 -0
  51. package/lib/command-browser/index.js +35 -0
  52. package/lib/http-browser/index.d.ts +28 -0
  53. package/lib/http-browser/index.js +110 -0
  54. package/lib/index.d.ts +27 -0
  55. package/lib/index.js +25 -0
  56. package/lib/task-todos/index.d.ts +25 -0
  57. package/lib/task-todos/index.js +100 -0
  58. package/lib/tool-browser/index.d.ts +31 -0
  59. package/lib/tool-browser/index.js +2026 -0
  60. package/package.json +120 -4
  61. package/screenshots.json +3 -0
  62. package/scripts/build-client.mjs +20 -0
  63. package/scripts/build-icons.mjs +80 -0
  64. package/scripts/capture-window.ps1 +79 -0
  65. package/scripts/crop-image.ps1 +20 -0
  66. package/scripts/smoke-browser-tools.mjs +2051 -0
  67. package/scripts/smoke-chrome-world.mjs +65 -0
  68. package/scripts/smoke-electron-host.mjs +50 -0
  69. package/scripts/test-orb-drag.mjs +83 -0
  70. package/src/browser/runtime.ts +506 -0
  71. package/src/browser/types.ts +741 -0
  72. package/src/browser-electron/auth-cookies.ts +125 -0
  73. package/src/browser-electron/chrome-state.ts +192 -0
  74. package/src/browser-electron/entry.ts +125 -0
  75. package/src/browser-electron/fingerprint.ts +45 -0
  76. package/src/browser-electron/host-main.ts +2526 -0
  77. package/src/browser-electron/icon.ts +26 -0
  78. package/src/browser-electron/page-chrome.ts +2281 -0
  79. package/src/browser-electron/provider.ts +3366 -0
  80. package/src/browser-electron/remote-host.ts +1051 -0
  81. package/src/browser-electron/task-summary.ts +10 -0
  82. package/src/browser-electron/task-thumbnail.ts +17 -0
  83. package/src/browser-electron/write-guard.ts +134 -0
  84. package/src/command-browser/index.ts +61 -0
  85. package/src/http-browser/index.ts +139 -0
  86. package/src/index.ts +65 -0
  87. package/src/task-todos/index.ts +114 -0
  88. package/src/tool-browser/index.ts +2071 -0
  89. package/src/types/electron-shim.d.ts +143 -0
package/package.json CHANGED
@@ -1,6 +1,122 @@
1
1
  {
2
2
  "name": "dsh-browser-plus",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.5.1",
4
+ "description": "Enhanced shared browser for DeepSeek Harness: one visible window with isolated task views, a page task manager, non-stealing background tasks, built-in toolbar and operation trail, and pinned Electron 42.9.3 compositing.",
5
+ "type": "module",
6
+ "main": "lib/index.js",
7
+ "types": "lib/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./lib/index.d.ts",
11
+ "default": "./lib/index.js"
12
+ },
13
+ "./browser": {
14
+ "types": "./lib/browser/runtime.d.ts",
15
+ "default": "./lib/browser/runtime.js"
16
+ },
17
+ "./browser-electron": {
18
+ "types": "./lib/browser-electron/entry.d.ts",
19
+ "default": "./lib/browser-electron/entry.js"
20
+ },
21
+ "./tool-browser": {
22
+ "types": "./lib/tool-browser/index.d.ts",
23
+ "default": "./lib/tool-browser/index.js"
24
+ },
25
+ "./command-browser": {
26
+ "types": "./lib/command-browser/index.d.ts",
27
+ "default": "./lib/command-browser/index.js"
28
+ },
29
+ "./http-browser": {
30
+ "types": "./lib/http-browser/index.d.ts",
31
+ "default": "./lib/http-browser/index.js"
32
+ },
33
+ "./task-todos": {
34
+ "types": "./lib/task-todos/index.d.ts",
35
+ "default": "./lib/task-todos/index.js"
36
+ },
37
+ "./client": {
38
+ "default": "./lib/client.js"
39
+ },
40
+ "./host-main": {
41
+ "default": "./lib/browser-electron/host-main.js"
42
+ },
43
+ "./cordis.patch.yml": "./cordis.patch.yml",
44
+ "./package.json": "./package.json"
45
+ },
46
+ "files": [
47
+ "lib",
48
+ "src",
49
+ "client",
50
+ "docs",
51
+ "assets",
52
+ "scripts",
53
+ "screenshots.json",
54
+ "cordis.patch.yml",
55
+ "README.md",
56
+ "README.en.md",
57
+ "CHANGELOG.md",
58
+ "LICENSE",
59
+ "NOTICE.md"
60
+ ],
61
+ "scripts": {
62
+ "build": "npm run build:icons && tsc -p tsconfig.json && npm run build:client",
63
+ "build:icons": "node scripts/build-icons.mjs",
64
+ "build:client": "node scripts/build-client.mjs",
65
+ "pretest": "tsc -p tsconfig.json",
66
+ "test": "node --test test/*.test.mjs",
67
+ "test:only": "node --test test/*.test.mjs",
68
+ "lint": "eslint src",
69
+ "smoke:electron-host": "node scripts/smoke-electron-host.mjs",
70
+ "smoke:browser-tools": "node scripts/smoke-browser-tools.mjs",
71
+ "smoke:chrome-world": "node scripts/smoke-chrome-world.mjs",
72
+ "test:orb-drag": "node scripts/test-orb-drag.mjs",
73
+ "prepack": "npm run build"
74
+ },
75
+ "keywords": [
76
+ "dsh-plugin",
77
+ "deepseek-harness",
78
+ "browser",
79
+ "cdp"
80
+ ],
81
+ "license": "MIT",
82
+ "repository": {
83
+ "type": "git",
84
+ "url": "https://github.com/ParticleLight/dsh-browser-plus.git"
85
+ },
86
+ "homepage": "https://github.com/ParticleLight/dsh-browser-plus",
87
+ "bugs": {
88
+ "url": "https://github.com/ParticleLight/dsh-browser-plus/issues"
89
+ },
90
+ "engines": {
91
+ "node": ">=22.19"
92
+ },
93
+ "peerDependencies": {
94
+ "@deepseek-ai/cordis": "^4.0.1",
95
+ "@deepseek-ai/dsh-llm": "^0.1.0-rc.1 || ^0.2.0-rc.1",
96
+ "@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.1 || ^0.2.0-rc.1",
97
+ "@deepseek-ai/dsh-tools": "^0.1.0-rc.1 || ^0.2.0-rc.1",
98
+ "@deepseek-ai/schemastery": "^3.18.1"
99
+ },
100
+ "devDependencies": {
101
+ "@resvg/resvg-js": "^2.6.2",
102
+ "@types/node": "^22.0.0",
103
+ "eslint": "^10.11.0",
104
+ "typescript": "^5.5.0",
105
+ "typescript-eslint": "^8.71.0"
106
+ },
107
+ "dsh": {
108
+ "bundle": {
109
+ "patch": "./cordis.patch.yml"
110
+ },
111
+ "client": {
112
+ "inject": [
113
+ "@deepseek-ai/dsh-client-ui-slots",
114
+ "@deepseek-ai/dsh-client-ui-sidebar-right"
115
+ ],
116
+ "platform": "web"
117
+ }
118
+ },
119
+ "optionalDependencies": {
120
+ "electron": "42.9.3"
121
+ }
122
+ }
@@ -0,0 +1,3 @@
1
+ [
2
+ "assets/readme-workspace.png"
3
+ ]
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Build the browser bundle: copy the single-file client source to lib/client.js.
3
+ *
4
+ * The client-modules loader serves the package's \`./client\` export, and the
5
+ * bundle is one file that only requires platform modules (React), so there is
6
+ * nothing to bundle or minify — the copy keeps the shipped artifact identical to
7
+ * the reviewed source. Run by \`npm run build\` and by \`prepack\`.
8
+ */
9
+ import { mkdir, readFile, writeFile } from 'node:fs/promises'
10
+
11
+ const source = new URL('../client/index.js', import.meta.url)
12
+ const output = new URL('../lib/client.js', import.meta.url)
13
+
14
+ const body = await readFile(source, 'utf8')
15
+ if (!body.includes('__ModuleLoader__.load')) {
16
+ throw new Error('client/index.js must register through window.__ModuleLoader__.load')
17
+ }
18
+ await mkdir(new URL('../lib/', import.meta.url), { recursive: true })
19
+ await writeFile(output, body)
20
+ process.stdout.write('built lib/client.js (' + String(body.length) + ' bytes)\n')
@@ -0,0 +1,80 @@
1
+ // Browser icon derivatives builder.
2
+ // Reads assets/dsh-browser-plus.svg (plus the -small variant for the sizes where the
3
+ // title-bar dots would be sub-pixel), renders 256/512 PNGs and an ICO
4
+ // (16/32/48/256 PNG entries) with @resvg/resvg-js, writes them to assets/.
5
+
6
+ import { readFile, writeFile, mkdir } from 'node:fs/promises'
7
+ import { existsSync } from 'node:fs'
8
+ import path from 'node:path'
9
+ import { fileURLToPath } from 'node:url'
10
+ import { Resvg } from '@resvg/resvg-js'
11
+
12
+ const root = path.dirname(path.dirname(fileURLToPath(import.meta.url)))
13
+ const assetsDir = path.join(root, 'assets')
14
+ const svgPath = path.join(assetsDir, 'dsh-browser-plus.svg')
15
+
16
+ const smallSvgPath = path.join(assetsDir, 'dsh-browser-plus-small.svg')
17
+
18
+ async function readSvg(file, label) {
19
+ try {
20
+ return await readFile(file, 'utf8')
21
+ } catch (error) {
22
+ console.error(`build-icons: cannot read ${label} at ${file}: ${error.message}`)
23
+ process.exit(1)
24
+ }
25
+ }
26
+
27
+ const svg = await readSvg(svgPath, 'source SVG')
28
+ // Falls back to the full-detail art, so a missing variant degrades instead of failing.
29
+ const smallSvg = existsSync(smallSvgPath) ? await readSvg(smallSvgPath, 'small SVG') : svg
30
+
31
+ function renderPng(size, source = svg) {
32
+ const data = new Resvg(source, { fitTo: { mode: 'width', value: size } }).render()
33
+ const buffer = data.asPng()
34
+ if (!buffer || buffer.length === 0) {
35
+ throw new Error(`build-icons: empty render at ${size}px`)
36
+ }
37
+ return buffer
38
+ }
39
+
40
+ function buildIco(images) {
41
+ const header = Buffer.alloc(6)
42
+ header.writeUInt16LE(0, 0)
43
+ header.writeUInt16LE(1, 2)
44
+ header.writeUInt16LE(images.length, 4)
45
+ const entries = Buffer.alloc(images.length * 16)
46
+ let offset = 6 + entries.length
47
+ for (let i = 0; i < images.length; i++) {
48
+ const image = images[i]
49
+ const size = image.size >= 256 ? 0 : image.size
50
+ entries.writeUInt8(size, i * 16)
51
+ entries.writeUInt8(size, i * 16 + 1)
52
+ entries.writeUInt8(0, i * 16 + 2)
53
+ entries.writeUInt8(0, i * 16 + 3)
54
+ entries.writeUInt16LE(1, i * 16 + 4)
55
+ entries.writeUInt16LE(32, i * 16 + 6)
56
+ entries.writeUInt32LE(image.bytes.length, i * 16 + 8)
57
+ entries.writeUInt32LE(offset, i * 16 + 12)
58
+ offset += image.bytes.length
59
+ }
60
+ return Buffer.concat([header, entries, ...images.map((image) => image.bytes)])
61
+ }
62
+
63
+ await mkdir(assetsDir, { recursive: true })
64
+
65
+ const png256 = renderPng(256)
66
+ const png512 = renderPng(512)
67
+ const ico = buildIco([
68
+ { size: 16, bytes: renderPng(16, smallSvg) },
69
+ { size: 32, bytes: renderPng(32, smallSvg) },
70
+ { size: 48, bytes: renderPng(48) },
71
+ { size: 256, bytes: png256 },
72
+ ])
73
+
74
+ await writeFile(path.join(assetsDir, 'dsh-browser-plus-256.png'), png256)
75
+ await writeFile(path.join(assetsDir, 'dsh-browser-plus-512.png'), png512)
76
+ await writeFile(path.join(assetsDir, 'dsh-browser-plus.ico'), ico)
77
+
78
+ console.log(`assets/dsh-browser-plus-256.png ${png256.length} bytes`)
79
+ console.log(`assets/dsh-browser-plus-512.png ${png512.length} bytes`)
80
+ console.log(`assets/dsh-browser-plus.ico ${ico.length} bytes`)
@@ -0,0 +1,79 @@
1
+ param([string]$Out = 'F:\deepseekharness\dsh-browser-plus\.tmp\window.png', [long]$Hwnd = 0)
2
+ Add-Type -AssemblyName System.Drawing
3
+ Add-Type @"
4
+ using System;
5
+ using System.Runtime.InteropServices;
6
+ public class CapFg {
7
+ [DllImport("user32.dll")] public static extern bool SetProcessDPIAware();
8
+ [DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr h, out RECT r);
9
+ [DllImport("user32.dll")] public static extern bool SetWindowPos(IntPtr h, IntPtr after, int x, int y, int cx, int cy, uint flags);
10
+ [DllImport("user32.dll")] public static extern bool ShowWindow(IntPtr h, int n);
11
+ [DllImport("user32.dll")] public static extern bool IsIconic(IntPtr h);
12
+ [DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr h);
13
+ [DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow();
14
+ [DllImport("user32.dll")] public static extern bool BringWindowToTop(IntPtr h);
15
+ [DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(IntPtr h, IntPtr pid);
16
+ [DllImport("user32.dll")] public static extern bool AttachThreadInput(uint a, uint b, bool f);
17
+ [DllImport("kernel32.dll")] public static extern uint GetCurrentThreadId();
18
+ [StructLayout(LayoutKind.Sequential)] public struct RECT { public int Left, Top, Right, Bottom; }
19
+ public static void Force(IntPtr h) {
20
+ uint fg = GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero);
21
+ uint me = GetCurrentThreadId();
22
+ AttachThreadInput(me, fg, true);
23
+ BringWindowToTop(h);
24
+ SetForegroundWindow(h);
25
+ AttachThreadInput(me, fg, false);
26
+ }
27
+ }
28
+ "@
29
+ [void][CapFg]::SetProcessDPIAware()
30
+ # An explicit handle wins: Electron can own several top-level windows and
31
+ # MainWindowHandle sometimes picks a helper instead of the browser window.
32
+ $h = if ($Hwnd -ne 0) { [IntPtr]$Hwnd } else { 0 }
33
+ $p = $null
34
+ if ($Hwnd -eq 0) {
35
+ # Pick the LARGEST top-level window owned by an electron process with our title.
36
+ # MainWindowHandle is unreliable here: Electron owns several top-level windows and
37
+ # Windows happily reports a small helper, which silently screenshots the wrong thing.
38
+ $p = Get-Process electron -ErrorAction SilentlyContinue | Where-Object { $_.MainWindowTitle -like '*dsh-browser-plus*' -and $_.MainWindowHandle -ne 0 } | Select-Object -First 1
39
+ if (-not $p) { Write-Output 'NO WINDOW'; exit 1 }
40
+ $best = [IntPtr]::Zero
41
+ $bestArea = 0
42
+ foreach ($proc in $p) {
43
+ foreach ($h in @($proc.MainWindowHandle)) {
44
+ if ($h -eq [IntPtr]::Zero) { continue }
45
+ $rect = New-Object CapFg+RECT
46
+ if (-not [CapFg]::GetWindowRect($h, [ref]$rect)) { continue }
47
+ $area = ($rect.Right - $rect.Left) * ($rect.Bottom - $rect.Top)
48
+ if ($area -gt $bestArea) { $bestArea = $area; $best = $h }
49
+ }
50
+ }
51
+ if ($best -eq [IntPtr]::Zero) { Write-Output 'NO WINDOW'; exit 1 }
52
+ $h = $best
53
+ }
54
+ [void][CapFg]::ShowWindow($h, 9)
55
+ Start-Sleep -Milliseconds 500
56
+ [CapFg]::Force($h)
57
+ [void][CapFg]::SetWindowPos($h, [IntPtr](-1), 0, 0, 0, 0, 0x0003 -bor 0x0040)
58
+ Start-Sleep -Milliseconds 1500
59
+ $r = New-Object CapFg+RECT
60
+ [void][CapFg]::GetWindowRect($h, [ref]$r)
61
+ $cw = $r.Right - $r.Left; $ch = $r.Bottom - $r.Top
62
+ $fg = [CapFg]::GetForegroundWindow()
63
+ Write-Output ("hwnd=" + $h + " fg=" + $fg + " same=" + ($fg -eq $h) + " rect=" + $cw + "x" + $ch)
64
+ $bmp = New-Object System.Drawing.Bitmap($cw, $ch)
65
+ $g = [System.Drawing.Graphics]::FromImage($bmp)
66
+ $g.CopyFromScreen($r.Left, $r.Top, 0, 0, (New-Object System.Drawing.Size($cw, $ch)))
67
+ $dir = Split-Path $Out
68
+ if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Force -Path $dir | Out-Null }
69
+ $bmp.Save($Out, [System.Drawing.Imaging.ImageFormat]::Png)
70
+ $g.Dispose(); $bmp.Dispose()
71
+ [void][CapFg]::SetWindowPos($h, [IntPtr](-2), 0, 0, 0, 0, 0x0003)
72
+ # The foreground dance above can leave the window minimised (seen 2026-10-02: the
73
+ # capture reported 276x45 = the iconic rect and the user's browser stayed in the
74
+ # taskbar). Put it back the way we found it before we hand control back.
75
+ if ([CapFg]::IsIconic($h)) {
76
+ [void][CapFg]::ShowWindow($h, 9)
77
+ Start-Sleep -Milliseconds 500
78
+ }
79
+ Write-Output ("saved " + $Out + " iconicAfter=" + [CapFg]::IsIconic($h))
@@ -0,0 +1,20 @@
1
+ <#
2
+ .SYNOPSIS
3
+ Crop a region out of a PNG (no image library, System.Drawing only).
4
+
5
+ .DESCRIPTION
6
+ Used with scripts/capture-window.ps1 to look at the chrome band at full
7
+ resolution: a downscaled full-window capture cannot show whether a tab icon
8
+ actually rendered. Omit -W/-H to crop to the bottom-right corner.
9
+ #>
10
+ param([string]$In, [string]$Out, [int]$X=0, [int]$Y=0, [int]$W=0, [int]$H=0)
11
+ Add-Type -AssemblyName System.Drawing
12
+ $src = [System.Drawing.Image]::FromFile($In)
13
+ if ($W -le 0) { $W = $src.Width - $X }
14
+ if ($H -le 0) { $H = $src.Height - $Y }
15
+ $dst = New-Object System.Drawing.Bitmap($W, $H)
16
+ $g = [System.Drawing.Graphics]::FromImage($dst)
17
+ $g.DrawImage($src, (New-Object System.Drawing.Rectangle(0,0,$W,$H)), (New-Object System.Drawing.Rectangle($X,$Y,$W,$H)), [System.Drawing.GraphicsUnit]::Pixel)
18
+ $dst.Save($Out, [System.Drawing.Imaging.ImageFormat]::Png)
19
+ $g.Dispose(); $dst.Dispose(); $src.Dispose()
20
+ Write-Output "cropped $Out $W x $H"